PluginProbe
Extendify / 2.2.0
Extendify v2.2.0
3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 0.7.0 All 126 releases
extendify / src / Agent / state / chat.js

chat.js in Extendify 2.2.0, at src/Agent/state/chat.js

95 lines 3.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import apiFetch from '@wordpress/api-fetch';
2 import { create } from 'zustand';
3 import { persist, devtools, createJSONStorage } from 'zustand/middleware';
4 import { makeId } from '@agent/lib/util';
5
6 const { chatHistory } = window.extAgentData;
7 const state = (set, get) => ({
8 messagesRaw: (chatHistory || []).toReversed(),
9 messages: chatHistory?.length
10 ? chatHistory
11 // Remove some noise on reload
12 .filter(
13 (message) =>
14 !['agent-working', 'calling-agent', 'tool-started'].includes(
15 message.details?.type,
16 ) &&
17 !(
18 // Keep workflow messages with status completed (to show rating)
19 (
20 ['workflow'].includes(message.type) &&
21 message.details.status === 'completed'
22 )
23 ),
24 )
25 .toReversed()
26 : [],
27
28 seenAgents: [],
29 // Messages sent to the api, user and assistant only. Up until the last workflow
30 getMessagesForAI: () => {
31 const messages = [];
32 let foundUserMessage = false;
33 for (const { type, details } of get().messagesRaw.toReversed()) {
34 const finished =
35 ['completed', 'canceled'].includes(details.status) ||
36 (['status'].includes(type) && details.type === 'workflow-canceled');
37 if (type === 'workflow' && finished) break;
38 if (type === 'workflow-component' && finished) break;
39 // This prevents a loop of assistant messages from being at the end
40 if (type === 'message' && details.role === 'user') {
41 foundUserMessage = true;
42 }
43 if (type === 'message' && !foundUserMessage) continue;
44 if (type === 'message') messages.push(details);
45 }
46 return messages.toReversed();
47 },
48 hasMessages: () => get().messages.length > 0,
49 addMessage: (type, details) => {
50 const id = makeId();
51 // If there's an agent, check if seen before
52 if (details?.agent?.name) {
53 const seenAgents = get().seenAgents;
54 if (!seenAgents.includes(details.agent.name)) {
55 details.firstSeen = true;
56 const seen = (state) =>
57 new Set([...state.seenAgents, details.agent.name]);
58 set((state) => ({
59 seenAgents: [...seen(state)],
60 }));
61 }
62 }
63 set((state) => {
64 // max 150 messages
65 const max = Math.max(0, state.messages.length - 149);
66 const next = { id, type, details };
67 return {
68 // { id: 1, type: message, details: { role: 'user', content: 'Hello' } }
69 // { id: 2, type: message, details: { role: 'assistant', content: 'Hi there!' } }
70 // { id: 3, type: workflow, details: { name: 'Workflow 1' } }
71 // { id: 5, type: status, details: { type: 'calling-agent' }
72 messages: [...state.messages.toSpliced(0, max), next],
73 messagesRaw: [...state.messagesRaw.toSpliced(0, max), next],
74 };
75 });
76 return id;
77 },
78 clearMessages: () => set({ messages: [] }),
79 });
80
81 const path = '/extendify/v1/agent/chat-events';
82 const storage = {
83 getItem: async () => await apiFetch({ path }),
84 setItem: async (_name, state) =>
85 await apiFetch({ path, method: 'POST', data: { state } }),
86 };
87
88 export const useChatStore = create()(
89 persist(devtools(state, { name: 'Extendify Agent Chat' }), {
90 name: 'extendify-agent-chat',
91 storage: createJSONStorage(() => storage),
92 skipHydration: true,
93 }),
94 );
95