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.
182 lines
5.2 KiB
182 lines
5.2 KiB
import { ref, computed, type Ref, type ComputedRef } from "vue";
|
|
import { useAgentChat, type AgentMessage, type UseAgentChatOptions } from "./useAgentChat";
|
|
import { useAgentRateLimit } from "./useAgentRateLimit";
|
|
|
|
export interface AgentChatInstance {
|
|
messages: Ref<AgentMessage[]>;
|
|
isLoading: Ref<boolean>;
|
|
hasPendingApproval: ComputedRef<boolean>;
|
|
errorMessage: Ref<string>;
|
|
isStopped: Ref<boolean>;
|
|
rateLimit: ReturnType<typeof useAgentRateLimit>;
|
|
send: ReturnType<typeof useAgentChat>["send"];
|
|
stopGeneration: ReturnType<typeof useAgentChat>["stopGeneration"];
|
|
stopAndSave: ReturnType<typeof useAgentChat>["stopAndSave"];
|
|
clear: ReturnType<typeof useAgentChat>["clear"];
|
|
loadMessages: ReturnType<typeof useAgentChat>["loadMessages"];
|
|
respondToApproval: ReturnType<typeof useAgentChat>["respondToApproval"];
|
|
sendFeedback: ReturnType<typeof useAgentChat>["sendFeedback"];
|
|
}
|
|
|
|
export interface UseAgentChatStoreOptions {
|
|
modelId: () => number | null;
|
|
enableThinking: () => boolean;
|
|
enableTools: () => boolean;
|
|
onStreamComplete?: (sessionId: string) => void;
|
|
}
|
|
|
|
export function useAgentChatStore(storeOptions: UseAgentChatStoreOptions) {
|
|
const instances = new Map<string, AgentChatInstance>();
|
|
const currentSessionId = ref<string | null>(null);
|
|
|
|
const loadedSessionIds = ref<Set<string>>(new Set());
|
|
|
|
function createInstance(sid: string): AgentChatInstance {
|
|
const options: UseAgentChatOptions = {
|
|
sessionId: () => sid,
|
|
modelId: storeOptions.modelId,
|
|
enableThinking: storeOptions.enableThinking,
|
|
enableTools: storeOptions.enableTools,
|
|
onStreamComplete: () => {
|
|
storeOptions.onStreamComplete?.(sid);
|
|
},
|
|
};
|
|
const chat = useAgentChat(options);
|
|
return chat;
|
|
}
|
|
|
|
function getInstance(sid: string): AgentChatInstance {
|
|
let inst = instances.get(sid);
|
|
if (!inst) {
|
|
inst = createInstance(sid);
|
|
instances.set(sid, inst);
|
|
}
|
|
return inst;
|
|
}
|
|
|
|
function removeInstance(sid: string) {
|
|
const inst = instances.get(sid);
|
|
if (inst) {
|
|
inst.stopGeneration();
|
|
inst.clear();
|
|
instances.delete(sid);
|
|
}
|
|
loadedSessionIds.value.delete(sid);
|
|
}
|
|
|
|
const currentInstance = computed<AgentChatInstance | null>(() => {
|
|
const sid = currentSessionId.value;
|
|
if (!sid) return null;
|
|
return getInstance(sid);
|
|
});
|
|
|
|
const messages = computed(() => currentInstance.value?.messages.value ?? []);
|
|
const isLoading = computed(() => currentInstance.value?.isLoading.value ?? false);
|
|
const hasPendingApproval = computed(() => currentInstance.value?.hasPendingApproval.value ?? false);
|
|
const errorMessage = computed(() => currentInstance.value?.errorMessage.value ?? "");
|
|
const isStopped = computed(() => currentInstance.value?.isStopped.value ?? false);
|
|
const fallbackRateLimit = useAgentRateLimit();
|
|
const rateLimit = computed(() => currentInstance.value?.rateLimit ?? fallbackRateLimit);
|
|
|
|
const anyLoading = computed(() => {
|
|
for (const inst of instances.values()) {
|
|
if (inst.isLoading.value) return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
function getLoadingSessionIds(): string[] {
|
|
const ids: string[] = [];
|
|
for (const [sid, inst] of instances) {
|
|
if (inst.isLoading.value) ids.push(sid);
|
|
}
|
|
return ids;
|
|
}
|
|
|
|
async function loadMessages(sid: string) {
|
|
const inst = getInstance(sid);
|
|
loadedSessionIds.value.add(sid);
|
|
await inst.loadMessages(sid);
|
|
}
|
|
|
|
async function send(content: string, opts?: { editMessageId?: string; regenerate?: boolean }) {
|
|
const sid = currentSessionId.value;
|
|
if (!sid) return;
|
|
const inst = getInstance(sid);
|
|
await inst.send(content, opts);
|
|
}
|
|
|
|
async function respondToApproval(toolCallId: string, approved: boolean, reason?: string) {
|
|
const sid = currentSessionId.value;
|
|
if (!sid) return;
|
|
const inst = getInstance(sid);
|
|
await inst.respondToApproval(toolCallId, approved, reason);
|
|
}
|
|
|
|
async function sendFeedback(messageId: string, feedback: "like" | "dislike") {
|
|
const sid = currentSessionId.value;
|
|
if (!sid) return;
|
|
const inst = getInstance(sid);
|
|
await inst.sendFeedback(messageId, feedback);
|
|
}
|
|
|
|
async function stopGeneration() {
|
|
const sid = currentSessionId.value;
|
|
if (!sid) return;
|
|
const inst = getInstance(sid);
|
|
await inst.stopAndSave();
|
|
}
|
|
|
|
function forceStop() {
|
|
const sid = currentSessionId.value;
|
|
if (!sid) return;
|
|
const inst = getInstance(sid);
|
|
inst.stopGeneration();
|
|
}
|
|
|
|
function stopAll() {
|
|
for (const inst of instances.values()) {
|
|
inst.stopGeneration();
|
|
}
|
|
}
|
|
|
|
function clear() {
|
|
const sid = currentSessionId.value;
|
|
if (!sid) return;
|
|
const inst = getInstance(sid);
|
|
inst.clear();
|
|
}
|
|
|
|
function setCurrentSessionId(sid: string | null) {
|
|
currentSessionId.value = sid;
|
|
}
|
|
|
|
function isSessionLoaded(sid: string): boolean {
|
|
return loadedSessionIds.value.has(sid);
|
|
}
|
|
|
|
return {
|
|
currentSessionId,
|
|
currentInstance,
|
|
messages,
|
|
isLoading,
|
|
hasPendingApproval,
|
|
errorMessage,
|
|
isStopped,
|
|
rateLimit,
|
|
anyLoading,
|
|
getLoadingSessionIds,
|
|
loadMessages,
|
|
send,
|
|
respondToApproval,
|
|
sendFeedback,
|
|
stopGeneration,
|
|
forceStop,
|
|
stopAll,
|
|
clear,
|
|
setCurrentSessionId,
|
|
isSessionLoaded,
|
|
removeInstance,
|
|
getInstance,
|
|
};
|
|
}
|
|
|