@copilotkit/runtime 1.1.2 → 1.1.3-feat-runtime-remote-actions.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +32 -37
  3. package/__snapshots__/schema/schema.graphql +42 -0
  4. package/dist/chunk-4WFNRUBE.mjs +260 -0
  5. package/dist/chunk-4WFNRUBE.mjs.map +1 -0
  6. package/dist/{chunk-MKV3LEJ6.mjs → chunk-6X5MPWIC.mjs} +795 -159
  7. package/dist/chunk-6X5MPWIC.mjs.map +1 -0
  8. package/dist/{chunk-URMISMK2.mjs → chunk-73NMP3DI.mjs} +2 -2
  9. package/dist/{chunk-CUVWSISN.mjs → chunk-BJ2LVHWA.mjs} +22 -4
  10. package/dist/chunk-BJ2LVHWA.mjs.map +1 -0
  11. package/dist/{chunk-74B76SMO.mjs → chunk-HYNSUFUM.mjs} +2 -2
  12. package/dist/{chunk-6PAC74F2.mjs → chunk-JV3CSVW6.mjs} +2 -2
  13. package/dist/{chunk-NPCP4YZB.mjs → chunk-OYUVLDJF.mjs} +2 -2
  14. package/dist/{chunk-GEIBJJQ4.mjs → chunk-TBZGOJJX.mjs} +14 -2
  15. package/dist/chunk-TBZGOJJX.mjs.map +1 -0
  16. package/dist/{shared-ec6c7db5.d.ts → copilot-runtime-d427e991.d.ts} +68 -37
  17. package/dist/graphql/types/converted/index.d.ts +1 -1
  18. package/dist/graphql/types/converted/index.js +13 -0
  19. package/dist/graphql/types/converted/index.js.map +1 -1
  20. package/dist/graphql/types/converted/index.mjs +3 -1
  21. package/dist/{index-aa091e3c.d.ts → index-0476e4f7.d.ts} +24 -2
  22. package/dist/{groq-adapter-675b30c6.d.ts → index-079752b9.d.ts} +31 -1
  23. package/dist/index.d.ts +9 -9
  24. package/dist/index.js +1012 -246
  25. package/dist/index.js.map +1 -1
  26. package/dist/index.mjs +8 -8
  27. package/dist/{langserve-a54438c6.d.ts → langserve-d6073a3b.d.ts} +24 -11
  28. package/dist/lib/index.d.ts +8 -8
  29. package/dist/lib/index.js +1012 -246
  30. package/dist/lib/index.js.map +1 -1
  31. package/dist/lib/index.mjs +8 -8
  32. package/dist/lib/integrations/index.d.ts +26 -7
  33. package/dist/lib/integrations/index.js +446 -183
  34. package/dist/lib/integrations/index.js.map +1 -1
  35. package/dist/lib/integrations/index.mjs +6 -6
  36. package/dist/lib/integrations/nest/index.d.ts +5 -5
  37. package/dist/lib/integrations/nest/index.js +446 -183
  38. package/dist/lib/integrations/nest/index.js.map +1 -1
  39. package/dist/lib/integrations/nest/index.mjs +4 -4
  40. package/dist/lib/integrations/node-express/index.d.ts +5 -5
  41. package/dist/lib/integrations/node-express/index.js +446 -183
  42. package/dist/lib/integrations/node-express/index.js.map +1 -1
  43. package/dist/lib/integrations/node-express/index.mjs +4 -4
  44. package/dist/lib/integrations/node-http/index.d.ts +5 -5
  45. package/dist/lib/integrations/node-http/index.js +446 -183
  46. package/dist/lib/integrations/node-http/index.js.map +1 -1
  47. package/dist/lib/integrations/node-http/index.mjs +3 -3
  48. package/dist/service-adapters/index.d.ts +3 -3
  49. package/dist/service-adapters/index.js +19 -1
  50. package/dist/service-adapters/index.js.map +1 -1
  51. package/dist/service-adapters/index.mjs +3 -3
  52. package/dist/utils/index.d.ts +49 -1
  53. package/package.json +7 -5
  54. package/src/agents/langgraph/event-source.ts +222 -0
  55. package/src/agents/langgraph/events.ts +309 -0
  56. package/src/graphql/inputs/agent-session.input.ts +13 -0
  57. package/src/graphql/inputs/agent-state.input.ts +10 -0
  58. package/src/graphql/inputs/frontend.input.ts +3 -0
  59. package/src/graphql/inputs/generate-copilot-response.input.ts +11 -0
  60. package/src/graphql/inputs/message.input.ts +30 -0
  61. package/src/graphql/resolvers/copilot.resolver.ts +57 -12
  62. package/src/graphql/types/converted/index.ts +15 -0
  63. package/src/graphql/types/copilot-response.type.ts +29 -0
  64. package/src/graphql/types/enums.ts +1 -0
  65. package/src/lib/index.ts +1 -1
  66. package/src/lib/integrations/shared.ts +1 -1
  67. package/src/lib/runtime/copilot-runtime.ts +360 -0
  68. package/src/lib/runtime/remote-actions.ts +241 -0
  69. package/src/service-adapters/conversion.ts +16 -0
  70. package/src/service-adapters/events.ts +101 -19
  71. package/src/service-adapters/groq/groq-adapter.ts +13 -0
  72. package/src/service-adapters/openai/openai-adapter.ts +13 -0
  73. package/src/service-adapters/openai/openai-assistant-adapter.ts +14 -0
  74. package/dist/chunk-CUVWSISN.mjs.map +0 -1
  75. package/dist/chunk-GEIBJJQ4.mjs.map +0 -1
  76. package/dist/chunk-MKV3LEJ6.mjs.map +0 -1
  77. package/dist/chunk-RDEOIOQR.mjs +0 -155
  78. package/dist/chunk-RDEOIOQR.mjs.map +0 -1
  79. package/dist/failed-response-status-reasons-0ab19e06.d.ts +0 -49
  80. package/dist/pages-router-df82c666.d.ts +0 -21
  81. package/src/lib/copilot-runtime.ts +0 -225
  82. /package/dist/{chunk-URMISMK2.mjs.map → chunk-73NMP3DI.mjs.map} +0 -0
  83. /package/dist/{chunk-74B76SMO.mjs.map → chunk-HYNSUFUM.mjs.map} +0 -0
  84. /package/dist/{chunk-6PAC74F2.mjs.map → chunk-JV3CSVW6.mjs.map} +0 -0
  85. /package/dist/{chunk-NPCP4YZB.mjs.map → chunk-OYUVLDJF.mjs.map} +0 -0
