@copilotkit/runtime 0.0.0-fix-debug-infosys-20251107162427 → 0.0.0-fix-restore-handle-method-node-http-20251222114321
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 +259 -3
- package/dist/index.d.ts +1493 -28
- package/dist/index.js +2568 -5467
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5429 -99
- package/dist/index.mjs.map +1 -1
- package/dist/{lib/index.d.ts → langgraph.d.ts} +104 -32
- package/dist/langgraph.js +211 -0
- package/dist/langgraph.js.map +1 -0
- package/dist/{chunk-XPALJ76U.mjs → langgraph.mjs} +57 -26
- package/dist/langgraph.mjs.map +1 -0
- package/dist/v2/index.d.ts +2 -0
- package/dist/v2/index.js +22 -0
- package/dist/v2/index.js.map +1 -0
- package/dist/v2/index.mjs +5 -0
- package/dist/v2/index.mjs.map +1 -0
- package/package.json +66 -22
- package/src/graphql/message-conversion/agui-to-gql.test.ts +1263 -0
- package/src/graphql/message-conversion/agui-to-gql.ts +333 -0
- package/src/graphql/message-conversion/gql-to-agui.test.ts +1580 -0
- package/src/graphql/message-conversion/gql-to-agui.ts +278 -0
- package/src/graphql/message-conversion/index.ts +2 -0
- package/src/graphql/message-conversion/roundtrip-conversion.test.ts +526 -0
- package/src/graphql/resolvers/copilot.resolver.ts +3 -51
- package/src/graphql/resolvers/state.resolver.ts +3 -2
- package/src/graphql/types/converted/index.ts +32 -6
- package/src/graphql/types/enums.ts +2 -2
- package/src/graphql/types/message-status.type.ts +3 -1
- package/src/langgraph.ts +1 -0
- package/src/lib/index.ts +42 -1
- package/src/lib/integrations/nextjs/app-router.ts +12 -11
- package/src/lib/integrations/nextjs/pages-router.ts +4 -11
- package/src/lib/integrations/node-http/index.ts +10 -5
- package/src/lib/integrations/shared.ts +1 -1
- package/src/lib/observability.ts +87 -0
- package/src/lib/runtime/{langgraph/langgraph-agent.ts → agent-integrations/langgraph/agent.ts} +10 -30
- package/src/lib/runtime/agent-integrations/langgraph/consts.ts +34 -0
- package/src/lib/runtime/agent-integrations/langgraph/index.ts +2 -0
- package/src/lib/runtime/copilot-runtime.ts +387 -1360
- package/src/lib/runtime/telemetry-agent-runner.ts +139 -0
- package/src/lib/runtime/types.ts +49 -0
- package/src/lib/runtime/utils.ts +87 -0
- package/src/lib/telemetry-client.ts +6 -5
- package/src/service-adapters/anthropic/anthropic-adapter.ts +21 -4
- package/src/service-adapters/bedrock/bedrock-adapter.ts +10 -2
- package/src/service-adapters/empty/empty-adapter.ts +3 -0
- package/src/service-adapters/events.ts +0 -254
- package/src/service-adapters/experimental/ollama/ollama-adapter.ts +7 -2
- package/src/service-adapters/google/google-genai-adapter.ts +33 -7
- package/src/service-adapters/groq/groq-adapter.ts +21 -4
- package/src/service-adapters/langchain/langchain-adapter.ts +8 -3
- package/src/service-adapters/langchain/langserve.ts +2 -1
- package/src/service-adapters/openai/openai-adapter.ts +22 -4
- package/src/service-adapters/openai/openai-assistant-adapter.ts +30 -11
- package/src/service-adapters/service-adapter.ts +3 -0
- package/src/service-adapters/unify/unify-adapter.ts +9 -2
- package/src/v2/index.ts +3 -0
- package/tsup.config.ts +6 -2
- package/dist/chunk-27JKTS6P.mjs +0 -1704
- package/dist/chunk-27JKTS6P.mjs.map +0 -1
- package/dist/chunk-2DHC2YFK.mjs +0 -6026
- package/dist/chunk-2DHC2YFK.mjs.map +0 -1
- package/dist/chunk-2OZAGFV3.mjs +0 -43
- package/dist/chunk-2OZAGFV3.mjs.map +0 -1
- package/dist/chunk-5HHH4SZ3.mjs +0 -25
- package/dist/chunk-5HHH4SZ3.mjs.map +0 -1
- package/dist/chunk-AMUJQ6IR.mjs +0 -50
- package/dist/chunk-AMUJQ6IR.mjs.map +0 -1
- package/dist/chunk-FHD4JECV.mjs +0 -33
- package/dist/chunk-FHD4JECV.mjs.map +0 -1
- package/dist/chunk-FVG3IQG7.mjs +0 -25
- package/dist/chunk-FVG3IQG7.mjs.map +0 -1
- package/dist/chunk-QU2FLIQB.mjs +0 -80
- package/dist/chunk-QU2FLIQB.mjs.map +0 -1
- package/dist/chunk-SHBDMA63.mjs +0 -141
- package/dist/chunk-SHBDMA63.mjs.map +0 -1
- package/dist/chunk-XPALJ76U.mjs.map +0 -1
- package/dist/chunk-XWBDEXDA.mjs +0 -153
- package/dist/chunk-XWBDEXDA.mjs.map +0 -1
- package/dist/graphql/types/base/index.d.ts +0 -6
- package/dist/graphql/types/base/index.js +0 -63
- package/dist/graphql/types/base/index.js.map +0 -1
- package/dist/graphql/types/base/index.mjs +0 -8
- package/dist/graphql/types/base/index.mjs.map +0 -1
- package/dist/graphql/types/converted/index.d.ts +0 -2
- package/dist/graphql/types/converted/index.js +0 -200
- package/dist/graphql/types/converted/index.js.map +0 -1
- package/dist/graphql/types/converted/index.mjs +0 -19
- package/dist/graphql/types/converted/index.mjs.map +0 -1
- package/dist/groq-adapter-c8aec5c5.d.ts +0 -321
- package/dist/index-96b330da.d.ts +0 -119
- package/dist/langserve-0c6100e3.d.ts +0 -257
- package/dist/lib/cloud/index.d.ts +0 -6
- package/dist/lib/cloud/index.js +0 -18
- package/dist/lib/cloud/index.js.map +0 -1
- package/dist/lib/cloud/index.mjs +0 -1
- package/dist/lib/cloud/index.mjs.map +0 -1
- package/dist/lib/index.js +0 -7849
- package/dist/lib/index.js.map +0 -1
- package/dist/lib/index.mjs +0 -76
- package/dist/lib/index.mjs.map +0 -1
- package/dist/lib/integrations/index.d.ts +0 -34
- package/dist/lib/integrations/index.js +0 -3055
- package/dist/lib/integrations/index.js.map +0 -1
- package/dist/lib/integrations/index.mjs +0 -37
- package/dist/lib/integrations/index.mjs.map +0 -1
- package/dist/lib/integrations/nest/index.d.ts +0 -15
- package/dist/lib/integrations/nest/index.js +0 -2962
- package/dist/lib/integrations/nest/index.js.map +0 -1
- package/dist/lib/integrations/nest/index.mjs +0 -14
- package/dist/lib/integrations/nest/index.mjs.map +0 -1
- package/dist/lib/integrations/node-express/index.d.ts +0 -15
- package/dist/lib/integrations/node-express/index.js +0 -2962
- package/dist/lib/integrations/node-express/index.js.map +0 -1
- package/dist/lib/integrations/node-express/index.mjs +0 -14
- package/dist/lib/integrations/node-express/index.mjs.map +0 -1
- package/dist/lib/integrations/node-http/index.d.ts +0 -15
- package/dist/lib/integrations/node-http/index.js +0 -2948
- package/dist/lib/integrations/node-http/index.js.map +0 -1
- package/dist/lib/integrations/node-http/index.mjs +0 -13
- package/dist/lib/integrations/node-http/index.mjs.map +0 -1
- package/dist/service-adapters/index.d.ts +0 -162
- package/dist/service-adapters/index.js +0 -1787
- package/dist/service-adapters/index.js.map +0 -1
- package/dist/service-adapters/index.mjs +0 -34
- package/dist/service-adapters/index.mjs.map +0 -1
- package/dist/service-adapters/shared/index.d.ts +0 -9
- package/dist/service-adapters/shared/index.js +0 -72
- package/dist/service-adapters/shared/index.js.map +0 -1
- package/dist/service-adapters/shared/index.mjs +0 -8
- package/dist/service-adapters/shared/index.mjs.map +0 -1
- package/dist/shared-0a7346ce.d.ts +0 -466
- package/dist/utils/index.d.ts +0 -65
- package/dist/utils/index.js +0 -175
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/index.mjs +0 -12
- package/dist/utils/index.mjs.map +0 -1
- package/src/lib/runtime/__tests__/remote-action-constructors.test.ts +0 -246
- package/src/lib/runtime/agui-action.ts +0 -180
- package/src/lib/runtime/remote-action-constructors.ts +0 -331
- package/src/lib/runtime/remote-actions.ts +0 -217
- package/src/lib/runtime/remote-lg-action.ts +0 -1006
|
@@ -13,111 +13,52 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
|
-
Action,
|
|
17
|
-
|
|
18
|
-
Parameter,
|
|
19
|
-
ResolvedCopilotKitError,
|
|
20
|
-
CopilotKitApiDiscoveryError,
|
|
21
|
-
randomId,
|
|
22
|
-
CopilotKitError,
|
|
23
|
-
CopilotKitAgentDiscoveryError,
|
|
16
|
+
type Action,
|
|
17
|
+
type CopilotErrorHandler,
|
|
24
18
|
CopilotKitMisuseError,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
19
|
+
type MaybePromise,
|
|
20
|
+
type NonEmptyRecord,
|
|
21
|
+
type Parameter,
|
|
22
|
+
readBody,
|
|
23
|
+
getZodParameters,
|
|
24
|
+
type PartialBy,
|
|
25
|
+
isTelemetryDisabled,
|
|
31
26
|
} from "@copilotkit/shared";
|
|
27
|
+
import type { RunAgentInput } from "@ag-ui/core";
|
|
28
|
+
import { aguiToGQL } from "../../graphql/message-conversion/agui-to-gql";
|
|
29
|
+
import type { CopilotServiceAdapter, RemoteChainParameters } from "../../service-adapters";
|
|
32
30
|
import {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} from "
|
|
38
|
-
|
|
39
|
-
import
|
|
40
|
-
|
|
41
|
-
import {
|
|
42
|
-
import {
|
|
43
|
-
import { Message } from "../../graphql/types/converted";
|
|
44
|
-
import { ForwardedParametersInput } from "../../graphql/inputs/forwarded-parameters.input";
|
|
31
|
+
CopilotRuntime as CopilotRuntimeVNext,
|
|
32
|
+
type CopilotRuntimeOptions,
|
|
33
|
+
type CopilotRuntimeOptions as CopilotRuntimeOptionsVNext,
|
|
34
|
+
InMemoryAgentRunner,
|
|
35
|
+
} from "@copilotkitnext/runtime";
|
|
36
|
+
import { TelemetryAgentRunner } from "./telemetry-agent-runner";
|
|
37
|
+
import telemetry from "../telemetry-client";
|
|
38
|
+
|
|
39
|
+
import type { MessageInput } from "../../graphql/inputs/message.input";
|
|
40
|
+
import type { Message } from "../../graphql/types/converted";
|
|
45
41
|
|
|
46
42
|
import {
|
|
47
|
-
isRemoteAgentAction,
|
|
48
43
|
EndpointType,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
import {
|
|
56
|
-
import { AgentSessionInput } from "../../graphql/inputs/agent-session.input";
|
|
57
|
-
import { from } from "rxjs";
|
|
58
|
-
import { AgentStateInput } from "../../graphql/inputs/agent-state.input";
|
|
59
|
-
import { ActionInputAvailability } from "../../graphql/types/enums";
|
|
60
|
-
import { createHeaders } from "./remote-action-constructors";
|
|
61
|
-
import { fetchWithRetry } from "./retry-utils";
|
|
62
|
-
import { Agent } from "../../graphql/types/agents-response.type";
|
|
63
|
-
import { ExtensionsInput } from "../../graphql/inputs/extensions.input";
|
|
64
|
-
import { ExtensionsResponse } from "../../graphql/types/extensions-response.type";
|
|
65
|
-
import { LoadAgentStateResponse } from "../../graphql/types/load-agent-state-response.type";
|
|
66
|
-
import { Client as LangGraphClient } from "@langchain/langgraph-sdk";
|
|
67
|
-
import { langchainMessagesToCopilotKit } from "./remote-lg-action";
|
|
68
|
-
import { MetaEventInput } from "../../graphql/inputs/meta-event.input";
|
|
69
|
-
import {
|
|
70
|
-
CopilotObservabilityConfig,
|
|
71
|
-
LLMRequestData,
|
|
72
|
-
LLMResponseData,
|
|
73
|
-
LLMErrorData,
|
|
74
|
-
} from "../observability";
|
|
75
|
-
import { AbstractAgent } from "@ag-ui/client";
|
|
76
|
-
import { MessageRole } from "../../graphql/types/enums";
|
|
44
|
+
type EndpointDefinition,
|
|
45
|
+
type CopilotKitEndpoint,
|
|
46
|
+
type LangGraphPlatformEndpoint,
|
|
47
|
+
} from "./types";
|
|
48
|
+
|
|
49
|
+
import type { CopilotObservabilityConfig, LLMRequestData, LLMResponseData } from "../observability";
|
|
50
|
+
import type { AbstractAgent } from "@ag-ui/client";
|
|
77
51
|
|
|
78
52
|
// +++ MCP Imports +++
|
|
79
53
|
import {
|
|
80
|
-
MCPClient,
|
|
81
|
-
MCPEndpointConfig,
|
|
82
|
-
MCPTool,
|
|
83
|
-
|
|
84
|
-
generateMcpToolInstructions,
|
|
54
|
+
type MCPClient,
|
|
55
|
+
type MCPEndpointConfig,
|
|
56
|
+
type MCPTool,
|
|
57
|
+
extractParametersFromSchema,
|
|
85
58
|
} from "./mcp-tools-utils";
|
|
86
|
-
import {
|
|
59
|
+
import { BuiltInAgent, type BuiltInAgentConfiguration } from "@copilotkitnext/agent";
|
|
87
60
|
// Define the function type alias here or import if defined elsewhere
|
|
88
61
|
type CreateMCPClientFunction = (config: MCPEndpointConfig) => Promise<MCPClient>;
|
|
89
|
-
// --- MCP Imports ---
|
|
90
|
-
|
|
91
|
-
import { generateHelpfulErrorMessage } from "../streaming";
|
|
92
|
-
import { CopilotContextInput } from "../../graphql/inputs/copilot-context.input";
|
|
93
|
-
import { RemoteAgentAction } from "./agui-action";
|
|
94
|
-
|
|
95
|
-
export interface CopilotRuntimeRequest {
|
|
96
|
-
serviceAdapter: CopilotServiceAdapter;
|
|
97
|
-
messages: MessageInput[];
|
|
98
|
-
actions: ActionInput[];
|
|
99
|
-
agentSession?: AgentSessionInput;
|
|
100
|
-
agentStates?: AgentStateInput[];
|
|
101
|
-
outputMessagesPromise: Promise<Message[]>;
|
|
102
|
-
threadId?: string;
|
|
103
|
-
runId?: string;
|
|
104
|
-
publicApiKey?: string;
|
|
105
|
-
graphqlContext: GraphQLContext;
|
|
106
|
-
forwardedParameters?: ForwardedParametersInput;
|
|
107
|
-
url?: string;
|
|
108
|
-
extensions?: ExtensionsInput;
|
|
109
|
-
metaEvents?: MetaEventInput[];
|
|
110
|
-
context?: CopilotContextInput[];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
interface CopilotRuntimeResponse {
|
|
114
|
-
threadId: string;
|
|
115
|
-
runId?: string;
|
|
116
|
-
eventSource: RuntimeEventSource;
|
|
117
|
-
serverSideActions: Action<any>[];
|
|
118
|
-
actionInputsWithoutAgents: ActionInput[];
|
|
119
|
-
extensions?: ExtensionsResponse;
|
|
120
|
-
}
|
|
121
62
|
|
|
122
63
|
type ActionsConfiguration<T extends Parameter[] | [] = []> =
|
|
123
64
|
| Action<T>[]
|
|
@@ -157,17 +98,23 @@ interface Middleware {
|
|
|
157
98
|
/**
|
|
158
99
|
* A function that is called before the request is processed.
|
|
159
100
|
*/
|
|
101
|
+
/**
|
|
102
|
+
* @deprecated This middleware hook is deprecated and will be removed in a future version.
|
|
103
|
+
* Use updated middleware integration methods in CopilotRuntimeVNext instead.
|
|
104
|
+
*/
|
|
160
105
|
onBeforeRequest?: OnBeforeRequestHandler;
|
|
161
106
|
|
|
162
107
|
/**
|
|
163
108
|
* A function that is called after the request is processed.
|
|
164
109
|
*/
|
|
110
|
+
/**
|
|
111
|
+
* @deprecated This middleware hook is deprecated and will be removed in a future version.
|
|
112
|
+
* Use updated middleware integration methods in CopilotRuntimeVNext instead.
|
|
113
|
+
*/
|
|
165
114
|
onAfterRequest?: OnAfterRequestHandler;
|
|
166
115
|
}
|
|
167
116
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
export interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []> {
|
|
117
|
+
export interface CopilotRuntimeConstructorParams_BASE<T extends Parameter[] | [] = []> {
|
|
171
118
|
/**
|
|
172
119
|
* Middleware to be used by the runtime.
|
|
173
120
|
*
|
|
@@ -190,6 +137,10 @@ export interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []
|
|
|
190
137
|
* }) => void | Promise<void>;
|
|
191
138
|
* ```
|
|
192
139
|
*/
|
|
140
|
+
/**
|
|
141
|
+
* @deprecated This middleware hook is deprecated and will be removed in a future version.
|
|
142
|
+
* Use updated middleware integration methods in CopilotRuntimeVNext instead.
|
|
143
|
+
*/
|
|
193
144
|
middleware?: Middleware;
|
|
194
145
|
|
|
195
146
|
/*
|
|
@@ -312,1345 +263,421 @@ export interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []
|
|
|
312
263
|
onError?: CopilotErrorHandler;
|
|
313
264
|
|
|
314
265
|
onStopGeneration?: OnStopGenerationHandler;
|
|
266
|
+
|
|
267
|
+
// /** Optional transcription service for audio processing. */
|
|
268
|
+
// transcriptionService?: CopilotRuntimeOptionsVNext["transcriptionService"];
|
|
269
|
+
// /** Optional *before* middleware – callback function or webhook URL. */
|
|
270
|
+
// beforeRequestMiddleware?: CopilotRuntimeOptionsVNext["beforeRequestMiddleware"];
|
|
271
|
+
// /** Optional *after* middleware – callback function or webhook URL. */
|
|
272
|
+
// afterRequestMiddleware?: CopilotRuntimeOptionsVNext["afterRequestMiddleware"];
|
|
315
273
|
}
|
|
316
274
|
|
|
275
|
+
type BeforeRequestMiddleware = CopilotRuntimeOptionsVNext["beforeRequestMiddleware"];
|
|
276
|
+
type AfterRequestMiddleware = CopilotRuntimeOptionsVNext["afterRequestMiddleware"];
|
|
277
|
+
type BeforeRequestMiddlewareFn = Exclude<BeforeRequestMiddleware, string>;
|
|
278
|
+
type BeforeRequestMiddlewareFnParameters = Parameters<BeforeRequestMiddlewareFn>;
|
|
279
|
+
type BeforeRequestMiddlewareFnResult = ReturnType<BeforeRequestMiddlewareFn>;
|
|
280
|
+
type AfterRequestMiddlewareFn = Exclude<AfterRequestMiddleware, string>;
|
|
281
|
+
type AfterRequestMiddlewareFnParameters = Parameters<AfterRequestMiddlewareFn>;
|
|
282
|
+
|
|
283
|
+
interface CopilotRuntimeConstructorParams<T extends Parameter[] | [] = []>
|
|
284
|
+
extends Omit<CopilotRuntimeConstructorParams_BASE<T>, "agents">,
|
|
285
|
+
Omit<CopilotRuntimeOptionsVNext, "agents" | "transcriptionService"> {
|
|
286
|
+
/**
|
|
287
|
+
* TODO: un-omit `transcriptionService` above once it's supported
|
|
288
|
+
*
|
|
289
|
+
* This satisfies...
|
|
290
|
+
* – the optional constraint in `CopilotRuntimeConstructorParams_BASE`
|
|
291
|
+
* – the `MaybePromise<NonEmptyRecord<T>>` constraint in `CopilotRuntimeOptionsVNext`
|
|
292
|
+
* – the `Record<string, AbstractAgent>` constraint in `both
|
|
293
|
+
*/
|
|
294
|
+
agents?: MaybePromise<NonEmptyRecord<Record<string, AbstractAgent>>>;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Central runtime object passed to all request handlers.
|
|
299
|
+
*/
|
|
317
300
|
export class CopilotRuntime<const T extends Parameter[] | [] = []> {
|
|
318
|
-
|
|
319
|
-
public agents: Record<string, AbstractAgent>;
|
|
320
|
-
public remoteEndpointDefinitions: EndpointDefinition[];
|
|
321
|
-
private langserve: Promise<Action<any>>[] = [];
|
|
322
|
-
private onBeforeRequest?: OnBeforeRequestHandler;
|
|
323
|
-
private onAfterRequest?: OnAfterRequestHandler;
|
|
324
|
-
private onStopGeneration?: OnStopGenerationHandler;
|
|
325
|
-
private delegateAgentProcessingToServiceAdapter: boolean;
|
|
301
|
+
params?: CopilotRuntimeConstructorParams<T>;
|
|
326
302
|
private observability?: CopilotObservabilityConfig;
|
|
327
|
-
|
|
328
|
-
private
|
|
329
|
-
private
|
|
303
|
+
// Cache MCP tools per endpoint to avoid re-fetching repeatedly
|
|
304
|
+
private mcpToolsCache: Map<string, BuiltInAgentConfiguration["tools"]> = new Map();
|
|
305
|
+
private runtimeArgs: CopilotRuntimeOptions;
|
|
306
|
+
private _instance: CopilotRuntimeVNext;
|
|
307
|
+
|
|
308
|
+
constructor(
|
|
309
|
+
params?: CopilotRuntimeConstructorParams<T> & PartialBy<CopilotRuntimeOptions, "agents">,
|
|
310
|
+
) {
|
|
311
|
+
const agents = params?.agents ?? {};
|
|
312
|
+
const endpointAgents = this.assignEndpointsToAgents(params?.remoteEndpoints ?? []);
|
|
313
|
+
|
|
314
|
+
// Determine the base runner (user-provided or default)
|
|
315
|
+
const baseRunner = params?.runner ?? new InMemoryAgentRunner();
|
|
316
|
+
|
|
317
|
+
// Wrap with TelemetryAgentRunner unless telemetry is disabled
|
|
318
|
+
// This ensures we always capture agent execution telemetry when enabled,
|
|
319
|
+
// even if the user provides their own custom runner
|
|
320
|
+
const runner = isTelemetryDisabled()
|
|
321
|
+
? baseRunner
|
|
322
|
+
: new TelemetryAgentRunner({ runner: baseRunner });
|
|
323
|
+
|
|
324
|
+
this.runtimeArgs = {
|
|
325
|
+
agents: { ...endpointAgents, ...agents },
|
|
326
|
+
runner,
|
|
327
|
+
// TODO: add support for transcriptionService from CopilotRuntimeOptionsVNext once it is ready
|
|
328
|
+
// transcriptionService: params?.transcriptionService,
|
|
329
|
+
|
|
330
|
+
beforeRequestMiddleware: this.createOnBeforeRequestHandler(params).bind(this),
|
|
331
|
+
afterRequestMiddleware: this.createOnAfterRequestHandler(params).bind(this),
|
|
332
|
+
};
|
|
333
|
+
this.params = params;
|
|
334
|
+
this.observability = params?.observability_c;
|
|
335
|
+
}
|
|
330
336
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
337
|
+
get instance() {
|
|
338
|
+
if (!this._instance) {
|
|
339
|
+
this._instance = new CopilotRuntimeVNext(this.runtimeArgs);
|
|
340
|
+
}
|
|
335
341
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
// --- MCP Client Factory ---
|
|
342
|
+
return this._instance;
|
|
343
|
+
}
|
|
339
344
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
) {
|
|
345
|
-
throw new CopilotKitMisuseError({
|
|
346
|
-
message:
|
|
347
|
-
"LangGraph Platform remote endpoints are deprecated in favor of the `agents` property. Refer to https://docs.copilotkit.ai/langgraph for more information.",
|
|
348
|
-
});
|
|
349
|
-
}
|
|
345
|
+
private assignEndpointsToAgents(
|
|
346
|
+
endpoints: CopilotRuntimeConstructorParams<T>["remoteEndpoints"],
|
|
347
|
+
): Record<string, AbstractAgent> {
|
|
348
|
+
let result: Record<string, AbstractAgent> = {};
|
|
350
349
|
|
|
351
350
|
if (
|
|
352
|
-
|
|
353
|
-
params?.remoteEndpoints &&
|
|
354
|
-
params?.remoteEndpoints.some((e) => e.type === EndpointType.LangGraphPlatform)
|
|
351
|
+
endpoints.some((endpoint) => resolveEndpointType(endpoint) == EndpointType.LangGraphPlatform)
|
|
355
352
|
) {
|
|
356
|
-
console.warn("Actions set in runtime instance will not be available for the agent");
|
|
357
|
-
console.warn(
|
|
358
|
-
`LangGraph Platform remote endpoints are deprecated in favor of the "agents" property`,
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// TODO: finalize
|
|
363
|
-
// if (
|
|
364
|
-
// params?.agents &&
|
|
365
|
-
// Object.values(params.agents).some((agent) => {
|
|
366
|
-
// return agent instanceof AguiLangGraphAgent && !(agent instanceof LangGraphAgent);
|
|
367
|
-
// })
|
|
368
|
-
// ) {
|
|
369
|
-
// console.warn('LangGraph Agent class should be imported from @copilotkit/runtime. ')
|
|
370
|
-
// }
|
|
371
|
-
|
|
372
|
-
this.actions = params?.actions || [];
|
|
373
|
-
this.availableAgents = [];
|
|
374
|
-
|
|
375
|
-
for (const chain of params?.langserve || []) {
|
|
376
|
-
const remoteChain = new RemoteChain(chain);
|
|
377
|
-
this.langserve.push(remoteChain.toAction());
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
this.remoteEndpointDefinitions = params?.remoteEndpoints ?? params?.remoteActions ?? [];
|
|
381
|
-
|
|
382
|
-
this.onBeforeRequest = params?.middleware?.onBeforeRequest;
|
|
383
|
-
this.onAfterRequest = params?.middleware?.onAfterRequest;
|
|
384
|
-
this.onStopGeneration = params?.onStopGeneration;
|
|
385
|
-
this.delegateAgentProcessingToServiceAdapter =
|
|
386
|
-
params?.delegateAgentProcessingToServiceAdapter || false;
|
|
387
|
-
this.observability = params?.observability_c;
|
|
388
|
-
this.agents = params?.agents ?? {};
|
|
389
|
-
this.onError = params?.onError;
|
|
390
|
-
// +++ MCP Initialization +++
|
|
391
|
-
this.mcpServersConfig = params?.mcpServers;
|
|
392
|
-
this.createMCPClientImpl = params?.createMCPClient;
|
|
393
|
-
|
|
394
|
-
// Validate: If mcpServers are provided, createMCPClient must also be provided
|
|
395
|
-
if (this.mcpServersConfig && this.mcpServersConfig.length > 0 && !this.createMCPClientImpl) {
|
|
396
353
|
throw new CopilotKitMisuseError({
|
|
397
354
|
message:
|
|
398
|
-
"
|
|
399
|
-
|
|
355
|
+
"LangGraphPlatformEndpoint in remoteEndpoints is deprecated. " +
|
|
356
|
+
'Please use the "agents" option instead with LangGraphAgent from "@copilotkit/runtime/langgraph". ' +
|
|
357
|
+
'Example: agents: { myAgent: new LangGraphAgent({ deploymentUrl: "...", graphId: "..." }) }',
|
|
400
358
|
});
|
|
401
359
|
}
|
|
402
360
|
|
|
403
|
-
|
|
404
|
-
if (
|
|
405
|
-
params?.actions &&
|
|
406
|
-
(params?.remoteEndpoints?.some((e) => e.type === EndpointType.LangGraphPlatform) ||
|
|
407
|
-
this.mcpServersConfig?.length)
|
|
408
|
-
) {
|
|
409
|
-
console.warn(
|
|
410
|
-
"Local 'actions' defined in CopilotRuntime might not be available to remote agents (LangGraph, MCP). Consider defining actions closer to the agent implementation if needed.",
|
|
411
|
-
);
|
|
412
|
-
}
|
|
361
|
+
return result;
|
|
413
362
|
}
|
|
414
363
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
// Create a map to deduplicate tools by name (keeping the last one if duplicates exist)
|
|
428
|
-
const uniqueMcpTools = new Map<string, Action<any>>();
|
|
429
|
-
|
|
430
|
-
// Add all MCP tools to the map with their names as keys
|
|
431
|
-
mcpActionsForRequest.forEach((action) => {
|
|
432
|
-
uniqueMcpTools.set(action.name, action);
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
// Format instructions from the unique tools map
|
|
436
|
-
// Convert Action objects to MCPTool format for the instruction generator
|
|
437
|
-
const toolsMap: Record<string, MCPTool> = {};
|
|
438
|
-
Array.from(uniqueMcpTools.values()).forEach((action) => {
|
|
439
|
-
toolsMap[action.name] = {
|
|
440
|
-
description: action.description || "",
|
|
441
|
-
schema: action.parameters
|
|
442
|
-
? {
|
|
443
|
-
parameters: {
|
|
444
|
-
properties: action.parameters.reduce(
|
|
445
|
-
(acc, p) => ({
|
|
446
|
-
...acc,
|
|
447
|
-
[p.name]: { type: p.type, description: p.description },
|
|
448
|
-
}),
|
|
449
|
-
{},
|
|
450
|
-
),
|
|
451
|
-
required: action.parameters.filter((p) => p.required).map((p) => p.name),
|
|
452
|
-
},
|
|
453
|
-
}
|
|
454
|
-
: {},
|
|
455
|
-
execute: async () => ({}), // Placeholder, not used for instructions
|
|
456
|
-
};
|
|
457
|
-
});
|
|
458
|
-
|
|
459
|
-
// Generate instructions using the exported helper
|
|
460
|
-
const mcpToolInstructions = generateMcpToolInstructions(toolsMap);
|
|
461
|
-
|
|
462
|
-
if (!mcpToolInstructions) {
|
|
463
|
-
return messages; // No MCP tools to describe
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
const instructions =
|
|
467
|
-
mcpToolInstructions + "\nUse them when appropriate to fulfill the user's request.";
|
|
468
|
-
|
|
469
|
-
const systemMessageIndex = messages.findIndex((msg) => msg.textMessage?.role === "system");
|
|
470
|
-
|
|
471
|
-
const newMessages = [...messages]; // Create a mutable copy
|
|
472
|
-
|
|
473
|
-
if (systemMessageIndex !== -1) {
|
|
474
|
-
const existingMsg = newMessages[systemMessageIndex];
|
|
475
|
-
if (existingMsg.textMessage) {
|
|
476
|
-
existingMsg.textMessage.content =
|
|
477
|
-
(existingMsg.textMessage.content ? existingMsg.textMessage.content + "\n\n" : "") +
|
|
478
|
-
instructions;
|
|
479
|
-
}
|
|
480
|
-
} else {
|
|
481
|
-
newMessages.unshift({
|
|
482
|
-
id: randomId(),
|
|
483
|
-
createdAt: new Date(),
|
|
484
|
-
textMessage: {
|
|
485
|
-
role: MessageRole.system,
|
|
486
|
-
content: instructions,
|
|
487
|
-
},
|
|
488
|
-
actionExecutionMessage: undefined,
|
|
489
|
-
resultMessage: undefined,
|
|
490
|
-
agentStateMessage: undefined,
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return newMessages;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
async processRuntimeRequest(request: CopilotRuntimeRequest): Promise<CopilotRuntimeResponse> {
|
|
498
|
-
const {
|
|
499
|
-
serviceAdapter,
|
|
500
|
-
messages: rawMessages,
|
|
501
|
-
actions: clientSideActionsInput,
|
|
502
|
-
threadId,
|
|
503
|
-
runId,
|
|
504
|
-
outputMessagesPromise,
|
|
505
|
-
graphqlContext,
|
|
506
|
-
forwardedParameters,
|
|
507
|
-
url,
|
|
508
|
-
extensions,
|
|
509
|
-
agentSession,
|
|
510
|
-
agentStates,
|
|
511
|
-
publicApiKey,
|
|
512
|
-
context,
|
|
513
|
-
} = request;
|
|
514
|
-
graphqlContext.request.signal.addEventListener(
|
|
515
|
-
"abort",
|
|
516
|
-
() =>
|
|
517
|
-
this.onStopGeneration?.({
|
|
518
|
-
threadId,
|
|
519
|
-
runId,
|
|
520
|
-
url,
|
|
521
|
-
agentName: agentSession?.agentName,
|
|
522
|
-
lastMessage: rawMessages[rawMessages.length - 1],
|
|
523
|
-
}),
|
|
524
|
-
{ once: true }, // optional: fire only once
|
|
525
|
-
);
|
|
526
|
-
|
|
527
|
-
const eventSource = new RuntimeEventSource({
|
|
528
|
-
errorHandler: async (error, context) => {
|
|
529
|
-
await this.error("error", context, error, publicApiKey);
|
|
530
|
-
},
|
|
531
|
-
errorContext: {
|
|
532
|
-
threadId,
|
|
533
|
-
runId,
|
|
534
|
-
source: "runtime",
|
|
535
|
-
request: {
|
|
536
|
-
operation: "processRuntimeRequest",
|
|
537
|
-
method: "POST",
|
|
538
|
-
url: url,
|
|
539
|
-
startTime: Date.now(),
|
|
540
|
-
},
|
|
541
|
-
agent: agentSession ? { name: agentSession.agentName } : undefined,
|
|
542
|
-
technical: {
|
|
543
|
-
environment: process.env.NODE_ENV,
|
|
544
|
-
},
|
|
545
|
-
},
|
|
546
|
-
});
|
|
547
|
-
// Track request start time for logging
|
|
548
|
-
const requestStartTime = Date.now();
|
|
549
|
-
// For storing streamed chunks if progressive logging is enabled
|
|
550
|
-
const streamedChunks: any[] = [];
|
|
551
|
-
|
|
552
|
-
try {
|
|
553
|
-
console.log("Processing request:", request);
|
|
554
|
-
if (
|
|
555
|
-
Object.keys(this.agents).length &&
|
|
556
|
-
agentSession?.agentName &&
|
|
557
|
-
!this.delegateAgentProcessingToServiceAdapter
|
|
558
|
-
) {
|
|
559
|
-
this.agents = { [agentSession.agentName]: this.agents[agentSession.agentName] };
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
if (agentSession && !this.delegateAgentProcessingToServiceAdapter) {
|
|
563
|
-
return await this.processAgentRequest(request);
|
|
564
|
-
}
|
|
565
|
-
if (serviceAdapter instanceof EmptyAdapter) {
|
|
566
|
-
throw new CopilotKitMisuseError({
|
|
567
|
-
message: `Invalid adapter configuration: EmptyAdapter is only meant to be used with agent lock mode.
|
|
568
|
-
For non-agent components like useCopilotChatSuggestions, CopilotTextarea, or CopilotTask,
|
|
569
|
-
please use an LLM adapter instead.`,
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
// +++ Get Server Side Actions (including dynamic MCP) EARLY +++
|
|
574
|
-
const serverSideActions = await this.getServerSideActions(request);
|
|
575
|
-
// --- Get Server Side Actions (including dynamic MCP) EARLY ---
|
|
576
|
-
|
|
577
|
-
// Filter raw messages *before* injection
|
|
578
|
-
const filteredRawMessages = rawMessages.filter((message) => !message.agentStateMessage);
|
|
579
|
-
|
|
580
|
-
// +++ Inject MCP Instructions based on current actions +++
|
|
581
|
-
const messagesWithInjectedInstructions = this.injectMCPToolInstructions(
|
|
582
|
-
filteredRawMessages,
|
|
583
|
-
serverSideActions,
|
|
584
|
-
);
|
|
585
|
-
const inputMessages = convertGqlInputToMessages(messagesWithInjectedInstructions);
|
|
586
|
-
// --- Inject MCP Instructions based on current actions ---
|
|
587
|
-
|
|
588
|
-
// Log LLM request if logging is enabled
|
|
589
|
-
if (this.observability?.enabled && publicApiKey) {
|
|
590
|
-
try {
|
|
591
|
-
const requestData: LLMRequestData = {
|
|
592
|
-
threadId,
|
|
593
|
-
runId,
|
|
594
|
-
model: forwardedParameters?.model,
|
|
595
|
-
messages: inputMessages,
|
|
596
|
-
actions: clientSideActionsInput,
|
|
597
|
-
forwardedParameters,
|
|
598
|
-
timestamp: requestStartTime,
|
|
599
|
-
provider: this.detectProvider(serviceAdapter),
|
|
600
|
-
};
|
|
601
|
-
|
|
602
|
-
await this.observability.hooks.handleRequest(requestData);
|
|
603
|
-
} catch (error) {
|
|
604
|
-
console.error("Error logging LLM request:", error);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
const serverSideActionsInput: ActionInput[] = serverSideActions.map((action) => ({
|
|
609
|
-
name: action.name,
|
|
610
|
-
description: action.description,
|
|
611
|
-
jsonSchema: JSON.stringify(actionParametersToJsonSchema(action.parameters)),
|
|
612
|
-
additionalConfig: action.additionalConfig,
|
|
613
|
-
}));
|
|
614
|
-
|
|
615
|
-
const actionInputs = flattenToolCallsNoDuplicates([
|
|
616
|
-
...serverSideActionsInput,
|
|
617
|
-
...clientSideActionsInput.filter(
|
|
618
|
-
// Filter remote actions from CopilotKit core loop
|
|
619
|
-
(action) => action.available !== ActionInputAvailability.remote,
|
|
620
|
-
),
|
|
621
|
-
]);
|
|
622
|
-
|
|
623
|
-
await this.onBeforeRequest?.({
|
|
624
|
-
threadId,
|
|
625
|
-
runId,
|
|
626
|
-
inputMessages,
|
|
627
|
-
properties: graphqlContext.properties,
|
|
628
|
-
url,
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
const result = await serviceAdapter.process({
|
|
632
|
-
messages: inputMessages,
|
|
633
|
-
actions: actionInputs,
|
|
634
|
-
threadId,
|
|
635
|
-
runId,
|
|
636
|
-
eventSource,
|
|
637
|
-
forwardedParameters,
|
|
638
|
-
extensions,
|
|
639
|
-
agentSession,
|
|
640
|
-
agentStates,
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
// for backwards compatibility, we deal with the case that no threadId is provided
|
|
644
|
-
// by the frontend, by using the threadId from the response
|
|
645
|
-
const nonEmptyThreadId = threadId ?? result.threadId;
|
|
364
|
+
handleServiceAdapter(serviceAdapter: CopilotServiceAdapter) {
|
|
365
|
+
this.runtimeArgs.agents = Promise.resolve(this.runtimeArgs.agents ?? {}).then(
|
|
366
|
+
async (agents) => {
|
|
367
|
+
let agentsList = agents;
|
|
368
|
+
const isAgentsListEmpty = !Object.keys(agents).length;
|
|
369
|
+
const hasServiceAdapter = Boolean(serviceAdapter);
|
|
370
|
+
const illegalServiceAdapterNames = ["EmptyAdapter"];
|
|
371
|
+
const serviceAdapterCanBeUsedForAgent = !illegalServiceAdapterNames.includes(
|
|
372
|
+
serviceAdapter.name,
|
|
373
|
+
);
|
|
646
374
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
runId: result.runId,
|
|
652
|
-
inputMessages,
|
|
653
|
-
outputMessages,
|
|
654
|
-
properties: graphqlContext.properties,
|
|
655
|
-
url,
|
|
375
|
+
if (isAgentsListEmpty && (!hasServiceAdapter || !serviceAdapterCanBeUsedForAgent)) {
|
|
376
|
+
throw new CopilotKitMisuseError({
|
|
377
|
+
message:
|
|
378
|
+
"No default agent provided. Please provide a default agent in the runtime config.",
|
|
656
379
|
});
|
|
657
|
-
})
|
|
658
|
-
.catch((_error) => {});
|
|
659
|
-
|
|
660
|
-
// After getting the response, log it if logging is enabled
|
|
661
|
-
if (this.observability?.enabled && publicApiKey) {
|
|
662
|
-
try {
|
|
663
|
-
outputMessagesPromise
|
|
664
|
-
.then((outputMessages) => {
|
|
665
|
-
const responseData: LLMResponseData = {
|
|
666
|
-
threadId: result.threadId,
|
|
667
|
-
runId: result.runId,
|
|
668
|
-
model: forwardedParameters?.model,
|
|
669
|
-
// Use collected chunks for progressive mode or outputMessages for regular mode
|
|
670
|
-
output: this.observability.progressive ? streamedChunks : outputMessages,
|
|
671
|
-
latency: Date.now() - requestStartTime,
|
|
672
|
-
timestamp: Date.now(),
|
|
673
|
-
provider: this.detectProvider(serviceAdapter),
|
|
674
|
-
// Indicate this is the final response
|
|
675
|
-
isFinalResponse: true,
|
|
676
|
-
};
|
|
677
|
-
|
|
678
|
-
try {
|
|
679
|
-
this.observability.hooks.handleResponse(responseData);
|
|
680
|
-
} catch (logError) {
|
|
681
|
-
console.error("Error logging LLM response:", logError);
|
|
682
|
-
}
|
|
683
|
-
})
|
|
684
|
-
.catch((error) => {
|
|
685
|
-
console.error("Failed to get output messages for logging:", error);
|
|
686
|
-
});
|
|
687
|
-
} catch (error) {
|
|
688
|
-
console.error("Error setting up logging for LLM response:", error);
|
|
689
380
|
}
|
|
690
|
-
}
|
|
691
381
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const originalStream = eventSource.stream.bind(eventSource);
|
|
696
|
-
|
|
697
|
-
// Wrap the stream function to intercept events
|
|
698
|
-
eventSource.stream = async (callback) => {
|
|
699
|
-
await originalStream(async (eventStream$) => {
|
|
700
|
-
// Create subscription to capture streaming events
|
|
701
|
-
eventStream$.subscribe({
|
|
702
|
-
next: (event) => {
|
|
703
|
-
// Only log content chunks
|
|
704
|
-
if (event.type === RuntimeEventTypes.TextMessageContent) {
|
|
705
|
-
// Store the chunk
|
|
706
|
-
streamedChunks.push(event.content);
|
|
707
|
-
|
|
708
|
-
// Log each chunk separately for progressive mode
|
|
709
|
-
try {
|
|
710
|
-
const progressiveData: LLMResponseData = {
|
|
711
|
-
threadId: threadId || "",
|
|
712
|
-
runId,
|
|
713
|
-
model: forwardedParameters?.model,
|
|
714
|
-
output: event.content,
|
|
715
|
-
latency: Date.now() - requestStartTime,
|
|
716
|
-
timestamp: Date.now(),
|
|
717
|
-
provider: this.detectProvider(serviceAdapter),
|
|
718
|
-
isProgressiveChunk: true,
|
|
719
|
-
};
|
|
720
|
-
|
|
721
|
-
// Use Promise to handle async logger without awaiting
|
|
722
|
-
Promise.resolve()
|
|
723
|
-
.then(() => {
|
|
724
|
-
this.observability.hooks.handleResponse(progressiveData);
|
|
725
|
-
})
|
|
726
|
-
.catch((error) => {
|
|
727
|
-
console.error("Error in progressive logging:", error);
|
|
728
|
-
});
|
|
729
|
-
} catch (error) {
|
|
730
|
-
console.error("Error preparing progressive log data:", error);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
},
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
// Call the original callback with the event stream
|
|
737
|
-
await callback(eventStream$);
|
|
382
|
+
if (isAgentsListEmpty) {
|
|
383
|
+
agentsList.default = new BuiltInAgent({
|
|
384
|
+
model: `${serviceAdapter.provider}/${serviceAdapter.model}`,
|
|
738
385
|
});
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
console.log("Request processed. result: ", result);
|
|
743
|
-
return {
|
|
744
|
-
threadId: nonEmptyThreadId,
|
|
745
|
-
runId: result.runId,
|
|
746
|
-
eventSource,
|
|
747
|
-
serverSideActions,
|
|
748
|
-
actionInputsWithoutAgents: actionInputs.filter(
|
|
749
|
-
(action) =>
|
|
750
|
-
// TODO-AGENTS: do not exclude ALL server side actions
|
|
751
|
-
!serverSideActions.find((serverSideAction) => serverSideAction.name == action.name),
|
|
752
|
-
// !isRemoteAgentAction(
|
|
753
|
-
// serverSideActions.find((serverSideAction) => serverSideAction.name == action.name),
|
|
754
|
-
// ),
|
|
755
|
-
),
|
|
756
|
-
extensions: result.extensions,
|
|
757
|
-
};
|
|
758
|
-
} catch (error) {
|
|
759
|
-
console.log("Error in processRuntimeRequest", error);
|
|
760
|
-
// Log error if logging is enabled
|
|
761
|
-
if (this.observability?.enabled && publicApiKey) {
|
|
762
|
-
try {
|
|
763
|
-
const errorData: LLMErrorData = {
|
|
764
|
-
threadId,
|
|
765
|
-
runId,
|
|
766
|
-
model: forwardedParameters?.model,
|
|
767
|
-
error: error instanceof Error ? error : String(error),
|
|
768
|
-
timestamp: Date.now(),
|
|
769
|
-
latency: Date.now() - requestStartTime,
|
|
770
|
-
provider: this.detectProvider(serviceAdapter),
|
|
771
|
-
};
|
|
772
|
-
|
|
773
|
-
await this.observability.hooks.handleError(errorData);
|
|
774
|
-
} catch (logError) {
|
|
775
|
-
console.error("Error logging LLM error:", logError);
|
|
776
386
|
}
|
|
777
|
-
}
|
|
778
387
|
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
this.convertStreamingErrorToStructured(err),
|
|
787
|
-
);
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
// Track the error
|
|
791
|
-
await this.error(
|
|
792
|
-
"error",
|
|
793
|
-
{
|
|
794
|
-
threadId,
|
|
795
|
-
runId,
|
|
796
|
-
source: "runtime",
|
|
797
|
-
request: {
|
|
798
|
-
operation: "processRuntimeRequest",
|
|
799
|
-
method: "POST",
|
|
800
|
-
url: url,
|
|
801
|
-
startTime: requestStartTime,
|
|
802
|
-
},
|
|
803
|
-
response: {
|
|
804
|
-
endTime: Date.now(),
|
|
805
|
-
latency: Date.now() - requestStartTime,
|
|
806
|
-
},
|
|
807
|
-
agent: agentSession ? { name: agentSession.agentName } : undefined,
|
|
808
|
-
technical: {
|
|
809
|
-
environment: process.env.NODE_ENV,
|
|
810
|
-
stackTrace: error instanceof Error ? error.stack : undefined,
|
|
811
|
-
},
|
|
812
|
-
},
|
|
813
|
-
structuredError,
|
|
814
|
-
publicApiKey,
|
|
815
|
-
);
|
|
816
|
-
|
|
817
|
-
throw structuredError;
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
async getAllAgents(graphqlContext: GraphQLContext): Promise<(AgentWithEndpoint | Agent)[]> {
|
|
822
|
-
const agentsWithEndpoints = await this.discoverAgentsFromEndpoints(graphqlContext);
|
|
823
|
-
const aguiAgents = this.discoverAgentsFromAgui();
|
|
824
|
-
|
|
825
|
-
this.availableAgents = [...agentsWithEndpoints, ...aguiAgents].map((a) => ({
|
|
826
|
-
name: a.name,
|
|
827
|
-
id: a.id,
|
|
828
|
-
}));
|
|
829
|
-
|
|
830
|
-
return [...agentsWithEndpoints, ...aguiAgents];
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
async discoverAgentsFromEndpoints(graphqlContext: GraphQLContext): Promise<AgentWithEndpoint[]> {
|
|
834
|
-
const agents: Promise<AgentWithEndpoint[]> = this.remoteEndpointDefinitions.reduce(
|
|
835
|
-
async (acc: Promise<Agent[]>, endpoint) => {
|
|
836
|
-
const agents = await acc;
|
|
837
|
-
if (endpoint.type === EndpointType.LangGraphPlatform) {
|
|
838
|
-
const propertyHeaders = graphqlContext.properties.authorization
|
|
839
|
-
? { authorization: `Bearer ${graphqlContext.properties.authorization}` }
|
|
840
|
-
: null;
|
|
841
|
-
|
|
842
|
-
const client = new LangGraphClient({
|
|
843
|
-
apiUrl: endpoint.deploymentUrl,
|
|
844
|
-
apiKey: endpoint.langsmithApiKey,
|
|
845
|
-
defaultHeaders: { ...propertyHeaders },
|
|
846
|
-
});
|
|
847
|
-
let data: Array<{ assistant_id: string; graph_id: string }> | { detail: string } = [];
|
|
848
|
-
try {
|
|
849
|
-
data = await client.assistants.search();
|
|
850
|
-
|
|
851
|
-
if (data && "detail" in data && (data.detail as string).toLowerCase() === "not found") {
|
|
852
|
-
throw new CopilotKitAgentDiscoveryError({ availableAgents: this.availableAgents });
|
|
853
|
-
}
|
|
854
|
-
} catch (e) {
|
|
855
|
-
throw new CopilotKitMisuseError({
|
|
856
|
-
message: `
|
|
857
|
-
Failed to find or contact remote endpoint at url ${endpoint.deploymentUrl}.
|
|
858
|
-
Make sure the API is running and that it's indeed a LangGraph platform url.
|
|
859
|
-
|
|
860
|
-
See more: https://docs.copilotkit.ai/troubleshooting/common-issues`,
|
|
861
|
-
});
|
|
862
|
-
}
|
|
863
|
-
const endpointAgents = data.map((entry) => ({
|
|
864
|
-
name: entry.graph_id,
|
|
865
|
-
id: entry.assistant_id,
|
|
866
|
-
description: "",
|
|
867
|
-
endpoint,
|
|
868
|
-
}));
|
|
869
|
-
return [...agents, ...endpointAgents];
|
|
388
|
+
const actions = this.params?.actions;
|
|
389
|
+
if (actions) {
|
|
390
|
+
const mcpTools = await this.getToolsFromMCP();
|
|
391
|
+
agentsList = this.assignToolsToAgents(agents, [
|
|
392
|
+
...this.getToolsFromActions(actions),
|
|
393
|
+
...mcpTools,
|
|
394
|
+
]);
|
|
870
395
|
}
|
|
871
396
|
|
|
872
|
-
|
|
873
|
-
agents?: Array<{
|
|
874
|
-
name: string;
|
|
875
|
-
description: string;
|
|
876
|
-
}>;
|
|
877
|
-
}
|
|
878
|
-
const cpkEndpoint = endpoint as CopilotKitEndpoint;
|
|
879
|
-
const fetchUrl = `${endpoint.url}/info`;
|
|
880
|
-
try {
|
|
881
|
-
const response = await fetchWithRetry(fetchUrl, {
|
|
882
|
-
method: "POST",
|
|
883
|
-
headers: createHeaders(cpkEndpoint.onBeforeRequest, graphqlContext),
|
|
884
|
-
body: JSON.stringify({ properties: graphqlContext.properties }),
|
|
885
|
-
});
|
|
886
|
-
if (!response.ok) {
|
|
887
|
-
if (response.status === 404) {
|
|
888
|
-
throw new CopilotKitApiDiscoveryError({ url: fetchUrl });
|
|
889
|
-
}
|
|
890
|
-
throw new ResolvedCopilotKitError({
|
|
891
|
-
status: response.status,
|
|
892
|
-
url: fetchUrl,
|
|
893
|
-
isRemoteEndpoint: true,
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
const data: InfoResponse = await response.json();
|
|
898
|
-
const endpointAgents = (data?.agents ?? []).map((agent) => ({
|
|
899
|
-
name: agent.name,
|
|
900
|
-
description: agent.description ?? "" ?? "",
|
|
901
|
-
id: randomId(), // Required by Agent type
|
|
902
|
-
endpoint,
|
|
903
|
-
}));
|
|
904
|
-
return [...agents, ...endpointAgents];
|
|
905
|
-
} catch (error) {
|
|
906
|
-
if (error instanceof CopilotKitError) {
|
|
907
|
-
throw error;
|
|
908
|
-
}
|
|
909
|
-
throw new CopilotKitLowLevelError({ error: error as Error, url: fetchUrl });
|
|
910
|
-
}
|
|
397
|
+
return agentsList;
|
|
911
398
|
},
|
|
912
|
-
Promise.resolve([]),
|
|
913
399
|
);
|
|
914
|
-
|
|
915
|
-
return agents;
|
|
916
400
|
}
|
|
917
401
|
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
async loadAgentState(
|
|
927
|
-
graphqlContext: GraphQLContext,
|
|
928
|
-
threadId: string,
|
|
929
|
-
agentName: string,
|
|
930
|
-
): Promise<LoadAgentStateResponse> {
|
|
931
|
-
const agents = await this.getAllAgents(graphqlContext);
|
|
932
|
-
|
|
933
|
-
const agent = agents.find((agent) => agent.name === agentName);
|
|
934
|
-
if (!agent) {
|
|
935
|
-
throw new Error("Agent not found");
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
if (
|
|
939
|
-
"endpoint" in agent &&
|
|
940
|
-
(agent.endpoint.type === EndpointType.CopilotKit || !("type" in agent.endpoint))
|
|
941
|
-
) {
|
|
942
|
-
const cpkEndpoint = agent.endpoint as CopilotKitEndpoint;
|
|
943
|
-
const fetchUrl = `${cpkEndpoint.url}/agents/state`;
|
|
944
|
-
try {
|
|
945
|
-
const response = await fetchWithRetry(fetchUrl, {
|
|
946
|
-
method: "POST",
|
|
947
|
-
headers: createHeaders(cpkEndpoint.onBeforeRequest, graphqlContext),
|
|
948
|
-
body: JSON.stringify({
|
|
949
|
-
properties: graphqlContext.properties,
|
|
950
|
-
threadId,
|
|
951
|
-
name: agentName,
|
|
952
|
-
}),
|
|
953
|
-
});
|
|
954
|
-
if (!response.ok) {
|
|
955
|
-
if (response.status === 404) {
|
|
956
|
-
throw new CopilotKitApiDiscoveryError({ url: fetchUrl });
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
// Extract semantic error information from response body
|
|
960
|
-
let errorMessage = `HTTP ${response.status} error`;
|
|
961
|
-
try {
|
|
962
|
-
const errorBody = await response.text();
|
|
963
|
-
const parsedError = JSON.parse(errorBody);
|
|
964
|
-
if (parsedError.error && typeof parsedError.error === "string") {
|
|
965
|
-
errorMessage = parsedError.error;
|
|
966
|
-
}
|
|
967
|
-
} catch {
|
|
968
|
-
// If parsing fails, fall back to generic message
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
throw new ResolvedCopilotKitError({
|
|
972
|
-
status: response.status,
|
|
973
|
-
url: fetchUrl,
|
|
974
|
-
isRemoteEndpoint: true,
|
|
975
|
-
message: errorMessage,
|
|
976
|
-
});
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
const data: LoadAgentStateResponse = await response.json();
|
|
980
|
-
|
|
981
|
-
return {
|
|
982
|
-
...data,
|
|
983
|
-
state: JSON.stringify(data.state),
|
|
984
|
-
messages: JSON.stringify(data.messages),
|
|
985
|
-
};
|
|
986
|
-
} catch (error) {
|
|
987
|
-
if (error instanceof CopilotKitError) {
|
|
988
|
-
throw error;
|
|
989
|
-
}
|
|
990
|
-
throw new CopilotKitLowLevelError({ error, url: fetchUrl });
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
const propertyHeaders = graphqlContext.properties.authorization
|
|
995
|
-
? { authorization: `Bearer ${graphqlContext.properties.authorization}` }
|
|
996
|
-
: null;
|
|
402
|
+
// Receive this.params.action and turn it into the AbstractAgent tools
|
|
403
|
+
private getToolsFromActions(
|
|
404
|
+
actions: ActionsConfiguration<any>,
|
|
405
|
+
): BuiltInAgentConfiguration["tools"] {
|
|
406
|
+
// Resolve actions to an array (handle function case)
|
|
407
|
+
const actionsArray =
|
|
408
|
+
typeof actions === "function" ? actions({ properties: {}, url: undefined }) : actions;
|
|
997
409
|
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
client = new LangGraphClient({
|
|
1003
|
-
apiUrl: agent.endpoint.deploymentUrl,
|
|
1004
|
-
apiKey: agent.endpoint.langsmithApiKey,
|
|
1005
|
-
defaultHeaders: { ...propertyHeaders },
|
|
1006
|
-
});
|
|
1007
|
-
} else {
|
|
1008
|
-
const aguiAgent = graphqlContext._copilotkit.runtime.agents[agent.name] as LangGraphAgent;
|
|
1009
|
-
if (!aguiAgent) {
|
|
1010
|
-
throw new Error(`Agent: ${agent.name} could not be resolved`);
|
|
1011
|
-
}
|
|
1012
|
-
// @ts-expect-error -- both clients are the same
|
|
1013
|
-
client = aguiAgent.client ?? null;
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
state = client ? ((await client.threads.getState(threadId)).values as any) : {};
|
|
1017
|
-
} catch (error) {
|
|
1018
|
-
// All errors from agent state loading are user configuration issues
|
|
1019
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1020
|
-
const errorStatus = error?.response?.status || error?.status;
|
|
1021
|
-
|
|
1022
|
-
if (errorStatus === 404) {
|
|
1023
|
-
state = {};
|
|
1024
|
-
} else {
|
|
1025
|
-
// Log user configuration errors at debug level to reduce noise
|
|
1026
|
-
console.debug(`Agent '${agentName}' configuration issue: ${errorMessage}`);
|
|
1027
|
-
|
|
1028
|
-
// Throw a configuration error - all agent state loading failures are user setup issues
|
|
1029
|
-
throw new ResolvedCopilotKitError({
|
|
1030
|
-
status: 400,
|
|
1031
|
-
message: `Agent '${agentName}' failed to execute: ${errorMessage}`,
|
|
1032
|
-
code: CopilotKitErrorCode.CONFIGURATION_ERROR,
|
|
1033
|
-
});
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
410
|
+
// Convert each Action to a ToolDefinition
|
|
411
|
+
return actionsArray.map((action) => {
|
|
412
|
+
// Convert JSON schema to Zod schema
|
|
413
|
+
const zodSchema = getZodParameters(action.parameters || []);
|
|
1036
414
|
|
|
1037
|
-
if (Object.keys(state).length === 0) {
|
|
1038
415
|
return {
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
};
|
|
1044
|
-
} else {
|
|
1045
|
-
const { messages, ...stateWithoutMessages } = state;
|
|
1046
|
-
const copilotkitMessages = langchainMessagesToCopilotKit(messages);
|
|
1047
|
-
return {
|
|
1048
|
-
threadId: threadId || "",
|
|
1049
|
-
threadExists: true,
|
|
1050
|
-
state: JSON.stringify(stateWithoutMessages),
|
|
1051
|
-
messages: JSON.stringify(copilotkitMessages),
|
|
416
|
+
name: action.name,
|
|
417
|
+
description: action.description || "",
|
|
418
|
+
parameters: zodSchema,
|
|
419
|
+
execute: () => Promise.resolve(),
|
|
1052
420
|
};
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
throw new Error(`Agent: ${agent.name} could not be resolved`);
|
|
421
|
+
});
|
|
1056
422
|
}
|
|
1057
423
|
|
|
1058
|
-
private
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
agentSession,
|
|
1066
|
-
threadId: threadIdFromRequest,
|
|
1067
|
-
metaEvents,
|
|
1068
|
-
publicApiKey,
|
|
1069
|
-
forwardedParameters,
|
|
1070
|
-
context,
|
|
1071
|
-
} = request;
|
|
1072
|
-
const { agentName, nodeName } = agentSession;
|
|
1073
|
-
|
|
1074
|
-
// Track request start time for observability
|
|
1075
|
-
const requestStartTime = Date.now();
|
|
1076
|
-
// For storing streamed chunks if progressive logging is enabled
|
|
1077
|
-
const streamedChunks: any[] = [];
|
|
1078
|
-
|
|
1079
|
-
// for backwards compatibility, deal with the case when no threadId is provided
|
|
1080
|
-
const threadId = threadIdFromRequest ?? agentSession.threadId;
|
|
1081
|
-
|
|
1082
|
-
// Track agent request start
|
|
1083
|
-
await this.error(
|
|
1084
|
-
"agent_state",
|
|
1085
|
-
{
|
|
1086
|
-
threadId,
|
|
1087
|
-
source: "agent",
|
|
1088
|
-
request: {
|
|
1089
|
-
operation: "processAgentRequest",
|
|
1090
|
-
method: "POST",
|
|
1091
|
-
startTime: requestStartTime,
|
|
1092
|
-
},
|
|
1093
|
-
agent: {
|
|
1094
|
-
name: agentName,
|
|
1095
|
-
nodeName: nodeName,
|
|
1096
|
-
},
|
|
1097
|
-
messages: {
|
|
1098
|
-
input: rawMessages,
|
|
1099
|
-
messageCount: rawMessages.length,
|
|
1100
|
-
},
|
|
1101
|
-
technical: {
|
|
1102
|
-
environment: process.env.NODE_ENV,
|
|
1103
|
-
},
|
|
1104
|
-
},
|
|
1105
|
-
undefined,
|
|
1106
|
-
publicApiKey,
|
|
1107
|
-
);
|
|
424
|
+
private assignToolsToAgents(
|
|
425
|
+
agents: Record<string, AbstractAgent>,
|
|
426
|
+
tools: BuiltInAgentConfiguration["tools"],
|
|
427
|
+
): Record<string, AbstractAgent> {
|
|
428
|
+
if (!tools?.length) {
|
|
429
|
+
return agents;
|
|
430
|
+
}
|
|
1108
431
|
|
|
1109
|
-
const
|
|
432
|
+
const enrichedAgents: Record<string, AbstractAgent> = { ...agents };
|
|
1110
433
|
|
|
1111
|
-
const
|
|
434
|
+
for (const [agentId, agent] of Object.entries(enrichedAgents)) {
|
|
435
|
+
const existingConfig = (Reflect.get(agent, "config") ?? {}) as BuiltInAgentConfiguration;
|
|
436
|
+
const existingTools = existingConfig.tools ?? [];
|
|
1112
437
|
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
438
|
+
const updatedConfig: BuiltInAgentConfiguration = {
|
|
439
|
+
...existingConfig,
|
|
440
|
+
tools: [...existingTools, ...tools],
|
|
441
|
+
};
|
|
1116
442
|
|
|
1117
|
-
|
|
1118
|
-
|
|
443
|
+
Reflect.set(agent, "config", updatedConfig);
|
|
444
|
+
enrichedAgents[agentId] = agent;
|
|
1119
445
|
}
|
|
1120
446
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
// 2. Other agents' actions (but prevent self-calls to avoid infinite loops)
|
|
1124
|
-
const availableActionsForCurrentAgent: ActionInput[] = serverSideActions
|
|
1125
|
-
.filter(
|
|
1126
|
-
(action) =>
|
|
1127
|
-
// Case 1: Keep all regular (non-agent) actions
|
|
1128
|
-
!isRemoteAgentAction(action) ||
|
|
1129
|
-
// Case 2: For agent actions, keep all except self (prevent infinite loops)
|
|
1130
|
-
(isRemoteAgentAction(action) && action.name !== agentName) /* prevent self-calls */,
|
|
1131
|
-
)
|
|
1132
|
-
.map((action) => ({
|
|
1133
|
-
name: action.name,
|
|
1134
|
-
description: action.description,
|
|
1135
|
-
jsonSchema: JSON.stringify(actionParametersToJsonSchema(action.parameters)),
|
|
1136
|
-
}));
|
|
1137
|
-
|
|
1138
|
-
const allAvailableActions = flattenToolCallsNoDuplicates([
|
|
1139
|
-
...availableActionsForCurrentAgent,
|
|
1140
|
-
...request.actions,
|
|
1141
|
-
]);
|
|
447
|
+
return enrichedAgents;
|
|
448
|
+
}
|
|
1142
449
|
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
await this.observability.hooks.handleRequest(requestData);
|
|
1160
|
-
} catch (error) {
|
|
1161
|
-
console.error("Error logging agent request:", error);
|
|
1162
|
-
}
|
|
1163
|
-
}
|
|
450
|
+
private createOnBeforeRequestHandler(
|
|
451
|
+
params?: CopilotRuntimeConstructorParams<T> & PartialBy<CopilotRuntimeOptions, "agents">,
|
|
452
|
+
) {
|
|
453
|
+
return async (hookParams: BeforeRequestMiddlewareFnParameters[0]) => {
|
|
454
|
+
const { request } = hookParams;
|
|
455
|
+
|
|
456
|
+
// Capture telemetry for copilot request creation
|
|
457
|
+
const publicApiKey = request.headers.get("x-copilotcloud-public-api-key");
|
|
458
|
+
const body = (await readBody(request)) as RunAgentInput;
|
|
459
|
+
const forwardedProps = body.forwardedProps as
|
|
460
|
+
| {
|
|
461
|
+
cloud?: { guardrails?: unknown };
|
|
462
|
+
metadata?: { requestType?: string };
|
|
463
|
+
}
|
|
464
|
+
| undefined;
|
|
1164
465
|
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
runId: undefined,
|
|
1168
|
-
inputMessages: messages,
|
|
1169
|
-
properties: graphqlContext.properties,
|
|
1170
|
-
});
|
|
466
|
+
// Get cloud base URL from environment or default
|
|
467
|
+
const cloudBaseUrl = process.env.COPILOT_CLOUD_BASE_URL || "https://api.cloud.copilotkit.ai";
|
|
1171
468
|
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
},
|
|
1177
|
-
|
|
1178
|
-
threadId,
|
|
1179
|
-
source: "agent",
|
|
1180
|
-
request: {
|
|
1181
|
-
operation: "processAgentRequest",
|
|
1182
|
-
method: "POST",
|
|
1183
|
-
startTime: requestStartTime,
|
|
1184
|
-
},
|
|
1185
|
-
agent: {
|
|
1186
|
-
name: agentName,
|
|
1187
|
-
nodeName: nodeName,
|
|
1188
|
-
},
|
|
1189
|
-
technical: {
|
|
1190
|
-
environment: process.env.NODE_ENV,
|
|
1191
|
-
},
|
|
1192
|
-
},
|
|
1193
|
-
});
|
|
1194
|
-
const stream = await currentAgent.remoteAgentHandler({
|
|
1195
|
-
name: agentName,
|
|
1196
|
-
threadId,
|
|
1197
|
-
nodeName,
|
|
1198
|
-
metaEvents,
|
|
1199
|
-
actionInputsWithoutAgents: allAvailableActions,
|
|
469
|
+
telemetry.capture("oss.runtime.copilot_request_created", {
|
|
470
|
+
"cloud.guardrails.enabled": forwardedProps?.cloud?.guardrails !== undefined,
|
|
471
|
+
requestType: forwardedProps?.metadata?.requestType ?? "unknown",
|
|
472
|
+
"cloud.api_key_provided": !!publicApiKey,
|
|
473
|
+
...(publicApiKey ? { "cloud.public_api_key": publicApiKey } : {}),
|
|
474
|
+
"cloud.base_url": cloudBaseUrl,
|
|
1200
475
|
});
|
|
1201
476
|
|
|
1202
|
-
//
|
|
1203
|
-
if (this.observability?.enabled && this.
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
try {
|
|
1219
|
-
const progressiveData: LLMResponseData = {
|
|
1220
|
-
threadId: threadId || "",
|
|
1221
|
-
runId: undefined,
|
|
1222
|
-
model: forwardedParameters?.model,
|
|
1223
|
-
output: event.content,
|
|
1224
|
-
latency: Date.now() - requestStartTime,
|
|
1225
|
-
timestamp: Date.now(),
|
|
1226
|
-
provider: "agent",
|
|
1227
|
-
isProgressiveChunk: true,
|
|
1228
|
-
agentName,
|
|
1229
|
-
nodeName,
|
|
1230
|
-
};
|
|
1231
|
-
|
|
1232
|
-
// Use Promise to handle async logger without awaiting
|
|
1233
|
-
Promise.resolve()
|
|
1234
|
-
.then(() => {
|
|
1235
|
-
this.observability.hooks.handleResponse(progressiveData);
|
|
1236
|
-
})
|
|
1237
|
-
.catch((error) => {
|
|
1238
|
-
console.error("Error in progressive agent logging:", error);
|
|
1239
|
-
});
|
|
1240
|
-
} catch (error) {
|
|
1241
|
-
console.error("Error preparing progressive agent log data:", error);
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
},
|
|
1245
|
-
});
|
|
1246
|
-
|
|
1247
|
-
// Call the original callback with the event stream
|
|
1248
|
-
await callback(eventStream$);
|
|
1249
|
-
});
|
|
1250
|
-
};
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
eventSource.stream(async (eventStream$) => {
|
|
1254
|
-
from(stream).subscribe({
|
|
1255
|
-
next: (event) => eventStream$.next(event),
|
|
1256
|
-
error: async (err) => {
|
|
1257
|
-
// Log error with observability if enabled
|
|
1258
|
-
if (this.observability?.enabled && publicApiKey) {
|
|
1259
|
-
try {
|
|
1260
|
-
const errorData: LLMErrorData = {
|
|
1261
|
-
threadId,
|
|
1262
|
-
runId: undefined,
|
|
1263
|
-
model: forwardedParameters?.model,
|
|
1264
|
-
error: err instanceof Error ? err : String(err),
|
|
1265
|
-
timestamp: Date.now(),
|
|
1266
|
-
latency: Date.now() - requestStartTime,
|
|
1267
|
-
provider: "agent",
|
|
1268
|
-
agentName,
|
|
1269
|
-
nodeName,
|
|
1270
|
-
};
|
|
1271
|
-
|
|
1272
|
-
this.observability.hooks.handleError(errorData);
|
|
1273
|
-
} catch (logError) {
|
|
1274
|
-
console.error("Error logging agent error:", logError);
|
|
1275
|
-
}
|
|
477
|
+
// TODO: get public api key and run with expected data
|
|
478
|
+
// if (this.observability?.enabled && this.params.publicApiKey) {
|
|
479
|
+
// this.logObservabilityBeforeRequest()
|
|
480
|
+
// }
|
|
481
|
+
|
|
482
|
+
// TODO: replace hooksParams top argument type with BeforeRequestMiddlewareParameters when exported
|
|
483
|
+
params?.beforeRequestMiddleware?.(hookParams);
|
|
484
|
+
|
|
485
|
+
if (params?.middleware?.onBeforeRequest) {
|
|
486
|
+
const { request, runtime, path } = hookParams;
|
|
487
|
+
const gqlMessages = (aguiToGQL(body.messages) as Message[]).reduce(
|
|
488
|
+
(acc, msg) => {
|
|
489
|
+
if ("role" in msg && msg.role === "user") {
|
|
490
|
+
acc.inputMessages.push(msg);
|
|
491
|
+
} else {
|
|
492
|
+
acc.outputMessages.push(msg);
|
|
1276
493
|
}
|
|
1277
|
-
|
|
1278
|
-
// Preserve structured CopilotKit errors, only convert unstructured errors
|
|
1279
|
-
const structuredError = ensureStructuredError(err, (error) =>
|
|
1280
|
-
this.convertStreamingErrorToStructured(error),
|
|
1281
|
-
);
|
|
1282
|
-
|
|
1283
|
-
// Track streaming errors
|
|
1284
|
-
await this.error(
|
|
1285
|
-
"error",
|
|
1286
|
-
{
|
|
1287
|
-
threadId,
|
|
1288
|
-
source: "agent",
|
|
1289
|
-
request: {
|
|
1290
|
-
operation: "processAgentRequest",
|
|
1291
|
-
method: "POST",
|
|
1292
|
-
startTime: requestStartTime,
|
|
1293
|
-
},
|
|
1294
|
-
response: {
|
|
1295
|
-
endTime: Date.now(),
|
|
1296
|
-
latency: Date.now() - requestStartTime,
|
|
1297
|
-
},
|
|
1298
|
-
agent: {
|
|
1299
|
-
name: agentName,
|
|
1300
|
-
nodeName: nodeName,
|
|
1301
|
-
},
|
|
1302
|
-
technical: {
|
|
1303
|
-
environment: process.env.NODE_ENV,
|
|
1304
|
-
stackTrace: err instanceof Error ? err.stack : undefined,
|
|
1305
|
-
},
|
|
1306
|
-
},
|
|
1307
|
-
structuredError,
|
|
1308
|
-
publicApiKey,
|
|
1309
|
-
);
|
|
1310
|
-
|
|
1311
|
-
eventStream$.error(structuredError);
|
|
1312
|
-
eventStream$.complete();
|
|
494
|
+
return acc;
|
|
1313
495
|
},
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
.
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
runId: undefined,
|
|
1325
|
-
model: forwardedParameters?.model,
|
|
1326
|
-
// Use collected chunks for progressive mode or outputMessages for regular mode
|
|
1327
|
-
output: this.observability.progressive ? streamedChunks : outputMessages,
|
|
1328
|
-
latency: Date.now() - requestStartTime,
|
|
1329
|
-
timestamp: Date.now(),
|
|
1330
|
-
provider: "agent",
|
|
1331
|
-
isFinalResponse: true,
|
|
1332
|
-
agentName,
|
|
1333
|
-
nodeName,
|
|
1334
|
-
};
|
|
1335
|
-
|
|
1336
|
-
try {
|
|
1337
|
-
this.observability.hooks.handleResponse(responseData);
|
|
1338
|
-
} catch (logError) {
|
|
1339
|
-
console.error("Error logging agent response:", logError);
|
|
1340
|
-
}
|
|
1341
|
-
})
|
|
1342
|
-
.catch((error) => {
|
|
1343
|
-
console.error("Failed to get output messages for agent logging:", error);
|
|
1344
|
-
});
|
|
496
|
+
{ inputMessages: [] as Message[], outputMessages: [] as Message[] },
|
|
497
|
+
);
|
|
498
|
+
const { inputMessages, outputMessages } = gqlMessages;
|
|
499
|
+
params.middleware.onBeforeRequest({
|
|
500
|
+
threadId: body.threadId,
|
|
501
|
+
runId: body.runId,
|
|
502
|
+
inputMessages,
|
|
503
|
+
properties: body.forwardedProps,
|
|
504
|
+
url: request.url,
|
|
505
|
+
} satisfies OnBeforeRequestOptions);
|
|
1345
506
|
}
|
|
507
|
+
};
|
|
508
|
+
}
|
|
1346
509
|
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
serverSideActions,
|
|
1364
|
-
actionInputsWithoutAgents: allAvailableActions,
|
|
1365
|
-
};
|
|
1366
|
-
} catch (error) {
|
|
1367
|
-
// Log error with observability if enabled
|
|
1368
|
-
if (this.observability?.enabled && publicApiKey) {
|
|
1369
|
-
try {
|
|
1370
|
-
const errorData: LLMErrorData = {
|
|
1371
|
-
threadId,
|
|
1372
|
-
runId: undefined,
|
|
1373
|
-
model: forwardedParameters?.model,
|
|
1374
|
-
error: error instanceof Error ? error : String(error),
|
|
1375
|
-
timestamp: Date.now(),
|
|
1376
|
-
latency: Date.now() - requestStartTime,
|
|
1377
|
-
provider: "agent",
|
|
1378
|
-
agentName,
|
|
1379
|
-
nodeName,
|
|
1380
|
-
};
|
|
1381
|
-
|
|
1382
|
-
await this.observability.hooks.handleError(errorData);
|
|
1383
|
-
} catch (logError) {
|
|
1384
|
-
console.error("Error logging agent error:", logError);
|
|
1385
|
-
}
|
|
510
|
+
private createOnAfterRequestHandler(
|
|
511
|
+
params?: CopilotRuntimeConstructorParams<T> & PartialBy<CopilotRuntimeOptions, "agents">,
|
|
512
|
+
) {
|
|
513
|
+
return async (hookParams: AfterRequestMiddlewareFnParameters[0]) => {
|
|
514
|
+
// TODO: get public api key and run with expected data
|
|
515
|
+
// if (this.observability?.enabled && publicApiKey) {
|
|
516
|
+
// this.logObservabilityAfterRequest()
|
|
517
|
+
// }
|
|
518
|
+
|
|
519
|
+
// TODO: replace hooksParams top argument type with AfterRequestMiddlewareParameters when exported
|
|
520
|
+
params?.afterRequestMiddleware?.(hookParams);
|
|
521
|
+
|
|
522
|
+
if (params?.middleware?.onAfterRequest) {
|
|
523
|
+
// TODO: provide old expected params here when available
|
|
524
|
+
// @ts-expect-error -- missing arguments.
|
|
525
|
+
params.middleware.onAfterRequest({});
|
|
1386
526
|
}
|
|
1387
|
-
|
|
1388
|
-
// Ensure error is structured
|
|
1389
|
-
const structuredError = ensureStructuredError(error, (err) =>
|
|
1390
|
-
this.convertStreamingErrorToStructured(err),
|
|
1391
|
-
);
|
|
1392
|
-
|
|
1393
|
-
// Track the agent error
|
|
1394
|
-
await this.error(
|
|
1395
|
-
"error",
|
|
1396
|
-
{
|
|
1397
|
-
threadId,
|
|
1398
|
-
source: "agent",
|
|
1399
|
-
request: {
|
|
1400
|
-
operation: "processAgentRequest",
|
|
1401
|
-
method: "POST",
|
|
1402
|
-
startTime: requestStartTime,
|
|
1403
|
-
},
|
|
1404
|
-
response: {
|
|
1405
|
-
endTime: Date.now(),
|
|
1406
|
-
latency: Date.now() - requestStartTime,
|
|
1407
|
-
},
|
|
1408
|
-
agent: {
|
|
1409
|
-
name: agentName,
|
|
1410
|
-
nodeName: nodeName,
|
|
1411
|
-
},
|
|
1412
|
-
technical: {
|
|
1413
|
-
environment: process.env.NODE_ENV,
|
|
1414
|
-
stackTrace: error instanceof Error ? error.stack : undefined,
|
|
1415
|
-
},
|
|
1416
|
-
},
|
|
1417
|
-
structuredError,
|
|
1418
|
-
publicApiKey,
|
|
1419
|
-
);
|
|
1420
|
-
|
|
1421
|
-
console.error("Error getting response:", error);
|
|
1422
|
-
throw structuredError;
|
|
1423
|
-
}
|
|
527
|
+
};
|
|
1424
528
|
}
|
|
1425
529
|
|
|
1426
|
-
|
|
1427
|
-
const { graphqlContext, messages: rawMessages, agentStates, url } = request;
|
|
530
|
+
// Observability Methods
|
|
1428
531
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
console.error("Error loading langserve chain:", error);
|
|
1438
|
-
}
|
|
532
|
+
/**
|
|
533
|
+
* Log LLM request if observability is enabled
|
|
534
|
+
*/
|
|
535
|
+
private async logObservabilityBeforeRequest(requestData: LLMRequestData): Promise<void> {
|
|
536
|
+
try {
|
|
537
|
+
await this.observability.hooks.handleRequest(requestData);
|
|
538
|
+
} catch (error) {
|
|
539
|
+
console.error("Error logging LLM request:", error);
|
|
1439
540
|
}
|
|
541
|
+
}
|
|
1440
542
|
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
const effectiveEndpointsMap = new Map<string, MCPEndpointConfig>();
|
|
1475
|
-
|
|
1476
|
-
// First add base endpoints (from runtime configuration)
|
|
1477
|
-
[...baseEndpoints].forEach((ep) => {
|
|
1478
|
-
if (ep && ep.endpoint) {
|
|
1479
|
-
effectiveEndpointsMap.set(ep.endpoint, ep);
|
|
1480
|
-
}
|
|
1481
|
-
});
|
|
1482
|
-
|
|
1483
|
-
// Then add request endpoints (from frontend), which will override duplicates
|
|
1484
|
-
[...requestEndpoints].forEach((ep) => {
|
|
1485
|
-
if (ep && ep.endpoint) {
|
|
1486
|
-
effectiveEndpointsMap.set(ep.endpoint, ep);
|
|
1487
|
-
}
|
|
1488
|
-
});
|
|
1489
|
-
|
|
1490
|
-
const effectiveEndpoints = Array.from(effectiveEndpointsMap.values());
|
|
1491
|
-
|
|
1492
|
-
// 2. Fetch/Cache actions for effective endpoints
|
|
1493
|
-
for (const config of effectiveEndpoints) {
|
|
1494
|
-
const endpointUrl = config.endpoint;
|
|
1495
|
-
let actionsForEndpoint: Action<any>[] | undefined = this.mcpActionCache.get(endpointUrl);
|
|
543
|
+
/**
|
|
544
|
+
* Log final LLM response after request completes
|
|
545
|
+
*/
|
|
546
|
+
private logObservabilityAfterRequest(
|
|
547
|
+
outputMessagesPromise: Promise<Message[]>,
|
|
548
|
+
baseData: {
|
|
549
|
+
threadId: string;
|
|
550
|
+
runId?: string;
|
|
551
|
+
model?: string;
|
|
552
|
+
provider?: string;
|
|
553
|
+
agentName?: string;
|
|
554
|
+
nodeName?: string;
|
|
555
|
+
},
|
|
556
|
+
streamedChunks: any[],
|
|
557
|
+
requestStartTime: number,
|
|
558
|
+
publicApiKey?: string,
|
|
559
|
+
): void {
|
|
560
|
+
try {
|
|
561
|
+
outputMessagesPromise
|
|
562
|
+
.then((outputMessages) => {
|
|
563
|
+
const responseData: LLMResponseData = {
|
|
564
|
+
threadId: baseData.threadId,
|
|
565
|
+
runId: baseData.runId,
|
|
566
|
+
model: baseData.model,
|
|
567
|
+
// Use collected chunks for progressive mode or outputMessages for regular mode
|
|
568
|
+
output: this.observability.progressive ? streamedChunks : outputMessages,
|
|
569
|
+
latency: Date.now() - requestStartTime,
|
|
570
|
+
timestamp: Date.now(),
|
|
571
|
+
provider: baseData.provider,
|
|
572
|
+
isFinalResponse: true,
|
|
573
|
+
agentName: baseData.agentName,
|
|
574
|
+
nodeName: baseData.nodeName,
|
|
575
|
+
};
|
|
1496
576
|
|
|
1497
|
-
if (!actionsForEndpoint) {
|
|
1498
|
-
// Not cached, fetch now
|
|
1499
|
-
let client: MCPClient | null = null;
|
|
1500
577
|
try {
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
this.mcpActionCache.set(endpointUrl, actionsForEndpoint); // Store in cache
|
|
1505
|
-
} catch (error) {
|
|
1506
|
-
console.error(
|
|
1507
|
-
`MCP: Failed to fetch tools from endpoint ${endpointUrl}. Skipping. Error:`,
|
|
1508
|
-
error,
|
|
1509
|
-
);
|
|
1510
|
-
actionsForEndpoint = []; // Assign empty array on error to prevent re-fetching constantly
|
|
1511
|
-
this.mcpActionCache.set(endpointUrl, actionsForEndpoint); // Cache the failure (empty array)
|
|
578
|
+
this.observability.hooks.handleResponse(responseData);
|
|
579
|
+
} catch (logError) {
|
|
580
|
+
console.error("Error logging LLM response:", logError);
|
|
1512
581
|
}
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
|
|
582
|
+
})
|
|
583
|
+
.catch((error) => {
|
|
584
|
+
console.error("Failed to get output messages for logging:", error);
|
|
585
|
+
});
|
|
586
|
+
} catch (error) {
|
|
587
|
+
console.error("Error setting up logging for LLM response:", error);
|
|
1516
588
|
}
|
|
1517
|
-
// --- Dynamic MCP Action Fetching ---
|
|
1518
|
-
|
|
1519
|
-
// Combine all action sources, including the dynamically fetched MCP actions
|
|
1520
|
-
return [
|
|
1521
|
-
...configuredActions,
|
|
1522
|
-
...langserveFunctions,
|
|
1523
|
-
...remoteActions,
|
|
1524
|
-
...requestSpecificMCPActions,
|
|
1525
|
-
];
|
|
1526
589
|
}
|
|
1527
590
|
|
|
1528
|
-
//
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
591
|
+
// Resolve MCP tools to BuiltInAgent tool definitions
|
|
592
|
+
// Optionally accepts request-scoped properties to merge request-provided mcpServers
|
|
593
|
+
private async getToolsFromMCP(options?: {
|
|
594
|
+
properties?: Record<string, unknown>;
|
|
595
|
+
}): Promise<BuiltInAgentConfiguration["tools"]> {
|
|
596
|
+
const runtimeMcpServers = (this.params?.mcpServers ?? []) as MCPEndpointConfig[];
|
|
597
|
+
const createMCPClient = this.params?.createMCPClient as CreateMCPClientFunction | undefined;
|
|
598
|
+
|
|
599
|
+
// If no runtime config and no request overrides, nothing to do
|
|
600
|
+
const requestMcpServers = ((
|
|
601
|
+
options?.properties as { mcpServers?: MCPEndpointConfig[] } | undefined
|
|
602
|
+
)?.mcpServers ??
|
|
603
|
+
(options?.properties as { mcpEndpoints?: MCPEndpointConfig[] } | undefined)?.mcpEndpoints ??
|
|
604
|
+
[]) as MCPEndpointConfig[];
|
|
605
|
+
|
|
606
|
+
const hasAnyServers =
|
|
607
|
+
(runtimeMcpServers?.length ?? 0) > 0 || (requestMcpServers?.length ?? 0) > 0;
|
|
608
|
+
if (!hasAnyServers) {
|
|
609
|
+
return [];
|
|
610
|
+
}
|
|
1542
611
|
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
error?.message?.includes("ETIMEDOUT") ||
|
|
1549
|
-
error?.message?.includes("terminated") ||
|
|
1550
|
-
error?.cause?.code === "UND_ERR_SOCKET" ||
|
|
1551
|
-
error?.message?.includes("other side closed") ||
|
|
1552
|
-
error?.code === "UND_ERR_SOCKET"
|
|
1553
|
-
) {
|
|
1554
|
-
return new CopilotKitLowLevelError({
|
|
1555
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
1556
|
-
url: "agent streaming connection",
|
|
1557
|
-
message: helpfulMessage,
|
|
612
|
+
if (!createMCPClient) {
|
|
613
|
+
// Mirror legacy behavior: when servers are provided without a factory, treat as misconfiguration
|
|
614
|
+
throw new CopilotKitMisuseError({
|
|
615
|
+
message:
|
|
616
|
+
"MCP Integration Error: `mcpServers` were provided, but the `createMCPClient` function was not passed to the CopilotRuntime constructor. Please provide an implementation for `createMCPClient`.",
|
|
1558
617
|
});
|
|
1559
618
|
}
|
|
1560
619
|
|
|
1561
|
-
//
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
this.hasWarnedAboutError = true;
|
|
620
|
+
// Merge and dedupe endpoints by URL; request-level overrides take precedence
|
|
621
|
+
const effectiveEndpoints = (() => {
|
|
622
|
+
const byUrl = new Map<string, MCPEndpointConfig>();
|
|
623
|
+
for (const ep of runtimeMcpServers) {
|
|
624
|
+
if (ep?.endpoint) byUrl.set(ep.endpoint, ep);
|
|
625
|
+
}
|
|
626
|
+
for (const ep of requestMcpServers) {
|
|
627
|
+
if (ep?.endpoint) byUrl.set(ep.endpoint, ep);
|
|
628
|
+
}
|
|
629
|
+
return Array.from(byUrl.values());
|
|
630
|
+
})();
|
|
631
|
+
|
|
632
|
+
const allTools: BuiltInAgentConfiguration["tools"] = [];
|
|
633
|
+
|
|
634
|
+
for (const config of effectiveEndpoints) {
|
|
635
|
+
const endpointUrl = config.endpoint;
|
|
636
|
+
// Return cached tool definitions when available
|
|
637
|
+
const cached = this.mcpToolsCache.get(endpointUrl);
|
|
638
|
+
if (cached) {
|
|
639
|
+
allTools.push(...cached);
|
|
640
|
+
continue;
|
|
1583
641
|
}
|
|
1584
|
-
return;
|
|
1585
|
-
}
|
|
1586
642
|
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
643
|
+
try {
|
|
644
|
+
const client = await createMCPClient(config);
|
|
645
|
+
const toolsMap = await client.tools();
|
|
646
|
+
|
|
647
|
+
const toolDefs: BuiltInAgentConfiguration["tools"] = Object.entries(toolsMap).map(
|
|
648
|
+
([toolName, tool]: [string, MCPTool]) => {
|
|
649
|
+
const params: Parameter[] = extractParametersFromSchema(tool);
|
|
650
|
+
const zodSchema = getZodParameters(params);
|
|
651
|
+
return {
|
|
652
|
+
name: toolName,
|
|
653
|
+
description: tool.description || `MCP tool: ${toolName} (from ${endpointUrl})`,
|
|
654
|
+
parameters: zodSchema,
|
|
655
|
+
execute: () => Promise.resolve(),
|
|
656
|
+
};
|
|
657
|
+
},
|
|
658
|
+
);
|
|
1594
659
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
660
|
+
// Cache per endpoint and add to aggregate
|
|
661
|
+
this.mcpToolsCache.set(endpointUrl, toolDefs);
|
|
662
|
+
allTools.push(...toolDefs);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
console.error(
|
|
665
|
+
`MCP: Failed to fetch tools from endpoint ${endpointUrl}. Skipping. Error:`,
|
|
666
|
+
error,
|
|
667
|
+
);
|
|
668
|
+
// Cache empty to prevent repeated attempts within lifecycle
|
|
669
|
+
this.mcpToolsCache.set(endpointUrl, []);
|
|
670
|
+
}
|
|
1599
671
|
}
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
/**
|
|
1603
|
-
* Public method to handle GraphQL validation errors
|
|
1604
|
-
* This allows the GraphQL resolver to send validation errors through the error system
|
|
1605
|
-
*/
|
|
1606
|
-
public async errorGraphQLError(
|
|
1607
|
-
error: { message: string; code: string; type: string },
|
|
1608
|
-
context: {
|
|
1609
|
-
operation: string;
|
|
1610
|
-
cloudConfigPresent: boolean;
|
|
1611
|
-
guardrailsEnabled: boolean;
|
|
1612
|
-
},
|
|
1613
|
-
): Promise<void> {
|
|
1614
|
-
if (!this.onError) return;
|
|
1615
672
|
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
context: {
|
|
1621
|
-
source: "runtime",
|
|
1622
|
-
request: {
|
|
1623
|
-
operation: context.operation,
|
|
1624
|
-
startTime: Date.now(),
|
|
1625
|
-
},
|
|
1626
|
-
technical: {
|
|
1627
|
-
environment: process.env.NODE_ENV,
|
|
1628
|
-
},
|
|
1629
|
-
metadata: {
|
|
1630
|
-
errorType: "GraphQLValidationError",
|
|
1631
|
-
cloudConfigPresent: context.cloudConfigPresent,
|
|
1632
|
-
guardrailsEnabled: context.guardrailsEnabled,
|
|
1633
|
-
},
|
|
1634
|
-
},
|
|
1635
|
-
error,
|
|
1636
|
-
});
|
|
1637
|
-
} catch (errorHandlerError) {
|
|
1638
|
-
// Don't let error handler errors break the main flow
|
|
1639
|
-
console.error("Error in onError handler:", errorHandlerError);
|
|
673
|
+
// Dedupe tools by name while preserving last-in wins (request overrides)
|
|
674
|
+
const dedupedByName = new Map<string, (typeof allTools)[number]>();
|
|
675
|
+
for (const tool of allTools) {
|
|
676
|
+
dedupedByName.set(tool.name, tool);
|
|
1640
677
|
}
|
|
1641
|
-
}
|
|
1642
|
-
}
|
|
1643
678
|
|
|
1644
|
-
|
|
1645
|
-
let allTools: ActionInput[] = [];
|
|
1646
|
-
const allToolNames: string[] = [];
|
|
1647
|
-
for (const tool of toolsByPriority) {
|
|
1648
|
-
if (!allToolNames.includes(tool.name)) {
|
|
1649
|
-
allTools.push(tool);
|
|
1650
|
-
allToolNames.push(tool.name);
|
|
1651
|
-
}
|
|
679
|
+
return Array.from(dedupedByName.values());
|
|
1652
680
|
}
|
|
1653
|
-
return allTools;
|
|
1654
681
|
}
|
|
1655
682
|
|
|
1656
683
|
// The two functions below are "factory functions", meant to create the action objects that adhere to the expected interfaces
|