@copilotkit/runtime 0.0.0-feat-dynamic-copilotcloud-qa-20250117190454

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 (147) hide show
  1. package/.eslintrc.js +7 -0
  2. package/CHANGELOG.md +913 -0
  3. package/README.md +46 -0
  4. package/__snapshots__/schema/schema.graphql +273 -0
  5. package/dist/chunk-44O2JGUY.mjs +12 -0
  6. package/dist/chunk-44O2JGUY.mjs.map +1 -0
  7. package/dist/chunk-BETLEV37.mjs +25 -0
  8. package/dist/chunk-BETLEV37.mjs.map +1 -0
  9. package/dist/chunk-CLGKEUOA.mjs +1408 -0
  10. package/dist/chunk-CLGKEUOA.mjs.map +1 -0
  11. package/dist/chunk-D2WLFQS6.mjs +43 -0
  12. package/dist/chunk-D2WLFQS6.mjs.map +1 -0
  13. package/dist/chunk-DFOKBSIS.mjs +1 -0
  14. package/dist/chunk-DFOKBSIS.mjs.map +1 -0
  15. package/dist/chunk-FA5DJ2TZ.mjs +3437 -0
  16. package/dist/chunk-FA5DJ2TZ.mjs.map +1 -0
  17. package/dist/chunk-HNUNXFTW.mjs +129 -0
  18. package/dist/chunk-HNUNXFTW.mjs.map +1 -0
  19. package/dist/chunk-SFLMY3ES.mjs +80 -0
  20. package/dist/chunk-SFLMY3ES.mjs.map +1 -0
  21. package/dist/chunk-U3V2BCGI.mjs +152 -0
  22. package/dist/chunk-U3V2BCGI.mjs.map +1 -0
  23. package/dist/chunk-ZCU6UPCY.mjs +25 -0
  24. package/dist/chunk-ZCU6UPCY.mjs.map +1 -0
  25. package/dist/copilot-runtime-1a224a0f.d.ts +196 -0
  26. package/dist/graphql/types/base/index.d.ts +6 -0
  27. package/dist/graphql/types/base/index.js +63 -0
  28. package/dist/graphql/types/base/index.js.map +1 -0
  29. package/dist/graphql/types/base/index.mjs +8 -0
  30. package/dist/graphql/types/base/index.mjs.map +1 -0
  31. package/dist/graphql/types/converted/index.d.ts +2 -0
  32. package/dist/graphql/types/converted/index.js +187 -0
  33. package/dist/graphql/types/converted/index.js.map +1 -0
  34. package/dist/graphql/types/converted/index.mjs +17 -0
  35. package/dist/graphql/types/converted/index.mjs.map +1 -0
  36. package/dist/groq-adapter-c35c5374.d.ts +281 -0
  37. package/dist/index-24315d90.d.ts +103 -0
  38. package/dist/index.d.ts +23 -0
  39. package/dist/index.js +5258 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/index.mjs +76 -0
  42. package/dist/index.mjs.map +1 -0
  43. package/dist/langserve-a16ef8f4.d.ts +180 -0
  44. package/dist/lib/cloud/index.d.ts +6 -0
  45. package/dist/lib/cloud/index.js +18 -0
  46. package/dist/lib/cloud/index.js.map +1 -0
  47. package/dist/lib/cloud/index.mjs +1 -0
  48. package/dist/lib/cloud/index.mjs.map +1 -0
  49. package/dist/lib/index.d.ts +20 -0
  50. package/dist/lib/index.js +4906 -0
  51. package/dist/lib/index.js.map +1 -0
  52. package/dist/lib/index.mjs +58 -0
  53. package/dist/lib/index.mjs.map +1 -0
  54. package/dist/lib/integrations/index.d.ts +33 -0
  55. package/dist/lib/integrations/index.js +2229 -0
  56. package/dist/lib/integrations/index.js.map +1 -0
  57. package/dist/lib/integrations/index.mjs +34 -0
  58. package/dist/lib/integrations/index.mjs.map +1 -0
  59. package/dist/lib/integrations/nest/index.d.ts +14 -0
  60. package/dist/lib/integrations/nest/index.js +2138 -0
  61. package/dist/lib/integrations/nest/index.js.map +1 -0
  62. package/dist/lib/integrations/nest/index.mjs +13 -0
  63. package/dist/lib/integrations/nest/index.mjs.map +1 -0
  64. package/dist/lib/integrations/node-express/index.d.ts +14 -0
  65. package/dist/lib/integrations/node-express/index.js +2138 -0
  66. package/dist/lib/integrations/node-express/index.js.map +1 -0
  67. package/dist/lib/integrations/node-express/index.mjs +13 -0
  68. package/dist/lib/integrations/node-express/index.mjs.map +1 -0
  69. package/dist/lib/integrations/node-http/index.d.ts +14 -0
  70. package/dist/lib/integrations/node-http/index.js +2124 -0
  71. package/dist/lib/integrations/node-http/index.js.map +1 -0
  72. package/dist/lib/integrations/node-http/index.mjs +12 -0
  73. package/dist/lib/integrations/node-http/index.mjs.map +1 -0
  74. package/dist/service-adapters/index.d.ts +84 -0
  75. package/dist/service-adapters/index.js +1448 -0
  76. package/dist/service-adapters/index.js.map +1 -0
  77. package/dist/service-adapters/index.mjs +26 -0
  78. package/dist/service-adapters/index.mjs.map +1 -0
  79. package/dist/utils/index.d.ts +49 -0
  80. package/dist/utils/index.js +174 -0
  81. package/dist/utils/index.js.map +1 -0
  82. package/dist/utils/index.mjs +12 -0
  83. package/dist/utils/index.mjs.map +1 -0
  84. package/jest.config.js +5 -0
  85. package/package.json +85 -0
  86. package/scripts/generate-gql-schema.ts +13 -0
  87. package/src/agents/langgraph/event-source.ts +287 -0
  88. package/src/agents/langgraph/events.ts +338 -0
  89. package/src/graphql/inputs/action.input.ts +16 -0
  90. package/src/graphql/inputs/agent-session.input.ts +13 -0
  91. package/src/graphql/inputs/agent-state.input.ts +10 -0
  92. package/src/graphql/inputs/cloud-guardrails.input.ts +16 -0
  93. package/src/graphql/inputs/cloud.input.ts +8 -0
  94. package/src/graphql/inputs/context-property.input.ts +10 -0
  95. package/src/graphql/inputs/custom-property.input.ts +15 -0
  96. package/src/graphql/inputs/forwarded-parameters.input.ts +22 -0
  97. package/src/graphql/inputs/frontend.input.ts +14 -0
  98. package/src/graphql/inputs/generate-copilot-response.input.ts +47 -0
  99. package/src/graphql/inputs/message.input.ts +92 -0
  100. package/src/graphql/resolvers/copilot.resolver.ts +556 -0
  101. package/src/graphql/types/agents-response.type.ts +22 -0
  102. package/src/graphql/types/base/index.ts +10 -0
  103. package/src/graphql/types/converted/index.ts +136 -0
  104. package/src/graphql/types/copilot-response.type.ts +113 -0
  105. package/src/graphql/types/enums.ts +37 -0
  106. package/src/graphql/types/guardrails-result.type.ts +20 -0
  107. package/src/graphql/types/message-status.type.ts +40 -0
  108. package/src/graphql/types/response-status.type.ts +66 -0
  109. package/src/index.ts +4 -0
  110. package/src/lib/cloud/index.ts +4 -0
  111. package/src/lib/index.ts +8 -0
  112. package/src/lib/integrations/index.ts +6 -0
  113. package/src/lib/integrations/nest/index.ts +17 -0
  114. package/src/lib/integrations/nextjs/app-router.ts +40 -0
  115. package/src/lib/integrations/nextjs/pages-router.ts +49 -0
  116. package/src/lib/integrations/node-express/index.ts +17 -0
  117. package/src/lib/integrations/node-http/index.ts +34 -0
  118. package/src/lib/integrations/shared.ts +109 -0
  119. package/src/lib/logger.ts +28 -0
  120. package/src/lib/runtime/copilot-runtime.ts +466 -0
  121. package/src/lib/runtime/remote-action-constructors.ts +304 -0
  122. package/src/lib/runtime/remote-actions.ts +174 -0
  123. package/src/lib/runtime/remote-lg-action.ts +657 -0
  124. package/src/lib/telemetry-client.ts +52 -0
  125. package/src/service-adapters/anthropic/anthropic-adapter.ts +205 -0
  126. package/src/service-adapters/anthropic/utils.ts +144 -0
  127. package/src/service-adapters/conversion.ts +64 -0
  128. package/src/service-adapters/events.ts +419 -0
  129. package/src/service-adapters/experimental/empty/empty-adapter.ts +33 -0
  130. package/src/service-adapters/experimental/ollama/ollama-adapter.ts +79 -0
  131. package/src/service-adapters/google/google-genai-adapter.ts +39 -0
  132. package/src/service-adapters/groq/groq-adapter.ts +173 -0
  133. package/src/service-adapters/index.ts +16 -0
  134. package/src/service-adapters/langchain/langchain-adapter.ts +99 -0
  135. package/src/service-adapters/langchain/langserve.ts +87 -0
  136. package/src/service-adapters/langchain/types.ts +14 -0
  137. package/src/service-adapters/langchain/utils.ts +306 -0
  138. package/src/service-adapters/openai/openai-adapter.ts +210 -0
  139. package/src/service-adapters/openai/openai-assistant-adapter.ts +304 -0
  140. package/src/service-adapters/openai/utils.ts +161 -0
  141. package/src/service-adapters/service-adapter.ts +30 -0
  142. package/src/service-adapters/unify/unify-adapter.ts +145 -0
  143. package/src/utils/failed-response-status-reasons.ts +48 -0
  144. package/src/utils/index.ts +1 -0
  145. package/tsconfig.json +11 -0
  146. package/tsup.config.ts +16 -0
  147. package/typedoc.json +4 -0
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Copilot Runtime adapter for OpenAI.
3
+ *
4
+ * ## Example
5
+ *
6
+ * ```ts
7
+ * import { CopilotRuntime, OpenAIAdapter } from "@copilotkit/runtime";
8
+ * import OpenAI from "openai";
9
+ *
10
+ * const copilotKit = new CopilotRuntime();
11
+ *
12
+ * const openai = new OpenAI({
13
+ * organization: "<your-organization-id>", // optional
14
+ * apiKey: "<your-api-key>",
15
+ * });
16
+ *
17
+ * return new OpenAIAdapter({ openai });
18
+ * ```
19
+ *
20
+ * ## Example with Azure OpenAI
21
+ *
22
+ * ```ts
23
+ * import { CopilotRuntime, OpenAIAdapter } from "@copilotkit/runtime";
24
+ * import OpenAI from "openai";
25
+ *
26
+ * // The name of your Azure OpenAI Instance.
27
+ * // https://learn.microsoft.com/en-us/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#create-a-resource
28
+ * const instance = "<your instance name>";
29
+ *
30
+ * // Corresponds to your Model deployment within your OpenAI resource, e.g. my-gpt35-16k-deployment
31
+ * // Navigate to the Azure OpenAI Studio to deploy a model.
32
+ * const model = "<your model>";
33
+ *
34
+ * const apiKey = process.env["AZURE_OPENAI_API_KEY"];
35
+ * if (!apiKey) {
36
+ * throw new Error("The AZURE_OPENAI_API_KEY environment variable is missing or empty.");
37
+ * }
38
+ *
39
+ * const copilotKit = new CopilotRuntime();
40
+ *
41
+ * const openai = new OpenAI({
42
+ * apiKey,
43
+ * baseURL: `https://${instance}.openai.azure.com/openai/deployments/${model}`,
44
+ * defaultQuery: { "api-version": "2024-04-01-preview" },
45
+ * defaultHeaders: { "api-key": apiKey },
46
+ * });
47
+ *
48
+ * return new OpenAIAdapter({ openai });
49
+ * ```
50
+ */
51
+ import OpenAI from "openai";
52
+ import {
53
+ CopilotServiceAdapter,
54
+ CopilotRuntimeChatCompletionRequest,
55
+ CopilotRuntimeChatCompletionResponse,
56
+ } from "../service-adapter";
57
+ import {
58
+ convertActionInputToOpenAITool,
59
+ convertMessageToOpenAIMessage,
60
+ limitMessagesToTokenCount,
61
+ } from "./utils";
62
+ import { randomId } from "@copilotkit/shared";
63
+
64
+ const DEFAULT_MODEL = "gpt-4o";
65
+
66
+ export interface OpenAIAdapterParams {
67
+ /**
68
+ * An optional OpenAI instance to use. If not provided, a new instance will be
69
+ * created.
70
+ */
71
+ openai?: OpenAI;
72
+
73
+ /**
74
+ * The model to use.
75
+ */
76
+ model?: string;
77
+
78
+ /**
79
+ * Whether to disable parallel tool calls.
80
+ * You can disable parallel tool calls to force the model to execute tool calls sequentially.
81
+ * This is useful if you want to execute tool calls in a specific order so that the state changes
82
+ * introduced by one tool call are visible to the next tool call. (i.e. new actions or readables)
83
+ *
84
+ * @default false
85
+ */
86
+ disableParallelToolCalls?: boolean;
87
+ }
88
+
89
+ export class OpenAIAdapter implements CopilotServiceAdapter {
90
+ private model: string = DEFAULT_MODEL;
91
+
92
+ private disableParallelToolCalls: boolean = false;
93
+ private _openai: OpenAI;
94
+ public get openai(): OpenAI {
95
+ return this._openai;
96
+ }
97
+
98
+ constructor(params?: OpenAIAdapterParams) {
99
+ this._openai = params?.openai || new OpenAI({});
100
+ if (params?.model) {
101
+ this.model = params.model;
102
+ }
103
+ this.disableParallelToolCalls = params?.disableParallelToolCalls || false;
104
+ }
105
+
106
+ async process(
107
+ request: CopilotRuntimeChatCompletionRequest,
108
+ ): Promise<CopilotRuntimeChatCompletionResponse> {
109
+ const {
110
+ threadId,
111
+ model = this.model,
112
+ messages,
113
+ actions,
114
+ eventSource,
115
+ forwardedParameters,
116
+ } = request;
117
+ const tools = actions.map(convertActionInputToOpenAITool);
118
+
119
+ let openaiMessages = messages.map(convertMessageToOpenAIMessage);
120
+ openaiMessages = limitMessagesToTokenCount(openaiMessages, tools, model);
121
+
122
+ let toolChoice: any = forwardedParameters?.toolChoice;
123
+ if (forwardedParameters?.toolChoice === "function") {
124
+ toolChoice = {
125
+ type: "function",
126
+ function: { name: forwardedParameters.toolChoiceFunctionName },
127
+ };
128
+ }
129
+
130
+ const stream = this.openai.beta.chat.completions.stream({
131
+ model: model,
132
+ stream: true,
133
+ messages: openaiMessages,
134
+ ...(tools.length > 0 && { tools }),
135
+ ...(forwardedParameters?.maxTokens && { max_tokens: forwardedParameters.maxTokens }),
136
+ ...(forwardedParameters?.stop && { stop: forwardedParameters.stop }),
137
+ ...(toolChoice && { tool_choice: toolChoice }),
138
+ ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
139
+ ...(forwardedParameters?.temperature && { temperature: forwardedParameters.temperature }),
140
+ });
141
+
142
+ eventSource.stream(async (eventStream$) => {
143
+ let mode: "function" | "message" | null = null;
144
+ let currentMessageId: string;
145
+ let currentToolCallId: string;
146
+ for await (const chunk of stream) {
147
+ if (chunk.choices.length === 0) {
148
+ continue;
149
+ }
150
+
151
+ const toolCall = chunk.choices[0].delta.tool_calls?.[0];
152
+ const content = chunk.choices[0].delta.content;
153
+
154
+ // When switching from message to function or vice versa,
155
+ // send the respective end event.
156
+ // If toolCall?.id is defined, it means a new tool call starts.
157
+ if (mode === "message" && toolCall?.id) {
158
+ mode = null;
159
+ eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
160
+ } else if (mode === "function" && (toolCall === undefined || toolCall?.id)) {
161
+ mode = null;
162
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
163
+ }
164
+
165
+ // If we send a new message type, send the appropriate start event.
166
+ if (mode === null) {
167
+ if (toolCall?.id) {
168
+ mode = "function";
169
+ currentToolCallId = toolCall!.id;
170
+ eventStream$.sendActionExecutionStart({
171
+ actionExecutionId: currentToolCallId,
172
+ parentMessageId: chunk.id,
173
+ actionName: toolCall!.function!.name,
174
+ });
175
+ } else if (content) {
176
+ mode = "message";
177
+ currentMessageId = chunk.id;
178
+ eventStream$.sendTextMessageStart({ messageId: currentMessageId });
179
+ }
180
+ }
181
+
182
+ // send the content events
183
+ if (mode === "message" && content) {
184
+ eventStream$.sendTextMessageContent({
185
+ messageId: currentMessageId,
186
+ content: content,
187
+ });
188
+ } else if (mode === "function" && toolCall?.function?.arguments) {
189
+ eventStream$.sendActionExecutionArgs({
190
+ actionExecutionId: currentToolCallId,
191
+ args: toolCall.function.arguments,
192
+ });
193
+ }
194
+ }
195
+
196
+ // send the end events
197
+ if (mode === "message") {
198
+ eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
199
+ } else if (mode === "function") {
200
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
201
+ }
202
+
203
+ eventStream$.complete();
204
+ });
205
+
206
+ return {
207
+ threadId: threadId || randomId(),
208
+ };
209
+ }
210
+ }
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Copilot Runtime adapter for the OpenAI Assistant API.
3
+ *
4
+ * ## Example
5
+ *
6
+ * ```ts
7
+ * import { CopilotRuntime, OpenAIAssistantAdapter } from "@copilotkit/runtime";
8
+ * import OpenAI from "openai";
9
+ *
10
+ * const copilotKit = new CopilotRuntime();
11
+ *
12
+ * const openai = new OpenAI({
13
+ * organization: "<your-organization-id>",
14
+ * apiKey: "<your-api-key>",
15
+ * });
16
+ *
17
+ * return new OpenAIAssistantAdapter({
18
+ * openai,
19
+ * assistantId: "<your-assistant-id>",
20
+ * codeInterpreterEnabled: true,
21
+ * fileSearchEnabled: true,
22
+ * });
23
+ * ```
24
+ */
25
+ import OpenAI from "openai";
26
+ import {
27
+ CopilotServiceAdapter,
28
+ CopilotRuntimeChatCompletionRequest,
29
+ CopilotRuntimeChatCompletionResponse,
30
+ } from "../service-adapter";
31
+ import { Message, ResultMessage, TextMessage } from "../../graphql/types/converted";
32
+ import {
33
+ convertActionInputToOpenAITool,
34
+ convertMessageToOpenAIMessage,
35
+ convertSystemMessageToAssistantAPI,
36
+ } from "./utils";
37
+ import { RunSubmitToolOutputsStreamParams } from "openai/resources/beta/threads/runs/runs";
38
+ import { AssistantStream } from "openai/lib/AssistantStream";
39
+ import { RuntimeEventSource } from "../events";
40
+ import { ActionInput } from "../../graphql/inputs/action.input";
41
+ import { AssistantStreamEvent, AssistantTool } from "openai/resources/beta/assistants";
42
+ import { ForwardedParametersInput } from "../../graphql/inputs/forwarded-parameters.input";
43
+
44
+ export interface OpenAIAssistantAdapterParams {
45
+ /**
46
+ * The ID of the assistant to use.
47
+ */
48
+ assistantId: string;
49
+
50
+ /**
51
+ * An optional OpenAI instance to use. If not provided, a new instance will be created.
52
+ */
53
+ openai?: OpenAI;
54
+
55
+ /**
56
+ * Whether to enable code interpretation.
57
+ * @default true
58
+ */
59
+ codeInterpreterEnabled?: boolean;
60
+
61
+ /**
62
+ * Whether to enable file search.
63
+ * @default true
64
+ */
65
+ fileSearchEnabled?: boolean;
66
+
67
+ /**
68
+ * Whether to disable parallel tool calls.
69
+ * You can disable parallel tool calls to force the model to execute tool calls sequentially.
70
+ * This is useful if you want to execute tool calls in a specific order so that the state changes
71
+ * introduced by one tool call are visible to the next tool call. (i.e. new actions or readables)
72
+ *
73
+ * @default false
74
+ */
75
+ disableParallelToolCalls?: boolean;
76
+ }
77
+
78
+ export class OpenAIAssistantAdapter implements CopilotServiceAdapter {
79
+ private openai: OpenAI;
80
+ private codeInterpreterEnabled: boolean;
81
+ private assistantId: string;
82
+ private fileSearchEnabled: boolean;
83
+ private disableParallelToolCalls: boolean;
84
+
85
+ constructor(params: OpenAIAssistantAdapterParams) {
86
+ this.openai = params.openai || new OpenAI({});
87
+ this.codeInterpreterEnabled = params.codeInterpreterEnabled === false || true;
88
+ this.fileSearchEnabled = params.fileSearchEnabled === false || true;
89
+ this.assistantId = params.assistantId;
90
+ this.disableParallelToolCalls = params?.disableParallelToolCalls || false;
91
+ }
92
+
93
+ async process(
94
+ request: CopilotRuntimeChatCompletionRequest,
95
+ ): Promise<CopilotRuntimeChatCompletionResponse> {
96
+ const { messages, actions, eventSource, runId, forwardedParameters } = request;
97
+ // if we don't have a threadId, create a new thread
98
+ let threadId = request.threadId || (await this.openai.beta.threads.create()).id;
99
+
100
+ const lastMessage = messages.at(-1);
101
+
102
+ let nextRunId: string | undefined = undefined;
103
+
104
+ // submit function outputs
105
+ if (lastMessage.isResultMessage() && runId) {
106
+ nextRunId = await this.submitToolOutputs(threadId, runId, messages, eventSource);
107
+ }
108
+ // submit user message
109
+ else if (lastMessage.isTextMessage()) {
110
+ nextRunId = await this.submitUserMessage(
111
+ threadId,
112
+ messages,
113
+ actions,
114
+ eventSource,
115
+ forwardedParameters,
116
+ );
117
+ }
118
+ // unsupported message
119
+ else {
120
+ throw new Error("No actionable message found in the messages");
121
+ }
122
+
123
+ return {
124
+ threadId,
125
+ runId: nextRunId,
126
+ };
127
+ }
128
+
129
+ private async submitToolOutputs(
130
+ threadId: string,
131
+ runId: string,
132
+ messages: Message[],
133
+ eventSource: RuntimeEventSource,
134
+ ) {
135
+ let run = await this.openai.beta.threads.runs.retrieve(threadId, runId);
136
+ if (!run.required_action) {
137
+ throw new Error("No tool outputs required");
138
+ }
139
+
140
+ // get the required tool call ids
141
+ const toolCallsIds = run.required_action.submit_tool_outputs.tool_calls.map(
142
+ (toolCall) => toolCall.id,
143
+ );
144
+
145
+ // search for these tool calls
146
+ const resultMessages = messages.filter(
147
+ (message) => message.isResultMessage() && toolCallsIds.includes(message.actionExecutionId),
148
+ ) as ResultMessage[];
149
+
150
+ if (toolCallsIds.length != resultMessages.length) {
151
+ throw new Error("Number of function results does not match the number of tool calls");
152
+ }
153
+
154
+ // submit the tool outputs
155
+ const toolOutputs: RunSubmitToolOutputsStreamParams.ToolOutput[] = resultMessages.map(
156
+ (message) => {
157
+ return {
158
+ tool_call_id: message.actionExecutionId,
159
+ output: message.result,
160
+ };
161
+ },
162
+ );
163
+
164
+ const stream = this.openai.beta.threads.runs.submitToolOutputsStream(threadId, runId, {
165
+ tool_outputs: toolOutputs,
166
+ ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
167
+ });
168
+
169
+ await this.streamResponse(stream, eventSource);
170
+ return runId;
171
+ }
172
+
173
+ private async submitUserMessage(
174
+ threadId: string,
175
+ messages: Message[],
176
+ actions: ActionInput[],
177
+ eventSource: RuntimeEventSource,
178
+ forwardedParameters: ForwardedParametersInput,
179
+ ) {
180
+ messages = [...messages];
181
+
182
+ // get the instruction message
183
+ const instructionsMessage = messages.shift();
184
+ const instructions = instructionsMessage.isTextMessage() ? instructionsMessage.content : "";
185
+
186
+ // get the latest user message
187
+ const userMessage = messages
188
+ .map(convertMessageToOpenAIMessage)
189
+ .map(convertSystemMessageToAssistantAPI)
190
+ .at(-1);
191
+
192
+ if (userMessage.role !== "user") {
193
+ throw new Error("No user message found");
194
+ }
195
+
196
+ // create a new message on the thread
197
+ await this.openai.beta.threads.messages.create(threadId, {
198
+ role: "user",
199
+ content: userMessage.content,
200
+ });
201
+
202
+ const openaiTools = actions.map(convertActionInputToOpenAITool);
203
+
204
+ const tools = [
205
+ ...openaiTools,
206
+ ...(this.codeInterpreterEnabled ? [{ type: "code_interpreter" } as AssistantTool] : []),
207
+ ...(this.fileSearchEnabled ? [{ type: "file_search" } as AssistantTool] : []),
208
+ ];
209
+
210
+ // run the thread
211
+ let stream = this.openai.beta.threads.runs.stream(threadId, {
212
+ assistant_id: this.assistantId,
213
+ instructions,
214
+ tools: tools,
215
+ ...(forwardedParameters?.maxTokens && {
216
+ max_completion_tokens: forwardedParameters.maxTokens,
217
+ }),
218
+ ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
219
+ });
220
+
221
+ await this.streamResponse(stream, eventSource);
222
+
223
+ return getRunIdFromStream(stream);
224
+ }
225
+
226
+ private async streamResponse(stream: AssistantStream, eventSource: RuntimeEventSource) {
227
+ eventSource.stream(async (eventStream$) => {
228
+ let inFunctionCall = false;
229
+ let currentMessageId: string;
230
+ let currentToolCallId: string;
231
+
232
+ for await (const chunk of stream) {
233
+ switch (chunk.event) {
234
+ case "thread.message.created":
235
+ if (inFunctionCall) {
236
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
237
+ }
238
+ currentMessageId = chunk.data.id;
239
+ eventStream$.sendTextMessageStart({ messageId: currentMessageId });
240
+ break;
241
+ case "thread.message.delta":
242
+ if (chunk.data.delta.content?.[0].type === "text") {
243
+ eventStream$.sendTextMessageContent({
244
+ messageId: currentMessageId,
245
+ content: chunk.data.delta.content?.[0].text.value,
246
+ });
247
+ }
248
+ break;
249
+ case "thread.message.completed":
250
+ eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
251
+ break;
252
+ case "thread.run.step.delta":
253
+ let toolCallId: string | undefined;
254
+ let toolCallName: string | undefined;
255
+ let toolCallArgs: string | undefined;
256
+ if (
257
+ chunk.data.delta.step_details.type === "tool_calls" &&
258
+ chunk.data.delta.step_details.tool_calls?.[0].type === "function"
259
+ ) {
260
+ toolCallId = chunk.data.delta.step_details.tool_calls?.[0].id;
261
+ toolCallName = chunk.data.delta.step_details.tool_calls?.[0].function.name;
262
+ toolCallArgs = chunk.data.delta.step_details.tool_calls?.[0].function.arguments;
263
+ }
264
+
265
+ if (toolCallName && toolCallId) {
266
+ if (inFunctionCall) {
267
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
268
+ }
269
+ inFunctionCall = true;
270
+ currentToolCallId = toolCallId;
271
+ eventStream$.sendActionExecutionStart({
272
+ actionExecutionId: currentToolCallId,
273
+ parentMessageId: chunk.data.id,
274
+ actionName: toolCallName,
275
+ });
276
+ } else if (toolCallArgs) {
277
+ eventStream$.sendActionExecutionArgs({
278
+ actionExecutionId: currentToolCallId,
279
+ args: toolCallArgs,
280
+ });
281
+ }
282
+ break;
283
+ }
284
+ }
285
+ if (inFunctionCall) {
286
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
287
+ }
288
+ eventStream$.complete();
289
+ });
290
+ }
291
+ }
292
+
293
+ function getRunIdFromStream(stream: AssistantStream): Promise<string> {
294
+ return new Promise<string>((resolve, reject) => {
295
+ let runIdGetter = (event: AssistantStreamEvent) => {
296
+ if (event.event === "thread.run.created") {
297
+ const runId = event.data.id;
298
+ stream.off("event", runIdGetter);
299
+ resolve(runId);
300
+ }
301
+ };
302
+ stream.on("event", runIdGetter);
303
+ });
304
+ }
@@ -0,0 +1,161 @@
1
+ import { Message } from "../../graphql/types/converted";
2
+ import { ActionInput } from "../../graphql/inputs/action.input";
3
+ import {
4
+ ChatCompletionMessageParam,
5
+ ChatCompletionTool,
6
+ ChatCompletionUserMessageParam,
7
+ ChatCompletionAssistantMessageParam,
8
+ ChatCompletionSystemMessageParam,
9
+ } from "openai/resources";
10
+
11
+ export function limitMessagesToTokenCount(
12
+ messages: any[],
13
+ tools: any[],
14
+ model: string,
15
+ maxTokens?: number,
16
+ ): any[] {
17
+ maxTokens ||= maxTokensForOpenAIModel(model);
18
+
19
+ const result: any[] = [];
20
+ const toolsNumTokens = countToolsTokens(model, tools);
21
+ if (toolsNumTokens > maxTokens) {
22
+ throw new Error(`Too many tokens in function definitions: ${toolsNumTokens} > ${maxTokens}`);
23
+ }
24
+ maxTokens -= toolsNumTokens;
25
+
26
+ for (const message of messages) {
27
+ if (message.role === "system") {
28
+ const numTokens = countMessageTokens(model, message);
29
+ maxTokens -= numTokens;
30
+
31
+ if (maxTokens < 0) {
32
+ throw new Error("Not enough tokens for system message.");
33
+ }
34
+ }
35
+ }
36
+
37
+ let cutoff: boolean = false;
38
+
39
+ const reversedMessages = [...messages].reverse();
40
+ for (const message of reversedMessages) {
41
+ if (message.role === "system") {
42
+ result.unshift(message);
43
+ continue;
44
+ } else if (cutoff) {
45
+ continue;
46
+ }
47
+ let numTokens = countMessageTokens(model, message);
48
+ if (maxTokens < numTokens) {
49
+ cutoff = true;
50
+ continue;
51
+ }
52
+ result.unshift(message);
53
+ maxTokens -= numTokens;
54
+ }
55
+
56
+ return result;
57
+ }
58
+
59
+ export function maxTokensForOpenAIModel(model: string): number {
60
+ return maxTokensByModel[model] || DEFAULT_MAX_TOKENS;
61
+ }
62
+
63
+ const DEFAULT_MAX_TOKENS = 128000;
64
+
65
+ const maxTokensByModel: { [key: string]: number } = {
66
+ // GPT-4
67
+ "gpt-4o": 128000,
68
+ "gpt-4o-2024-05-13": 128000,
69
+ "gpt-4-turbo": 128000,
70
+ "gpt-4-turbo-2024-04-09": 128000,
71
+ "gpt-4-0125-preview": 128000,
72
+ "gpt-4-turbo-preview": 128000,
73
+ "gpt-4-1106-preview": 128000,
74
+ "gpt-4-vision-preview": 128000,
75
+ "gpt-4-1106-vision-preview": 128000,
76
+ "gpt-4-32k": 32768,
77
+ "gpt-4-32k-0613": 32768,
78
+ "gpt-4-32k-0314": 32768,
79
+ "gpt-4": 8192,
80
+ "gpt-4-0613": 8192,
81
+ "gpt-4-0314": 8192,
82
+
83
+ // GPT-3.5
84
+ "gpt-3.5-turbo-0125": 16385,
85
+ "gpt-3.5-turbo": 16385,
86
+ "gpt-3.5-turbo-1106": 16385,
87
+ "gpt-3.5-turbo-instruct": 4096,
88
+ "gpt-3.5-turbo-16k": 16385,
89
+ "gpt-3.5-turbo-0613": 4096,
90
+ "gpt-3.5-turbo-16k-0613": 16385,
91
+ "gpt-3.5-turbo-0301": 4097,
92
+ };
93
+
94
+ function countToolsTokens(model: string, tools: any[]): number {
95
+ if (tools.length === 0) {
96
+ return 0;
97
+ }
98
+ const json = JSON.stringify(tools);
99
+ return countTokens(model, json);
100
+ }
101
+
102
+ function countMessageTokens(model: string, message: any): number {
103
+ return countTokens(model, message.content || "");
104
+ }
105
+
106
+ function countTokens(model: string, text: string): number {
107
+ return text.length / 3;
108
+ }
109
+
110
+ export function convertActionInputToOpenAITool(action: ActionInput): ChatCompletionTool {
111
+ return {
112
+ type: "function",
113
+ function: {
114
+ name: action.name,
115
+ description: action.description,
116
+ parameters: JSON.parse(action.jsonSchema),
117
+ },
118
+ };
119
+ }
120
+
121
+ export function convertMessageToOpenAIMessage(message: Message): ChatCompletionMessageParam {
122
+ if (message.isTextMessage()) {
123
+ return {
124
+ role: message.role as ChatCompletionUserMessageParam["role"],
125
+ content: message.content,
126
+ } satisfies
127
+ | ChatCompletionUserMessageParam
128
+ | ChatCompletionAssistantMessageParam
129
+ | ChatCompletionSystemMessageParam;
130
+ } else if (message.isActionExecutionMessage()) {
131
+ return {
132
+ role: "assistant",
133
+ tool_calls: [
134
+ {
135
+ id: message.id,
136
+ type: "function",
137
+ function: {
138
+ name: message.name,
139
+ arguments: JSON.stringify(message.arguments),
140
+ },
141
+ },
142
+ ],
143
+ };
144
+ } else if (message.isResultMessage()) {
145
+ return {
146
+ role: "tool",
147
+ content: message.result,
148
+ tool_call_id: message.actionExecutionId,
149
+ };
150
+ }
151
+ }
152
+
153
+ export function convertSystemMessageToAssistantAPI(message: ChatCompletionMessageParam) {
154
+ return {
155
+ ...message,
156
+ ...(message.role === "system" && {
157
+ role: "assistant",
158
+ content: "THE FOLLOWING MESSAGE IS A SYSTEM MESSAGE: " + message.content,
159
+ }),
160
+ };
161
+ }
@@ -0,0 +1,30 @@
1
+ import { Message } from "../graphql/types/converted";
2
+ import { RuntimeEventSource } from "./events";
3
+ import { ActionInput } from "../graphql/inputs/action.input";
4
+ import { ForwardedParametersInput } from "../graphql/inputs/forwarded-parameters.input";
5
+
6
+ export interface CopilotKitResponse {
7
+ stream: ReadableStream;
8
+ headers?: Record<string, string>;
9
+ }
10
+
11
+ export interface CopilotRuntimeChatCompletionRequest {
12
+ eventSource: RuntimeEventSource;
13
+ messages: Message[];
14
+ actions: ActionInput[];
15
+ model?: string;
16
+ threadId?: string;
17
+ runId?: string;
18
+ forwardedParameters?: ForwardedParametersInput;
19
+ }
20
+
21
+ export interface CopilotRuntimeChatCompletionResponse {
22
+ threadId: string;
23
+ runId?: string;
24
+ }
25
+
26
+ export interface CopilotServiceAdapter {
27
+ process(
28
+ request: CopilotRuntimeChatCompletionRequest,
29
+ ): Promise<CopilotRuntimeChatCompletionResponse>;
30
+ }