@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.
- package/.eslintrc.js +7 -0
- package/CHANGELOG.md +913 -0
- package/README.md +46 -0
- package/__snapshots__/schema/schema.graphql +273 -0
- package/dist/chunk-44O2JGUY.mjs +12 -0
- package/dist/chunk-44O2JGUY.mjs.map +1 -0
- package/dist/chunk-BETLEV37.mjs +25 -0
- package/dist/chunk-BETLEV37.mjs.map +1 -0
- package/dist/chunk-CLGKEUOA.mjs +1408 -0
- package/dist/chunk-CLGKEUOA.mjs.map +1 -0
- package/dist/chunk-D2WLFQS6.mjs +43 -0
- package/dist/chunk-D2WLFQS6.mjs.map +1 -0
- package/dist/chunk-DFOKBSIS.mjs +1 -0
- package/dist/chunk-DFOKBSIS.mjs.map +1 -0
- package/dist/chunk-FA5DJ2TZ.mjs +3437 -0
- package/dist/chunk-FA5DJ2TZ.mjs.map +1 -0
- package/dist/chunk-HNUNXFTW.mjs +129 -0
- package/dist/chunk-HNUNXFTW.mjs.map +1 -0
- package/dist/chunk-SFLMY3ES.mjs +80 -0
- package/dist/chunk-SFLMY3ES.mjs.map +1 -0
- package/dist/chunk-U3V2BCGI.mjs +152 -0
- package/dist/chunk-U3V2BCGI.mjs.map +1 -0
- package/dist/chunk-ZCU6UPCY.mjs +25 -0
- package/dist/chunk-ZCU6UPCY.mjs.map +1 -0
- package/dist/copilot-runtime-1a224a0f.d.ts +196 -0
- package/dist/graphql/types/base/index.d.ts +6 -0
- package/dist/graphql/types/base/index.js +63 -0
- package/dist/graphql/types/base/index.js.map +1 -0
- package/dist/graphql/types/base/index.mjs +8 -0
- package/dist/graphql/types/base/index.mjs.map +1 -0
- package/dist/graphql/types/converted/index.d.ts +2 -0
- package/dist/graphql/types/converted/index.js +187 -0
- package/dist/graphql/types/converted/index.js.map +1 -0
- package/dist/graphql/types/converted/index.mjs +17 -0
- package/dist/graphql/types/converted/index.mjs.map +1 -0
- package/dist/groq-adapter-c35c5374.d.ts +281 -0
- package/dist/index-24315d90.d.ts +103 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +5258 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +76 -0
- package/dist/index.mjs.map +1 -0
- package/dist/langserve-a16ef8f4.d.ts +180 -0
- package/dist/lib/cloud/index.d.ts +6 -0
- package/dist/lib/cloud/index.js +18 -0
- package/dist/lib/cloud/index.js.map +1 -0
- package/dist/lib/cloud/index.mjs +1 -0
- package/dist/lib/cloud/index.mjs.map +1 -0
- package/dist/lib/index.d.ts +20 -0
- package/dist/lib/index.js +4906 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/index.mjs +58 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/integrations/index.d.ts +33 -0
- package/dist/lib/integrations/index.js +2229 -0
- package/dist/lib/integrations/index.js.map +1 -0
- package/dist/lib/integrations/index.mjs +34 -0
- package/dist/lib/integrations/index.mjs.map +1 -0
- package/dist/lib/integrations/nest/index.d.ts +14 -0
- package/dist/lib/integrations/nest/index.js +2138 -0
- package/dist/lib/integrations/nest/index.js.map +1 -0
- package/dist/lib/integrations/nest/index.mjs +13 -0
- package/dist/lib/integrations/nest/index.mjs.map +1 -0
- package/dist/lib/integrations/node-express/index.d.ts +14 -0
- package/dist/lib/integrations/node-express/index.js +2138 -0
- package/dist/lib/integrations/node-express/index.js.map +1 -0
- package/dist/lib/integrations/node-express/index.mjs +13 -0
- package/dist/lib/integrations/node-express/index.mjs.map +1 -0
- package/dist/lib/integrations/node-http/index.d.ts +14 -0
- package/dist/lib/integrations/node-http/index.js +2124 -0
- package/dist/lib/integrations/node-http/index.js.map +1 -0
- package/dist/lib/integrations/node-http/index.mjs +12 -0
- package/dist/lib/integrations/node-http/index.mjs.map +1 -0
- package/dist/service-adapters/index.d.ts +84 -0
- package/dist/service-adapters/index.js +1448 -0
- package/dist/service-adapters/index.js.map +1 -0
- package/dist/service-adapters/index.mjs +26 -0
- package/dist/service-adapters/index.mjs.map +1 -0
- package/dist/utils/index.d.ts +49 -0
- package/dist/utils/index.js +174 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +12 -0
- package/dist/utils/index.mjs.map +1 -0
- package/jest.config.js +5 -0
- package/package.json +85 -0
- package/scripts/generate-gql-schema.ts +13 -0
- package/src/agents/langgraph/event-source.ts +287 -0
- package/src/agents/langgraph/events.ts +338 -0
- package/src/graphql/inputs/action.input.ts +16 -0
- package/src/graphql/inputs/agent-session.input.ts +13 -0
- package/src/graphql/inputs/agent-state.input.ts +10 -0
- package/src/graphql/inputs/cloud-guardrails.input.ts +16 -0
- package/src/graphql/inputs/cloud.input.ts +8 -0
- package/src/graphql/inputs/context-property.input.ts +10 -0
- package/src/graphql/inputs/custom-property.input.ts +15 -0
- package/src/graphql/inputs/forwarded-parameters.input.ts +22 -0
- package/src/graphql/inputs/frontend.input.ts +14 -0
- package/src/graphql/inputs/generate-copilot-response.input.ts +47 -0
- package/src/graphql/inputs/message.input.ts +92 -0
- package/src/graphql/resolvers/copilot.resolver.ts +556 -0
- package/src/graphql/types/agents-response.type.ts +22 -0
- package/src/graphql/types/base/index.ts +10 -0
- package/src/graphql/types/converted/index.ts +136 -0
- package/src/graphql/types/copilot-response.type.ts +113 -0
- package/src/graphql/types/enums.ts +37 -0
- package/src/graphql/types/guardrails-result.type.ts +20 -0
- package/src/graphql/types/message-status.type.ts +40 -0
- package/src/graphql/types/response-status.type.ts +66 -0
- package/src/index.ts +4 -0
- package/src/lib/cloud/index.ts +4 -0
- package/src/lib/index.ts +8 -0
- package/src/lib/integrations/index.ts +6 -0
- package/src/lib/integrations/nest/index.ts +17 -0
- package/src/lib/integrations/nextjs/app-router.ts +40 -0
- package/src/lib/integrations/nextjs/pages-router.ts +49 -0
- package/src/lib/integrations/node-express/index.ts +17 -0
- package/src/lib/integrations/node-http/index.ts +34 -0
- package/src/lib/integrations/shared.ts +109 -0
- package/src/lib/logger.ts +28 -0
- package/src/lib/runtime/copilot-runtime.ts +466 -0
- package/src/lib/runtime/remote-action-constructors.ts +304 -0
- package/src/lib/runtime/remote-actions.ts +174 -0
- package/src/lib/runtime/remote-lg-action.ts +657 -0
- package/src/lib/telemetry-client.ts +52 -0
- package/src/service-adapters/anthropic/anthropic-adapter.ts +205 -0
- package/src/service-adapters/anthropic/utils.ts +144 -0
- package/src/service-adapters/conversion.ts +64 -0
- package/src/service-adapters/events.ts +419 -0
- package/src/service-adapters/experimental/empty/empty-adapter.ts +33 -0
- package/src/service-adapters/experimental/ollama/ollama-adapter.ts +79 -0
- package/src/service-adapters/google/google-genai-adapter.ts +39 -0
- package/src/service-adapters/groq/groq-adapter.ts +173 -0
- package/src/service-adapters/index.ts +16 -0
- package/src/service-adapters/langchain/langchain-adapter.ts +99 -0
- package/src/service-adapters/langchain/langserve.ts +87 -0
- package/src/service-adapters/langchain/types.ts +14 -0
- package/src/service-adapters/langchain/utils.ts +306 -0
- package/src/service-adapters/openai/openai-adapter.ts +210 -0
- package/src/service-adapters/openai/openai-assistant-adapter.ts +304 -0
- package/src/service-adapters/openai/utils.ts +161 -0
- package/src/service-adapters/service-adapter.ts +30 -0
- package/src/service-adapters/unify/unify-adapter.ts +145 -0
- package/src/utils/failed-response-status-reasons.ts +48 -0
- package/src/utils/index.ts +1 -0
- package/tsconfig.json +11 -0
- package/tsup.config.ts +16 -0
- 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
|
+
}
|