@@ -0,0 +1,241 @@
1
+ import { Action } from "@copilotkit/shared";
2
+ import { GraphQLContext } from "../integrations/shared";
3
+ import { Logger } from "pino";
4
+ import telemetry from "../../lib/telemetry-client";
5
+ import { Message } from "../../graphql/types/converted";
6
+ import { RuntimeEvent, RuntimeEventSubject } from "../../service-adapters/events";
7
+ import { RemoteLangGraphEventSource } from "../../agents/langgraph/event-source";
8
+ import { Observable } from "rxjs";
9
+ import { ActionInput } from "../../graphql/inputs/action.input";
10
+ import { AgentStateInput } from "../../graphql/inputs/agent-state.input";
11
+
12
+ export type RemoteActionDefinition = {
13
+ url: string;
14
+ onBeforeRequest?: ({ ctx }: { ctx: GraphQLContext }) => {
15
+ headers?: Record<string, string> | undefined;
16
+ };
17
+ };
18
+
19
+ export type LangGraphAgentHandlerParams = {
20
+ name: string;
21
+ actionInputsWithoutAgents: ActionInput[];
22
+ threadId?: string;
23
+ nodeName?: string;
24
+ };
25
+
26
+ export type LangGraphAgentAction = Action<any> & {
27
+ langGraphAgentHandler: (params: LangGraphAgentHandlerParams) => Promise<Observable<RuntimeEvent>>;
28
+ };
29
+
30
+ export function isLangGraphAgentAction(action: Action<any>): action is LangGraphAgentAction {
31
+ if (!action) {
32
+ return false;
33
+ }
34
+ return typeof (action as LangGraphAgentAction).langGraphAgentHandler === "function";
35
+ }
36
+
37
+ function createHeaders(
38
+ onBeforeRequest: RemoteActionDefinition["onBeforeRequest"],
39
+ graphqlContext: GraphQLContext,
40
+ ) {
41
+ const headers = {
42
+ "Content-Type": "application/json",
43
+ };
44
+
45
+ if (onBeforeRequest) {
46
+ const { headers: additionalHeaders } = onBeforeRequest({ ctx: graphqlContext });
47
+ if (additionalHeaders) {
48
+ Object.assign(headers, additionalHeaders);
49
+ }
50
+ }
51
+
52
+ return headers;
53
+ }
54
+
55
+ async function fetchRemoteInfo({
56
+ url,
57
+ onBeforeRequest,
58
+ graphqlContext,
59
+ logger,
60
+ }: {
61
+ url: string;
62
+ onBeforeRequest?: RemoteActionDefinition["onBeforeRequest"];
63
+ graphqlContext: GraphQLContext;
64
+ logger: Logger;
65
+ }): Promise<any[]> {
66
+ logger.debug({ url }, "Fetching actions from url");
67
+ const headers = createHeaders(onBeforeRequest, graphqlContext);
68
+
69
+ const response = await fetch(`${url}/info`, {
70
+ method: "POST",
71
+ headers,
72
+ body: JSON.stringify({ properties: graphqlContext.properties }),
73
+ });
74
+
75
+ if (!response.ok) {
76
+ logger.error(
77
+ { url, status: response.status, body: await response.text() },
78
+ "Failed to fetch actions from url",
79
+ );
80
+ return [];
81
+ }
82
+
83
+ const json = await response.json();
84
+ logger.debug({ json }, "Fetched actions from url");
85
+ return json;
86
+ }
87
+
88
+ function constructRemoteActions({
89
+ json,
90
+ url,
91
+ onBeforeRequest,
92
+ graphqlContext,
93
+ logger,
94
+ messages,
95
+ agentStates,
96
+ }: {
97
+ json: any[];
98
+ url: string;
99
+ onBeforeRequest?: RemoteActionDefinition["onBeforeRequest"];
100
+ graphqlContext: GraphQLContext;
101
+ logger: Logger;
102
+ messages: Message[];
103
+ agentStates?: AgentStateInput[];
104
+ }): Action<any>[] {
105
+ const actions = json["actions"].map((action) => ({
106
+ name: action.name,
107
+ description: action.description,
108
+ parameters: action.parameters,
109
+ handler: async (args: any) => {
110
+ logger.debug({ actionName: action.name, args }, "Executing remote action");
111
+
112
+ const headers = createHeaders(onBeforeRequest, graphqlContext);
113
+ telemetry.capture("oss.runtime.remote_action_executed", {});
114
+
115
+ const response = await fetch(`${url}/actions/execute`, {
116
+ method: "POST",
117
+ headers,
118
+ body: JSON.stringify({
119
+ name: action.name,
120
+ arguments: args,
121
+ properties: graphqlContext.properties,
122
+ }),
123
+ });
124
+
125
+ if (!response.ok) {
126
+ logger.error(
127
+ { url, status: response.status, body: await response.text() },
128
+ "Failed to execute remote action",
129
+ );
130
+ return "Failed to execute remote action";
131
+ }
132
+
133
+ const requestResult = await response.json();
134
+
135
+ const result = requestResult["result"];
136
+ logger.debug({ actionName: action.name, result }, "Executed remote action");
137
+ return result;
138
+ },
139
+ }));
140
+
141
+ const agents = json["agents"].map((agent) => ({
142
+ name: agent.name,
143
+ description: agent.description,
144
+ parameters: [],
145
+ handler: async (_args: any) => {},
146
+
147
+ langGraphAgentHandler: async ({
148
+ name,
149
+ actionInputsWithoutAgents,
150
+ threadId,
151
+ nodeName,
152
+ }: LangGraphAgentHandlerParams): Promise<Observable<RuntimeEvent>> => {
153
+ logger.debug({ actionName: agent.name }, "Executing remote agent");
154
+
155
+ const headers = createHeaders(onBeforeRequest, graphqlContext);
156
+ telemetry.capture("oss.runtime.remote_action_executed", {});
157
+
158
+ let state = {};
159
+ if (agentStates) {
160
+ const jsonState = agentStates.find((state) => state.agentName === name)?.state;
161
+ if (jsonState) {
162
+ state = JSON.parse(jsonState);
163
+ }
164
+ }
165
+
166
+ const response = await fetch(`${url}/agents/execute`, {
167
+ method: "POST",
168
+ headers,
169
+ body: JSON.stringify({
170
+ name,
171
+ threadId,
172
+ nodeName,
173
+ messages,
174
+ state,
175
+ properties: graphqlContext.properties,
176
+ actions: actionInputsWithoutAgents.map((action) => ({
177
+ name: action.name,
178
+ description: action.description,
179
+ parameters: JSON.parse(action.jsonSchema),
180
+ })),
181
+ }),
182
+ });
183
+
184
+ if (!response.ok) {
185
+ logger.error(
186
+ { url, status: response.status, body: await response.text() },
187
+ "Failed to execute remote agent",
188
+ );
189
+ throw new Error("Failed to execute remote agent");
190
+ }
191
+
192
+ const eventSource = new RemoteLangGraphEventSource();
193
+ eventSource.streamResponse(response);
194
+ return eventSource.processLangGraphEvents();
195
+ },
196
+ }));
197
+
198
+ return [...actions, ...agents];
199
+ }
200
+
201
+ export async function setupRemoteActions({
202
+ remoteActionDefinitions,
203
+ graphqlContext,
204
+ messages,
205
+ agentStates,
206
+ }: {
207
+ remoteActionDefinitions: RemoteActionDefinition[];
208
+ graphqlContext: GraphQLContext;
209
+ messages: Message[];
210
+ agentStates?: AgentStateInput[];
211
+ }): Promise<Action[]> {
212
+ const logger = graphqlContext.logger.child({ component: "remote-actions.fetchRemoteActions" });
213
+ logger.debug({ remoteActionDefinitions }, "Fetching remote actions");
214
+
215
+ // Remove duplicates of remoteActionDefinitions.url
216
+ const filtered = remoteActionDefinitions.filter(
217
+ (value, index, self) => index === self.findIndex((t) => t.url === value.url),
218
+ );
219
+
220
+ const result = await Promise.all(
221
+ filtered.map(async (actionDefinition) => {
222
+ const json = await fetchRemoteInfo({
223
+ url: actionDefinition.url,
224
+ onBeforeRequest: actionDefinition.onBeforeRequest,
225
+ graphqlContext,
226
+ logger: logger.child({ component: "remote-actions.fetchActionsFromUrl", actionDefinition }),
227
+ });
228
+ return constructRemoteActions({
229
+ json,
230
+ messages,
231
+ url: actionDefinition.url,
232
+ onBeforeRequest: actionDefinition.onBeforeRequest,
233
+ graphqlContext,
234
+ logger: logger.child({ component: "remote-actions.constructActions", actionDefinition }),
235
+ agentStates,
236
+ });
237
+ }),
238
+ );
239
+
240
+ return result.flat();
241
+ }
@@ -3,6 +3,7 @@ import {
3
3
  Message,
4
4
  ResultMessage,
5
5
  TextMessage,
6
+ AgentStateMessage,
6
7
  } from "../graphql/types/converted";
