@copilotkit/react-core 0.37.0 → 0.38.0-beta.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 (168) hide show
  1. package/.turbo/turbo-build.log +187 -234
  2. package/CHANGELOG.md +12 -0
  3. package/dist/{chunk-HUKLBIUK.mjs → chunk-5CKW6KE7.mjs} +13 -22
  4. package/dist/chunk-5CKW6KE7.mjs.map +1 -0
  5. package/dist/{chunk-S5LWO5V3.mjs → chunk-7GPIOOXB.mjs} +2 -2
  6. package/dist/{chunk-LLU5URI5.mjs → chunk-DDHJCFFV.mjs} +18 -33
  7. package/dist/chunk-DDHJCFFV.mjs.map +1 -0
  8. package/dist/{chunk-SR4RW4CU.mjs → chunk-IQCLNCGL.mjs} +6 -6
  9. package/dist/chunk-IQCLNCGL.mjs.map +1 -0
  10. package/dist/{chunk-DY63PD22.mjs → chunk-K6EV2CNB.mjs} +4 -5
  11. package/dist/chunk-K6EV2CNB.mjs.map +1 -0
  12. package/dist/{chunk-NSUYO6TP.mjs → chunk-LZVHW3T3.mjs} +9 -9
  13. package/dist/chunk-LZVHW3T3.mjs.map +1 -0
  14. package/dist/{chunk-Z5FB4WBL.mjs → chunk-NY6QSOU7.mjs} +43 -40
  15. package/dist/chunk-NY6QSOU7.mjs.map +1 -0
  16. package/dist/chunk-PHMHNAYC.mjs +192 -0
  17. package/dist/chunk-PHMHNAYC.mjs.map +1 -0
  18. package/dist/{chunk-B244LK6F.mjs → chunk-QNJQKKD4.mjs} +2 -2
  19. package/dist/{chunk-4OIVQMEM.mjs → chunk-WU3I3G3G.mjs} +60 -42
  20. package/dist/chunk-WU3I3G3G.mjs.map +1 -0
  21. package/dist/chunk-YJLRG5U3.mjs +1 -0
  22. package/dist/{chunk-MNZXQ6UH.mjs → chunk-YM2JV2YQ.mjs} +2 -2
  23. package/dist/components/copilot-provider/copilotkit-props.d.ts +2 -2
  24. package/dist/components/copilot-provider/copilotkit-props.js.map +1 -1
  25. package/dist/components/copilot-provider/copilotkit.js +18 -33
  26. package/dist/components/copilot-provider/copilotkit.js.map +1 -1
  27. package/dist/components/copilot-provider/copilotkit.mjs +2 -2
  28. package/dist/components/copilot-provider/index.js +18 -33
  29. package/dist/components/copilot-provider/index.js.map +1 -1
  30. package/dist/components/copilot-provider/index.mjs +2 -2
  31. package/dist/components/index.js +18 -33
  32. package/dist/components/index.js.map +1 -1
  33. package/dist/components/index.mjs +2 -2
  34. package/dist/context/copilot-context.d.ts +7 -18
  35. package/dist/context/copilot-context.js +3 -4
  36. package/dist/context/copilot-context.js.map +1 -1
  37. package/dist/context/copilot-context.mjs +1 -1
  38. package/dist/context/index.d.ts +1 -0
  39. package/dist/context/index.js +3 -4
  40. package/dist/context/index.js.map +1 -1
  41. package/dist/context/index.mjs +1 -1
  42. package/dist/hooks/index.d.ts +2 -1
  43. package/dist/hooks/index.js +140 -229
  44. package/dist/hooks/index.js.map +1 -1
  45. package/dist/hooks/index.mjs +9 -10
  46. package/dist/hooks/use-chat.d.ts +25 -51
  47. package/dist/hooks/use-chat.js +109 -203
  48. package/dist/hooks/use-chat.js.map +1 -1
  49. package/dist/hooks/use-chat.mjs +1 -2
  50. package/dist/hooks/use-copilot-action.js +10 -11
  51. package/dist/hooks/use-copilot-action.js.map +1 -1
  52. package/dist/hooks/use-copilot-action.mjs +2 -2
  53. package/dist/hooks/use-copilot-chat.d.ts +1 -1
  54. package/dist/hooks/use-copilot-chat.js +123 -212
  55. package/dist/hooks/use-copilot-chat.js.map +1 -1
  56. package/dist/hooks/use-copilot-chat.mjs +4 -5
  57. package/dist/hooks/use-copilot-readable.js +3 -4
  58. package/dist/hooks/use-copilot-readable.js.map +1 -1
  59. package/dist/hooks/use-copilot-readable.mjs +2 -2
  60. package/dist/hooks/use-make-copilot-actionable.js +7 -8
  61. package/dist/hooks/use-make-copilot-actionable.js.map +1 -1
  62. package/dist/hooks/use-make-copilot-actionable.mjs +2 -2
  63. package/dist/hooks/use-make-copilot-document-readable.js +3 -4
  64. package/dist/hooks/use-make-copilot-document-readable.js.map +1 -1
  65. package/dist/hooks/use-make-copilot-document-readable.mjs +2 -2
  66. package/dist/hooks/use-make-copilot-readable.js +3 -4
  67. package/dist/hooks/use-make-copilot-readable.js.map +1 -1
  68. package/dist/hooks/use-make-copilot-readable.mjs +2 -2
  69. package/dist/index.d.ts +1 -3
  70. package/dist/index.js +239 -497
  71. package/dist/index.js.map +1 -1
  72. package/dist/index.mjs +12 -31
  73. package/dist/lib/copilot-task.d.ts +1 -0
  74. package/dist/lib/copilot-task.js +34 -110
  75. package/dist/lib/copilot-task.js.map +1 -1
  76. package/dist/lib/copilot-task.mjs +3 -4
  77. package/dist/lib/index.d.ts +1 -0
  78. package/dist/lib/index.js +36 -110
  79. package/dist/lib/index.js.map +1 -1
  80. package/dist/lib/index.mjs +3 -4
  81. package/dist/utils/extract.d.ts +1 -0
  82. package/dist/utils/extract.js +53 -109
  83. package/dist/utils/extract.js.map +1 -1
  84. package/dist/utils/extract.mjs +3 -4
  85. package/dist/utils/index.d.ts +1 -1
  86. package/dist/utils/index.js +56 -132
  87. package/dist/utils/index.js.map +1 -1
  88. package/dist/utils/index.mjs +5 -13
  89. package/package.json +6 -5
  90. package/src/components/copilot-provider/copilotkit-props.tsx +2 -2
  91. package/src/components/copilot-provider/copilotkit.tsx +16 -37
  92. package/src/context/copilot-context.tsx +11 -30
  93. package/src/hooks/use-chat.ts +179 -208
  94. package/src/hooks/use-copilot-action.ts +7 -8
  95. package/src/hooks/use-copilot-chat.ts +9 -23
  96. package/src/hooks/use-make-copilot-actionable.ts +4 -4
  97. package/src/index.tsx +0 -1
  98. package/src/lib/copilot-task.ts +45 -42
  99. package/src/utils/extract.ts +64 -36
  100. package/src/utils/index.ts +0 -7
  101. package/dist/chunk-2EQGN5QK.mjs +0 -137
  102. package/dist/chunk-2EQGN5QK.mjs.map +0 -1
  103. package/dist/chunk-4OIVQMEM.mjs.map +0 -1
  104. package/dist/chunk-7GFKOIO7.mjs +0 -1
  105. package/dist/chunk-B2H3NC4E.mjs +0 -204
  106. package/dist/chunk-B2H3NC4E.mjs.map +0 -1
  107. package/dist/chunk-BABVSMJR.mjs +0 -1
  108. package/dist/chunk-BABVSMJR.mjs.map +0 -1
  109. package/dist/chunk-CYDWEPFL.mjs +0 -1
  110. package/dist/chunk-CYDWEPFL.mjs.map +0 -1
  111. package/dist/chunk-DY63PD22.mjs.map +0 -1
  112. package/dist/chunk-FRAKUJWH.mjs +0 -1
  113. package/dist/chunk-FRAKUJWH.mjs.map +0 -1
  114. package/dist/chunk-HUKLBIUK.mjs.map +0 -1
  115. package/dist/chunk-LLU5URI5.mjs.map +0 -1
  116. package/dist/chunk-MJKBCG4U.mjs +0 -91
  117. package/dist/chunk-MJKBCG4U.mjs.map +0 -1
  118. package/dist/chunk-NSUYO6TP.mjs.map +0 -1
  119. package/dist/chunk-SR4RW4CU.mjs.map +0 -1
  120. package/dist/chunk-Z5FB4WBL.mjs.map +0 -1
  121. package/dist/chunk-ZFS5SQUT.mjs +0 -31
  122. package/dist/chunk-ZFS5SQUT.mjs.map +0 -1
  123. package/dist/openai-assistants/hooks/index.d.ts +0 -2
  124. package/dist/openai-assistants/hooks/index.js +0 -277
  125. package/dist/openai-assistants/hooks/index.js.map +0 -1
  126. package/dist/openai-assistants/hooks/index.mjs +0 -18
  127. package/dist/openai-assistants/hooks/index.mjs.map +0 -1
  128. package/dist/openai-assistants/hooks/use-assistants.d.ts +0 -17
  129. package/dist/openai-assistants/hooks/use-assistants.js +0 -154
  130. package/dist/openai-assistants/hooks/use-assistants.js.map +0 -1
  131. package/dist/openai-assistants/hooks/use-assistants.mjs +0 -92
  132. package/dist/openai-assistants/hooks/use-assistants.mjs.map +0 -1
  133. package/dist/openai-assistants/hooks/use-copilot-chat-v2.d.ts +0 -44
  134. package/dist/openai-assistants/hooks/use-copilot-chat-v2.js +0 -277
  135. package/dist/openai-assistants/hooks/use-copilot-chat-v2.js.map +0 -1
  136. package/dist/openai-assistants/hooks/use-copilot-chat-v2.mjs +0 -19
  137. package/dist/openai-assistants/hooks/use-copilot-chat-v2.mjs.map +0 -1
  138. package/dist/openai-assistants/index.d.ts +0 -3
  139. package/dist/openai-assistants/index.js +0 -279
  140. package/dist/openai-assistants/index.js.map +0 -1
  141. package/dist/openai-assistants/index.mjs +0 -22
  142. package/dist/openai-assistants/index.mjs.map +0 -1
  143. package/dist/openai-assistants/utils/index.d.ts +0 -1
  144. package/dist/openai-assistants/utils/index.js +0 -73
  145. package/dist/openai-assistants/utils/index.js.map +0 -1
  146. package/dist/openai-assistants/utils/index.mjs +0 -9
  147. package/dist/openai-assistants/utils/index.mjs.map +0 -1
  148. package/dist/openai-assistants/utils/process-message-stream.d.ts +0 -3
  149. package/dist/openai-assistants/utils/process-message-stream.js +0 -71
  150. package/dist/openai-assistants/utils/process-message-stream.js.map +0 -1
  151. package/dist/openai-assistants/utils/process-message-stream.mjs +0 -8
  152. package/dist/openai-assistants/utils/process-message-stream.mjs.map +0 -1
  153. package/dist/utils/fetch-chat-completion.d.ts +0 -36
  154. package/dist/utils/fetch-chat-completion.js +0 -141
  155. package/dist/utils/fetch-chat-completion.js.map +0 -1
  156. package/dist/utils/fetch-chat-completion.mjs +0 -12
  157. package/dist/utils/fetch-chat-completion.mjs.map +0 -1
  158. package/src/openai-assistants/hooks/index.ts +0 -9
  159. package/src/openai-assistants/hooks/use-assistants.ts +0 -112
  160. package/src/openai-assistants/hooks/use-copilot-chat-v2.ts +0 -189
  161. package/src/openai-assistants/index.ts +0 -2
  162. package/src/openai-assistants/utils/index.ts +0 -1
  163. package/src/openai-assistants/utils/process-message-stream.ts +0 -25
  164. package/src/utils/fetch-chat-completion.ts +0 -120
  165. /package/dist/{chunk-S5LWO5V3.mjs.map → chunk-7GPIOOXB.mjs.map} +0 -0
  166. /package/dist/{chunk-B244LK6F.mjs.map → chunk-QNJQKKD4.mjs.map} +0 -0
  167. /package/dist/{chunk-7GFKOIO7.mjs.map → chunk-YJLRG5U3.mjs.map} +0 -0
  168. /package/dist/{chunk-MNZXQ6UH.mjs.map → chunk-YM2JV2YQ.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
  */
@@ -36,50 +31,16 @@ export type UseChatOptions = {
36
31
  * automatically to the API and will be used to update the chat.
37
32
  */
38
33
  onFunctionCall?: 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;
56
34
  /**
57
35
  * Function definitions to be sent to the API.
58
36
  */
59
- tools?: ToolDefinition[];
60
- };
37
+ actions: Action[];
61
38
 
62
- export type UseChatHelpers = {
63
39
  /**
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
40
+ * The CopilotKit API configuration.
67
41
  */
68
- append: (message: Message) => Promise<void>;
69
- /**
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.
77
- */
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,109 @@ 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,
150
- headers: headers,
151
- signal: abortController.signal,
152
- });
153
-
154
- if (response.headers.get("threadid")) {
155
- threadIdRef.current = response.headers.get("threadid");
156
- }
157
-
158
- if (response.headers.get("runid")) {
159
- runIdRef.current = response.headers.get("runid");
160
- }
133
+ const messagesWithContext = [systemMessage, ...(initialMessages || []), ...previousMessages];
161
134
 
