@copilotkit/react-core 0.38.0-mme-pre.0 → 0.38.0-mme-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/.turbo/turbo-build.log +187 -234
  2. package/CHANGELOG.md +4 -3
  3. package/dist/{chunk-NKIUZSGZ.mjs → chunk-5Q7DU3XW.mjs} +40 -40
  4. package/dist/chunk-5Q7DU3XW.mjs.map +1 -0
  5. package/dist/{chunk-B244LK6F.mjs → chunk-ATWK243A.mjs} +2 -2
  6. package/dist/{chunk-AJSY6LS5.mjs → chunk-FY3TQ7Q6.mjs} +17 -32
  7. package/dist/chunk-FY3TQ7Q6.mjs.map +1 -0
  8. package/dist/{chunk-SR4RW4CU.mjs → chunk-GFQBAEJL.mjs} +6 -6
  9. package/dist/chunk-GFQBAEJL.mjs.map +1 -0
  10. package/dist/{chunk-DY63PD22.mjs → chunk-GLPZSBNA.mjs} +4 -5
  11. package/dist/chunk-GLPZSBNA.mjs.map +1 -0
  12. package/dist/{chunk-S5LWO5V3.mjs → chunk-IBOL6LE5.mjs} +2 -2
  13. package/dist/{chunk-NSUYO6TP.mjs → chunk-MI7BERU7.mjs} +9 -9
  14. package/dist/chunk-MI7BERU7.mjs.map +1 -0
  15. package/dist/chunk-MW2IVCDP.mjs +188 -0
  16. package/dist/chunk-MW2IVCDP.mjs.map +1 -0
  17. package/dist/{chunk-ZHYCHXIO.mjs → chunk-PSZAPRNZ.mjs} +56 -42
  18. package/dist/chunk-PSZAPRNZ.mjs.map +1 -0
  19. package/dist/{chunk-DY4YVUO3.mjs → chunk-WJIS7HTH.mjs} +14 -25
  20. package/dist/chunk-WJIS7HTH.mjs.map +1 -0
  21. package/dist/{chunk-MNZXQ6UH.mjs → chunk-WTGFKR3D.mjs} +2 -2
  22. package/dist/chunk-YJLRG5U3.mjs +1 -0
  23. package/dist/components/copilot-provider/copilotkit.js +17 -32
  24. package/dist/components/copilot-provider/copilotkit.js.map +1 -1
  25. package/dist/components/copilot-provider/copilotkit.mjs +2 -2
  26. package/dist/components/copilot-provider/index.js +17 -32
  27. package/dist/components/copilot-provider/index.js.map +1 -1
  28. package/dist/components/copilot-provider/index.mjs +2 -2
  29. package/dist/components/index.js +17 -32
  30. package/dist/components/index.js.map +1 -1
  31. package/dist/components/index.mjs +2 -2
  32. package/dist/context/copilot-context.d.ts +5 -5
  33. package/dist/context/copilot-context.js +3 -4
  34. package/dist/context/copilot-context.js.map +1 -1
  35. package/dist/context/copilot-context.mjs +1 -1
  36. package/dist/context/index.d.ts +1 -0
  37. package/dist/context/index.js +3 -4
  38. package/dist/context/index.js.map +1 -1
  39. package/dist/context/index.mjs +1 -1
  40. package/dist/hooks/index.d.ts +2 -1
  41. package/dist/hooks/index.js +136 -232
  42. package/dist/hooks/index.js.map +1 -1
  43. package/dist/hooks/index.mjs +9 -10
  44. package/dist/hooks/use-chat.d.ts +26 -52
  45. package/dist/hooks/use-chat.js +104 -203
  46. package/dist/hooks/use-chat.js.map +1 -1
  47. package/dist/hooks/use-chat.mjs +1 -2
  48. package/dist/hooks/use-copilot-action.js +10 -11
  49. package/dist/hooks/use-copilot-action.js.map +1 -1
  50. package/dist/hooks/use-copilot-action.mjs +2 -2
  51. package/dist/hooks/use-copilot-chat.d.ts +1 -1
  52. package/dist/hooks/use-copilot-chat.js +119 -215
  53. package/dist/hooks/use-copilot-chat.js.map +1 -1
  54. package/dist/hooks/use-copilot-chat.mjs +4 -5
  55. package/dist/hooks/use-copilot-readable.js +3 -4
  56. package/dist/hooks/use-copilot-readable.js.map +1 -1
  57. package/dist/hooks/use-copilot-readable.mjs +2 -2
  58. package/dist/hooks/use-make-copilot-actionable.js +7 -8
  59. package/dist/hooks/use-make-copilot-actionable.js.map +1 -1
  60. package/dist/hooks/use-make-copilot-actionable.mjs +2 -2
  61. package/dist/hooks/use-make-copilot-document-readable.js +3 -4
  62. package/dist/hooks/use-make-copilot-document-readable.js.map +1 -1
  63. package/dist/hooks/use-make-copilot-document-readable.mjs +2 -2
  64. package/dist/hooks/use-make-copilot-readable.js +3 -4
  65. package/dist/hooks/use-make-copilot-readable.js.map +1 -1
  66. package/dist/hooks/use-make-copilot-readable.mjs +2 -2
  67. package/dist/index.d.ts +1 -3
  68. package/dist/index.js +227 -499
  69. package/dist/index.js.map +1 -1
  70. package/dist/index.mjs +12 -31
  71. package/dist/lib/copilot-task.d.ts +1 -0
  72. package/dist/lib/copilot-task.js +31 -110
  73. package/dist/lib/copilot-task.js.map +1 -1
  74. package/dist/lib/copilot-task.mjs +3 -4
  75. package/dist/lib/index.d.ts +1 -0
  76. package/dist/lib/index.js +33 -110
  77. package/dist/lib/index.js.map +1 -1
  78. package/dist/lib/index.mjs +3 -4
  79. package/dist/utils/extract.d.ts +1 -0
  80. package/dist/utils/extract.js +49 -109
  81. package/dist/utils/extract.js.map +1 -1
  82. package/dist/utils/extract.mjs +3 -4
  83. package/dist/utils/index.d.ts +1 -1
  84. package/dist/utils/index.js +52 -132
  85. package/dist/utils/index.js.map +1 -1
  86. package/dist/utils/index.mjs +5 -13
  87. package/package.json +6 -5
  88. package/src/components/copilot-provider/copilotkit.tsx +16 -33
  89. package/src/context/copilot-context.tsx +9 -16
  90. package/src/hooks/use-chat.ts +204 -212
  91. package/src/hooks/use-copilot-action.ts +7 -8
  92. package/src/hooks/use-copilot-chat.ts +12 -36
  93. package/src/hooks/use-make-copilot-actionable.ts +4 -4
  94. package/src/index.tsx +0 -1
  95. package/src/lib/copilot-task.ts +42 -42
  96. package/src/utils/extract.ts +61 -36
  97. package/src/utils/index.ts +0 -7
  98. package/dist/chunk-36FKUOWM.mjs +0 -137
  99. package/dist/chunk-36FKUOWM.mjs.map +0 -1
  100. package/dist/chunk-3XVO5UEG.mjs +0 -205
  101. package/dist/chunk-3XVO5UEG.mjs.map +0 -1
  102. package/dist/chunk-7GFKOIO7.mjs +0 -1
  103. package/dist/chunk-AJSY6LS5.mjs.map +0 -1
  104. package/dist/chunk-BABVSMJR.mjs +0 -1
  105. package/dist/chunk-BABVSMJR.mjs.map +0 -1
  106. package/dist/chunk-CYDWEPFL.mjs +0 -1
  107. package/dist/chunk-CYDWEPFL.mjs.map +0 -1
  108. package/dist/chunk-DY4YVUO3.mjs.map +0 -1
  109. package/dist/chunk-DY63PD22.mjs.map +0 -1
  110. package/dist/chunk-FRAKUJWH.mjs +0 -1
  111. package/dist/chunk-FRAKUJWH.mjs.map +0 -1
  112. package/dist/chunk-MJKBCG4U.mjs +0 -91
  113. package/dist/chunk-MJKBCG4U.mjs.map +0 -1
  114. package/dist/chunk-NKIUZSGZ.mjs.map +0 -1
  115. package/dist/chunk-NSUYO6TP.mjs.map +0 -1
  116. package/dist/chunk-SR4RW4CU.mjs.map +0 -1
  117. package/dist/chunk-ZFS5SQUT.mjs +0 -31
  118. package/dist/chunk-ZFS5SQUT.mjs.map +0 -1
  119. package/dist/chunk-ZHYCHXIO.mjs.map +0 -1
  120. package/dist/openai-assistants/hooks/index.d.ts +0 -2
  121. package/dist/openai-assistants/hooks/index.js +0 -277
  122. package/dist/openai-assistants/hooks/index.js.map +0 -1
  123. package/dist/openai-assistants/hooks/index.mjs +0 -18
  124. package/dist/openai-assistants/hooks/index.mjs.map +0 -1
  125. package/dist/openai-assistants/hooks/use-assistants.d.ts +0 -17
  126. package/dist/openai-assistants/hooks/use-assistants.js +0 -154
  127. package/dist/openai-assistants/hooks/use-assistants.js.map +0 -1
  128. package/dist/openai-assistants/hooks/use-assistants.mjs +0 -92
  129. package/dist/openai-assistants/hooks/use-assistants.mjs.map +0 -1
  130. package/dist/openai-assistants/hooks/use-copilot-chat-v2.d.ts +0 -44
  131. package/dist/openai-assistants/hooks/use-copilot-chat-v2.js +0 -277
  132. package/dist/openai-assistants/hooks/use-copilot-chat-v2.js.map +0 -1
  133. package/dist/openai-assistants/hooks/use-copilot-chat-v2.mjs +0 -19
  134. package/dist/openai-assistants/hooks/use-copilot-chat-v2.mjs.map +0 -1
  135. package/dist/openai-assistants/index.d.ts +0 -3
  136. package/dist/openai-assistants/index.js +0 -279
  137. package/dist/openai-assistants/index.js.map +0 -1
  138. package/dist/openai-assistants/index.mjs +0 -22
  139. package/dist/openai-assistants/index.mjs.map +0 -1
  140. package/dist/openai-assistants/utils/index.d.ts +0 -1
  141. package/dist/openai-assistants/utils/index.js +0 -73
  142. package/dist/openai-assistants/utils/index.js.map +0 -1
  143. package/dist/openai-assistants/utils/index.mjs +0 -9
  144. package/dist/openai-assistants/utils/index.mjs.map +0 -1
  145. package/dist/openai-assistants/utils/process-message-stream.d.ts +0 -3
  146. package/dist/openai-assistants/utils/process-message-stream.js +0 -71
  147. package/dist/openai-assistants/utils/process-message-stream.js.map +0 -1
  148. package/dist/openai-assistants/utils/process-message-stream.mjs +0 -8
  149. package/dist/openai-assistants/utils/process-message-stream.mjs.map +0 -1
  150. package/dist/utils/fetch-chat-completion.d.ts +0 -36
  151. package/dist/utils/fetch-chat-completion.js +0 -141
  152. package/dist/utils/fetch-chat-completion.js.map +0 -1
  153. package/dist/utils/fetch-chat-completion.mjs +0 -12
  154. package/dist/utils/fetch-chat-completion.mjs.map +0 -1
  155. package/src/openai-assistants/hooks/index.ts +0 -9
  156. package/src/openai-assistants/hooks/use-assistants.ts +0 -112
  157. package/src/openai-assistants/hooks/use-copilot-chat-v2.ts +0 -189
  158. package/src/openai-assistants/index.ts +0 -2
  159. package/src/openai-assistants/utils/index.ts +0 -1
  160. package/src/openai-assistants/utils/process-message-stream.ts +0 -25
  161. package/src/utils/fetch-chat-completion.ts +0 -120
  162. /package/dist/{chunk-B244LK6F.mjs.map → chunk-ATWK243A.mjs.map} +0 -0
  163. /package/dist/{chunk-S5LWO5V3.mjs.map → chunk-IBOL6LE5.mjs.map} +0 -0
  164. /package/dist/{chunk-MNZXQ6UH.mjs.map → chunk-WTGFKR3D.mjs.map} +0 -0
  165. /package/dist/{chunk-7GFKOIO7.mjs.map → chunk-YJLRG5U3.mjs.map} +0 -0
