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

<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>