7
8
  import { MessageInput } from "../graphql/inputs/message.input";
8
9
  import { plainToInstance } from "class-transformer";
@@ -40,6 +41,21 @@ export function convertGqlInputToMessages(inputMessages: MessageInput[]): Messag
40
41
  result: message.resultMessage.result,
41
42
  }),
42
43
  );
44
+ } else if (message.agentStateMessage) {
45
+ messages.push(
46
+ plainToInstance(AgentStateMessage, {
47
+ id: message.id,
48
+ threadId: message.agentStateMessage.threadId,
49
+ createdAt: message.createdAt,
50
+ agentName: message.agentStateMessage.agentName,
51
+ nodeName: message.agentStateMessage.nodeName,
52
+ runId: message.agentStateMessage.runId,
53
+ active: message.agentStateMessage.active,
54
+ role: message.agentStateMessage.role,
55
+ state: JSON.parse(message.agentStateMessage.state),
56
+ running: message.agentStateMessage.running,
57
+ }),
58
+ );
43
59
  }
44
60
  }
45
61
 
@@ -1,7 +1,20 @@
1
1
  import { Action } from "@copilotkit/shared";
2
- import { of, concat, map, scan, concatMap, ReplaySubject, Subject, firstValueFrom } from "rxjs";
2
+ import {
3
+ of,
4
+ concat,
5
+ map,
6
+ scan,
7
+ concatMap,
8
+ ReplaySubject,
9
+ Subject,
10
+ firstValueFrom,
11
+ from,
12
+ } from "rxjs";
3
13
  import { streamLangChainResponse } from "./langchain/utils";