162
- if (!response.events) {
163
- setMessages([
164
- ...messages,
135
+ const stream = CopilotRuntimeClient.asStream(
136
+ runtimeClient.generateCopilotResponse(
165
137
  {
166
- id: nanoid(),
167
- createdAt: new Date(),
168
- content: response.statusText,
169
- role: "assistant",
138
+ frontend: {
139
+ actions: actions.map((action) => ({
140
+ name: action.name,
141
+ description: action.description || "",
142
+ jsonSchema: JSON.stringify(actionParametersToJsonSchema(action.parameters || [])),
143
+ })),
144
+ },
145
+ threadId: threadIdRef.current,
146
+ runId: runIdRef.current,
147
+ messages: convertMessagesToGqlInput(messagesWithContext),
148
+ ...(copilotConfig.cloud
149
+ ? {
150
+ cloud: {
151
+ guardrails: {
152
+ inputValidationRules: {
153
+ allowList: copilotConfig.cloud.guardrails.input.restrictToTopic.validTopics,
154
+ denyList: copilotConfig.cloud.guardrails.input.restrictToTopic.invalidTopics,
155
+ },
156
+ },
157
+ },
158
+ }
159
+ : {}),
170
160
  },
171
- ]);
172
- options.setIsLoading(false);
173
- throw new Error("Failed to fetch chat completion");
174
- }
161
+ copilotConfig.properties,
162
+ abortControllerRef.current?.signal,
163
+ ),
164
+ );
165
+
166
+ const guardrailsEnabled =
167
+ copilotConfig.cloud?.guardrails?.input?.restrictToTopic.enabled || false;
175
168
 
