@copilotkit/runtime 1.9.2-next.9 → 1.9.3-next.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.
- package/CHANGELOG.md +176 -0
- package/dist/{chunk-Z5GYTKMD.mjs → chunk-EK5RTZVJ.mjs} +225 -149
- package/dist/chunk-EK5RTZVJ.mjs.map +1 -0
- package/dist/{chunk-SMDVD4VG.mjs → chunk-KCYFFRJY.mjs} +2 -2
- package/dist/{chunk-4JBKY7XT.mjs → chunk-QLLV2QVK.mjs} +48 -28
- package/dist/chunk-QLLV2QVK.mjs.map +1 -0
- package/dist/{chunk-5YGKE5SN.mjs → chunk-R5D7D7YN.mjs} +2 -2
- package/dist/{chunk-UUXRYAB4.mjs → chunk-RCCT2GOF.mjs} +2 -2
- package/dist/{chunk-ALZ5H3VD.mjs → chunk-YGS5B7PN.mjs} +2 -2
- package/dist/{groq-adapter-172a2ca4.d.ts → groq-adapter-742818f2.d.ts} +5 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +267 -171
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9 -9
- package/dist/{langserve-fc5cac89.d.ts → langserve-3e8d0e06.d.ts} +6 -0
- package/dist/lib/index.d.ts +155 -5
- package/dist/lib/index.js +221 -168
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/index.mjs +9 -9
- package/dist/lib/integrations/index.d.ts +3 -3
- package/dist/lib/integrations/index.js +11 -11
- package/dist/lib/integrations/index.js.map +1 -1
- package/dist/lib/integrations/index.mjs +8 -8
- package/dist/lib/integrations/nest/index.d.ts +2 -2
- package/dist/lib/integrations/nest/index.js +11 -11
- package/dist/lib/integrations/nest/index.js.map +1 -1
- package/dist/lib/integrations/nest/index.mjs +4 -4
- package/dist/lib/integrations/node-express/index.d.ts +2 -2
- package/dist/lib/integrations/node-express/index.js +11 -11
- package/dist/lib/integrations/node-express/index.js.map +1 -1
- package/dist/lib/integrations/node-express/index.mjs +4 -4
- package/dist/lib/integrations/node-http/index.d.ts +2 -2
- package/dist/lib/integrations/node-http/index.js +11 -11
- package/dist/lib/integrations/node-http/index.js.map +1 -1
- package/dist/lib/integrations/node-http/index.mjs +3 -3
- package/dist/service-adapters/index.d.ts +5 -4
- package/dist/service-adapters/index.js +47 -27
- package/dist/service-adapters/index.js.map +1 -1
- package/dist/service-adapters/index.mjs +1 -1
- package/dist/{shared-bd953ebf.d.ts → shared-96b46379.d.ts} +16 -18
- package/package.json +11 -11
- package/src/graphql/resolvers/copilot.resolver.ts +1 -2
- package/src/lib/runtime/__tests__/{copilot-runtime-trace.test.ts → copilot-runtime-error.test.ts} +27 -27
- package/src/lib/runtime/__tests__/mcp-tools-utils.test.ts +464 -0
- package/src/lib/runtime/agui-action.ts +9 -3
- package/src/lib/runtime/copilot-runtime.ts +112 -124
- package/src/lib/runtime/mcp-tools-utils.ts +84 -18
- package/src/lib/runtime/remote-actions.ts +6 -0
- package/src/service-adapters/anthropic/anthropic-adapter.ts +64 -4
- package/src/service-adapters/anthropic/utils.ts +3 -8
- package/src/service-adapters/events.ts +40 -1
- package/src/service-adapters/google/google-genai-adapter.ts +5 -0
- package/src/service-adapters/openai/openai-adapter.ts +0 -14
- package/tests/service-adapters/anthropic/anthropic-adapter.test.ts +172 -387
- package/dist/chunk-4JBKY7XT.mjs.map +0 -1
- package/dist/chunk-Z5GYTKMD.mjs.map +0 -1
- /package/dist/{chunk-SMDVD4VG.mjs.map → chunk-KCYFFRJY.mjs.map} +0 -0
- /package/dist/{chunk-5YGKE5SN.mjs.map → chunk-R5D7D7YN.mjs.map} +0 -0
- /package/dist/{chunk-UUXRYAB4.mjs.map → chunk-RCCT2GOF.mjs.map} +0 -0
- /package/dist/{chunk-ALZ5H3VD.mjs.map → chunk-YGS5B7PN.mjs.map} +0 -0
|
@@ -61,6 +61,36 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
private shouldGenerateFallbackResponse(messages: Anthropic.Messages.MessageParam[]): boolean {
|
|
65
|
+
if (messages.length === 0) return false;
|
|
66
|
+
|
|
67
|
+
const lastMessage = messages[messages.length - 1];
|
|
68
|
+
|
|
69
|
+
// Check if the last message is a tool result
|
|
70
|
+
const endsWithToolResult =
|
|
71
|
+
lastMessage.role === "user" &&
|
|
72
|
+
Array.isArray(lastMessage.content) &&
|
|
73
|
+
lastMessage.content.some((content: any) => content.type === "tool_result");
|
|
74
|
+
|
|
75
|
+
// Also check if we have a recent pattern of user message -> assistant tool use -> user tool result
|
|
76
|
+
// This indicates a completed action that might not need a response
|
|
77
|
+
if (messages.length >= 3 && endsWithToolResult) {
|
|
78
|
+
const lastThree = messages.slice(-3);
|
|
79
|
+
const hasRecentToolPattern =
|
|
80
|
+
lastThree[0]?.role === "user" && // Initial user message
|
|
81
|
+
lastThree[1]?.role === "assistant" && // Assistant tool use
|
|
82
|
+
Array.isArray(lastThree[1].content) &&
|
|
83
|
+
lastThree[1].content.some((content: any) => content.type === "tool_use") &&
|
|
84
|
+
lastThree[2]?.role === "user" && // Tool result
|
|
85
|
+
Array.isArray(lastThree[2].content) &&
|
|
86
|
+
lastThree[2].content.some((content: any) => content.type === "tool_result");
|
|
87
|
+
|
|
88
|
+
return hasRecentToolPattern;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return endsWithToolResult;
|
|
92
|
+
}
|
|
93
|
+
|
|
64
94
|
async process(
|
|
65
95
|
request: CopilotRuntimeChatCompletionRequest,
|
|
66
96
|
): Promise<CopilotRuntimeChatCompletionResponse> {
|
|
@@ -95,24 +125,30 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
|
|
|
95
125
|
}
|
|
96
126
|
|
|
97
127
|
// Step 2: Map each message to an Anthropic message, eliminating invalid tool_results
|
|
128
|
+
const processedToolResultIds = new Set<string>();
|
|
98
129
|
const anthropicMessages = messages
|
|
99
130
|
.map((message) => {
|
|
100
|
-
// For tool results, only include if they match a valid tool_use ID
|
|
131
|
+
// For tool results, only include if they match a valid tool_use ID AND haven't been processed
|
|
101
132
|
if (message.isResultMessage()) {
|
|
102
133
|
// Skip if there's no corresponding tool_use
|
|
103
134
|
if (!validToolUseIds.has(message.actionExecutionId)) {
|
|
104
135
|
return null; // Will be filtered out later
|
|
105
136
|
}
|
|
106
137
|
|
|
107
|
-
//
|
|
108
|
-
|
|
138
|
+
// Skip if we've already processed a result for this tool_use ID
|
|
139
|
+
if (processedToolResultIds.has(message.actionExecutionId)) {
|
|
140
|
+
return null; // Will be filtered out later
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Mark this tool result as processed
|
|
144
|
+
processedToolResultIds.add(message.actionExecutionId);
|
|
109
145
|
|
|
110
146
|
return {
|
|
111
147
|
role: "user",
|
|
112
148
|
content: [
|
|
113
149
|
{
|
|
114
150
|
type: "tool_result",
|
|
115
|
-
content: message.result,
|
|
151
|
+
content: message.result || "Action completed successfully",
|
|
116
152
|
tool_use_id: message.actionExecutionId,
|
|
117
153
|
},
|
|
118
154
|
],
|
|
@@ -140,6 +176,7 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
|
|
|
140
176
|
// Apply token limits
|
|
141
177
|
const limitedMessages = limitMessagesToTokenCount(anthropicMessages, tools, model);
|
|
142
178
|
|
|
179
|
+
// We'll check if we need a fallback response after seeing what Anthropic returns
|
|
143
180
|
// We skip grouping by role since we've already ensured uniqueness of tool_results
|
|
144
181
|
|
|
145
182
|
let toolChoice: any = forwardedParameters?.toolChoice;
|
|
@@ -172,12 +209,14 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
|
|
|
172
209
|
let currentMessageId = randomId();
|
|
173
210
|
let currentToolCallId = randomId();
|
|
174
211
|
let filterThinkingTextBuffer = new FilterThinkingTextBuffer();
|
|
212
|
+
let hasReceivedContent = false;
|
|
175
213
|
|
|
176
214
|
try {
|
|
177
215
|
for await (const chunk of stream as AsyncIterable<any>) {
|
|
178
216
|
if (chunk.type === "message_start") {
|
|
179
217
|
currentMessageId = chunk.message.id;
|
|
180
218
|
} else if (chunk.type === "content_block_start") {
|
|
219
|
+
hasReceivedContent = true;
|
|
181
220
|
if (chunk.content_block.type === "text") {
|
|
182
221
|
didOutputText = false;
|
|
183
222
|
filterThinkingTextBuffer.reset();
|
|
@@ -224,6 +263,27 @@ export class AnthropicAdapter implements CopilotServiceAdapter {
|
|
|
224
263
|
throw convertServiceAdapterError(error, "Anthropic");
|
|
225
264
|
}
|
|
226
265
|
|
|
266
|
+
// Generate fallback response only if Anthropic produced no content
|
|
267
|
+
if (!hasReceivedContent && this.shouldGenerateFallbackResponse(limitedMessages)) {
|
|
268
|
+
// Extract the tool result content for a more contextual response
|
|
269
|
+
let fallbackContent = "Task completed successfully.";
|
|
270
|
+
const lastMessage = limitedMessages[limitedMessages.length - 1];
|
|
271
|
+
if (lastMessage?.role === "user" && Array.isArray(lastMessage.content)) {
|
|
272
|
+
const toolResult = lastMessage.content.find((c: any) => c.type === "tool_result");
|
|
273
|
+
if (toolResult?.content && toolResult.content !== "Action completed successfully") {
|
|
274
|
+
fallbackContent = toolResult.content;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
currentMessageId = randomId();
|
|
279
|
+
eventStream$.sendTextMessageStart({ messageId: currentMessageId });
|
|
280
|
+
eventStream$.sendTextMessageContent({
|
|
281
|
+
messageId: currentMessageId,
|
|
282
|
+
content: fallbackContent,
|
|
283
|
+
});
|
|
284
|
+
eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
|
|
285
|
+
}
|
|
286
|
+
|
|
227
287
|
eventStream$.complete();
|
|
228
288
|
});
|
|
229
289
|
} catch (error) {
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ActionExecutionMessage,
|
|
3
|
-
Message,
|
|
4
|
-
ResultMessage,
|
|
5
|
-
TextMessage,
|
|
6
|
-
} from "../../graphql/types/converted";
|
|
7
|
-
import { ActionInput } from "../../graphql/inputs/action.input";
|
|
8
1
|
import { Anthropic } from "@anthropic-ai/sdk";
|
|
2
|
+
import { ActionInput } from "../../graphql/inputs/action.input";
|
|
3
|
+
import { Message } from "../../graphql/types/converted";
|
|
9
4
|
|
|
10
5
|
export function limitMessagesToTokenCount(
|
|
11
6
|
messages: any[],
|
|
@@ -148,7 +143,7 @@ export function convertMessageToAnthropicMessage(
|
|
|
148
143
|
content: [
|
|
149
144
|
{
|
|
150
145
|
type: "tool_result",
|
|
151
|
-
content: message.result,
|
|
146
|
+
content: message.result || "Action completed successfully",
|
|
152
147
|
tool_use_id: message.actionExecutionId,
|
|
153
148
|
},
|
|
154
149
|
],
|
|
@@ -240,6 +240,16 @@ export class RuntimeEventSubject extends ReplaySubject<RuntimeEvent> {
|
|
|
240
240
|
export class RuntimeEventSource {
|
|
241
241
|
private eventStream$ = new RuntimeEventSubject();
|
|
242
242
|
private callback!: EventSourceCallback;
|
|
243
|
+
private errorHandler?: (error: any, context: any) => Promise<void>;
|
|
244
|
+
private errorContext?: any;
|
|
245
|
+
|
|
246
|
+
constructor(params?: {
|
|
247
|
+
errorHandler?: (error: any, context: any) => Promise<void>;
|
|
248
|
+
errorContext?: any;
|
|
249
|
+
}) {
|
|
250
|
+
this.errorHandler = params?.errorHandler;
|
|
251
|
+
this.errorContext = params?.errorContext;
|
|
252
|
+
}
|
|
243
253
|
|
|
244
254
|
async stream(callback: EventSourceCallback): Promise<void> {
|
|
245
255
|
this.callback = callback;
|
|
@@ -267,9 +277,19 @@ export class RuntimeEventSource {
|
|
|
267
277
|
actionInputsWithoutAgents: ActionInput[];
|
|
268
278
|
threadId: string;
|
|
269
279
|
}) {
|
|
270
|
-
this.callback(this.eventStream$).catch((error) => {
|
|
280
|
+
this.callback(this.eventStream$).catch(async (error) => {
|
|
271
281
|
// Convert streaming errors to structured errors, but preserve already structured ones
|
|
272
282
|
const structuredError = ensureStructuredError(error, convertStreamingErrorToStructured);
|
|
283
|
+
|
|
284
|
+
// Call the runtime error handler if provided
|
|
285
|
+
if (this.errorHandler && this.errorContext) {
|
|
286
|
+
try {
|
|
287
|
+
await this.errorHandler(structuredError, this.errorContext);
|
|
288
|
+
} catch (errorHandlerError) {
|
|
289
|
+
console.error("Error in streaming error handler:", errorHandlerError);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
273
293
|
this.eventStream$.error(structuredError);
|
|
274
294
|
this.eventStream$.complete();
|
|
275
295
|
});
|
|
@@ -333,6 +353,25 @@ export class RuntimeEventSource {
|
|
|
333
353
|
error,
|
|
334
354
|
convertStreamingErrorToStructured,
|
|
335
355
|
);
|
|
356
|
+
|
|
357
|
+
// Call the runtime error handler if provided
|
|
358
|
+
if (this.errorHandler && this.errorContext) {
|
|
359
|
+
// Use from() to handle async error handler
|
|
360
|
+
from(
|
|
361
|
+
this.errorHandler(structuredError, {
|
|
362
|
+
...this.errorContext,
|
|
363
|
+
action: {
|
|
364
|
+
name: eventWithState.action!.name,
|
|
365
|
+
executionId: eventWithState.actionExecutionId,
|
|
366
|
+
},
|
|
367
|
+
}),
|
|
368
|
+
).subscribe({
|
|
369
|
+
error: (errorHandlerError) => {
|
|
370
|
+
console.error("Error in action execution error handler:", errorHandlerError);
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
336
375
|
toolCallEventStream$.sendActionExecutionResult({
|
|
337
376
|
actionExecutionId: eventWithState.actionExecutionId!,
|
|
338
377
|
actionName: eventWithState.action!.name,
|
|
@@ -23,6 +23,10 @@ interface GoogleGenerativeAIAdapterOptions {
|
|
|
23
23
|
* A custom Google Generative AI model to use.
|
|
24
24
|
*/
|
|
25
25
|
model?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The API key to use.
|
|
28
|
+
*/
|
|
29
|
+
apiKey?: string;
|
|
26
30
|
}
|
|
27
31
|
|
|
28
32
|
export class GoogleGenerativeAIAdapter extends LangChainAdapter {
|
|
@@ -46,6 +50,7 @@ export class GoogleGenerativeAIAdapter extends LangChainAdapter {
|
|
|
46
50
|
});
|
|
47
51
|
|
|
48
52
|
const model = new ChatGoogle({
|
|
53
|
+
apiKey: options?.apiKey ?? process.env.GOOGLE_API_KEY,
|
|
49
54
|
modelName: options?.model ?? "gemini-1.5-pro",
|
|
50
55
|
apiVersion: "v1beta",
|
|
51
56
|
}).bindTools(tools);
|
|
@@ -129,8 +129,6 @@ export class OpenAIAdapter implements CopilotServiceAdapter {
|
|
|
129
129
|
const tools = actions.map(convertActionInputToOpenAITool);
|
|
130
130
|
const threadId = threadIdFromRequest ?? randomUUID();
|
|
131
131
|
|
|
132
|
-
console.log("messages", messages);
|
|
133
|
-
|
|
134
132
|
// ALLOWLIST APPROACH: Only include tool_result messages that correspond to valid tool_calls
|
|
135
133
|
// Step 1: Extract valid tool_call IDs
|
|
136
134
|
const validToolUseIds = new Set<string>();
|
|
@@ -171,18 +169,6 @@ export class OpenAIAdapter implements CopilotServiceAdapter {
|
|
|
171
169
|
};
|
|
172
170
|
}
|
|
173
171
|
|
|
174
|
-
console.log("INPUT", {
|
|
175
|
-
model: model,
|
|
176
|
-
stream: true,
|
|
177
|
-
messages: openaiMessages,
|
|
178
|
-
...(tools.length > 0 && { tools }),
|
|
179
|
-
...(forwardedParameters?.maxTokens && { max_tokens: forwardedParameters.maxTokens }),
|
|
180
|
-
...(forwardedParameters?.stop && { stop: forwardedParameters.stop }),
|
|
181
|
-
...(toolChoice && { tool_choice: toolChoice }),
|
|
182
|
-
...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
|
|
183
|
-
...(forwardedParameters?.temperature && { temperature: forwardedParameters.temperature }),
|
|
184
|
-
});
|
|
185
|
-
|
|
186
172
|
try {
|
|
187
173
|
const stream = this.openai.beta.chat.completions.stream({
|
|
188
174
|
model: model,
|