PluginProbe
Extendify / 3.1.3
Extendify v3.1.3
3.2.1 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 All 127 releases
extendify / src / Agent / state / chat.js

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

118 lines 4.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { buildToolMessages } from '@agent/lib/tool-messages';
2 import { isChangeSiteDesignWorkflowAvailable, makeId } from '@agent/lib/util';
3 import { useStatusStore } from '@agent/state/status';
4 import apiFetch from '@wordpress/api-fetch';
5 import { __ } from '@wordpress/i18n';
6 import { create } from 'zustand';
7 import { createJSONStorage, devtools, persist } from 'zustand/middleware';
8
9 const { chatHistory } = window.extAgentData;
10
11 const welcomeMessage = [
12 {
13 id: 1,
14 type: 'message',
15 details: {
16 role: 'assistant',
17 // translators: this is the initial message in the agent chat, welcoming the user. Keep it short and friendly and follow the same markdown format and emoji.
18 content: isChangeSiteDesignWorkflowAvailable()
19 ? __(
20 '#### Your site is ready 🎉\nWant to explore other website designs?',
21 'extendify-local',
22 )
23 : __(
24 '#### Your site is ready 🎉\nWant to explore other site colors?',
25 'extendify-local',
26 ),
27 },
28 },
29 ];
30 const state = (set, get) => ({
31 messages: chatHistory?.length ? chatHistory.toReversed() : welcomeMessage,
32 // API messages, back to the last finished workflow.
33 getCurrentMessages: ({ includeTools = true } = {}) => {
34 const messages = [];
35 let foundUserMessage = false;
36 for (const { type, details } of get().messages.toReversed()) {
37 const finished = ['completed', 'canceled'].includes(details.status);
38 if (type === 'workflow' && finished) break;
39 if (type === 'workflow-component' && finished) break;
40 if (type === 'tool' && includeTools) {
41 // buildToolMessages returns [call, result]; push reversed so the
42 // final toReversed() restores call-before-result order.
43 for (const m of buildToolMessages(details).toReversed())
44 messages.push(m);
45 }
46 // This prevents a loop of assistant messages from being at the end
47 if (type === 'message' && details.role === 'user') {
48 foundUserMessage = true;
49 }
50 if (type === 'message' && !foundUserMessage) continue;
51 if (type === 'message') messages.push(details);
52 }
53 return messages.toReversed();
54 },
55 // API messages from every finished run of the given workflow.
56 getMessagesFor: (workflowId) => {
57 if (!workflowId) return [];
58 const messages = [];
59 let segment = [];
60 for (const { type, details } of get().messages) {
61 const finished = ['completed', 'canceled'].includes(details.status);
62 if (['workflow', 'workflow-component'].includes(type) && finished) {
63 if (details.workflowId === workflowId) messages.push(...segment);
64 segment = [];
65 continue;
66 }
67 if (type === 'tool') segment.push(...buildToolMessages(details));
68 if (type === 'message') segment.push(details);
69 }
70 return messages;
71 },
72 getLastAssistantMessage: () =>
73 get()?.messages?.findLast(
74 (message) =>
75 message.type === 'message' && message.details?.role === 'assistant',
76 ),
77 hasMessages: () => get().messages.length > 0,
78 addMessage: (type, details) => {
79 const id = makeId();
80 set((state) => {
81 // max 250 messages
82 const max = Math.max(0, state.messages.length - 249);
83 const next = { id, type, details };
84 return {
85 // { id: 1, type: message, details: { role: 'user', content: 'Hello' } }
86 // { id: 2, type: message, details: { role: 'assistant', content: 'Hi there!' } }
87 // { id: 3, type: workflow, details: { name: 'Workflow 1' } }
88 messages: [...state.messages.toSpliced(0, max), next],
89 };
90 });
91 // A real message supersedes any in-flight progress status.
92 useStatusStore.getState().clearStatuses();
93 return id;
94 },
95 // pop messages all the way back to the last agent message
96 popMessage: () => {
97 set((state) => ({
98 messages: state.messages?.slice(0, -1) || [],
99 }));
100 },
101 clearMessages: () => set({ messages: [] }),
102 });
103
104 const path = '/extendify/v1/agent/chat-events';
105 const storage = {
106 getItem: async () => await apiFetch({ path }),
107 setItem: async (_name, state) =>
108 await apiFetch({ path, method: 'POST', data: { state } }),
109 };
110
111 export const useChatStore = create()(
112 persist(devtools(state, { name: 'Extendify Agent Chat' }), {
113 name: `extendify-agent-chat-${window.extSharedData.siteId}`,
114 storage: createJSONStorage(() => storage),
115 skipHydration: true,
116 }),
117 );
118