176
- const reader = response.events.getReader();
169
+ const reader = stream.getReader();
177
170
 
178
- // Whether to feed back the new messages to GPT
179
- let feedback = false;
171
+ let results: { [id: string]: string } = {};
180
172
 
181
173
  try {
182
174
  while (true) {
@@ -186,116 +178,94 @@ export function useChat(options: UseChatOptionsWithCopilotConfig): UseChatHelper
186
178
  break;
187
179
  }
188
180
 
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
- }
181
+ if (!value?.generateCopilotResponse) {
182
+ continue;
183
+ }
184
+
185
+ threadIdRef.current = value.generateCopilotResponse.threadId || null;
186
+ runIdRef.current = value.generateCopilotResponse.runId || null;
187
+
188
+ const messages = convertGqlOutputToMessages(value.generateCopilotResponse.messages);
189
+
190
+ if (messages.length === 0) {
191
+ continue;
192
+ }
193
+
194
+ newMessages = [];
195
+
196
+ // request failed, display error message
197
+ if (
198
+ value.generateCopilotResponse.status?.__typename === "FailedResponseStatus" &&
199
+ value.generateCopilotResponse.status.reason === "GUARDRAILS_VALIDATION_FAILED"
200
+ ) {
201
+ newMessages = [
202
+ new TextMessage({
203
+ role: MessageRole.Assistant,
204
+ content: value.generateCopilotResponse.status.details?.guardrailsReason || "",
205
+ }),
206
+ ];
207
+ }
250
208
 
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 && value.scope === "client") {
258
- const result = await options.onFunctionCall(
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;
209
+ // add messages to the chat
210
+ else {
211
+ for (const message of messages) {
212
+ newMessages.push(message);
213
+
214
+ if (
215
+ message instanceof ActionExecutionMessage &&
216
+ message.status.code !== MessageStatusCode.Pending &&
217
+ message.scope === "client" &&
218
+ onFunctionCall
219
+ ) {
220
+ if (!(message.id in results)) {
221
+ // Do not execute a function call if guardrails are enabled but the status is not known
222
+ if (guardrailsEnabled && value.generateCopilotResponse.status === undefined) {
223
+ break;
224
+ }
225
+ // execute action
226
+ const result = await onFunctionCall({
227
+ messages: previousMessages,
228
+ name: message.name,
229
+ args: message.arguments,
230
+ });
231
+ results[message.id] = result;
274
232
  }
275
- } catch (error) {
276
- console.error("Failed to execute function call", error);
277
- // TODO: Handle error
278
- // this should go to the message itself
233
+
234
+ // add the result message
235
+ newMessages.push(
236
+ new ResultMessage({
237
+ result: ResultMessage.encodeResult(results[message.id]),
238
+ actionExecutionId: message.id,
239
+ actionName: message.name,
240
+ }),
241
+ );
279
242
  }
280
243
  }
281
244
  }
245
+
246
+ if (newMessages.length > 0) {
247
+ setMessages([...previousMessages, ...newMessages]);
248
+ }
282
249
  }
283
250
 
284
- // If we want feedback, run the completion again and return the results
285
- if (feedback) {
251
+ if (
252
+ // if we have client side results
253
+ Object.values(results).length ||
254
+ // or the last message we received is a result
255
+ (newMessages.length && newMessages[newMessages.length - 1] instanceof ResultMessage)
256
+ ) {
257
+ // run the completion again and return the result
258
+
286
259
  // 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
260
  // - tried using react-dom's flushSync, but it did not work
289
261
  await new Promise((resolve) => setTimeout(resolve, 10));
290
262
 
291
- return await runChatCompletion([...messages, ...newMessages]);
292
- }
293
- // otherwise, return the new messages
294
- else {
263
+ return await runChatCompletion([...previousMessages, ...newMessages]);
264
+ } else {
295
265
  return newMessages.slice();
296
266
  }
297
267
  } finally {
298
- options.setIsLoading(false);
268
+ setIsLoading(false);
299
269
  }
300
270
  };
