@copilotkit/runtime 0.0.0-0.0.0-max-changeset-10101010101010-20260109191632

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 (122) hide show
  1. package/.eslintrc.js +7 -0
  2. package/CHANGELOG.md +2905 -0
  3. package/LICENSE +21 -0
  4. package/README.md +76 -0
  5. package/__snapshots__/schema/schema.graphql +371 -0
  6. package/dist/index.d.ts +1495 -0
  7. package/dist/index.js +5644 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/index.mjs +5601 -0
  10. package/dist/index.mjs.map +1 -0
  11. package/dist/langgraph.d.ts +284 -0
  12. package/dist/langgraph.js +211 -0
  13. package/dist/langgraph.js.map +1 -0
  14. package/dist/langgraph.mjs +206 -0
  15. package/dist/langgraph.mjs.map +1 -0
  16. package/dist/v2/index.d.ts +2 -0
  17. package/dist/v2/index.js +22 -0
  18. package/dist/v2/index.js.map +1 -0
  19. package/dist/v2/index.mjs +5 -0
  20. package/dist/v2/index.mjs.map +1 -0
  21. package/jest.config.js +10 -0
  22. package/package.json +143 -0
  23. package/scripts/generate-gql-schema.ts +13 -0
  24. package/src/agents/langgraph/event-source.ts +329 -0
  25. package/src/agents/langgraph/events.ts +377 -0
  26. package/src/graphql/inputs/action.input.ts +16 -0
  27. package/src/graphql/inputs/agent-session.input.ts +13 -0
  28. package/src/graphql/inputs/agent-state.input.ts +13 -0
  29. package/src/graphql/inputs/cloud-guardrails.input.ts +16 -0
  30. package/src/graphql/inputs/cloud.input.ts +8 -0
  31. package/src/graphql/inputs/context-property.input.ts +10 -0
  32. package/src/graphql/inputs/copilot-context.input.ts +10 -0
  33. package/src/graphql/inputs/custom-property.input.ts +15 -0
  34. package/src/graphql/inputs/extensions.input.ts +21 -0
  35. package/src/graphql/inputs/forwarded-parameters.input.ts +22 -0
  36. package/src/graphql/inputs/frontend.input.ts +14 -0
  37. package/src/graphql/inputs/generate-copilot-response.input.ts +59 -0
  38. package/src/graphql/inputs/load-agent-state.input.ts +10 -0
  39. package/src/graphql/inputs/message.input.ts +110 -0
  40. package/src/graphql/inputs/meta-event.input.ts +18 -0
  41. package/src/graphql/message-conversion/agui-to-gql.test.ts +1263 -0
  42. package/src/graphql/message-conversion/agui-to-gql.ts +333 -0
  43. package/src/graphql/message-conversion/gql-to-agui.test.ts +1580 -0
  44. package/src/graphql/message-conversion/gql-to-agui.ts +278 -0
  45. package/src/graphql/message-conversion/index.ts +2 -0
  46. package/src/graphql/message-conversion/roundtrip-conversion.test.ts +526 -0
  47. package/src/graphql/resolvers/copilot.resolver.ts +708 -0
  48. package/src/graphql/resolvers/state.resolver.ts +27 -0
  49. package/src/graphql/types/agents-response.type.ts +19 -0
  50. package/src/graphql/types/base/index.ts +10 -0
  51. package/src/graphql/types/converted/index.ts +176 -0
  52. package/src/graphql/types/copilot-response.type.ts +138 -0
  53. package/src/graphql/types/enums.ts +38 -0
  54. package/src/graphql/types/extensions-response.type.ts +23 -0
  55. package/src/graphql/types/guardrails-result.type.ts +20 -0
  56. package/src/graphql/types/load-agent-state-response.type.ts +17 -0
  57. package/src/graphql/types/message-status.type.ts +42 -0
  58. package/src/graphql/types/meta-events.type.ts +71 -0
  59. package/src/graphql/types/response-status.type.ts +66 -0
  60. package/src/index.ts +4 -0
  61. package/src/langgraph.ts +1 -0
  62. package/src/lib/cloud/index.ts +4 -0
  63. package/src/lib/error-messages.ts +200 -0
  64. package/src/lib/index.ts +52 -0
  65. package/src/lib/integrations/index.ts +6 -0
  66. package/src/lib/integrations/nest/index.ts +14 -0
  67. package/src/lib/integrations/nextjs/app-router.ts +38 -0
  68. package/src/lib/integrations/nextjs/pages-router.ts +39 -0
  69. package/src/lib/integrations/node-express/index.ts +14 -0
  70. package/src/lib/integrations/node-http/index.ts +143 -0
  71. package/src/lib/integrations/node-http/request-handler.ts +111 -0
  72. package/src/lib/integrations/shared.ts +161 -0
  73. package/src/lib/logger.ts +28 -0
  74. package/src/lib/observability.ts +160 -0
  75. package/src/lib/runtime/__tests__/copilot-runtime-error.test.ts +169 -0
  76. package/src/lib/runtime/__tests__/mcp-tools-utils.test.ts +464 -0
  77. package/src/lib/runtime/agent-integrations/langgraph/agent.ts +209 -0
  78. package/src/lib/runtime/agent-integrations/langgraph/consts.ts +34 -0
  79. package/src/lib/runtime/agent-integrations/langgraph/index.ts +2 -0
  80. package/src/lib/runtime/copilot-runtime.ts +710 -0
  81. package/src/lib/runtime/mcp-tools-utils.ts +254 -0
  82. package/src/lib/runtime/retry-utils.ts +96 -0
  83. package/src/lib/runtime/telemetry-agent-runner.ts +139 -0
  84. package/src/lib/runtime/types.ts +49 -0
  85. package/src/lib/runtime/utils.ts +87 -0
  86. package/src/lib/streaming.ts +202 -0
  87. package/src/lib/telemetry-client.ts +64 -0
  88. package/src/service-adapters/anthropic/anthropic-adapter.ts +452 -0
  89. package/src/service-adapters/anthropic/utils.ts +152 -0
  90. package/src/service-adapters/bedrock/bedrock-adapter.ts +73 -0
  91. package/src/service-adapters/conversion.ts +67 -0
  92. package/src/service-adapters/empty/empty-adapter.ts +38 -0
  93. package/src/service-adapters/events.ts +294 -0
  94. package/src/service-adapters/experimental/ollama/ollama-adapter.ts +84 -0
  95. package/src/service-adapters/google/google-genai-adapter.test.ts +104 -0
  96. package/src/service-adapters/google/google-genai-adapter.ts +88 -0
  97. package/src/service-adapters/groq/groq-adapter.ts +203 -0
  98. package/src/service-adapters/index.ts +18 -0
  99. package/src/service-adapters/langchain/langchain-adapter.ts +111 -0
  100. package/src/service-adapters/langchain/langserve.ts +88 -0
  101. package/src/service-adapters/langchain/types.ts +14 -0
  102. package/src/service-adapters/langchain/utils.ts +313 -0
  103. package/src/service-adapters/openai/openai-adapter.ts +283 -0
  104. package/src/service-adapters/openai/openai-assistant-adapter.ts +344 -0
  105. package/src/service-adapters/openai/utils.ts +199 -0
  106. package/src/service-adapters/service-adapter.ts +41 -0
  107. package/src/service-adapters/shared/error-utils.ts +61 -0
  108. package/src/service-adapters/shared/index.ts +1 -0
  109. package/src/service-adapters/unify/unify-adapter.ts +151 -0
  110. package/src/utils/failed-response-status-reasons.ts +70 -0
  111. package/src/utils/index.ts +1 -0
  112. package/src/v2/index.ts +3 -0
  113. package/tests/global.d.ts +13 -0
  114. package/tests/service-adapters/anthropic/allowlist-approach.test.ts +226 -0
  115. package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +389 -0
  116. package/tests/service-adapters/openai/allowlist-approach.test.ts +238 -0
  117. package/tests/service-adapters/openai/openai-adapter.test.ts +301 -0
  118. package/tests/setup.jest.ts +21 -0
  119. package/tests/tsconfig.json +10 -0
  120. package/tsconfig.json +13 -0
  121. package/tsup.config.ts +20 -0
  122. package/typedoc.json +4 -0
