enchance ai

This commit is contained in:
Emily
2024-09-16 15:37:18 +02:00
parent c3904ebd55
commit e6adbf9c7b
11 changed files with 276 additions and 75 deletions

View File

@@ -9,10 +9,10 @@ const props = defineProps<{ title: string, sub?: string }>();
<div class="flex flex-col gap-4">
<div class="flex items-center">
<div class="flex flex-col grow">
<div class="poppins font-semibold text-[1.1rem] md:text-[1.4rem] text-text">
<div class="poppins font-semibold text-[1rem] md:text-[1.3rem] text-text">
{{ props.title }}
</div>
<div v-if="props.sub" class="poppins text-[.8rem] md:text-[1.1rem] text-text-sub">
<div v-if="props.sub" class="poppins text-[.7rem] md:text-[1rem] text-text-sub">
{{ props.sub }}
</div>
</div>

View File

@@ -16,10 +16,10 @@ const emits = defineEmits<{
<template>
<div class="flex gap-2 border-[1px] border-gray-400 p-1 md:p-2 rounded-xl">
<div class="flex gap-2 border-[1px] border-lyx-widget-lighter p-1 md:p-2 rounded-xl bg-lyx-widget">
<div @click="$emit('changeIndex', index)" v-for="(opt, index) of options"
class="hover:bg-white/10 select-btn-animated cursor-pointer rounded-lg poppins font-semibold px-2 md:px-3 py-1 text-[.8rem] md:text-[1rem]"
:class="{ 'bg-accent hover:!bg-accent': currentIndex == index }">
class="hover:bg-lyx-widget-lighter/60 select-btn-animated cursor-pointer rounded-lg poppins font-regular px-2 md:px-3 py-1 text-[.8rem] md:text-[1rem]"
:class="{ 'bg-lyx-widget-lighter hover:!bg-lyx-widget-lighter': currentIndex == index }">
{{ opt.label }}
</div>
</div>

View File

@@ -5,14 +5,16 @@ import * as datefns from 'date-fns';
registerChartComponents();
const errored = ref<boolean>(false);
const props = defineProps<{
labels: string[],
title:string,
title: string,
datasets: {
points: number[],
color: string,
type: string,
name:string
chartType: string,
name: string
}[]
}>();
@@ -67,7 +69,11 @@ const chartOptions = ref<ChartOptions<'line'>>({
const chartData = ref<ChartData<'line'>>({
labels: props.labels.map(e => {
try {
return datefns.format(new Date(e), 'dd/MM');
} catch (ex) {
return e;
}
}),
datasets: props.datasets.map(e => ({
data: e.points,
@@ -82,7 +88,7 @@ const chartData = ref<ChartData<'line'>>({
hoverBackgroundColor: e.color,
hoverBorderColor: 'white',
hoverBorderWidth: 2,
type: e.type
type: e.chartType
} as any))
});
@@ -106,10 +112,18 @@ function createGradient(startColor: string) {
}
onMounted(async () => {
try {
chartData.value.datasets.forEach(dataset => {
if (dataset.borderColor && dataset.borderColor.toString().startsWith('#')) {
dataset.backgroundColor = [createGradient(dataset.borderColor as string)]
} else {
dataset.backgroundColor = [createGradient('#3d59a4')]
}
});
} catch (ex) {
errored.value = true;
console.error(ex);
}
});
@@ -117,5 +131,8 @@ onMounted(async () => {
<template>
<LineChart ref="lineChartRef" v-bind="lineChartProps"> </LineChart>
<div>
<div v-if="errored"> ERROR CREATING CHART </div>
<LineChart v-if="!errored" ref="lineChartRef" v-bind="lineChartProps"> </LineChart>
</div>
</template>

View File

@@ -262,12 +262,19 @@ onMounted(async () => {
<template>
<CardTitled title="Trend chart" sub="Easily match Visits, Unique sessions and Events trends." class="w-full">
<template #header>
<SelectButton class="w-fit" @changeIndex="selectedLabelIndex = $event" :currentIndex="selectedLabelIndex"
:options="selectLabels">
<SelectButton class="w-fit" @changeIndex="selectedLabelIndex = $event"
:currentIndex="selectedLabelIndex" :options="selectLabels">
</SelectButton>
</template>
<div class="flex gap-6 w-full justify-end">
<div class="flex gap-6 w-full justify-between">
<LyxUiButton type="secondary" to="/analyst">
<div class="flex items-center gap-2 px-10">
<i class="far fa-sparkles text-yellow-400"></i>
<div class="poppins text-lyx-text"> Ask AI </div>
</div>
</LyxUiButton>
<div class="flex gap-6">
<div v-for="(dataset, index) of chartData.datasets" class="flex gap-2 items-center text-[.9rem]">
<UCheckbox :ui="{
color: `text-[${legendColors[index]}]`
@@ -275,6 +282,7 @@ onMounted(async () => {
<label class="mt-[2px]"> {{ dataset.label }} </label>
</div>
</div>
</div>

View File

@@ -17,7 +17,7 @@ const sections: Section[] = [
entries: [
{ label: 'Dashboard', to: '/', icon: 'fal fa-table-layout' },
{ label: 'Events', to: '/events', icon: 'fal fa-square-bolt' },
{ label: 'Analyst', to: '/analyst', icon: 'fal fa-microchip-ai' },
{ label: 'AI Analyst', to: '/analyst', icon: 'fal fa-sparkles' },
{ label: 'Insights (soon)', to: '#', icon: 'fal fa-lightbulb', disabled: true },
{ label: 'Links (soon)', to: '#', icon: 'fal fa-globe-pointer', disabled: true },
{ label: 'Integrations (soon)', to: '#', icon: 'fal fa-cube', disabled: true },
@@ -29,6 +29,7 @@ const sections: Section[] = [
},
{
label: 'Slack support', icon: 'fab fa-slack',
to:'#',
premiumOnly: true,
action() {
if (isGuest.value === true) return;

View File

@@ -35,11 +35,13 @@
"v-calendar": "^3.1.2",
"vue": "^3.4.21",
"vue-chart-3": "^3.1.8",
"vue-markdown-render": "^2.2.1",
"vue-router": "^4.3.0"
},
"devDependencies": {
"@nuxt/ui": "^2.15.2",
"@types/jsonwebtoken": "^9.0.6",
"@types/markdown-it": "^14.1.2",
"@types/nodemailer": "^6.4.15",
"@types/pdfkit": "^0.13.4",
"autoprefixer": "^10.4.19",

View File

@@ -1,16 +1,19 @@
<script lang="ts" setup>
import VueMarkdown from 'vue-markdown-render';
definePageMeta({ layout: 'dashboard' });
const activeProject = useActiveProject();
const { data: chatsList, refresh: reloadChatsList } = useFetch(`/api/ai/${activeProject.value?._id}/chats_list`, {
...signHeaders(),
transform: (data) => {
return data?.toReversed();
}
...signHeaders()
});
const viewChatsList = computed(() => (chatsList.value || []).toReversed());
const { data: chatsRemaining, refresh: reloadChatsRemaining } = useFetch(`/api/ai/${activeProject.value?._id}/chats_remaining`, signHeaders());
const currentText = ref<string>("");
@@ -72,8 +75,10 @@ async function sendMessage() {
async function openChat(chat_id?: string) {
menuOpen.value = false;
if (!activeProject.value) return;
if (!chat_id) {
currentChatMessages.value = [];
if (!chat_id) {
currentChatId.value = '';
return;
}
@@ -105,10 +110,10 @@ function onKeyDown(e: KeyboardEvent) {
const menuOpen = ref<boolean>(false);
const defaultPrompts = [
'How many visits i got last week ?',
'How many visits i got last month ?',
'How many visits i got today ?',
'How many events i got last week ?',
"Create a line chart with this data: \n[100, 200, 30, 300, 500, 40]",
"Create a chart with Events (bar) and Visits (line) data from last week.",
"How many visits did I get last week?",
"Create a line chart of last week's visits."
]
async function deleteChat(chat_id: string) {
@@ -123,6 +128,8 @@ async function deleteChat(chat_id: string) {
await reloadChatsList();
}
const { visible: pricingDrawerVisible } = usePricingDrawer()
</script>
<template>
@@ -144,7 +151,7 @@ async function deleteChat(chat_id: string) {
</div>
<div class="grid grid-cols-2 gap-4 mt-6" v-if="!isGuest">
<div v-for="prompt of defaultPrompts" @click="currentText = prompt"
class="bg-lyx-widget-light hover:bg-lyx-widget-lighter cursor-pointer p-4 rounded-lg poppins text-center">
class="bg-lyx-widget-light hover:bg-lyx-widget-lighter cursor-pointer p-4 rounded-lg poppins text-center whitespace-pre-wrap flex items-center justify-center text-[.9rem]">
{{ prompt }}
</div>
</div>
@@ -164,15 +171,20 @@ async function deleteChat(chat_id: string) {
<div class="flex items-center justify-center shrink-0">
<img class="h-[3.5rem] w-auto" :src="'analyst.png'">
</div>
<div v-html="parseMessageContent(message.content)"
class="max-w-[70%] text-text/90 whitespace-pre-wrap">
<div class="max-w-[70%] text-text/90 ai-message">
<vue-markdown :source="message.content" :options="{
html: true,
breaks: true,
}" />
</div>
</div>
<div v-if="message.charts && message.charts.length > 0"
class="flex items-center gap-3 justify-start w-full poppins text-[1.1rem] flex-col mt-4">
<div v-for="chart of message.charts" class="w-full">
<AnalystComposableChart :datasets="chart.datasets" :labels="chart.labels" :title="chart.title">
<AnalystComposableChart :datasets="chart.datasets" :labels="chart.labels"
:title="chart.title">
</AnalystComposableChart>
</div>
</div>
@@ -217,21 +229,26 @@ async function deleteChat(chat_id: string) {
<i @click="menuOpen = false" class="fas fa-close cursor-pointer"></i>
</div>
<div class="poppins font-semibold text-[1.5rem]">
Lit, your AI Analyst is here!
What Lit can do for you?
</div>
<div class="poppins text-text/75">
Ask anything you want on your analytics,
and understand more Trends and Key Points to take Strategic moves!
Ask anything from your data history, visualize and overlap charts, explore events or metadata,
and enjoy a highly personalized data analysis experience.
</div>
</div>
<div class="flex gap-2 items-center py-3">
<div class="flex gap-2 items-center pt-3">
<div class="bg-accent w-5 h-5 rounded-full animate-pulse">
</div>
<div class="manrope font-semibold"> {{ chatsRemaining }} remaining messages </div>
<div class="manrope font-semibold"> {{ chatsRemaining }} remaining AI requests </div>
</div>
<div class="poppins font-semibold text-[1.1rem]"> History: </div>
<LyxUiButton type="primary" class="text-[.9rem] text-center w-full"
@click="pricingDrawerVisible = true">
Upgrade plan for more requests
</LyxUiButton>
<div class="poppins font-semibold text-[1.1rem]"> History </div>
<div class="px-2">
<div @click="openChat()"
@@ -246,7 +263,7 @@ async function deleteChat(chat_id: string) {
<div class="flex flex-col gap-2 px-2">
<div :class="{ '!bg-accent/60': chat._id.toString() === currentChatId }"
class="flex rounded-lg items-center gap-4 w-full px-4 bg-lyx-widget-lighter hover:bg-lyx-widget"
v-for="chat of chatsList">
v-for="chat of viewChatsList">
<i @click="deleteChat(chat._id.toString())"
class="far fa-trash hover:text-gray-300 cursor-pointer"></i>
<div @click="openChat(chat._id.toString())"
@@ -266,3 +283,82 @@ async function deleteChat(chat_id: string) {
</div>
</template>
<style lang="scss">
.ai-message {
/* Ensure headings stand out */
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: bold;
margin-top: 1.5em;
margin-bottom: 0.5em;
color: white;
}
/* Paragraphs for better line spacing */
p {
line-height: 1.8;
margin-bottom: 1em;
max-width: 750px;
/* Prevent very wide paragraphs for readability */
}
/* Blockquotes */
blockquote {
margin: 1.5em 10px;
padding: 10px 20px;
color: #555;
border-left: 5px solid #ccc;
background-color: #f5f5f5;
}
/* Code blocks */
pre {
background-color: #f4f4f4;
padding: 15px;
border-radius: 5px;
font-size: 14px;
overflow-x: auto;
}
code {
background-color: #f1f1f1;
padding: 2px 5px;
border-radius: 3px;
font-size: 90%;
}
/* Lists */
ul,
ol {
margin-left: 30px;
margin-bottom: 1.5em;
}
li {
margin-bottom: 0.5em;
}
/* Links */
a {
color: #007acc;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Horizontal rules */
hr {
border: 1px solid #ddd;
margin: 2em 0;
}
}
</style>

111
dashboard/pnpm-lock.yaml generated
View File

@@ -74,6 +74,9 @@ importers:
vue-chart-3:
specifier: ^3.1.8
version: 3.1.8(chart.js@3.9.1)(vue@3.4.27(typescript@5.4.2))
vue-markdown-render:
specifier: ^2.2.1
version: 2.2.1(vue@3.4.27(typescript@5.4.2))
vue-router:
specifier: ^4.3.0
version: 4.3.2(vue@3.4.27(typescript@5.4.2))
@@ -84,6 +87,9 @@ importers:
'@types/jsonwebtoken':
specifier: ^9.0.6
version: 9.0.6
'@types/markdown-it':
specifier: ^14.1.2
version: 14.1.2
'@types/nodemailer':
specifier: ^6.4.15
version: 6.4.15
@@ -1171,9 +1177,18 @@ packages:
'@types/jsonwebtoken@9.0.6':
resolution: {integrity: sha512-/5hndP5dCjloafCXns6SZyESp3Ldq7YjH3zwzwczYnjxIT0Fqzk5ROSYVGfFyczIue7IUEj8hkvLbPoLQ18vQw==}
'@types/linkify-it@5.0.0':
resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
'@types/lodash@4.17.7':
resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==}
'@types/markdown-it@14.1.2':
resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
'@types/mdurl@2.0.0':
resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
'@types/node-fetch@2.6.11':
resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
@@ -1431,20 +1446,20 @@ packages:
'@vue/reactivity@3.4.27':
resolution: {integrity: sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==}
'@vue/reactivity@3.5.5':
resolution: {integrity: sha512-V4tTWElZQhT73PSK3Wnax9R9m4qvMX+LeKHnfylZc6SLh4Jc5/BPakp6e3zEhKWi5AN8TDzRkGnLkp8OqycYng==}
'@vue/reactivity@3.5.6':
resolution: {integrity: sha512-shZ+KtBoHna5GyUxWfoFVBCVd7k56m6lGhk5e+J9AKjheHF6yob5eukssHRI+rzvHBiU1sWs/1ZhNbLExc5oYQ==}
'@vue/runtime-core@3.4.27':
resolution: {integrity: sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==}
'@vue/runtime-core@3.5.5':
resolution: {integrity: sha512-2/CFaRN17jgsXy4MpigWFBCAMmLkXPb4CjaHrndglwYSra7ajvkH2cat21dscuXaH91G8fXAeg5gCyxWJ+wCRA==}
'@vue/runtime-core@3.5.6':
resolution: {integrity: sha512-FpFULR6+c2lI+m1fIGONLDqPQO34jxV8g6A4wBOgne8eSRHP6PQL27+kWFIx5wNhhjkO7B4rgtsHAmWv7qKvbg==}
'@vue/runtime-dom@3.4.27':
resolution: {integrity: sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==}
'@vue/runtime-dom@3.5.5':
resolution: {integrity: sha512-0bQGgCuL+4Muz5PsCLgF4Ata9BTdhHi5VjsxtTDyI0Wy4MgoSvBGaA6bDc7W7CGgZOyirf9LNeetMYHQ05pgpw==}
'@vue/runtime-dom@3.5.6':
resolution: {integrity: sha512-SDPseWre45G38ENH2zXRAHL1dw/rr5qp91lS4lt/nHvMr0MhsbCbihGAWLXNB/6VfFOJe2O+RBRkXU+CJF7/sw==}
'@vue/server-renderer@3.4.27':
resolution: {integrity: sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==}
@@ -1454,8 +1469,8 @@ packages:
'@vue/shared@3.4.27':
resolution: {integrity: sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==}
'@vue/shared@3.5.5':
resolution: {integrity: sha512-0KyMXyEgnmFAs6rNUL+6eUHtUCqCaNrVd+AW3MX3LyA0Yry5SA0Km03CDKiOua1x1WWnIr+W9+S0GMFoSDWERQ==}
'@vue/shared@3.5.6':
resolution: {integrity: sha512-eidH0HInnL39z6wAt6SFIwBrvGOpDWsDxlw3rCgo1B+CQ1781WzQUSU3YjxgdkcJo9Q8S6LmXTkvI+cLHGkQfA==}
'@vueuse/components@10.10.0':
resolution: {integrity: sha512-HiA10NQ9HJAGnju+8ZK4TyA8LIc0a6BnJmVWDa/k+TRhaYCVacSDU04k0BQ2otV+gghUDdwu98upf6TDRXpoeg==}
@@ -2283,6 +2298,10 @@ packages:
resolution: {integrity: sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==}
engines: {node: '>=10.13.0'}
entities@3.0.1:
resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==}
engines: {node: '>=0.12'}
entities@4.5.0:
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
engines: {node: '>=0.12'}
@@ -3179,6 +3198,9 @@ packages:
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
linkify-it@4.0.1:
resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==}
listhen@1.7.2:
resolution: {integrity: sha512-7/HamOm5YD9Wb7CFgAZkKgVPA96WwhcTQoqtm2VTZGVbVVn3IWKRBTgrU7cchA3Q8k9iCsG8Osoi9GX4JsGM9g==}
hasBin: true
@@ -3281,12 +3303,19 @@ packages:
resolution: {integrity: sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==}
engines: {node: ^16.14.0 || >=18.0.0}
markdown-it@13.0.2:
resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==}
hasBin: true
mdn-data@2.0.28:
resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
mdn-data@2.0.30:
resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
mdurl@1.0.1:
resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==}
media-typer@0.3.0:
resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
engines: {node: '>= 0.6'}
@@ -4691,6 +4720,9 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
uc.micro@1.0.6:
resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==}
ufo@1.5.3:
resolution: {integrity: sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==}
@@ -5045,6 +5077,11 @@ packages:
vue-devtools-stub@0.1.0:
resolution: {integrity: sha512-RutnB7X8c5hjq39NceArgXg28WZtZpGc3+J16ljMiYnFhKvd8hITxSWQSQ5bvldxMDU6gG5mkxl1MTQLXckVSQ==}
vue-markdown-render@2.2.1:
resolution: {integrity: sha512-XkYnC0PMdbs6Vy6j/gZXSvCuOS0787Se5COwXlepRqiqPiunyCIeTPQAO2XnB4Yl04EOHXwLx5y6IuszMWSgyQ==}
peerDependencies:
vue: ^3.3.4
vue-observe-visibility@2.0.0-alpha.1:
resolution: {integrity: sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==}
peerDependencies:
@@ -6547,8 +6584,17 @@ snapshots:
dependencies:
'@types/node': 20.12.12
'@types/linkify-it@5.0.0': {}
'@types/lodash@4.17.7': {}
'@types/markdown-it@14.1.2':
dependencies:
'@types/linkify-it': 5.0.0
'@types/mdurl': 2.0.0
'@types/mdurl@2.0.0': {}
'@types/node-fetch@2.6.11':
dependencies:
'@types/node': 20.12.12
@@ -7001,7 +7047,7 @@ snapshots:
'@volar/language-core': 1.11.1
'@volar/source-map': 1.11.1
'@vue/compiler-dom': 3.4.27
'@vue/shared': 3.5.5
'@vue/shared': 3.5.6
computeds: 0.0.1
minimatch: 9.0.4
muggle-string: 0.3.1
@@ -7014,19 +7060,19 @@ snapshots:
dependencies:
'@vue/shared': 3.4.27
'@vue/reactivity@3.5.5':
'@vue/reactivity@3.5.6':
dependencies:
'@vue/shared': 3.5.5
'@vue/shared': 3.5.6
'@vue/runtime-core@3.4.27':
dependencies:
'@vue/reactivity': 3.4.27
'@vue/shared': 3.4.27
'@vue/runtime-core@3.5.5':
'@vue/runtime-core@3.5.6':
dependencies:
'@vue/reactivity': 3.5.5
'@vue/shared': 3.5.5
'@vue/reactivity': 3.5.6
'@vue/shared': 3.5.6
'@vue/runtime-dom@3.4.27':
dependencies:
@@ -7034,11 +7080,11 @@ snapshots:
'@vue/shared': 3.4.27
csstype: 3.1.3
'@vue/runtime-dom@3.5.5':
'@vue/runtime-dom@3.5.6':
dependencies:
'@vue/reactivity': 3.5.5
'@vue/runtime-core': 3.5.5
'@vue/shared': 3.5.5
'@vue/reactivity': 3.5.6
'@vue/runtime-core': 3.5.6
'@vue/shared': 3.5.6
csstype: 3.1.3
'@vue/server-renderer@3.4.27(vue@3.4.27(typescript@5.4.2))':
@@ -7049,7 +7095,7 @@ snapshots:
'@vue/shared@3.4.27': {}
'@vue/shared@3.5.5': {}
'@vue/shared@3.5.6': {}
'@vueuse/components@10.10.0(vue@3.4.27(typescript@5.4.2))':
dependencies:
@@ -7840,6 +7886,8 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.2.1
entities@3.0.1: {}
entities@4.5.0: {}
env-paths@2.2.1: {}
@@ -8867,6 +8915,10 @@ snapshots:
lines-and-columns@1.2.4: {}
linkify-it@4.0.1:
dependencies:
uc.micro: 1.0.6
listhen@1.7.2:
dependencies:
'@parcel/watcher': 2.4.1
@@ -8986,10 +9038,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
markdown-it@13.0.2:
dependencies:
argparse: 2.0.1
entities: 3.0.1
linkify-it: 4.0.1
mdurl: 1.0.1
uc.micro: 1.0.6
mdn-data@2.0.28: {}
mdn-data@2.0.30: {}
mdurl@1.0.1: {}
media-typer@0.3.0: {}
memory-pager@1.5.0: {}
@@ -10643,6 +10705,8 @@ snapshots:
typescript@5.4.2: {}
uc.micro@1.0.6: {}
ufo@1.5.3: {}
ultrahtml@1.5.3: {}
@@ -11042,8 +11106,8 @@ snapshots:
vue-chart-3@3.1.8(chart.js@3.9.1)(vue@3.4.27(typescript@5.4.2)):
dependencies:
'@vue/runtime-core': 3.5.5
'@vue/runtime-dom': 3.5.5
'@vue/runtime-core': 3.5.6
'@vue/runtime-dom': 3.5.6
chart.js: 3.9.1
csstype: 3.1.3
lodash-es: 4.17.21
@@ -11055,6 +11119,11 @@ snapshots:
vue-devtools-stub@0.1.0: {}
vue-markdown-render@2.2.1(vue@3.4.27(typescript@5.4.2)):
dependencies:
markdown-it: 13.0.2
vue: 3.4.27(typescript@5.4.2)
vue-observe-visibility@2.0.0-alpha.1(vue@3.4.27(typescript@5.4.2)):
dependencies:
vue: 3.4.27(typescript@5.4.2)

View File

@@ -43,7 +43,7 @@ export class AiComposableChart extends AIPlugin<['createComposableChart']> {
},
color: {
type: 'string',
description: 'Color used to represent the dataset'
description: 'Color used to represent the dataset in format "#RRGGBB"'
},
name: {
type: 'string',

View File

@@ -1,5 +1,5 @@
import { EventModel } from "@schema/metrics/EventSchema";
import { executeTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
import { AdvancedTimelineAggregationOptions, executeAdvancedTimelineAggregation, executeTimelineAggregation, fillAndMergeTimelineAggregationV2 } from "~/server/services/TimelineService";
import { Types } from "mongoose";
import { AIPlugin, AIPlugin_TTool } from "../Plugin";
@@ -32,6 +32,8 @@ const getEventsTimelineTool: AIPlugin_TTool<'getEventsTimeline'> = {
properties: {
from: { type: 'string', description: 'ISO string of start date including hours' },
to: { type: 'string', description: 'ISO string of end date including hours' },
name: { type: 'string', description: 'Name of the events to get' },
metadata: { type: 'object', description: 'Metadata of events to get' },
},
required: ['from', 'to']
}
@@ -60,12 +62,17 @@ export class AiEvents extends AIPlugin<['getEventsCount', 'getEventsTimeline']>
tool: getEventsCountTool
},
'getEventsTimeline': {
handler: async (data: { project_id: string, from: string, to: string }) => {
const timelineData = await executeTimelineAggregation({
handler: async (data: { project_id: string, from: string, to: string, name?: string, metadata?: string }) => {
const query: AdvancedTimelineAggregationOptions & { customMatch: Record<string, any> } = {
projectId: new Types.ObjectId(data.project_id) as any,
model: EventModel,
from: data.from, to: data.to, slice: 'day'
});
from: data.from, to: data.to, slice: 'day',
customMatch: {}
}
if (data.metadata) query.customMatch.metadata = data.metadata;
if (data.name) query.customMatch.name = data.name;
const timelineData = await executeAdvancedTimelineAggregation(query);
const timelineFilledMerged = fillAndMergeTimelineAggregationV2(timelineData, 'day', data.from, data.to);
return { data: timelineFilledMerged };
},

View File

@@ -76,7 +76,8 @@ export async function sendMessageOnChat(text: string, pid: string, initial_chat_
messages.push(...chatMessages);
} else {
const roleMessage: OpenAI.Chat.Completions.ChatCompletionMessageParam = {
role: 'system', content: "Today is " + new Date().toISOString()
role: 'system',
content: + "Today is " + new Date().toISOString()
}
messages.push(roleMessage);
await addMessageToChat(roleMessage, chat_id);