301
271
 
@@ -304,7 +274,7 @@ export function useChat(options: UseChatOptionsWithCopilotConfig): UseChatHelper
304
274
  };
305
275
 
306
276
  const append = async (message: Message): Promise<void> => {
307
- if (options.isLoading) {
277
+ if (isLoading) {
308
278
  return;
309
279
  }
310
280
  const newMessages = [...messages, message];
@@ -313,15 +283,16 @@ export function useChat(options: UseChatOptionsWithCopilotConfig): UseChatHelper
313
283
  };
314
284
 
315
285
  const reload = async (): Promise<void> => {
316
- if (options.isLoading || messages.length === 0) {
286
+ if (isLoading || messages.length === 0) {
317
287
  return;
318
288
  }
319
289
  let newMessages = [...messages];
320
290
  const lastMessage = messages[messages.length - 1];
321
291
 
322
- if (lastMessage.role === "assistant") {
292
+ if (lastMessage instanceof TextMessage && lastMessage.role === "assistant") {
323
293
  newMessages = newMessages.slice(0, -1);
324
294
  }
295
+
325
296
  setMessages(newMessages);
326
297
 
327
298
  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,28 +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
121
  const { append, reload, stop } = useChat({
126
122
  ...options,
123
+ actions: Object.values(actions),
127
124
  copilotConfig: copilotApiConfig,
128
- id: options.id,
129
125
  initialMessages: options.initialMessages || [],
130
- tools: functionDescriptions,
131
126
  onFunctionCall: getFunctionCallHandler(),
132
- headers: { ...options.headers },
133
- body: {
134
- ...options.body,
135
- },
136
127
  messages,
137
128
  setMessages,
138
129
  makeSystemMessageCallback,
@@ -140,13 +131,8 @@ export function useCopilotChat({
140
131
  setIsLoading,
141
132
  });
142
133
 
143
- const visibleMessages = messages.filter(
144
- (message) =>
145
- message.role === "user" || message.role === "assistant" || message.role === "function",
146
- );
147
-
148
134
  return {
149
- visibleMessages,
135
+ visibleMessages: messages,
150
136
  appendMessage: append,
151
137
  setMessages,
152
138
  reloadMessages: reload,
@@ -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
  }
package/src/index.tsx CHANGED
@@ -3,6 +3,5 @@ export * from "./components";
3
3
  export * from "./context";
4
4
  export * from "./hooks";
5
5
  export * from "./types";
6
- export * from "./openai-assistants";
7
6
  export * from "./lib";
8
7
  export * from "./utils";