You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
5.4 KiB
198 lines
5.4 KiB
<script setup lang="ts">
|
|
import { useAgentSessions } from "~/composables/useAgentSessions";
|
|
import { useAgentChat } from "~/composables/useAgentChat";
|
|
import { useAuthSession } from "~/composables/useAuthSession";
|
|
|
|
definePageMeta({
|
|
layout: "agent",
|
|
});
|
|
|
|
const auth = useAuthSession();
|
|
const sessions = useAgentSessions();
|
|
const sidebarCollapsed = ref(false);
|
|
const showPromptModal = ref(false);
|
|
const systemPromptText = ref("");
|
|
|
|
interface ModelOption {
|
|
id: number;
|
|
name: string;
|
|
modelId: string;
|
|
providerName: string;
|
|
}
|
|
|
|
const models = ref<ModelOption[]>([]);
|
|
|
|
const currentModelId = computed({
|
|
get: () => sessions.currentSession.value?.modelId ?? null,
|
|
set: (val: number | null) => {
|
|
if (sessions.currentSessionId.value) {
|
|
sessions.updateSessionConfig(sessions.currentSessionId.value, { modelId: val });
|
|
}
|
|
},
|
|
});
|
|
|
|
const enableThinking = computed({
|
|
get: () => !!(sessions.currentSession.value?.enableThinking),
|
|
set: (val: boolean) => {
|
|
if (sessions.currentSessionId.value) {
|
|
sessions.updateSessionConfig(sessions.currentSessionId.value, { enableThinking: val });
|
|
}
|
|
},
|
|
});
|
|
|
|
const enableTools = computed({
|
|
get: () => !!(sessions.currentSession.value?.enableTools),
|
|
set: (val: boolean) => {
|
|
if (sessions.currentSessionId.value) {
|
|
sessions.updateSessionConfig(sessions.currentSessionId.value, { enableTools: val });
|
|
}
|
|
},
|
|
});
|
|
|
|
const chat = useAgentChat({
|
|
sessionId: () => sessions.currentSessionId.value,
|
|
modelId: () => currentModelId.value,
|
|
enableThinking: () => enableThinking.value,
|
|
enableTools: () => enableTools.value,
|
|
});
|
|
|
|
const rateLimitInfo = computed(() => {
|
|
if (auth.loggedIn.value) return undefined;
|
|
return {
|
|
sessionRemaining: chat.rateLimit.sessionRemaining.value,
|
|
ipRemaining: chat.rateLimit.ipRemaining.value,
|
|
};
|
|
});
|
|
|
|
async function loadModels() {
|
|
try {
|
|
const res = await $fetch<{ code: number; data: { list: any[] } }>("/api/llm/models");
|
|
const list = res.data?.list ?? [];
|
|
models.value = list.map((m) => ({
|
|
id: m.id,
|
|
name: m.name,
|
|
modelId: m.modelId,
|
|
providerName: m.providerName ?? m.provider?.name ?? "—",
|
|
}));
|
|
} catch {
|
|
models.value = [];
|
|
}
|
|
}
|
|
|
|
async function loadSystemPrompt() {
|
|
try {
|
|
const res = await $fetch<{ code: number; data: { systemPrompt?: string } }>(
|
|
"/api/agent/system-prompt",
|
|
);
|
|
systemPromptText.value = res.data?.systemPrompt ?? "";
|
|
} catch {
|
|
systemPromptText.value = "";
|
|
}
|
|
}
|
|
|
|
watch(
|
|
() => sessions.currentSessionId.value,
|
|
async (sid) => {
|
|
if (sid) {
|
|
await chat.loadMessages(sid);
|
|
} else {
|
|
chat.clear();
|
|
}
|
|
},
|
|
);
|
|
|
|
async function handleSend(content: string) {
|
|
if (!sessions.currentSessionId.value) {
|
|
await sessions.newSession();
|
|
}
|
|
if (sessions.currentSessionId.value) {
|
|
const sid = sessions.currentSessionId.value;
|
|
const wasNewSession = chat.messages.value.filter((m) => m.role === "assistant").length === 0;
|
|
await chat.send(content);
|
|
sessions.touchSessionOrder(sid);
|
|
if (wasNewSession) {
|
|
setTimeout(() => sessions.refreshSessionTitle(sid), 8000);
|
|
setTimeout(() => sessions.refreshSessionTitle(sid), 15000);
|
|
setTimeout(() => sessions.refreshSessionTitle(sid), 25000);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function handleApprove(toolCallId: string, approved: boolean) {
|
|
await chat.respondToApproval(toolCallId, approved);
|
|
}
|
|
|
|
async function handleFeedback(messageId: string, feedback: "like" | "dislike") {
|
|
await chat.sendFeedback(messageId, feedback);
|
|
}
|
|
|
|
function handleEdit(messageId: string, content: string) {
|
|
chat.send(content, { editMessageId: messageId });
|
|
}
|
|
|
|
function handleRegenerate() {
|
|
const lastUserMsg = [...chat.messages.value].reverse().find((m) => m.role === "user");
|
|
if (lastUserMsg) {
|
|
chat.send(lastUserMsg.content, { regenerate: true });
|
|
}
|
|
}
|
|
|
|
function handleExample(content: string) {
|
|
handleSend(content);
|
|
}
|
|
|
|
function handleLogin() {
|
|
navigateTo("/auth/login");
|
|
}
|
|
|
|
onMounted(async () => {
|
|
await Promise.all([sessions.init(), loadModels(), loadSystemPrompt()]);
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<AgentSidebar
|
|
:sessions="sessions.sessions.value"
|
|
:current-session-id="sessions.currentSessionId.value"
|
|
:logged-in="auth.loggedIn.value"
|
|
:user="auth.user.value"
|
|
:collapsed="sidebarCollapsed"
|
|
@new-session="sessions.newSession()"
|
|
@select="sessions.selectSession($event)"
|
|
@rename="sessions.renameSession"
|
|
@delete="sessions.deleteSession"
|
|
@login="handleLogin"
|
|
@toggle="sidebarCollapsed = !sidebarCollapsed"
|
|
/>
|
|
|
|
<AgentChatArea
|
|
:messages="chat.messages.value"
|
|
:is-loading="chat.isLoading.value"
|
|
:logged-in="auth.loggedIn.value"
|
|
:model-id="currentModelId"
|
|
:models="models"
|
|
:enable-thinking="enableThinking"
|
|
:enable-tools="enableTools"
|
|
:rate-limit-info="rateLimitInfo"
|
|
:sidebar-collapsed="sidebarCollapsed"
|
|
:error-message="chat.errorMessage.value"
|
|
@send="handleSend"
|
|
@stop="chat.stopGeneration()"
|
|
@update:model-id="currentModelId = $event"
|
|
@update:enable-thinking="enableThinking = $event"
|
|
@update:enable-tools="enableTools = $event"
|
|
@approve="handleApprove"
|
|
@feedback="handleFeedback"
|
|
@edit="handleEdit"
|
|
@regenerate="handleRegenerate"
|
|
@toggle-sidebar="sidebarCollapsed = !sidebarCollapsed"
|
|
@show-prompt="showPromptModal = true"
|
|
@example="handleExample"
|
|
/>
|
|
|
|
<AgentSystemPromptModal
|
|
:show="showPromptModal"
|
|
:system-prompt="systemPromptText"
|
|
@close="showPromptModal = false"
|
|
/>
|
|
</template>
|
|
|