@@ -1,31 +1,26 @@
1
- import { useRef, useState, useContext, useEffect } from "react";
2
- import { CopilotContext } from "../context/copilot-context";
1
+ import { useRef } from "react";
3
2
  import {
4
- Message,
5
- ToolDefinition,
6
3
  FunctionCallHandler,
7
- encodeResult,
8
- FunctionCall,
9
4
  COPILOT_CLOUD_PUBLIC_API_KEY_HEADER,
5
+ Action,
6
+ actionParametersToJsonSchema,
10
7
  } from "@copilotkit/shared";
8
+ import {
9
+ Message,
10
+ TextMessage,
11
+ ActionExecutionMessage,
12
+ ResultMessage,
13
+ CopilotRuntimeClient,
14
+ convertMessagesToGqlInput,
15
+ convertGqlOutputToMessages,
16
+ MessageStatusCode,
17
+ MessageRole,
18
+ Role,
19
+ } from "@copilotkit/runtime-client-gql";
11
20
 
12
- import { nanoid } from "nanoid";
13
- import { fetchAndDecodeChatCompletion } from "../utils/fetch-chat-completion";
14
21
  import { CopilotApiConfig } from "../context";
15
- import untruncateJson from "untruncate-json";
16
22
 
17
23
  export type UseChatOptions = {
18
- /**
19
- * The API endpoint that accepts a `{ messages: Message[] }` object and returns
20
- * a stream of tokens of the AI chat response. Defaults to `/api/chat`.
21
- */
22
- api?: string;
23
- /**
24
- * A unique identifier for the chat. If not provided, a random one will be
25
- * generated. When provided, the `useChat` hook with the same `id` will
26
- * have shared states across components.
27
- */
28
- id?: string;
29
24
  /**
30
25
  * System messages of the chat. Defaults to an empty array.
31
26
  */
@@ -35,51 +30,17 @@ export type UseChatOptions = {
35
30
  * If the function returns a `ChatRequest` object, the request will be sent
36
31
  * automatically to the API and will be used to update the chat.
37
32
  */
38
- onFunctionCall?: React.MutableRefObject<FunctionCallHandler>;
39
- /**
40
- * HTTP headers to be sent with the API request.
41
- */
42
- headers?: Record<string, string> | Headers;
43
- /**
44
- * Extra body object to be sent with the API request.
45
- * @example
46
- * Send a `sessionId` to the API along with the messages.
47
- * ```js
48
- * useChat({
49
- * body: {
50
- * sessionId: '123',
51
- * }
52
- * })
53
- * ```
54
- */
55
- body?: object;
33
+ onFunctionCall?: FunctionCallHandler;
56
34
  /**
57
35
  * Function definitions to be sent to the API.
58
36
  */
59
- tools?: React.MutableRefObject<ToolDefinition[]>;
60
- };
37
+ actions: Action[];
61
38
 
62
- export type UseChatHelpers = {
63
- /**
64
- * Append a user message to the chat list. This triggers the API call to fetch
65
- * the assistant's response.
66
- * @param message The message to append
67
- */
68
- append: (message: Message) => Promise<void>;
69
39
  /**
70
- * Reload the last AI chat response for the given chat history. If the last
71
- * message isn't from the assistant, it will request the API to generate a
72
- * new response.
73
- */
74
- reload: () => Promise<void>;
75
- /**
76
- * Abort the current request immediately, keep the generated tokens if any.
40
+ * The CopilotKit API configuration.
77
41
  */
78
- stop: () => void;
79
- };
80
-
81
- export type UseChatOptionsWithCopilotConfig = UseChatOptions & {
82
42
  copilotConfig: CopilotApiConfig;
43
+
83
44
  /**
84
45
  * The current list of messages in the chat.
85
46
  */
@@ -92,7 +53,7 @@ export type UseChatOptionsWithCopilotConfig = UseChatOptions & {
92
53
  /**
93
54
  * A callback to get the latest system message.
94
55
  */
95
- makeSystemMessageCallback: () => Message;
56
+ makeSystemMessageCallback: () => TextMessage;
96
57
 
97
58
  /**
98
59
  * Whether the API request is in progress
@@ -105,78 +66,130 @@ export type UseChatOptionsWithCopilotConfig = UseChatOptions & {
105
66
  setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
106
67
  };
107
68
 
108
- export function useChat(options: UseChatOptionsWithCopilotConfig): UseChatHelpers {
109
- const { messages, setMessages, makeSystemMessageCallback } = options;
69
+ export type UseChatHelpers = {
70
+ /**
71
+ * Append a user message to the chat list. This triggers the API call to fetch
72
+ * the assistant's response.
73
+ * @param message The message to append
74
+ */
75
+ append: (message: Message) => Promise<void>;
76
+ /**
77
+ * Reload the last AI chat response for the given chat history. If the last
78
+ * message isn't from the assistant, it will request the API to generate a
79
+ * new response.
80
+ */
81
+ reload: () => Promise<void>;
82
+ /**
83
+ * Abort the current request immediately, keep the generated tokens if any.
84
+ */
85
+ stop: () => void;
86
+ };
87
+
88
+ export function useChat(options: UseChatOptions): UseChatHelpers {
89
+ const {
90
+ messages,
91
+ setMessages,
92
+ makeSystemMessageCallback,
93
+ copilotConfig,
94
+ setIsLoading,
95
+ initialMessages,
96
+ isLoading,
97
+ actions,
98
+ onFunctionCall,
99
+ } = options;
110
100
  const abortControllerRef = useRef<AbortController>();
111
101
  const threadIdRef = useRef<string | null>(null);
112
102
  const runIdRef = useRef<string | null>(null);
113
- const publicApiKey = options.copilotConfig.publicApiKey;
103
+ const publicApiKey = copilotConfig.publicApiKey;
114
104
  const headers = {
115
- ...(options.headers || {}),
105
+ ...(copilotConfig.headers || {}),
116
106
  ...(publicApiKey ? { [COPILOT_CLOUD_PUBLIC_API_KEY_HEADER]: publicApiKey } : {}),
117
107
  };
118
108
 
119
- const runChatCompletion = async (messages: Message[]): Promise<Message[]> => {
120
- options.setIsLoading(true);
109
+ const runtimeClient = new CopilotRuntimeClient({
110
+ url: copilotConfig.chatApiEndpoint,
111
+ publicApiKey: copilotConfig.publicApiKey,
112
+ headers,
113
+ });
114
+
115
+ const runChatCompletion = async (previousMessages: Message[]): Promise<Message[]> => {
116
+ setIsLoading(true);
121
117
 
122
- const newMessages: Message[] = [
123
- {
124
- id: nanoid(),
125
- createdAt: new Date(),
118
+ // this message is just a placeholder. It will disappear once the first real message
119
+ // is received
120
+ let newMessages: Message[] = [
121
+ new TextMessage({
126
122
  content: "",
127
- role: "assistant",
128
- },
123
+ role: Role.Assistant,
124
+ }),
129
125
  ];
130
126
  const abortController = new AbortController();
131
127
  abortControllerRef.current = abortController;
132
128
 
133
- setMessages([...messages, ...newMessages]);
134
- // add threadId and runId to the body if it exists
135
- const copilotConfigBody = options.copilotConfig.body || {};
136
- if (threadIdRef.current) {
137
- copilotConfigBody.threadId = threadIdRef.current;
138
- }
139
- if (runIdRef.current) {
140
- copilotConfigBody.runId = runIdRef.current;
141
- }
129
+ setMessages([...previousMessages, ...newMessages]);
142
130
 
143
131
  const systemMessage = makeSystemMessageCallback();
144
132
 
145
- const messagesWithContext = [systemMessage, ...(options.initialMessages || []), ...messages];
146
- const response = await fetchAndDecodeChatCompletion({
147
- copilotConfig: { ...options.copilotConfig, body: copilotConfigBody },
148
- messages: messagesWithContext,
149
- tools: options.tools?.current,
150
- headers: headers,
151
- signal: abortController.signal,
152
- });
153
-
154
- if (response.headers.get("threadid")) {
155
- threadIdRef.current = response.headers.get("threadid");
156
- }
133
+ const messagesWithContext = [systemMessage, ...(initialMessages || []), ...previousMessages];
157
134
 
158
- if (response.headers.get("runid")) {
159
- runIdRef.current = response.headers.get("runid");
160
- }
161
-
162
- if (!response.events) {
163
- setMessages([
164
- ...messages,
165
- {
166
- id: nanoid(),
167
- createdAt: new Date(),
168
- content: response.statusText,
169
- role: "assistant",
135
+ const stream = CopilotRuntimeClient.asStream(
136
+ runtimeClient.generateCopilotResponse({
137
+ frontend: {
138
+ actions: actions.map((action) => ({
139
+ name: action.name,
140
+ description: action.description || "",
141
+ jsonSchema: JSON.stringify(actionParametersToJsonSchema(action.parameters || [])),
142
+ })),
170
143
  },
171
- ]);
172
- options.setIsLoading(false);
173
- throw new Error("Failed to fetch chat completion");
174
- }
175
-
176
- const reader = response.events.getReader();
177
-
178
- // Whether to feed back the new messages to GPT
179
- let feedback = false;
144
+ threadId: threadIdRef.current,
145
+ runId: runIdRef.current,
146
+ messages: convertMessagesToGqlInput(messagesWithContext),
147
+ ...(copilotConfig.cloud
148
+ ? {
149
+ cloud: {
150
+ guardrails: {
151
+ inputValidationRules: {
152
+ allowList: copilotConfig.cloud.guardrails.input.restrictToTopic.validTopics,
153
+ denyList: copilotConfig.cloud.guardrails.input.restrictToTopic.invalidTopics,
154
+ },
155
+ },
156
+ },
157
+ }
158
+ : {}),
159
+ }),
160
+ );
161
+
162
+ const guardrailsEnabled =
163
+ copilotConfig.cloud?.guardrails?.input?.restrictToTopic.enabled || false;
164
+
165
+ // TODO-PROTOCOL make sure all options are included in the final version
166
+ //
167
+ // const response = await fetchAndDecodeChatCompletion({
168
+ // copilotConfig: { ...options.copilotConfig, body: copilotConfigBody },
169
+ // messages: messagesWithContext,
170
+ // tools: options.tools,
171
+ // headers: headers,
172
+ // signal: abortController.signal,
173
+ // });
174
+
175
+ // TODO-PROTOCOL handle errors
176
+ // if (!response.events) {
177
+ // setMessages([
178
+ // ...messages,
179
+ // {
180
+ // id: nanoid(),
181
+ // createdAt: new Date(),
182
+ // content: response.statusText,
183
+ // role: "assistant",
184
+ // },
185
+ // ]);
186
+ // options.setIsLoading(false);
187
+ // throw new Error("Failed to fetch chat completion");
188
+ // }
189
+
190
+ const reader = stream.getReader();
191
+
192
+ let results: { [id: string]: string } = {};
180
193
 
181
194
  try {
182
195
  while (true) {
@@ -186,116 +199,94 @@ export function useChat(options: UseChatOptionsWithCopilotConfig): UseChatHelper
186
199
  break;
187
200
  }
188
201
 
189
- let currentMessage = Object.assign({}, newMessages[newMessages.length - 1]);
190
-
191
- if (value.type === "content") {
192
- if (currentMessage.function_call || currentMessage.role === "function") {
193
- // Create a new message if the previous one is a function call or result
194
- currentMessage = {
195
- id: nanoid(),
196
- createdAt: new Date(),
197
- content: "",
198
- role: "assistant",
199
- };
200
- newMessages.push(currentMessage);
201
- }
202
- currentMessage.content += value.content;
203
- newMessages[newMessages.length - 1] = currentMessage;
204
- setMessages([...messages, ...newMessages]);
205
- } else if (value.type === "result") {
206
- // When we get a result message, it is already complete
207
- currentMessage = {
208
- id: nanoid(),
209
- role: "function",
210
- content: value.content,
211
- name: value.name,
212
- };
213
- newMessages.push(currentMessage);
214
- setMessages([...messages, ...newMessages]);
215
-
216
- // After receiving a result, feed back the new messages to GPT
217
- feedback = true;
218
- } else if (value.type === "function" || value.type === "partial") {
219
- // Create a new message if the previous one is not empty
220
- if (
221
- currentMessage.content != "" ||
222
- currentMessage.function_call ||
223
- currentMessage.role == "function"
224
- ) {
225
- currentMessage = {
226
- id: nanoid(),
227
- createdAt: new Date(),
228
- content: "",
229
- role: "assistant",
230
- };
231
- newMessages.push(currentMessage);
232
- }
233
- if (value.type === "function") {
234
- currentMessage.function_call = {
235
- name: value.name,
236
- arguments: JSON.stringify(value.arguments),
237
- scope: value.scope,
238
- };
239
- } else if (value.type === "partial") {
240
- let partialArguments: any = {};
241
- try {
242
- partialArguments = JSON.parse(untruncateJson(value.arguments));
243
- } catch (e) {}
244
-
245
- currentMessage.partialFunctionCall = {
246
- name: value.name,
247
- arguments: partialArguments,
248
- };
249
- }
202
+ if (!value.generateCopilotResponse) {
203
+ continue;
204
+ }
205
+
206
+ threadIdRef.current = value.generateCopilotResponse.threadId || null;
207
+ runIdRef.current = value.generateCopilotResponse.runId || null;
208
+
209
+ const messages = convertGqlOutputToMessages(value.generateCopilotResponse.messages);
210
+
211
+ if (messages.length === 0) {
212
+ continue;
213
+ }
214
+
215
+ newMessages = [];
216
+
217
+ // request failed, display error message
218
+ if (
219
+ value.generateCopilotResponse.status?.__typename === "FailedResponseStatus" &&
220
+ value.generateCopilotResponse.status.reason === "GUARDRAILS_VALIDATION_FAILED"
221
+ ) {
222
+ newMessages = [
223
+ new TextMessage({
224
+ role: MessageRole.Assistant,
225
+ content: value.generateCopilotResponse.status.details?.guardrailsReason || "",
226
+ }),
227
+ ];
228
+ }
250
229
 
251
- newMessages[newMessages.length - 1] = currentMessage;
252
- setMessages([...messages, ...newMessages]);
253
-
254
- if (value.type === "function") {
255
- // Execute the function call
256
- try {
257
- if (options.onFunctionCall?.current && value.scope === "client") {
258
- const result = await options.onFunctionCall.current(
259
- messages,
260
- currentMessage.function_call as FunctionCall,
261
- );
262
-
263
- currentMessage = {
264
- id: nanoid(),
265
- role: "function",
266
- content: encodeResult(result),
267
- name: (currentMessage.function_call! as FunctionCall).name!,
268
- };
269
- newMessages.push(currentMessage);
270
- setMessages([...messages, ...newMessages]);
271
-
272
- // After a function call, feed back the new messages to GPT
273
- feedback = true;
230
+ // add messages to the chat
231
+ else {
232
+ for (const message of messages) {
233
+ newMessages.push(message);
234
+
235
+ if (
236
+ message instanceof ActionExecutionMessage &&
237
+ message.status.code !== MessageStatusCode.Pending &&
238
+ message.scope === "client" &&
239
+ onFunctionCall
240
+ ) {
241
+ if (!(message.id in results)) {
242
+ // Do not execute a function call if guardrails are enabled but the status is not known
243
+ if (guardrailsEnabled && value.generateCopilotResponse.status === undefined) {
244
+ break;
245
+ }
246
+ // execute action
247
+ const result = await onFunctionCall({
248
+ messages: previousMessages,
249
+ name: message.name,
250
+ args: message.arguments,
251
+ });
252
+ results[message.id] = result;
274
253
  }
275
- } catch (error) {
276
- console.error("Failed to execute function call", error);
277
- // TODO: Handle error
278
- // this should go to the message itself
254
+
255
+ // add the result message
256
+ newMessages.push(
257
+ new ResultMessage({
258
+ result: ResultMessage.encodeResult(results[message.id]),
259
+ actionExecutionId: message.id,
260
+ actionName: message.name,
261
+ }),
262
+ );
279
263
  }
280
264
  }
281
265
  }
266
+
267
+ if (newMessages.length > 0) {
268
+ setMessages([...previousMessages, ...newMessages]);
269
+ }
282
270
  }
283
271
 
284
- // If we want feedback, run the completion again and return the results
285
- if (feedback) {
272
+ if (
273
+ // if we have client side results
274
+ Object.values(results).length ||
275
+ // or the last message we received is a result
276
+ (newMessages.length && newMessages[newMessages.length - 1] instanceof ResultMessage)
277
+ ) {
278
+ // run the completion again and return the result
279
+
286
280
  // wait for next tick to make sure all the react state updates
287
- // TODO: This is a hack, is there a more robust way to do this?
288
281
  // - tried using react-dom's flushSync, but it did not work
289
282
  await new Promise((resolve) => setTimeout(resolve, 10));
290
283
 
291
- return await runChatCompletion([...messages, ...newMessages]);
292
- }
293
- // otherwise, return the new messages
294
- else {
284
+ return await runChatCompletion([...previousMessages, ...newMessages]);
285
+ } else {
295
286
  return newMessages.slice();
296
287
  }
297
288
  } finally {
298
- options.setIsLoading(false);
289
+ setIsLoading(false);
299
290
  }
300
291
  };
301
292
 
@@ -304,7 +295,7 @@ export function useChat(options: UseChatOptionsWithCopilotConfig): UseChatHelper
304
295
  };
305
296
 
306
297
  const append = async (message: Message): Promise<void> => {
307
- if (options.isLoading) {
298
+ if (isLoading) {
308
299
  return;
309
300
  }
310
301
  const newMessages = [...messages, message];
@@ -313,15 +304,16 @@ export function useChat(options: UseChatOptionsWithCopilotConfig): UseChatHelper
313
304
  };
314
305
 
315
306
  const reload = async (): Promise<void> => {
316
- if (options.isLoading || messages.length === 0) {
307
+ if (isLoading || messages.length === 0) {
317
308
  return;
318
309
  }
319
310
  let newMessages = [...messages];
320
311
  const lastMessage = messages[messages.length - 1];
321
312
 
322
- if (lastMessage.role === "assistant") {
313
+ if (lastMessage instanceof TextMessage && lastMessage.role === "assistant") {
323
314
  newMessages = newMessages.slice(0, -1);
324
315
  }
316
+
325
317
  setMessages(newMessages);
326
318
 
327
319
  return runChatCompletionAndHandleFunctionCall(newMessages);
@@ -17,16 +17,15 @@ export function useCopilotAction<const T extends Parameter[] | [] = []>(
17
17
  action: FrontendAction<T>,
18
18
  dependencies?: any[],
19
19
  ): void {
20
- const { setEntryPoint, removeEntryPoint, entryPoints, chatComponentsCache } =
21
- useContext(CopilotContext);
20
+ const { setAction, removeAction, actions, chatComponentsCache } = useContext(CopilotContext);
22
21
  const idRef = useRef<string>(nanoid());
23
22
 
24
23
  // If the developer doesn't provide dependencies, we assume they want to
25
24
  // update handler and render function when the action object changes.
26
25
  // This ensures that any captured variables in the handler are up to date.
27
26
  if (dependencies === undefined) {
28
- if (entryPoints[idRef.current]) {
29
- entryPoints[idRef.current].handler = action.handler as any;
27
+ if (actions[idRef.current]) {
28
+ actions[idRef.current].handler = action.handler as any;
30
29
  if (typeof action.render === "function") {
31
30
  if (chatComponentsCache.current !== null) {
32
31
  chatComponentsCache.current[action.name] = action.render;
@@ -36,18 +35,18 @@ export function useCopilotAction<const T extends Parameter[] | [] = []>(
36
35
  }
37
36
 
38
37
  useEffect(() => {
39
- setEntryPoint(idRef.current, action as any);
38
+ setAction(idRef.current, action as any);
40
39
  if (chatComponentsCache.current !== null && action.render !== undefined) {
41
40
  chatComponentsCache.current[action.name] = action.render;
42
41
  }
43
42
  return () => {
44
43
  // NOTE: For now, we don't remove the chatComponentsCache entry when the action is removed.
45
44
  // This is because we currently don't have access to the messages array in CopilotContext.
46
- removeEntryPoint(idRef.current);
45
+ removeAction(idRef.current);
47
46
  };
48
47
  }, [
49
- setEntryPoint,
50
- removeEntryPoint,
48
+ setAction,
49
+ removeAction,
51
50
  action.description,
52
51
  action.name,
53
52
  // This should be faster than deep equality checking
@@ -26,12 +26,13 @@
26
26
  * - `isLoading`: A boolean indicating if the chat is loading.
27
27
  *
28
28
  */
29
- import { useMemo, useContext, useRef, useEffect, useCallback } from "react";
29
+ import { useContext, useRef, useEffect, useCallback } from "react";
30
30
  import { CopilotContext } from "../context/copilot-context";
31
- import { Message, ToolDefinition } from "@copilotkit/shared";
31
+ import { Message, Role, TextMessage } from "@copilotkit/runtime-client-gql";
32
32
  import { SystemMessageFunction } from "../types";
33
33
  import { useChat } from "./use-chat";
34
34
  import { defaultCopilotContextCategories } from "../components";
35
+ import { MessageStatusCode } from "@copilotkit/runtime-client-gql";
35
36
 
36
37
  export interface UseCopilotChatOptions {
37
38
  /**
@@ -86,7 +87,6 @@ export function useCopilotChat({
86
87
  }: UseCopilotChatOptions = {}): UseCopilotChatReturn {
87
88
  const {
88
89
  getContextString,
89
- getChatCompletionFunctionDescriptions,
90
90
  getFunctionCallHandler,
91
91
  copilotApiConfig,
92
92
  messages,
@@ -94,6 +94,7 @@ export function useCopilotChat({
94
94
  isLoading,
95
95
  setIsLoading,
96
96
  chatInstructions,
97
+ actions,
97
98
  } = useContext(CopilotContext);
98
99
 
99
100
  // We need to ensure that makeSystemMessageCallback always uses the latest
@@ -111,31 +112,18 @@ export function useCopilotChat({
111
112
  // this always gets the latest context string
112
113
  const contextString = latestGetContextString.current([], defaultCopilotContextCategories); // TODO: make the context categories configurable
113
114
 
114
- return {
115
- id: "system",
115
+ return new TextMessage({
116
116
  content: systemMessageMaker(contextString, chatInstructions),
117
- role: "system",
118
- } as Message;
117
+ role: Role.System,
118
+ });
119
119
  }, [getContextString, makeSystemMessage, chatInstructions]);
120
120
 
121
- const functionDescriptions: ToolDefinition[] = useMemo(() => {
122
- return getChatCompletionFunctionDescriptions();
123
- }, [getChatCompletionFunctionDescriptions]);
124
-
125
- const tools = useUpdatedRef(functionDescriptions);
126
- const onFunctionCall = useUpdatedRef(getFunctionCallHandler());
127
-
128
121
  const { append, reload, stop } = useChat({
129
122
  ...options,
123
+ actions: Object.values(actions),
130
124
  copilotConfig: copilotApiConfig,
131
- id: options.id,
132
125
  initialMessages: options.initialMessages || [],
133
- tools: tools,
134
- onFunctionCall: onFunctionCall,
135
- headers: { ...options.headers },
136
- body: {
137
- ...options.body,
138
- },
126
+ onFunctionCall: getFunctionCallHandler(),
139
127
  messages,
140
128
  setMessages,
141
129
  makeSystemMessageCallback,
@@ -143,13 +131,8 @@ export function useCopilotChat({
143
131
  setIsLoading,
144
132
  });
145
133
 
146
- const visibleMessages = messages.filter(
147
- (message) =>
148
- message.role === "user" || message.role === "assistant" || message.role === "function",
149
- );
150
-
151
134
  return {
152
- visibleMessages,
135
+ visibleMessages: messages,
153
136
  appendMessage: append,
154
137
  setMessages,
155
138
  reloadMessages: reload,
@@ -159,15 +142,8 @@ export function useCopilotChat({
159
142
  };
160
143
  }
161
144
 
162
- /**
163
- * A lot of our functions get called in the same run loop. Hooks get called with state,
164
- * then process async long running loops. If the hook gets called with the new state,
165
- * we want ongoing runloops to use the latest state.
166
- *
167
- * For example, the first action updated the context, now the second action should
168
- * see the latest context.
169
- */
170
-
145
+ // store `value` in a ref and update
146
+ // it whenever it changes.
171
147
  function useUpdatedRef<T>(value: T) {
172
148
  const ref = useRef(value);
173
149
 
@@ -12,7 +12,7 @@ export function useMakeCopilotActionable<ActionInput extends any[]>(
12
12
  dependencies: any[],
13
13
  ) {
14
14
  const idRef = useRef(nanoid()); // generate a unique id
15
- const { setEntryPoint, removeEntryPoint } = useContext(CopilotContext);
15
+ const { setAction, removeAction } = useContext(CopilotContext);
16
16
 
17
17
  const memoizedAnnotatedFunction: AnnotatedFunction<ActionInput> = useMemo(
18
18
  () => ({
@@ -26,10 +26,10 @@ export function useMakeCopilotActionable<ActionInput extends any[]>(
26
26
 
27
27
  useEffect(() => {
28
28
  const action = annotatedFunctionToAction(memoizedAnnotatedFunction as AnnotatedFunction<any[]>);
29
- setEntryPoint(idRef.current, action);
29
+ setAction(idRef.current, action);
30
30
 
31
31
  return () => {
32
- removeEntryPoint(idRef.current);
32
+ removeAction(idRef.current);
33
33
  };
34
- }, [memoizedAnnotatedFunction, setEntryPoint, removeEntryPoint]);
34
+ }, [memoizedAnnotatedFunction, setAction, removeAction]);
35
35
  }