Agent skill: chart generation (#1103)

* WIP agent support

* move agent folder

* wip frontend socket

* checkpoint

* fix schema

* Checkpoint for plugins and AgentHandler

* refactor plugins and agent arch

* agent error reporting and handling

* add frontend elements for agents in prompt input

* WIP integrations for agents

* enable web-search agent config from frontend

* persist chat history

* update alert

* update migration
remove console logs
update close state for agent invocations

* add examples to dockerignore
Extract statusResponse to its own component

* update close method

* wrap scraping rejections

* add RAG search as funciton

* Add telem and link highlight

* chat support

* patch memory

* Add rechart as a plugin option

* Toggles for abilites of default agent (system wide)
Validate values for agent skills
Enable dynamic loading of skills
UI for toggle of skills

* add UI for toggle of configs for agent

* toggle WS or WSS protocol

* update NGNIX proxy pass

* move components around and capture failed websocket creation

* fix name

* tmp docker image

* reset workflow

* safety mark functions

* telem on tool calls

* remove hardcode short circuit

* separate web-browser from scrape

* extract summarizer to util
add abort handlers and controller for langchain stuff so socket close kills process

* langchain summarize verbose when in dev

* chart styling improvements + add title to chart

* fix legend from being cutoff in chart downloads

* remove cursor blink

---------

Co-authored-by: shatfield4 <seanhatfield5@gmail.com>
This commit is contained in:
Timothy Carambat 2024-04-26 11:18:55 -07:00 committed by GitHub
parent 331423f864
commit 2e813846dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1361 additions and 7 deletions

View File

@ -5,7 +5,10 @@
"aibitat",
"anythingllm",
"Astra",
"Chartable",
"comkey",
"cooldown",
"cooldowns",
"Deduplicator",
"Dockerized",
"Embeddable",

View File

@ -14,6 +14,7 @@
"@metamask/jazzicon": "^2.0.0",
"@microsoft/fetch-event-source": "^2.0.1",
"@phosphor-icons/react": "^2.0.13",
"@tremor/react": "^3.15.1",
"dompurify": "^3.0.8",
"file-saver": "^2.0.5",
"he": "^1.2.0",
@ -30,6 +31,8 @@
"react-tag-input-component": "^2.0.2",
"react-toastify": "^9.1.3",
"react-tooltip": "^5.25.2",
"recharts": "^2.12.5",
"recharts-to-png": "^2.3.1",
"text-case": "^1.0.9",
"truncate": "^3.0.0",
"uuid": "^9.0.0"

View File

@ -0,0 +1,50 @@
export default function CustomCell({ ...props }) {
const {
root,
depth,
x,
y,
width,
height,
index,
payload,
colors,
rank,
name,
} = props;
return (
<g>
<rect
x={x}
y={y}
width={width}
height={height}
style={{
fill:
depth < 2
? colors[Math.floor((index / root.children.length) * 6)]
: "#ffffff00",
stroke: "#fff",
strokeWidth: 2 / (depth + 1e-10),
strokeOpacity: 1 / (depth + 1e-10),
}}
/>
{depth === 1 ? (
<text
x={x + width / 2}
y={y + height / 2 + 7}
textAnchor="middle"
fill="#fff"
fontSize={14}
>
{name}
</text>
) : null}
{depth === 1 ? (
<text x={x + 4} y={y + 18} fill="#fff" fontSize={16} fillOpacity={0.9}>
{index + 1}
</text>
) : null}
</g>
);
}

View File

@ -0,0 +1,89 @@
import { Tooltip as RechartsTooltip } from "recharts";
// Given a hex, convert to the opposite highest-contrast color
// and if `bw` is enabled, force it to be black/white to normalize
// interface.
function invertColor(hex, bw) {
if (hex.indexOf("#") === 0) {
hex = hex.slice(1);
}
// convert 3-digit hex to 6-digits.
if (hex.length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
if (hex.length !== 6) {
throw new Error("Invalid HEX color.");
}
var r = parseInt(hex.slice(0, 2), 16),
g = parseInt(hex.slice(2, 4), 16),
b = parseInt(hex.slice(4, 6), 16);
if (bw) {
// https://stackoverflow.com/a/3943023/112731
return r * 0.299 + g * 0.587 + b * 0.114 > 186 ? "#FFFFFF" : "#000000";
// : '#FFFFFF';
}
// invert color components
r = (255 - r).toString(16);
g = (255 - g).toString(16);
b = (255 - b).toString(16);
// pad each with zeros and return
return "#" + padZero(r) + padZero(g) + padZero(b);
}
function padZero(str, len) {
len = len || 2;
var zeros = new Array(len).join("0");
return (zeros + str).slice(-len);
}
export default function Tooltip({ legendColor, ...props }) {
return (
<RechartsTooltip
wrapperStyle={{ outline: "none" }}
isAnimationActive={false}
cursor={{ fill: "#d1d5db", opacity: "0.15" }}
position={{ y: 0 }}
{...props}
content={({ active, payload, label }) => {
return active && payload ? (
<div className="bg-white text-sm rounded-md border shadow-lg">
<div className="border-b py-2 px-4">
<p className="text-elem text-gray-700 font-medium">{label}</p>
</div>
<div className="space-y-1 py-2 px-4">
{payload.map(({ value, name }, idx) => (
<div
key={`id-${idx}`}
className="flex items-center justify-between space-x-8"
>
<div className="flex items-center space-x-2">
<span
className="shrink-0 h-3 w-3 border-white rounded-md rounded-full border-2 shadow-md"
style={{ backgroundColor: legendColor }}
/>
<p
style={{
color: invertColor(legendColor, true),
}}
className="font-medium tabular-nums text-right whitespace-nowrap"
>
{value}
</p>
</div>
<p
style={{
color: invertColor(legendColor, true),
}}
className="whitespace-nowrap font-normal"
>
{name}
</p>
</div>
))}
</div>
</div>
) : null;
}}
/>
);
}

View File

@ -0,0 +1,98 @@
export const Colors = {
blue: "#3b82f6",
sky: "#0ea5e9",
cyan: "#06b6d4",
teal: "#14b8a6",
emerald: "#10b981",
green: "#22c55e",
lime: "#84cc16",
yellow: "#eab308",
amber: "#f59e0b",
orange: "#f97316",
red: "#ef4444",
rose: "#f43f5e",
pink: "#ec4899",
fuchsia: "#d946ef",
purple: "#a855f7",
violet: "#8b5cf6",
indigo: "#6366f1",
neutral: "#737373",
stone: "#78716c",
gray: "#6b7280",
slate: "#64748b",
zinc: "#71717a",
};
export function getTremorColor(color) {
switch (color) {
case "blue":
return Colors.blue;
case "sky":
return Colors.sky;
case "cyan":
return Colors.cyan;
case "teal":
return Colors.teal;
case "emerald":
return Colors.emerald;
case "green":
return Colors.green;
case "lime":
return Colors.lime;
case "yellow":
return Colors.yellow;
case "amber":
return Colors.amber;
case "orange":
return Colors.orange;
case "red":
return Colors.red;
case "rose":
return Colors.rose;
case "pink":
return Colors.pink;
case "fuchsia":
return Colors.fuchsia;
case "purple":
return Colors.purple;
case "violet":
return Colors.violet;
case "indigo":
return Colors.indigo;
case "neutral":
return Colors.neutral;
case "stone":
return Colors.stone;
case "gray":
return Colors.gray;
case "slate":
return Colors.slate;
case "zinc":
return Colors.zinc;
}
}
export const themeColorRange = [
"slate",
"gray",
"zinc",
"neutral",
"stone",
"red",
"orange",
"amber",
"yellow",
"lime",
"green",
"emerald",
"teal",
"cyan",
"sky",
"blue",
"indigo",
"violet",
"purple",
"fuchsia",
"pink",
"rose",
];

View File

@ -0,0 +1,467 @@
import { v4 } from "uuid";
import {
AreaChart,
BarChart,
DonutChart,
Legend,
LineChart,
} from "@tremor/react";
import {
Bar,
CartesianGrid,
ComposedChart,
Funnel,
FunnelChart,
Line,
PolarAngleAxis,
PolarGrid,
PolarRadiusAxis,
Radar,
RadarChart,
RadialBar,
RadialBarChart,
Scatter,
ScatterChart,
Treemap,
XAxis,
YAxis,
} from "recharts";
import { Colors, getTremorColor } from "./chart-utils.js";
import CustomCell from "./CustomCell.jsx";
import Tooltip from "./CustomTooltip.jsx";
import { safeJsonParse } from "@/utils/request.js";
import renderMarkdown from "@/utils/chat/markdown.js";
import { WorkspaceProfileImage } from "../PromptReply/index.jsx";
import { memo, useCallback, useState } from "react";
import { saveAs } from "file-saver";
import { useGenerateImage } from "recharts-to-png";
import { CircleNotch, DownloadSimple } from "@phosphor-icons/react";
const dataFormatter = (number) => {
return Intl.NumberFormat("us").format(number).toString();
};
export function Chartable({ props, workspace }) {
const [getDivJpeg, { ref }] = useGenerateImage({
quality: 1,
type: "image/jpeg",
options: {
backgroundColor: "#393d43",
padding: 20,
},
});
const handleDownload = useCallback(async () => {
const jpeg = await getDivJpeg();
if (jpeg) saveAs(jpeg, `chart-${v4().split("-")[0]}.jpg`);
}, []);
const color = null;
const showLegend = true;
const content =
typeof props.content === "string"
? safeJsonParse(props.content, null)
: props.content;
if (content === null) return null;
const chartType = content?.type?.toLowerCase();
const data =
typeof content.dataset === "string"
? safeJsonParse(content.dataset, null)
: content.dataset;
const value = data.length > 0 ? Object.keys(data[0])[1] : "value";
const title = content?.title;
const renderChart = () => {
switch (chartType) {
case "area":
return (
<div className="bg-zinc-900 p-8 rounded-xl text-white">
<h3 className="text-lg font-medium">{title}</h3>
<AreaChart
className="h-[350px]"
data={data}
index="name"
categories={[value]}
colors={[color || "blue", "cyan"]}
showLegend={showLegend}
valueFormatter={dataFormatter}
/>
</div>
);
case "bar":
return (
<div className="bg-zinc-900 p-8 rounded-xl text-white">
<h3 className="text-lg font-medium">{title}</h3>
<BarChart
className="h-[350px]"
data={data}
index="name"
categories={[value]}
colors={[color || "blue"]}
showLegend={showLegend}
valueFormatter={dataFormatter}
layout={"vertical"}
yAxisWidth={100}
/>
</div>
);
case "line":
return (
<div className="bg-zinc-900 p-8 pb-12 rounded-xl text-white h-[500px]">
<h3 className="text-lg font-medium">{title}</h3>
<LineChart
className="h-[400px]"
data={data}
index="name"
categories={[value]}
colors={[color || "blue"]}
showLegend={showLegend}
valueFormatter={dataFormatter}
/>
</div>
);
case "composed":
return (
<div className="bg-zinc-900 p-8 rounded-xl text-white">
<h3 className="text-lg font-medium">{title}</h3>
{showLegend && (
<Legend
categories={[value]}
colors={[color || "blue", color || "blue"]}
className="mb-5 justify-end"
/>
)}
<ComposedChart width={500} height={260} data={data}>
<CartesianGrid
strokeDasharray="3 3"
horizontal
vertical={false}
/>
<XAxis
dataKey="name"
tickLine={false}
axisLine={false}
interval="preserveStartEnd"
tick={{ transform: "translate(0, 6)", fill: "white" }}
style={{
fontSize: "12px",
fontFamily: "Inter; Helvetica",
}}
padding={{ left: 10, right: 10 }}
/>
<YAxis
tickLine={false}
axisLine={false}
type="number"
tick={{ transform: "translate(-3, 0)", fill: "white" }}
style={{
fontSize: "12px",
fontFamily: "Inter; Helvetica",
}}
/>
<Tooltip legendColor={getTremorColor(color || "blue")} />
<Line
type="linear"
dataKey={value}
stroke={getTremorColor(color || "blue")}
dot={false}
strokeWidth={2}
/>
<Bar
dataKey="value"
name="value"
type="linear"
fill={getTremorColor(color || "blue")}
/>
</ComposedChart>
</div>
);
case "scatter":
return (
<div className="bg-zinc-900 p-8 rounded-xl text-white">
<h3 className="text-lg font-medium">{title}</h3>
{showLegend && (
<div className="flex justify-end">
<Legend
categories={[value]}
colors={[color || "blue", color || "blue"]}
className="mb-5"
/>
</div>
)}
<ScatterChart width={500} height={260} data={data}>
<CartesianGrid
strokeDasharray="3 3"
horizontal
vertical={false}
/>
<XAxis
dataKey="name"
tickLine={false}
axisLine={false}
interval="preserveStartEnd"
tick={{ transform: "translate(0, 6)", fill: "white" }}
style={{
fontSize: "12px",
fontFamily: "Inter; Helvetica",
}}
padding={{ left: 10, right: 10 }}
/>
<YAxis
tickLine={false}
axisLine={false}
type="number"
tick={{ transform: "translate(-3, 0)", fill: "white" }}
style={{
fontSize: "12px",
fontFamily: "Inter; Helvetica",
}}
/>
<Tooltip legendColor={getTremorColor(color || "blue")} />
<Scatter dataKey={value} fill={getTremorColor(color || "blue")} />
</ScatterChart>
</div>
);
case "pie":
return (
<div className="bg-zinc-900 p-8 rounded-xl text-white">
<h3 className="text-lg font-medium">{title}</h3>
<DonutChart
data={data}
category={value}
index="name"
colors={[
color || "cyan",
"violet",
"rose",
"amber",
"emerald",
"teal",
"fuchsia",
]}
// No actual legend for pie chart, but this will toggle the central text
showLabel={showLegend}
valueFormatter={dataFormatter}
customTooltip={customTooltip}
/>
</div>
);
case "radar":
return (
<div className="bg-zinc-900 p-8 rounded-xl text-white">
<h3 className="text-lg font-medium">{title}</h3>
{showLegend && (
<div className="flex justify-end">
<Legend
categories={[value]}
colors={[color || "blue", color || "blue"]}
className="mb-5"
/>
</div>
)}
<RadarChart
cx={300}
cy={250}
outerRadius={150}
width={600}
height={500}
data={data}
>
<PolarGrid />
<PolarAngleAxis dataKey="name" tick={{ fill: "white" }} />
<PolarRadiusAxis tick={{ fill: "white" }} />
<Tooltip legendColor={getTremorColor(color || "blue")} />
<Radar
dataKey="value"
stroke={getTremorColor(color || "blue")}
fill={getTremorColor(color || "blue")}
fillOpacity={0.6}
/>
</RadarChart>
</div>
);
case "radialbar":
return (
<div className="bg-zinc-900 p-8 rounded-xl text-white">
<h3 className="text-lg font-medium">{title}</h3>
{showLegend && (
<div className="flex justify-end">
<Legend
categories={[value]}
colors={[color || "blue", color || "blue"]}
className="mb-5"
/>
</div>
)}
<RadialBarChart
width={500}
height={300}
cx={150}
cy={150}
innerRadius={20}
outerRadius={140}
barSize={10}
data={data}
>
<RadialBar
angleAxisId={15}
label={{
position: "insideStart",
fill: getTremorColor(color || "blue"),
}}
dataKey="value"
/>
<Tooltip legendColor={getTremorColor(color || "blue")} />
</RadialBarChart>
</div>
);
case "treemap":
return (
<div className="bg-zinc-900 p-8 rounded-xl text-white">
<h3 className="text-lg font-medium">{title}</h3>
{showLegend && (
<div className="flex justify-end">
<Legend
categories={[value]}
colors={[color || "blue", color || "blue"]}
className="mb-5"
/>
</div>
)}
<Treemap
width={500}
height={260}
data={data}
dataKey="value"
stroke="#fff"
fill={getTremorColor(color || "blue")}
content={<CustomCell colors={Object.values(Colors)} />}
>
<Tooltip legendColor={getTremorColor(color || "blue")} />
</Treemap>
</div>
);
case "funnel":
return (
<div className="bg-zinc-900 p-8 rounded-xl text-white">
<h3 className="text-lg font-medium">{title}</h3>
{showLegend && (
<div className="flex justify-end">
<Legend
categories={[value]}
colors={[color || "blue", color || "blue"]}
className="mb-5"
/>
</div>
)}
<FunnelChart width={500} height={300} data={data}>
<Tooltip legendColor={getTremorColor(color || "blue")} />
<Funnel dataKey="value" color={getTremorColor(color || "blue")} />
</FunnelChart>
</div>
);
default:
return <p>Unsupported chart type.</p>;
}
};
if (!!props.chatId) {
return (
<div className="flex justify-center items-end w-full">
<div className="py-2 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col">
<div className="flex gap-x-5">
<WorkspaceProfileImage workspace={workspace} />
<div className="relative">
<DownloadGraph onClick={handleDownload} />
<div ref={ref}>{renderChart()}</div>
<span
className={`flex flex-col gap-y-1 mt-2`}
dangerouslySetInnerHTML={{
__html: renderMarkdown(content.caption),
}}
/>
</div>
</div>
</div>
</div>
);
}
return (
<div className="flex justify-center items-end w-full">
<div className="py-2 px-4 w-full flex gap-x-5 md:max-w-[800px] flex-col">
<div className="relative">
<DownloadGraph onClick={handleDownload} />
<div ref={ref}>{renderChart()}</div>
</div>
<div className="flex gap-x-5">
<span
className={`flex flex-col gap-y-1 mt-2`}
dangerouslySetInnerHTML={{
__html: renderMarkdown(content.caption),
}}
/>
</div>
</div>
</div>
);
}
const customTooltip = (props) => {
const { payload, active } = props;
if (!active || !payload) return null;
const categoryPayload = payload?.[0];
if (!categoryPayload) return null;
return (
<div className="w-56 bg-zinc-400 rounded-lg border p-2 text-white">
<div className="flex flex-1 space-x-2.5">
<div
className={`flex w-1.5 flex-col bg-${categoryPayload?.color}-500 rounded`}
/>
<div className="w-full">
<div className="flex items-center justify-between space-x-8">
<p className="whitespace-nowrap text-right text-tremor-content">
{categoryPayload.name}
</p>
<p className="whitespace-nowrap text-right font-medium text-tremor-content-emphasis">
{categoryPayload.value}
</p>
</div>
</div>
</div>
</div>
);
};
function DownloadGraph({ onClick }) {
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
await onClick?.();
setLoading(false);
};
return (
<div className="absolute top-3 right-3 z-50 cursor-pointer">
<div className="flex flex-col items-center">
<div className="p-1 rounded-full border-none">
{loading ? (
<CircleNotch
className="text-white/50 w-5 h-5 animate-spin"
aria-label="Downloading image..."
/>
) : (
<DownloadSimple
weight="bold"
className="text-white/50 w-5 h-5 hover:text-white"
onClick={handleClick}
aria-label="Download graph image"
/>
)}
</div>
</div>
</div>
);
}
export default memo(Chartable);

View File

@ -71,7 +71,7 @@ const PromptReply = ({
);
};
function WorkspaceProfileImage({ workspace }) {
export function WorkspaceProfileImage({ workspace }) {
if (!!workspace.pfpUrl) {
return (
<div className="relative w-[35px] h-[35px] rounded-full flex-shrink-0 overflow-hidden">

View File

@ -6,6 +6,7 @@ import ManageWorkspace from "../../../Modals/MangeWorkspace";
import { ArrowDown } from "@phosphor-icons/react";
import debounce from "lodash.debounce";
import useUser from "@/hooks/useUser";
import Chartable from "./Chartable";
export default function ChatHistory({ history = [], workspace, sendCommand }) {
const { user } = useUser();
@ -133,6 +134,12 @@ export default function ChatHistory({ history = [], workspace, sendCommand }) {
return <StatusResponse key={props.uuid} props={props} />;
}
if (props.type === "rechartVisualize" && !!props.content) {
return (
<Chartable key={props.uuid} workspace={workspace} props={props} />
);
}
if (isLastBotReply && props.animate) {
return (
<PromptReply

View File

@ -89,6 +89,7 @@ export function AvailableAgents({
<AbilityTag text="save-file-to-browser" />
<AbilityTag text="list-documents" />
<AbilityTag text="summarize-document" />
<AbilityTag text="chart-generation" />
</div>
</div>
</button>

View File

@ -679,3 +679,16 @@ does not extend the close button beyond the viewport. */
.white-scrollbar::-webkit-scrollbar-thumb:hover {
background-color: #cccccc;
}
/* Recharts rendering styles */
.recharts-text > * {
fill: #fff;
}
.recharts-legend-wrapper {
margin-bottom: 10px;
}
.text-tremor-content {
padding-bottom: 10px;
}

View File

@ -170,7 +170,7 @@ function AvailableAgentSkills({ skills, settings, toggleAgentSkill }) {
disabled={true}
/>
<GenericSkill
title="View and summarize documents"
title="View & summarize documents"
description="Allow the agent to list and summarize the content of workspace files currently embedded."
settings={settings}
enabled={true}
@ -183,6 +183,14 @@ function AvailableAgentSkills({ skills, settings, toggleAgentSkill }) {
enabled={true}
disabled={true}
/>
<GenericSkill
title="Generate charts"
description="Enable the default agent to generate various types of charts from data provided or given in chat."
skill="create-chart"
settings={settings}
toggleSkill={toggleAgentSkill}
enabled={skills.includes("create-chart")}
/>
<GenericSkill
title="Generate & save files to browser"
description="Enable the default agent to generate and write to files that save and can be downloaded in your browser."

View File

@ -11,6 +11,7 @@ const handledEvents = [
"fileDownload",
"awaitingFeedback",
"wssFailure",
"rechartVisualize",
];
export function websocketURI() {
@ -50,6 +51,25 @@ export default function handleSocketResponse(event, setChatHistory) {
return;
}
if (data.type === "rechartVisualize") {
return setChatHistory((prev) => {
return [
...prev.filter((msg) => !!msg.content),
{
type: "rechartVisualize",
uuid: v4(),
content: data.content,
role: "assistant",
sources: [],
closed: true,
error: null,
animate: false,
pending: false,
},
];
});
}
if (data.type === "wssFailure") {
return setChatHistory((prev) => {
return [

View File

@ -1,5 +1,6 @@
/** @type {import('tailwindcss').Config} */
export default {
darkMode: 'false',
content: {
relative: true,
files: [
@ -9,7 +10,8 @@ export default {
"./src/pages/**/*.{js,jsx}",
"./src/utils/**/*.js",
"./src/*.jsx",
"./index.html"
"./index.html",
'./node_modules/@tremor/**/*.{js,ts,jsx,tsx}'
]
},
theme: {
@ -86,5 +88,35 @@ export default {
}
}
},
// Required for rechart styles to show since they can be rendered dynamically and will be tree-shaken if not safe-listed.
safelist: [
{
pattern:
/^(bg-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
variants: ['hover', 'ui-selected'],
},
{
pattern:
/^(text-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
variants: ['hover', 'ui-selected'],
},
{
pattern:
/^(border-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
variants: ['hover', 'ui-selected'],
},
{
pattern:
/^(ring-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
},
{
pattern:
/^(stroke-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
},
{
pattern:
/^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
},
],
plugins: []
}

View File

@ -184,6 +184,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/runtime@^7.21.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7":
version "7.24.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.4.tgz#de795accd698007a66ba44add6cc86542aff1edd"
integrity sha512-dkxf7+hn8mFBwKjs9bvBlArzLVxVbS8usaPUDd5p2a9JCL9tB8OaOVN1isD4+Xyk4ns89/xeOmbQvgdK7IIVdA==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/template@^7.22.15":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
@ -365,6 +372,13 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.53.0.tgz#bea56f2ed2b5baea164348ff4d5a879f6f81f20d"
integrity sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==
"@floating-ui/core@^1.0.0":
version "1.6.0"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.6.0.tgz#fa41b87812a16bf123122bf945946bae3fdf7fc1"
integrity sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==
dependencies:
"@floating-ui/utils" "^0.2.1"
"@floating-ui/core@^1.5.3":
version "1.5.3"
resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.3.tgz#b6aa0827708d70971c8679a16cf680a515b8a52a"
@ -380,11 +394,48 @@
"@floating-ui/core" "^1.5.3"
"@floating-ui/utils" "^0.2.0"
"@floating-ui/utils@^0.2.0":
"@floating-ui/dom@^1.2.1":
version "1.6.3"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.3.tgz#954e46c1dd3ad48e49db9ada7218b0985cee75ef"
integrity sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==
dependencies:
"@floating-ui/core" "^1.0.0"
"@floating-ui/utils" "^0.2.0"
"@floating-ui/react-dom@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-1.3.0.tgz#4d35d416eb19811c2b0e9271100a6aa18c1579b3"
integrity sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==
dependencies:
"@floating-ui/dom" "^1.2.1"
"@floating-ui/react@^0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@floating-ui/react/-/react-0.19.2.tgz#c6e4d2097ed0dca665a7c042ddf9cdecc95e9412"
integrity sha512-JyNk4A0Ezirq8FlXECvRtQOX/iBe5Ize0W/pLkrZjfHW9GUV7Xnq6zm6fyZuQzaHHqEnVizmvlA96e1/CkZv+w==
dependencies:
"@floating-ui/react-dom" "^1.3.0"
aria-hidden "^1.1.3"
tabbable "^6.0.1"
"@floating-ui/utils@^0.2.0", "@floating-ui/utils@^0.2.1":
version "0.2.1"
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2"
integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==
"@headlessui/react@^1.7.18":
version "1.7.18"
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.18.tgz#30af4634d2215b2ca1aa29d07f33d02bea82d9d7"
integrity sha512-4i5DOrzwN4qSgNsL4Si61VMkUcWbcSKueUV7sFhpHzQcSShdlHENE5+QBntMSRvHt8NyoFO2AGG8si9lq+w4zQ==
dependencies:
"@tanstack/react-virtual" "^3.0.0-beta.60"
client-only "^0.0.1"
"@headlessui/tailwindcss@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@headlessui/tailwindcss/-/tailwindcss-0.2.0.tgz#2c55c98fd8eee4b4f21ec6eb35a014b840059eec"
integrity sha512-fpL830Fln1SykOCboExsWr3JIVeQKieLJ3XytLe/tt1A0XzqUthOftDmjcCYLW62w7mQI7wXcoPXr3tZ9QfGxw==
"@humanwhocodes/config-array@^0.11.13":
version "0.11.13"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.13.tgz#075dc9684f40a531d9b26b0822153c1e832ee297"
@ -492,6 +543,32 @@
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.14.1.tgz#6d2dd03d52e604279c38911afc1079d58c50a755"
integrity sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==
"@tanstack/react-virtual@^3.0.0-beta.60":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.2.1.tgz#58ac9af23ff08b5f05a6dfe6a59deac2f9451508"
integrity sha512-i9Nt0ssIh2bSjomJZlr6Iq5usT/9+ewo2/fKHRNk6kjVKS8jrhXbnO8NEawarCuBx/efv0xpoUUKKGxa0cQb4Q==
dependencies:
"@tanstack/virtual-core" "3.2.1"
"@tanstack/virtual-core@3.2.1":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.2.1.tgz#b3e4214b8f462054501d80e8777068faa139bd06"
integrity sha512-nO0d4vRzsmpBQCJYyClNHPPoUMI4nXNfrm6IcCRL33ncWMoNVpURh9YebEHPw8KrtsP2VSJIHE4gf4XFGk1OGg==
"@tremor/react@^3.15.1":
version "3.15.1"
resolved "https://registry.yarnpkg.com/@tremor/react/-/react-3.15.1.tgz#a9c10887bd067ffe0e18ca763e425db057f3722f"
integrity sha512-vCUqgYo993VePn6yOs4102ibY2XYcDDp7I1ZV/+i5hdfp+XgsHyQvYeixQcETBMpcajwM8E8NOOO7k9ANLkrrw==
dependencies:
"@floating-ui/react" "^0.19.2"
"@headlessui/react" "^1.7.18"
"@headlessui/tailwindcss" "^0.2.0"
date-fns "^2.30.0"
react-day-picker "^8.9.1"
react-transition-state "^2.1.1"
recharts "^2.10.3"
tailwind-merge "^1.14.0"
"@types/babel__core@^7.20.3":
version "7.20.3"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.3.tgz#d5625a50b6f18244425a1359a858c73d70340778"
@ -525,6 +602,57 @@
dependencies:
"@babel/types" "^7.20.7"
"@types/d3-array@^3.0.3":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5"
integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==
"@types/d3-color@*":
version "3.1.3"
resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2"
integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==
"@types/d3-ease@^3.0.0":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b"
integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==
"@types/d3-interpolate@^3.0.1":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c"
integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==
dependencies:
"@types/d3-color" "*"
"@types/d3-path@*":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.0.tgz#2b907adce762a78e98828f0b438eaca339ae410a"
integrity sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==
"@types/d3-scale@^4.0.2":
version "4.0.8"
resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.8.tgz#d409b5f9dcf63074464bf8ddfb8ee5a1f95945bb"
integrity sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==
dependencies:
"@types/d3-time" "*"
"@types/d3-shape@^3.1.0":
version "3.1.6"
resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.6.tgz#65d40d5a548f0a023821773e39012805e6e31a72"
integrity sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==
dependencies:
"@types/d3-path" "*"
"@types/d3-time@*", "@types/d3-time@^3.0.0":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.3.tgz#3c186bbd9d12b9d84253b6be6487ca56b54f88be"
integrity sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==
"@types/d3-timer@^3.0.0":
version "3.0.2"
resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70"
integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==
"@types/history@^4.7.11":
version "4.7.11"
resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
@ -651,6 +779,13 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
aria-hidden@^1.1.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.2.4.tgz#b78e383fdbc04d05762c78b4a25a501e736c4522"
integrity sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==
dependencies:
tslib "^2.0.0"
array-buffer-byte-length@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead"
@ -748,6 +883,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
base64-arraybuffer@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==
base64-js@^1.3.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@ -871,6 +1011,11 @@ classnames@^2.3.0:
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
client-only@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
cliui@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
@ -890,6 +1035,11 @@ clsx@^1.1.1:
resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
clsx@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.0.tgz#e851283bcb5c80ee7608db18487433f7b23f77cb"
integrity sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==
color-convert@^1.3.0, color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
@ -954,6 +1104,13 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
css-line-break@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0"
integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==
dependencies:
utrie "^1.0.2"
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@ -964,6 +1121,84 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6:
version "3.2.4"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5"
integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
dependencies:
internmap "1 - 2"
"d3-color@1 - 3":
version "3.1.0"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
d3-ease@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
"d3-format@1 - 3":
version "3.1.0"
resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641"
integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==
"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
dependencies:
d3-color "1 - 3"
d3-path@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526"
integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==
d3-scale@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396"
integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
dependencies:
d3-array "2.10.0 - 3"
d3-format "1 - 3"
d3-interpolate "1.2.0 - 3"
d3-time "2.1.1 - 3"
d3-time-format "2 - 4"
d3-shape@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5"
integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==
dependencies:
d3-path "^3.1.0"
"d3-time-format@2 - 4":
version "4.1.0"
resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a"
integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==
dependencies:
d3-time "1 - 3"
"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7"
integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==
dependencies:
d3-array "2 - 3"
d3-timer@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
date-fns@^2.30.0:
version "2.30.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"
integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==
dependencies:
"@babel/runtime" "^7.21.0"
debug@^4.1.0, debug@^4.1.1, debug@^4.3.2:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
@ -971,6 +1206,11 @@ debug@^4.1.0, debug@^4.1.1, debug@^4.3.2:
dependencies:
ms "2.1.2"
decimal.js-light@^2.4.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934"
integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==
deep-is@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
@ -1046,6 +1286,14 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
dom-helpers@^5.0.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
dependencies:
"@babel/runtime" "^7.8.7"
csstype "^3.0.2"
dompurify@^3.0.8:
version "3.0.8"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-3.0.8.tgz#e0021ab1b09184bc8af7e35c7dd9063f43a8a437"
@ -1342,6 +1590,11 @@ esutils@^2.0.2:
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
eventemitter3@^4.0.1:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
execa@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
@ -1382,6 +1635,11 @@ fast-diff@^1.1.2:
resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
fast-equals@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.0.1.tgz#a4eefe3c5d1c0d021aeed0bc10ba5e0c12ee405d"
integrity sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==
fast-glob@^3.3.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4"
@ -1698,6 +1956,14 @@ highlight.js@^11.9.0:
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.9.0.tgz#04ab9ee43b52a41a047432c8103e2158a1b8b5b0"
integrity sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==
html2canvas@^1.2.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543"
integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==
dependencies:
css-line-break "^2.1.0"
text-segmentation "^1.0.3"
human-signals@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
@ -1753,6 +2019,11 @@ internal-slot@^1.0.5:
hasown "^2.0.0"
side-channel "^1.0.4"
"internmap@1 - 2":
version "2.0.3"
resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009"
integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==
is-array-buffer@^3.0.1, is-array-buffer@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe"
@ -2483,7 +2754,7 @@ prettier@^3.0.3:
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643"
integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==
prop-types@^15.8.1:
prop-types@^15.6.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@ -2502,6 +2773,11 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
react-day-picker@^8.9.1:
version "8.10.0"
resolved "https://registry.yarnpkg.com/react-day-picker/-/react-day-picker-8.10.0.tgz#729c5b9564967a924213978fb9c0751884a60595"
integrity sha512-mz+qeyrOM7++1NCb1ARXmkjMkzWVh2GL9YiPbRjKe0zHccvekk4HE+0MPOZOrosn8r8zTHIIeOUXTmXRqmkRmg==
react-device-detect@^2.2.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-2.2.3.tgz#97a7ae767cdd004e7c3578260f48cf70c036e7ca"
@ -2526,7 +2802,7 @@ react-dropzone@^14.2.3:
file-selector "^0.6.0"
prop-types "^15.8.1"
react-is@^16.13.1:
react-is@^16.10.2, react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@ -2556,6 +2832,15 @@ react-router@6.21.1:
dependencies:
"@remix-run/router" "1.14.1"
react-smooth@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-4.0.1.tgz#6200d8699bfe051ae40ba187988323b1449eab1a"
integrity sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==
dependencies:
fast-equals "^5.0.1"
prop-types "^15.8.1"
react-transition-group "^4.4.5"
react-tag-input-component@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/react-tag-input-component/-/react-tag-input-component-2.0.2.tgz#f62f013c6a535141dd1c6c3a88858223170150f1"
@ -2576,6 +2861,21 @@ react-tooltip@^5.25.2:
"@floating-ui/dom" "^1.0.0"
classnames "^2.3.0"
react-transition-group@^4.4.5:
version "4.4.5"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
dependencies:
"@babel/runtime" "^7.5.5"
dom-helpers "^5.0.1"
loose-envify "^1.4.0"
prop-types "^15.6.2"
react-transition-state@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/react-transition-state/-/react-transition-state-2.1.1.tgz#1601a6177926b647041b7d598bf124321ab8d25b"
integrity sha512-kQx5g1FVu9knoz1T1WkapjUgFz08qQ/g1OmuWGi3/AoEFfS0kStxrPlZx81urjCXdz2d+1DqLpU6TyLW/Ro04Q==
react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
@ -2597,6 +2897,34 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
recharts-scale@^0.4.4:
version "0.4.5"
resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.5.tgz#0969271f14e732e642fcc5bd4ab270d6e87dd1d9"
integrity sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==
dependencies:
decimal.js-light "^2.4.1"
recharts-to-png@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/recharts-to-png/-/recharts-to-png-2.3.1.tgz#94d4edb8461ba4b16318edea77a34c421c16d7c1"
integrity sha512-a+OaAi03oFJMa+Burf3vyH060iFTrb35W8bBYUatNjZVrrMKUcFM3VOI1ym078WIH7XfgYQb17K9p2spVA2FzQ==
dependencies:
html2canvas "^1.2.0"
recharts@^2.10.3, recharts@^2.12.5:
version "2.12.5"
resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.12.5.tgz#b335eb66173317dccb3e126fce1d7ac5b3cee1e9"
integrity sha512-Cy+BkqrFIYTHJCyKHJEPvbHE2kVQEP6PKbOHJ8ztRGTAhvHuUnCwDaKVb13OwRFZ0QNUk1QvGTDdgWSMbuMtKw==
dependencies:
clsx "^2.0.0"
eventemitter3 "^4.0.1"
lodash "^4.17.21"
react-is "^16.10.2"
react-smooth "^4.0.0"
recharts-scale "^0.4.4"
tiny-invariant "^1.3.1"
victory-vendor "^36.6.8"
reflect.getprototypeof@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3"
@ -2609,6 +2937,11 @@ reflect.getprototypeof@^1.0.4:
globalthis "^1.0.3"
which-builtin-type "^1.1.3"
regenerator-runtime@^0.14.0:
version "0.14.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e"
@ -2893,6 +3226,16 @@ synckit@^0.8.5:
"@pkgr/utils" "^2.3.1"
tslib "^2.5.0"
tabbable@^6.0.1:
version "6.2.0"
resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-6.2.0.tgz#732fb62bc0175cfcec257330be187dcfba1f3b97"
integrity sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==
tailwind-merge@^1.14.0:
version "1.14.0"
resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-1.14.0.tgz#e677f55d864edc6794562c63f5001f45093cdb8b"
integrity sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==
tailwindcss@^3.3.1:
version "3.3.5"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.3.5.tgz#22a59e2fbe0ecb6660809d9cc5f3976b077be3b8"
@ -3031,6 +3374,13 @@ text-path-case@^1.0.2:
dependencies:
text-dot-case "^1.0.2"
text-segmentation@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==
dependencies:
utrie "^1.0.2"
text-sentence-case@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/text-sentence-case/-/text-sentence-case-1.0.2.tgz#e692a9aea3c8dcb1fb12242838e0ca3e9a22a90f"
@ -3085,6 +3435,11 @@ thenify-all@^1.0.0:
dependencies:
any-promise "^1.0.0"
tiny-invariant@^1.3.1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127"
integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==
titleize@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53"
@ -3112,7 +3467,7 @@ ts-interface-checker@^0.1.9:
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.0:
tslib@^2.0.0, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.0:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
@ -3213,11 +3568,38 @@ util-deprecate@^1.0.2:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
utrie@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645"
integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==
dependencies:
base64-arraybuffer "^1.0.2"
uuid@^9.0.0:
version "9.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
victory-vendor@^36.6.8:
version "36.9.2"
resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.9.2.tgz#668b02a448fa4ea0f788dbf4228b7e64669ff801"
integrity sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==
dependencies:
"@types/d3-array" "^3.0.3"
"@types/d3-ease" "^3.0.0"
"@types/d3-interpolate" "^3.0.1"
"@types/d3-scale" "^4.0.2"
"@types/d3-shape" "^3.1.0"
"@types/d3-time" "^3.0.0"
"@types/d3-timer" "^3.0.0"
d3-array "^3.1.6"
d3-ease "^3.0.1"
d3-interpolate "^3.0.1"
d3-scale "^4.0.2"
d3-shape "^3.1.0"
d3-time "^3.0.0"
d3-timer "^3.0.1"
vite@^4.3.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.0.tgz#ec406295b4167ac3bc23e26f9c8ff559287cff26"

View File

@ -21,6 +21,19 @@ const chatHistory = {
// We need a full conversation reply with prev being from
// the USER and the last being from anyone other than the user.
if (prev.from !== "USER" || last.from === "USER") return;
// If we have a post-reply flow we should save the chat using this special flow
// so that post save cleanup and other unique properties can be run as opposed to regular chat.
if (aibitat.hasOwnProperty("_replySpecialAttributes")) {
await this._storeSpecial(aibitat, {
prompt: prev.content,
response: last.content,
options: aibitat._replySpecialAttributes,
});
delete aibitat._replySpecialAttributes;
return;
}
await this._store(aibitat, {
prompt: prev.content,
response: last.content,
@ -42,6 +55,28 @@ const chatHistory = {
threadId: invocation?.thread_id || null,
});
},
_storeSpecial: async function (
aibitat,
{ prompt, response, options = {} } = {}
) {
const invocation = aibitat.handlerProps.invocation;
await WorkspaceChats.new({
workspaceId: Number(invocation.workspace_id),
prompt,
response: {
sources: options?.sources ?? [],
// when we have a _storeSpecial called the options param can include a storedResponse() function
// that will override the text property to store extra information in, depending on the special type of chat.
text: options.hasOwnProperty("storedResponse")
? options.storedResponse(response)
: response,
type: options?.saveAsType ?? "chat",
},
user: { id: invocation?.user_id || null },
threadId: invocation?.thread_id || null,
});
options?.postSave();
},
};
},
};

View File

@ -5,6 +5,7 @@ const { docSummarizer } = require("./summarize.js");
const { saveFileInBrowser } = require("./save-file-browser.js");
const { chatHistory } = require("./chat-history.js");
const { memory } = require("./memory.js");
const { rechart } = require("./rechart.js");
module.exports = {
webScraping,
@ -14,6 +15,7 @@ module.exports = {
saveFileInBrowser,
chatHistory,
memory,
rechart,
// Plugin name aliases so they can be pulled by slug as well.
[webScraping.name]: webScraping,
@ -23,4 +25,5 @@ module.exports = {
[saveFileInBrowser.name]: saveFileInBrowser,
[chatHistory.name]: chatHistory,
[memory.name]: memory,
[rechart.name]: rechart,
};

View File

@ -0,0 +1,109 @@
const { safeJsonParse } = require("../../../http");
const { Deduplicator } = require("../utils/dedupe");
const rechart = {
name: "create-chart",
startupConfig: {
params: {},
},
plugin: function () {
return {
name: this.name,
setup(aibitat) {
// Scrape a website and summarize the content based on objective if the content is too large.',
aibitat.function({
super: aibitat,
name: this.name,
tracker: new Deduplicator(),
description:
"Generates the JSON data required to generate a RechartJS chart to the user based on their prompt and available data.",
parameters: {
$schema: "http://json-schema.org/draft-07/schema#",
type: "object",
properties: {
type: {
type: "string",
enum: [
"area",
"bar",
"line",
"composed",
"scatter",
"pie",
"radar",
"radialBar",
"treemap",
"funnel",
],
description: "The type of chart to be generated.",
},
title: {
type: "string",
description:
"Title of the chart. There MUST always be a title. Do not leave it blank.",
},
dataset: {
type: "string",
description: `Valid JSON in which each element is an object for Recharts API for the 'type' of chart defined WITHOUT new line characters. Strictly using this FORMAT and naming:
{ "name": "a", "value": 12 }].
Make sure field "name" always stays named "name". Instead of naming value field value in JSON, name it based on user metric and make it the same across every item.
Make sure the format use double quotes and property names are string literals. Provide JSON data only.`,
},
},
additionalProperties: false,
},
required: ["type", "title", "dataset"],
handler: async function ({ type, dataset, title }) {
try {
if (!this.tracker.isUnique(this.name)) {
this.super.handlerProps.log(
`${this.name} has been run for this chat response already. It can only be called once per chat.`
);
return "The chart was generated and returned to the user. This function completed successfully. Do not call this function again.";
}
const data = safeJsonParse(dataset, null);
if (data === null) {
this.super.introspect(
`${this.caller}: ${this.name} provided invalid JSON data - so we cant make a ${type} chart.`
);
return "Invalid JSON provided. Please only provide valid RechartJS JSON to generate a chart.";
}
this.super.introspect(`${this.caller}: Rendering ${type} chart.`);
this.super.socket.send("rechartVisualize", {
type,
dataset,
title,
});
this.super._replySpecialAttributes = {
saveAsType: "rechartVisualize",
storedResponse: (additionalText = "") =>
JSON.stringify({
type,
dataset,
title,
caption: additionalText,
}),
postSave: () => this.tracker.removeUniqueConstraint(this.name),
};
this.tracker.markUnique(this.name);
return "The chart was generated and returned to the user. This function completed successfully. Do not make another chart.";
} catch (error) {
this.super.handlerProps.log(
`create-chart raised an error. ${error.message}`
);
return `Let the user know this action was not successful. An error was raised while generating the chart. ${error.message}`;
}
},
});
},
};
},
};
module.exports = {
rechart,
};

View File

@ -9,10 +9,17 @@
// ... do random # of times.
// We want to block all the reruns of a plugin, so we can add this to prevent that behavior from
// spamming the user (or other costly function) that have the exact same signatures.
// Track Run/isDuplicate prevents _exact_ data re-runs based on the SHA of their inputs
// StartCooldown/isOnCooldown does prevention of _near-duplicate_ runs based on only the function name that is running.
// isUnique/markUnique/removeUniqueConstraint prevents one-time functions from re-running. EG: charting.
const crypto = require("crypto");
const DEFAULT_COOLDOWN_MS = 5 * 1000;
class Deduplicator {
#hashes = {};
#cooldowns = {};
#uniques = {};
constructor() {}
trackRun(key, params = {}) {
@ -30,6 +37,32 @@ class Deduplicator {
.digest("hex");
return this.#hashes.hasOwnProperty(newSig);
}
startCooldown(
key,
parameters = {
cooldownInMs: DEFAULT_COOLDOWN_MS,
}
) {
this.#cooldowns[key] = Number(new Date()) + Number(parameters.cooldownInMs);
}
isOnCooldown(key) {
if (!this.#cooldowns.hasOwnProperty(key)) return false;
return Number(new Date()) <= this.#cooldowns[key];
}
isUnique(key) {
return !this.#uniques.hasOwnProperty(key);
}
removeUniqueConstraint(key) {
delete this.#uniques[key];
}
markUnique(key) {
this.#uniques[key] = Number(new Date());
}
}
module.exports.Deduplicator = Deduplicator;

View File

@ -123,6 +123,7 @@ function convertToChatHistory(history = []) {
sentAt: moment(createdAt).unix(),
},
{
type: data?.type || "chart",
role: "assistant",
content: data.text,
sources: data.sources || [],