4
14
  import { GuardrailsResult } from "../graphql/types/guardrails-result.type";
15
+ import telemetry from "../lib/telemetry-client";
16
+ import { isLangGraphAgentAction } from "../lib/runtime/remote-actions";
17
+ import { ActionInput } from "../graphql/inputs/action.input";
5
18
 
6
19
  export enum RuntimeEventTypes {
7
20
  TextMessageStart = "TextMessageStart",
@@ -11,9 +24,10 @@ export enum RuntimeEventTypes {
11
24
  ActionExecutionArgs = "ActionExecutionArgs",
12
25
  ActionExecutionEnd = "ActionExecutionEnd",
13
26
  ActionExecutionResult = "ActionExecutionResult",
27
+ AgentStateMessage = "AgentStateMessage",
14
28
  }
15
29
 
16
- type FunctionCallScope = "client" | "server";
30
+ type FunctionCallScope = "client" | "server" | "passThrough";
17
31
 
18
32
  export type RuntimeEvent =
19
33
  | { type: RuntimeEventTypes.TextMessageStart; messageId: string }
@@ -35,6 +49,17 @@ export type RuntimeEvent =
35
49
  actionName: string;
36
50
  actionExecutionId: string;
37
51
  result: string;
52
+ }
53
+ | {
54
+ type: RuntimeEventTypes.AgentStateMessage;
55
+ threadId: string;
56
+ agentName: string;
57
+ nodeName: string;
58
+ runId: string;
59
+ active: boolean;
60
+ role: string;
61
+ state: string;
62
+ running: boolean;
38
63
  };