@@ -0,0 +1,344 @@
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 type OpenAI from "openai";
26
+ import type { RunSubmitToolOutputsStreamParams } from "openai/resources/beta/threads/runs/runs";
27
+ import type { AssistantStream } from "openai/lib/AssistantStream";
28
+ import type { AssistantStreamEvent, AssistantTool } from "openai/resources/beta/assistants";
29
+ import {
30
+ CopilotServiceAdapter,
31
+ CopilotRuntimeChatCompletionRequest,
32
+ CopilotRuntimeChatCompletionResponse,
33
+ } from "../service-adapter";
34
+ import { Message, ResultMessage, TextMessage } from "../../graphql/types/converted";
35
+ import {
36
+ convertActionInputToOpenAITool,
37
+ convertMessageToOpenAIMessage,
38
+ convertSystemMessageToAssistantAPI,
39
+ } from "./utils";
40
+ import { RuntimeEventSource } from "../events";
41
+ import { ActionInput } from "../../graphql/inputs/action.input";
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
+ * Whether to keep the role in system messages as "System".
79
+ * By default, it is converted to "developer", which is used by newer OpenAI models
80
+ *
81
+ * @default false
82
+ */
83
+ keepSystemRole?: boolean;
84
+ }
85
+
86
+ export class OpenAIAssistantAdapter implements CopilotServiceAdapter {
87
+ private _openai: OpenAI;
88
+ private codeInterpreterEnabled: boolean;
89
+ private assistantId: string;
90
+ private fileSearchEnabled: boolean;
91
+ private disableParallelToolCalls: boolean;
92
+ private keepSystemRole: boolean = false;
93
+
94
+ public get name() {
95
+ return "OpenAIAssistantAdapter";
96
+ }
97
+
98
+ constructor(params: OpenAIAssistantAdapterParams) {
99
+ if (params.openai) {
100
+ this._openai = params.openai;
101
+ }
102
+ // If no instance provided, we'll lazy-load in ensureOpenAI()
103
+ this.codeInterpreterEnabled = params.codeInterpreterEnabled === false || true;
104
+ this.fileSearchEnabled = params.fileSearchEnabled === false || true;
105
+ this.assistantId = params.assistantId;
106
+ this.disableParallelToolCalls = params?.disableParallelToolCalls || false;
107
+ this.keepSystemRole = params?.keepSystemRole ?? false;
108
+ }
109
+
110
+ private ensureOpenAI(): OpenAI {
111
+ if (!this._openai) {
112
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
113
+ const OpenAI = require("openai").default;
114
+ this._openai = new OpenAI({});
115
+ }
116
+ return this._openai;
117
+ }
118
+
119
+ async process(
120
+ request: CopilotRuntimeChatCompletionRequest,
121
+ ): Promise<CopilotRuntimeChatCompletionResponse> {
122
+ const { messages, actions, eventSource, runId, forwardedParameters } = request;
123
+
124
+ // if we don't have a threadId, create a new thread
125
+ let threadId = request.extensions?.openaiAssistantAPI?.threadId;
126
+ const openai = this.ensureOpenAI();
127
+
128
+ if (!threadId) {
129
+ threadId = (await openai.beta.threads.create()).id;
130
+ }
131
+
132
+ const lastMessage = messages.at(-1);
133
+
134
+ let nextRunId: string | undefined = undefined;
135
+
136
+ // submit function outputs
137
+ if (lastMessage.isResultMessage() && runId) {
138
+ nextRunId = await this.submitToolOutputs(threadId, runId, messages, eventSource);
139
+ }
140
+ // submit user message
141
+ else if (lastMessage.isTextMessage()) {
142
+ nextRunId = await this.submitUserMessage(
143
+ threadId,
144
+ messages,
145
+ actions,
146
+ eventSource,
147
+ forwardedParameters,
148
+ );
149
+ }
150
+ // unsupported message
151
+ else {
152
+ throw new Error("No actionable message found in the messages");
153
+ }
154
+
155
+ return {
156
+ runId: nextRunId,
157
+ threadId,
158
+ extensions: {
159
+ ...request.extensions,
160
+ openaiAssistantAPI: {
161
+ threadId: threadId,
162
+ runId: nextRunId,
163
+ },
164
+ },
165
+ };
166
+ }
167
+
168
+ private async submitToolOutputs(
169
+ threadId: string,
170
+ runId: string,
171
+ messages: Message[],
172
+ eventSource: RuntimeEventSource,
173
+ ) {
174
+ const openai = this.ensureOpenAI();
175
+ let run = await openai.beta.threads.runs.retrieve(threadId, runId);
176
+
177
+ if (!run.required_action) {
178
+ throw new Error("No tool outputs required");
179
+ }
180
+
181
+ // get the required tool call ids
182
+ const toolCallsIds = run.required_action.submit_tool_outputs.tool_calls.map(
183
+ (toolCall) => toolCall.id,
184
+ );
185
+
186
+ // search for these tool calls
187
+ const resultMessages = messages.filter(
188
+ (message) => message.isResultMessage() && toolCallsIds.includes(message.actionExecutionId),
189
+ ) as ResultMessage[];
190
+
191
+ if (toolCallsIds.length != resultMessages.length) {
192
+ throw new Error("Number of function results does not match the number of tool calls");
193
+ }
194
+
195
+ // submit the tool outputs
196
+ const toolOutputs: RunSubmitToolOutputsStreamParams.ToolOutput[] = resultMessages.map(
197
+ (message) => {
198
+ return {
199
+ tool_call_id: message.actionExecutionId,
200
+ output: message.result,
201
+ };
202
+ },
203
+ );
204
+
205
+ const stream = openai.beta.threads.runs.submitToolOutputsStream(threadId, runId, {
206
+ tool_outputs: toolOutputs,
207
+ ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
208
+ });
209
+
210
+ await this.streamResponse(stream, eventSource);
211
+ return runId;
212
+ }
213
+
214
+ private async submitUserMessage(
215
+ threadId: string,
216
+ messages: Message[],
217
+ actions: ActionInput[],
218
+ eventSource: RuntimeEventSource,
219
+ forwardedParameters: ForwardedParametersInput,
220
+ ) {
221
+ const openai = this.ensureOpenAI();
222
+ messages = [...messages];
223
+
224
+ // get the instruction message
225
+ const instructionsMessage = messages.shift();
226
+ const instructions = instructionsMessage.isTextMessage() ? instructionsMessage.content : "";
227
+
228
+ // get the latest user message
229
+ const userMessage = messages
230
+ .map((m) => convertMessageToOpenAIMessage(m, { keepSystemRole: this.keepSystemRole }))
231
+ .map(convertSystemMessageToAssistantAPI)
232
+ .at(-1);
233
+
234
+ if (userMessage.role !== "user") {
235
+ throw new Error("No user message found");
236
+ }
237
+
238
+ await openai.beta.threads.messages.create(threadId, {
239
+ role: "user",
240
+ content: userMessage.content,
241
+ });
242
+
243
+ const openaiTools = actions.map(convertActionInputToOpenAITool);
244
+
245
+ const tools = [
246
+ ...openaiTools,
247
+ ...(this.codeInterpreterEnabled ? [{ type: "code_interpreter" } as AssistantTool] : []),
248
+ ...(this.fileSearchEnabled ? [{ type: "file_search" } as AssistantTool] : []),
249
+ ];
250
+
251
+ let stream = openai.beta.threads.runs.stream(threadId, {
252
+ assistant_id: this.assistantId,
253
+ instructions,
254
+ tools: tools,
255
+ ...(forwardedParameters?.maxTokens && {
256
+ max_completion_tokens: forwardedParameters.maxTokens,
257
+ }),
258
+ ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
259
+ });
260
+
261
+ await this.streamResponse(stream, eventSource);
262
+
263
+ return getRunIdFromStream(stream);
264
+ }
265
+
266
+ private async streamResponse(stream: AssistantStream, eventSource: RuntimeEventSource) {
267
+ eventSource.stream(async (eventStream$) => {
268
+ let inFunctionCall = false;
269
+ let currentMessageId: string;
270
+ let currentToolCallId: string;
271
+
272
+ for await (const chunk of stream) {
273
+ switch (chunk.event) {
274
+ case "thread.message.created":
275
+ if (inFunctionCall) {
276
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
277
+ }
278
+ currentMessageId = chunk.data.id;
279
+ eventStream$.sendTextMessageStart({ messageId: currentMessageId });
280
+ break;
281
+ case "thread.message.delta":
282
+ if (chunk.data.delta.content?.[0].type === "text") {
283
+ eventStream$.sendTextMessageContent({
284
+ messageId: currentMessageId,
285
+ content: chunk.data.delta.content?.[0].text.value,
286
+ });
287
+ }
288
+ break;
289
+ case "thread.message.completed":
290
+ eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
291
+ break;
292
+ case "thread.run.step.delta":
293
+ let toolCallId: string | undefined;
294
+ let toolCallName: string | undefined;
295
+ let toolCallArgs: string | undefined;
296
+ if (
297
+ chunk.data.delta.step_details.type === "tool_calls" &&
298
+ chunk.data.delta.step_details.tool_calls?.[0].type === "function"
299
+ ) {
300
+ toolCallId = chunk.data.delta.step_details.tool_calls?.[0].id;
301
+ toolCallName = chunk.data.delta.step_details.tool_calls?.[0].function.name;
302
+ toolCallArgs = chunk.data.delta.step_details.tool_calls?.[0].function.arguments;
303
+ }
304
+
305
+ if (toolCallName && toolCallId) {
306
+ if (inFunctionCall) {
307
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
308
+ }
309
+ inFunctionCall = true;
310
+ currentToolCallId = toolCallId;
311
+ eventStream$.sendActionExecutionStart({
312
+ actionExecutionId: currentToolCallId,
313
+ parentMessageId: chunk.data.id,
314
+ actionName: toolCallName,
315
+ });
316
+ } else if (toolCallArgs) {
317
+ eventStream$.sendActionExecutionArgs({
318
+ actionExecutionId: currentToolCallId,
319
+ args: toolCallArgs,
320
+ });
321
+ }
322
+ break;
323
+ }
324
+ }
325
+ if (inFunctionCall) {
326
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
327
+ }
328
+ eventStream$.complete();
329
+ });
330
+ }
331
+ }
332
+
333
+ function getRunIdFromStream(stream: AssistantStream): Promise<string> {
334
+ return new Promise<string>((resolve, reject) => {
335
+ let runIdGetter = (event: AssistantStreamEvent) => {
336
+ if (event.event === "thread.run.created") {
337
+ const runId = event.data.id;
338
+ stream.off("event", runIdGetter);
339
+ resolve(runId);
340
+ }
341
+ };
342
+ stream.on("event", runIdGetter);
343
+ });
344
+ }
@@ -0,0 +1,199 @@
1
+ import { Message } from "../../graphql/types/converted";
2
+ import { ActionInput } from "../../graphql/inputs/action.input";
3
+ import {
4
+ ChatCompletionAssistantMessageParam,
5
+ ChatCompletionMessageParam,
6
+ ChatCompletionSystemMessageParam,
7
+ ChatCompletionTool,
8
+ ChatCompletionUserMessageParam,
9
+ ChatCompletionDeveloperMessageParam,
10
+ } from "openai/resources/chat";
11
+ import { parseJson } from "@copilotkit/shared";
12
+
13
+ export function limitMessagesToTokenCount(
14
+ messages: any[],
15
+ tools: any[],
16
+ model: string,
17
+ maxTokens?: number,
18
+ ): any[] {
19
+ maxTokens ||= maxTokensForOpenAIModel(model);
20
+
21
+ const result: any[] = [];
22
+ const toolsNumTokens = countToolsTokens(model, tools);
23
+ if (toolsNumTokens > maxTokens) {
24
+ throw new Error(`Too many tokens in function definitions: ${toolsNumTokens} > ${maxTokens}`);
25
+ }
26
+ maxTokens -= toolsNumTokens;
27
+
28
+ for (const message of messages) {
29
+ if (["system", "developer"].includes(message.role)) {
30
+ const numTokens = countMessageTokens(model, message);
31
+ maxTokens -= numTokens;
32
+
33
+ if (maxTokens < 0) {
34
+ throw new Error("Not enough tokens for system message.");
35
+ }
36
+ }
37
+ }
38
+
39
+ let cutoff: boolean = false;
40
+
41
+ const reversedMessages = [...messages].reverse();
42
+ for (const message of reversedMessages) {
43
+ if (["system", "developer"].includes(message.role)) {
44
+ result.unshift(message);
45
+ continue;
46
+ } else if (cutoff) {
47
+ continue;
48
+ }
49
+ let numTokens = countMessageTokens(model, message);
50
+ if (maxTokens < numTokens) {
51
+ cutoff = true;
52
+ continue;
53
+ }
54
+ result.unshift(message);
55
+ maxTokens -= numTokens;
56
+ }
57
+
58
+ return result;
59
+ }
60
+
61
+ export function maxTokensForOpenAIModel(model: string): number {
62
+ return maxTokensByModel[model] || DEFAULT_MAX_TOKENS;
63
+ }
64
+
65
+ const DEFAULT_MAX_TOKENS = 128000;
66
+
67
+ const maxTokensByModel: { [key: string]: number } = {
68
+ // o1
69
+ o1: 200000,
70
+ "o1-2024-12-17": 200000,
71
+ "o1-mini": 128000,
72
+ "o1-mini-2024-09-12": 128000,
73
+ "o1-preview": 128000,
74
+ "o1-preview-2024-09-12": 128000,
75
+ // o3-mini
76
+ "o3-mini": 200000,
77
+ "o3-mini-2025-01-31": 200000,
78
+ // GPT-4
79
+ "gpt-4o": 128000,
80
+ "chatgpt-4o-latest": 128000,
81
+ "gpt-4o-2024-08-06": 128000,
82
+ "gpt-4o-2024-05-13": 128000,
83
+ "gpt-4o-mini": 128000,
84
+ "gpt-4o-mini-2024-07-18": 128000,
85
+ "gpt-4-turbo": 128000,
86
+ "gpt-4-turbo-2024-04-09": 128000,
87
+ "gpt-4-0125-preview": 128000,
88
+ "gpt-4-turbo-preview": 128000,
89
+ "gpt-4-1106-preview": 128000,
90
+ "gpt-4-vision-preview": 128000,
91
+ "gpt-4-1106-vision-preview": 128000,
92
+ "gpt-4-32k": 32768,
93
+ "gpt-4-32k-0613": 32768,
94
+ "gpt-4-32k-0314": 32768,
95
+ "gpt-4": 8192,
96
+ "gpt-4-0613": 8192,
97
+ "gpt-4-0314": 8192,
98
+
99
+ // GPT-3.5
100
+ "gpt-3.5-turbo-0125": 16385,
101
+ "gpt-3.5-turbo": 16385,
102
+ "gpt-3.5-turbo-1106": 16385,
103
+ "gpt-3.5-turbo-instruct": 4096,
104
+ "gpt-3.5-turbo-16k": 16385,
105
+ "gpt-3.5-turbo-0613": 4096,
106
+ "gpt-3.5-turbo-16k-0613": 16385,
107
+ "gpt-3.5-turbo-0301": 4097,
108
+ };
109
+
110
+ function countToolsTokens(model: string, tools: any[]): number {
111
+ if (tools.length === 0) {
112
+ return 0;
113
+ }
114
+ const json = JSON.stringify(tools);
115
+ return countTokens(model, json);
116
+ }
117
+
118
+ function countMessageTokens(model: string, message: any): number {
119
+ return countTokens(model, message.content || "");
120
+ }
121
+
122
+ function countTokens(model: string, text: string): number {
123
+ return text.length / 3;
124
+ }
125
+
126
+ export function convertActionInputToOpenAITool(action: ActionInput): ChatCompletionTool {
127
+ return {
128
+ type: "function",
129
+ function: {
130
+ name: action.name,
131
+ description: action.description,
132
+ parameters: parseJson(action.jsonSchema, {}),
133
+ },
134
+ };
135
+ }
136
+
137
+ type UsedMessageParams =
138
+ | ChatCompletionUserMessageParam
139
+ | ChatCompletionAssistantMessageParam
140
+ | ChatCompletionDeveloperMessageParam
141
+ | ChatCompletionSystemMessageParam;
142
+ export function convertMessageToOpenAIMessage(
143
+ message: Message,
144
+ options?: { keepSystemRole: boolean },
145
+ ): ChatCompletionMessageParam {
146
+ const { keepSystemRole } = options || { keepSystemRole: false };
147
+ if (message.isTextMessage()) {
148
+ let role = message.role as UsedMessageParams["role"];
149
+ if (message.role === "system" && !keepSystemRole) {
150
+ role = "developer";
151
+ }
152
+ return {
153
+ role,
154
+ content: message.content,
155
+ } satisfies UsedMessageParams;
156
+ } else if (message.isImageMessage()) {
157
+ return {
158
+ role: "user",
159
+ content: [
160
+ {
161
+ type: "image_url",
162
+ image_url: {
163
+ url: `data:image/${message.format};base64,${message.bytes}`,
164
+ },
165
+ },
166
+ ],
167
+ } satisfies UsedMessageParams;
168
+ } else if (message.isActionExecutionMessage()) {
169
+ return {
170
+ role: "assistant",
171
+ tool_calls: [
172
+ {
173
+ id: message.id,
174
+ type: "function",
175
+ function: {
176
+ name: message.name,
177
+ arguments: JSON.stringify(message.arguments),
178
+ },
179
+ },
180
+ ],
181
+ };
182
+ } else if (message.isResultMessage()) {
183
+ return {
184
+ role: "tool",
185
+ content: message.result,
186
+ tool_call_id: message.actionExecutionId,
187
+ };
188
+ }
189
+ }
190
+
191
+ export function convertSystemMessageToAssistantAPI(message: ChatCompletionMessageParam) {
192
+ return {
193
+ ...message,
194
+ ...(["system", "developer"].includes(message.role) && {
195
+ role: "assistant",
196
+ content: "THE FOLLOWING MESSAGE IS A SYSTEM MESSAGE: " + message.content,
197
+ }),
198
+ };
199
+ }
@@ -0,0 +1,41 @@
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
+ import { ExtensionsInput } from "../graphql/inputs/extensions.input";
6
+ import { ExtensionsResponse } from "../graphql/types/extensions-response.type";
7
+ import { AgentSessionInput } from "../graphql/inputs/agent-session.input";
8
+ import { AgentStateInput } from "../graphql/inputs/agent-state.input";
9
+
10
+ export interface CopilotKitResponse {
11
+ stream: ReadableStream;
12
+ headers?: Record<string, string>;
13
+ }
14
+
15
+ export interface CopilotRuntimeChatCompletionRequest {
16
+ eventSource: RuntimeEventSource;
17
+ messages: Message[];
18
+ actions: ActionInput[];
19
+ model?: string;
20
+ threadId?: string;
21
+ runId?: string;
22
+ forwardedParameters?: ForwardedParametersInput;
23
+ extensions?: ExtensionsInput;
24
+ agentSession?: AgentSessionInput;
25
+ agentStates?: AgentStateInput[];
26
+ }
27
+
28
+ export interface CopilotRuntimeChatCompletionResponse {
29
+ threadId: string;
30
+ runId?: string;
31
+ extensions?: ExtensionsResponse;
32
+ }
33
+
34
+ export interface CopilotServiceAdapter {
35
+ provider?: string;
36
+ model?: string;
37
+ process(
38
+ request: CopilotRuntimeChatCompletionRequest,
39
+ ): Promise<CopilotRuntimeChatCompletionResponse>;
40
+ name?: string;
41
+ }
@@ -0,0 +1,61 @@
1
+ import { CopilotKitLowLevelError, CopilotKitErrorCode } from "@copilotkit/shared";
2
+
3
+ /**
4
+ * Converts service adapter errors to structured CopilotKitError format using HTTP status codes.
5
+ * This provides consistent error classification across all service adapters.
6
+ */
7
+ export function convertServiceAdapterError(
8
+ error: any,
9
+ adapterName: string,
10
+ ): CopilotKitLowLevelError {
11
+ const errorName = error?.constructor?.name || error.name;
12
+ const errorMessage = error?.message || String(error);
13
+ const statusCode = error.status || error.statusCode || error.response?.status;
14
+ const responseData = error.error || error.response?.data || error.data;
15
+
16
+ // Create the base error with the constructor signature
17
+ const structuredError = new CopilotKitLowLevelError({
18
+ error: error instanceof Error ? error : new Error(errorMessage),
19
+ url: `${adapterName} service adapter`,
20
+ message: `${adapterName} API error: ${errorMessage}`,
21
+ });
22
+
23
+ // Add additional properties after construction
24
+ if (statusCode) {
25
+ (structuredError as any).statusCode = statusCode;
26
+ }
27
+ if (responseData) {
28
+ (structuredError as any).responseData = responseData;
29
+ }
30
+ if (errorName) {
31
+ (structuredError as any).originalErrorType = errorName;
32
+ }
33
+
34
+ // Classify error based on HTTP status codes (reliable and provider-agnostic)
35
+ let newCode: CopilotKitErrorCode;
36
+
37
+ if (statusCode === 401) {
38
+ // 401 = Authentication/API key issues
39
+ newCode = CopilotKitErrorCode.AUTHENTICATION_ERROR;
40
+ } else if (statusCode >= 400 && statusCode < 500) {
41
+ // 4xx = Client errors (bad request, invalid params, etc.) - these are configuration issues
42
+ newCode = CopilotKitErrorCode.CONFIGURATION_ERROR;
43
+ } else if (statusCode >= 500) {
44
+ // 5xx = Server errors - keep as NETWORK_ERROR since it's infrastructure related
45
+ newCode = CopilotKitErrorCode.NETWORK_ERROR;
46
+ } else if (statusCode) {
47
+ // Any other HTTP status with an error - likely configuration
48
+ newCode = CopilotKitErrorCode.CONFIGURATION_ERROR;
49
+ } else {
50
+ // No status code - likely a genuine network/connection error
51
+ newCode = CopilotKitErrorCode.NETWORK_ERROR;
52
+ }
53
+
54
+ // Update both the instance property and the extensions
55
+ (structuredError as any).code = newCode;
56
+ if ((structuredError as any).extensions) {
57
+ (structuredError as any).extensions.code = newCode;
58
+ }
59
+
60
+ return structuredError;
61
+ }
@@ -0,0 +1 @@
1
+ export * from "./error-utils";