fix ai message + add typer

This commit is contained in:
Emily
2024-12-18 16:49:18 +01:00
parent 68d362d1b3
commit b38363ddf5
4 changed files with 91 additions and 27 deletions

View File

@@ -0,0 +1,36 @@
export function useTextType(options: { ms: number, increase: number }, onTickAction?: () => any) {
let interval: any;
const index = ref<number>(0);
function onTick() {
index.value += options.increase;
onTickAction?.();
}
function pause() {
if (interval) clearInterval(interval);
}
function resume() {
if (interval) clearInterval(interval);
interval = setInterval(() => onTick(), options.ms);
}
function stop() {
if (interval) clearTimeout(interval);
}
function start() {
index.value = 0;
if (interval) clearInterval(interval);
interval = setInterval(() => onTick(), options.ms);
}
return { start, stop, resume, pause, index, interval }
}

View File

@@ -26,13 +26,28 @@ const loading = ref<boolean>(false);
const currentChatId = ref<string>("");
const currentChatMessages = ref<{ role: string, content: string, charts?: any[], tool_calls?: any }[]>([]);
const currentChatMessageDelta = ref<string>('');
const currentChatMessageDelta = ref<string>("");
const currentChatMessageDeltaHtml = computed(() => {
const lastData = currentChatMessageDelta.value.match(/\[(data:(.*?))\]/g);
const typer = useTextType({ ms: 10, increase: 2 }, () => {
const cleanMessage = currentChatMessageDelta.value.replace(/\[(data:(.*?))\]/g, '');
if (!lastData || lastData.length == 0) return cleanMessage;
return `<div class="flex items-center gap-1"> <i class="fas fa-loader animate-spin"></i> <div> ${lastData.at(-1)}</div> </div> <div> ${cleanMessage} </div>`;
if (typer.index.value >= cleanMessage.length) typer.pause();
});
onUnmounted(() => {
typer.stop();
})
const currentChatMessageDeltaTextVisible = computed(() => {
const cleanMessage = currentChatMessageDelta.value.replace(/\[(data:(.*?))\]/g, '');
const textVisible = cleanMessage.substring(0, typer.index.value);
setTimeout(() => scrollToBottom(), 1);
return textVisible;
});
const currentChatMessageDeltaShowLoader = computed(() => {
const lastData = currentChatMessageDelta.value.match(/\[(data:(.*?))\]$/);
return lastData != null;
});
const scroller = ref<HTMLDivElement | null>(null);
@@ -51,10 +66,16 @@ async function pollSendMessageStatus(chat_id: string, times: number, updateStatu
updateStatus(res.status);
typer.resume();
if (res.completed === false) {
setTimeout(() => pollSendMessageStatus(chat_id, times + 1, updateStatus), (times > 20 ? 1000 : 500));
setTimeout(() => pollSendMessageStatus(chat_id, times + 1, updateStatus), (times > 10 ? 2000 : 1000));
} else {
typer.stop();
const messages = await $fetch(`/api/ai/${chat_id}/get_messages`, {
headers: useComputedHeaders({ useSnapshotDates: false }).value
});
@@ -62,18 +83,13 @@ async function pollSendMessageStatus(chat_id: string, times: number, updateStatu
currentChatMessages.value = messages.map(e => ({ ...e, charts: e.charts.map(k => JSON.parse(k)) })) as any;
currentChatMessageDelta.value = '';
// currentChatMessages.value.push({
// role: 'assistant',
// content: currentChatMessageDelta.value.replace(/\[data:.*?\]/g, ''),
// });
}
}
async function sendMessage() {
if (loading.value) return;
if (!project.value) return;
@@ -100,14 +116,15 @@ async function sendMessage() {
await new Promise(e => setTimeout(e, 200));
typer.start();
await pollSendMessageStatus(res.chat_id, 0, status => {
if (!status) return;
if (status.length > 0) loading.value = false;
currentChatMessageDelta.value = status;
});
} catch (ex: any) {
if (ex.message.includes('CHAT_LIMIT_REACHED')) {
@@ -237,14 +254,14 @@ async function clearAllChats() {
<div class="flex w-full flex-col" v-for="(message, messageIndex) of currentChatMessages">
<div class="flex justify-end w-full poppins text-[1.1rem]" v-if="message.role === 'user'">
<div v-if="message.role === 'user'" class="flex justify-end w-full poppins text-[1.1rem]">
<div class="bg-lyx-widget-light px-5 py-3 rounded-lg">
{{ message.content }}
</div>
</div>
<div class="flex items-center gap-3 justify-start w-full poppins text-[1.1rem]"
v-if="message.role === 'assistant' && (debugModeAi ? true : message.content)">
<div v-if="message.role === 'assistant' && (debugModeAi ? true : message.content)"
class="flex items-center gap-3 justify-start w-full poppins text-[1.1rem]">
<div class="flex items-center justify-center shrink-0">
<img class="h-[3.5rem] w-auto" :src="'analyst.png'">
</div>
@@ -256,7 +273,6 @@ async function clearAllChats() {
}" />
<div v-if="debugModeAi && !message.content">
<div class="flex flex-col"
v-if="message.tool_calls && message.tool_calls.length > 0">
@@ -285,17 +301,26 @@ async function clearAllChats() {
</div>
<div class="flex items-center gap-3 justify-start w-full poppins text-[1.1rem]"
v-if="currentChatMessageDelta">
<div class="flex items-center justify-center shrink-0">
<img class="h-[3.5rem] w-auto" :src="'analyst.png'">
</div>
<div class="max-w-[70%] text-text/90 ai-message whitespace-pre-wrap">
<vue-markdown :source="currentChatMessageDeltaHtml" :options="{
<div class="max-w-[70%] text-text/90 ai-message">
<div v-if="currentChatMessageDeltaShowLoader" class="flex items-center gap-1">
<i class="fas fa-loader animate-spin"></i>
<div> Loading </div>
</div>
<vue-markdown :source="currentChatMessageDeltaTextVisible" :options="{
html: true,
breaks: true,
}" />
</div>
</div>
@@ -356,9 +381,10 @@ async function clearAllChats() {
<div class="flex items-center gap-4">
<div class="poppins font-semibold text-[1.1rem]"> History </div>
<LyxUiButton v-if="chatsList && chatsList.length > 0" @click="clearAllChats()" type="secondary" class="text-center text-[.8rem]">
<LyxUiButton v-if="chatsList && chatsList.length > 0" @click="clearAllChats()" type="secondary"
class="text-center text-[.8rem]">
Clear all
</LyxUiButton>
</LyxUiButton>
</div>
<div class="px-2">

View File

@@ -8,7 +8,7 @@ const getSessionsCountsTool: AIPlugin_TTool<'getSessionsCount'> = {
type: 'function',
function: {
name: 'getSessionsCount',
description: 'Gets the number of sessions received on a date range',
description: 'Gets the number of sessions (unique visitors) received on a date range',
parameters: {
type: 'object',
properties: {
@@ -83,4 +83,4 @@ export class AiSessions extends AIPlugin<['getSessionsCount', 'getSessionsTimeli
}
}
export const ASessionsInstance = new AiSessions();
export const AiSessionsInstance = new AiSessions();

View File

@@ -2,11 +2,11 @@
import OpenAI from "openai";
import { AiChatModel } from '@schema/ai/AiChatSchema';
import { ProjectCountModel } from '@schema/project/ProjectsCounts';
import { ProjectLimitModel } from '@schema/project/ProjectsLimits';
import { AiEventsInstance } from '../ai/functions/AI_Events';
import { AiVisitsInstance } from '../ai/functions/AI_Visits';
import { AiSessionsInstance } from '../ai/functions/AI_Sessions';
import { AiComposableChartInstance } from '../ai/functions/AI_ComposableChart';
const { AI_KEY, AI_ORG, AI_PROJECT } = useRuntimeConfig();
@@ -18,13 +18,15 @@ const openai = new OpenAI({ apiKey: AI_KEY, organization: AI_ORG, project: AI_PR
const tools: OpenAI.Chat.Completions.ChatCompletionTool[] = [
...AiVisitsInstance.getTools(),
...AiEventsInstance.getTools(),
...AiComposableChartInstance.getTools()
...AiSessionsInstance.getTools(),
...AiComposableChartInstance.getTools(),
]
const functions: any = {
...AiVisitsInstance.getHandlers(),
...AiEventsInstance.getHandlers(),
...AiSessionsInstance.getHandlers(),
...AiComposableChartInstance.getHandlers()
}
@@ -188,7 +190,7 @@ export async function sendMessageOnChat(text: string, pid: string, time_offset:
} else {
const roleMessage: OpenAI.Chat.Completions.ChatCompletionMessageParam = {
role: 'system',
content: "Today ISO date: " + new Date().toISOString()
content: "You are an AI Data Analyst and Growth Hacker specialized in helping users analyze data collected within Litlyx and providing strategies to grow their website, app, or business. Your scope is strictly limited to data creation, visualization, and growth-related advice. If a user asks something outside this domain, politely inform them that you are not designed to answer such questions. Today ISO date is " + new Date().toISOString() + "take this in count when the user ask relative dates"
}
messages.push(roleMessage);
await addMessageToChat(roleMessage, chat_id);