39
64
 
40
65
  interface RuntimeEventWithState {
@@ -100,6 +125,29 @@ export class RuntimeEventSubject extends ReplaySubject<RuntimeEvent> {
100
125
  result,
101
126
  });
102
127
  }
128
+
129
+ sendAgentStateMessage(
130
+ threadId: string,
131
+ agentName: string,
132
+ nodeName: string,
133
+ runId: string,
134
+ active: boolean,
135
+ role: string,
136
+ state: string,
137
+ running: boolean,
138
+ ) {
139
+ this.next({
140
+ type: RuntimeEventTypes.AgentStateMessage,
141
+ threadId,
142
+ agentName,
143
+ nodeName,
144
+ runId,
145
+ active,
146
+ role,
147
+ state,
148
+ running,
149
+ });
150
+ }
103
151
  }
104
152
 
105
153
  export class RuntimeEventSource {
@@ -110,12 +158,14 @@ export class RuntimeEventSource {
110
158
  this.callback = callback;
111
159
  }
112
160
 
113
- process({
114
- serversideActions,
161
+ processRuntimeEvents({
162
+ serverSideActions,
115
163
  guardrailsResult$,
164
+ actionInputsWithoutAgents,
116
165
  }: {
117
- serversideActions: Action<any>[];
166
+ serverSideActions: Action<any>[];
118
167
  guardrailsResult$?: Subject<GuardrailsResult>;
168
+ actionInputsWithoutAgents: ActionInput[];
119
169
  }) {
120
170
  this.callback(this.eventStream$).catch((error) => {
121
171
  console.error("Error in event source callback", error);
@@ -124,27 +174,35 @@ export class RuntimeEventSource {
124
174
  // mark tools for server side execution
125
175
  map((event) => {
126
176
  if (event.type === RuntimeEventTypes.ActionExecutionStart) {
127
- event.scope = serversideActions.find((action) => action.name === event.actionName)
128
- ? "server"
129
- : "client";
177
+ if (event.scope !== "passThrough") {
178
+ event.scope = serverSideActions.find((action) => action.name === event.actionName)
179
+ ? "server"
180
+ : "client";
181
+ }
130
182
  }
131
183
  return event;
132
184
  }),
133
185
  // track state
134
186
  scan(
135
187
  (acc, event) => {
188
+ // It seems like this is needed so that rxjs recognizes the object has changed
189
+ // This fixes an issue where action were executed multiple times
190
+ // Not investigating further for now (Markus)
191
+ acc = { ...acc };
192
+
136
193
  if (event.type === RuntimeEventTypes.ActionExecutionStart) {
137
194
  acc.callActionServerSide = event.scope === "server";
138
195
  acc.args = "";
139
196
  acc.actionExecutionId = event.actionExecutionId;
140
197
  if (acc.callActionServerSide) {
141
- acc.action = serversideActions.find((action) => action.name === event.actionName);
198
+ acc.action = serverSideActions.find((action) => action.name === event.actionName);
142
199
  }
143
200
  } else if (event.type === RuntimeEventTypes.ActionExecutionArgs) {
144
201
  acc.args += event.args;
145
202
  }
146
203
 
147
204
  acc.event = event;
205
+
148
206
  return acc;
149
207
  },
150
208
  {
@@ -167,9 +225,12 @@ export class RuntimeEventSource {
167
225
  eventWithState.action!,
168
226
  eventWithState.args,
169
227
  eventWithState.actionExecutionId,
228
+ actionInputsWithoutAgents,
170
229
  ).catch((error) => {
171
230
  console.error(error);
172
231
  });
232
+
233
+ telemetry.capture("oss.runtime.server_action_executed", {});
173
234
  return concat(of(eventWithState.event!), toolCallEventStream$);
174
235
  } else {
175
236
  return of(eventWithState.event!);
@@ -185,6 +246,7 @@ async function executeAction(
185
246
  action: Action<any>,
186
247
  actionArguments: string,
187
248
  actionExecutionId: string,
249
+ actionInputsWithoutAgents: ActionInput[],
188
250
  ) {
189
251
  if (guardrailsResult$) {
190
252
  const { status } = await firstValueFrom(guardrailsResult$);
@@ -201,15 +263,35 @@ async function executeAction(
201
263
  args = JSON.parse(actionArguments);
202
264
  }
203
265
 
204
- // call the function
205
- const result = await action.handler(args);
206
-
207
- await streamLangChainResponse({
208
- result,
209
- eventStream$,
210
- actionExecution: {
266
+ // handle LangGraph agents
267
+ if (isLangGraphAgentAction(action)) {
268
+ eventStream$.sendActionExecutionResult(
269
+ actionExecutionId,
270
+ action.name,
271
+ `${action.name} agent started`,
272
+ );
273
+ const stream = await action.langGraphAgentHandler({
211
274
  name: action.name,
212
- id: actionExecutionId,
213
- },
214
- });
275
+ actionInputsWithoutAgents,
276
+ });
277
+
278
+ // forward to eventStream$
279
+ from(stream).subscribe({
280
+ next: (event) => eventStream$.next(event),
281
+ error: (err) => console.error("Error in stream", err),
282
+ complete: () => eventStream$.complete(),
283
+ });
284
+ } else {
285
+ // call the function
286
+ const result = await action.handler?.(args);
287
+
288
+ await streamLangChainResponse({
289
+ result,
290
+ eventStream$,
291
+ actionExecution: {
292
+ name: action.name,
293
+ id: actionExecutionId,
294
+ },
295
+ });
296
+ }
215
297
  }
@@ -41,11 +41,22 @@ export interface GroqAdapterParams {
41
41
  * The model to use.
42
42
  */
43
43
  model?: string;
44
+
45
+ /**
46
+ * Whether to disable parallel tool calls.
47
+ * You can disable parallel tool calls to force the model to execute tool calls sequentially.
48
+ * This is useful if you want to execute tool calls in a specific order so that the state changes
49
+ * introduced by one tool call are visible to the next tool call. (i.e. new actions or readables)
50
+ *
51
+ * @default false
52
+ */
53
+ disableParallelToolCalls?: boolean;
44
54
  }
45
55
 
46
56
  export class GroqAdapter implements CopilotServiceAdapter {
47
57
  private model: string = DEFAULT_MODEL;
48
58
 
59
+ private disableParallelToolCalls: boolean = false;
49
60
  private _groq: Groq;
50
61
  public get groq(): Groq {
51
62
  return this._groq;
@@ -56,6 +67,7 @@ export class GroqAdapter implements CopilotServiceAdapter {
56
67
  if (params?.model) {
57
68
  this.model = params.model;
58
69
  }
70
+ this.disableParallelToolCalls = params?.disableParallelToolCalls || false;
59
71
  }
60
72
 
61
73
  async process(
@@ -91,6 +103,7 @@ export class GroqAdapter implements CopilotServiceAdapter {
91
103
  }),
92
104
  ...(forwardedParameters?.stop && { stop: forwardedParameters.stop }),
93
105
  ...(toolChoice && { tool_choice: toolChoice }),
106
+ ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
94
107
  });
95
108
 
96
109
  eventSource.stream(async (eventStream$) => {
@@ -45,11 +45,22 @@ export interface OpenAIAdapterParams {
45
45
  * The model to use.
46
46
  */
47
47
  model?: string;
48
+
49
+ /**
50
+ * Whether to disable parallel tool calls.
51
+ * You can disable parallel tool calls to force the model to execute tool calls sequentially.
52
+ * This is useful if you want to execute tool calls in a specific order so that the state changes
53
+ * introduced by one tool call are visible to the next tool call. (i.e. new actions or readables)
54
+ *
55
+ * @default false
56
+ */
57
+ disableParallelToolCalls?: boolean;
48
58
  }
49
59
 
50
60
  export class OpenAIAdapter implements CopilotServiceAdapter {
51
61
  private model: string = DEFAULT_MODEL;
52
62
 
63
+ private disableParallelToolCalls: boolean = false;
53
64
  private _openai: OpenAI;
54
65
  public get openai(): OpenAI {
55
66
  return this._openai;
@@ -60,6 +71,7 @@ export class OpenAIAdapter implements CopilotServiceAdapter {
60
71
  if (params?.model) {
61
72
  this.model = params.model;
62
73
  }
74
+ this.disableParallelToolCalls = params?.disableParallelToolCalls || false;
63
75
  }
64
76
 
65
77
  async process(
@@ -94,6 +106,7 @@ export class OpenAIAdapter implements CopilotServiceAdapter {
94
106
  ...(forwardedParameters?.maxTokens && { max_tokens: forwardedParameters.maxTokens }),
95
107
  ...(forwardedParameters?.stop && { stop: forwardedParameters.stop }),
96
108
  ...(toolChoice && { tool_choice: toolChoice }),
109
+ ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
97
110
  });
98
111
 
99
112
  eventSource.stream(async (eventStream$) => {
@@ -65,6 +65,16 @@ export interface OpenAIAssistantAdapterParams {
65
65
  * @default true
66
66
  */
67
67
  fileSearchEnabled?: boolean;
68
+
69
+ /**
70
+ * Whether to disable parallel tool calls.
71
+ * You can disable parallel tool calls to force the model to execute tool calls sequentially.
72
+ * This is useful if you want to execute tool calls in a specific order so that the state changes
73
+ * introduced by one tool call are visible to the next tool call. (i.e. new actions or readables)
74
+ *
75
+ * @default false
76
+ */
77
+ disableParallelToolCalls?: boolean;
68
78
  }
69
79
 
70
80
  export class OpenAIAssistantAdapter implements CopilotServiceAdapter {
@@ -72,12 +82,14 @@ export class OpenAIAssistantAdapter implements CopilotServiceAdapter {
72
82
  private codeInterpreterEnabled: boolean;
73
83
  private assistantId: string;
74
84
  private fileSearchEnabled: boolean;
85
+ private disableParallelToolCalls: boolean;
75
86
 
76
87
  constructor(params: OpenAIAssistantAdapterParams) {
77
88
  this.openai = params.openai || new OpenAI({});
78
89
  this.codeInterpreterEnabled = params.codeInterpreterEnabled === false || true;
79
90
  this.fileSearchEnabled = params.fileSearchEnabled === false || true;
80
91
  this.assistantId = params.assistantId;
92
+ this.disableParallelToolCalls = params?.disableParallelToolCalls || false;
81
93
  }
82
94
 
83
95
  async process(
@@ -154,6 +166,7 @@ export class OpenAIAssistantAdapter implements CopilotServiceAdapter {
154
166
 
155
167
  const stream = this.openai.beta.threads.runs.submitToolOutputsStream(threadId, runId, {
156
168
  tool_outputs: toolOutputs,
169
+ ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
157
170
  });
158
171
 
159
172
  await this.streamResponse(stream, eventSource);
@@ -206,6 +219,7 @@ export class OpenAIAssistantAdapter implements CopilotServiceAdapter {
206
219
  ...(forwardedParameters?.maxTokens && {
207
220
  max_completion_tokens: forwardedParameters.maxTokens,
208
221
  }),
222
+ ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
209
223
  });
210
224
 
211
225
  await this.streamResponse(stream, eventSource);