@copilotkit/runtime 0.0.0-feat-dynamic-copilotcloud-qa-20250117190454

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/.eslintrc.js +7 -0
  2. package/CHANGELOG.md +913 -0
  3. package/README.md +46 -0
  4. package/__snapshots__/schema/schema.graphql +273 -0
  5. package/dist/chunk-44O2JGUY.mjs +12 -0
  6. package/dist/chunk-44O2JGUY.mjs.map +1 -0
  7. package/dist/chunk-BETLEV37.mjs +25 -0
  8. package/dist/chunk-BETLEV37.mjs.map +1 -0
  9. package/dist/chunk-CLGKEUOA.mjs +1408 -0
  10. package/dist/chunk-CLGKEUOA.mjs.map +1 -0
  11. package/dist/chunk-D2WLFQS6.mjs +43 -0
  12. package/dist/chunk-D2WLFQS6.mjs.map +1 -0
  13. package/dist/chunk-DFOKBSIS.mjs +1 -0
  14. package/dist/chunk-DFOKBSIS.mjs.map +1 -0
  15. package/dist/chunk-FA5DJ2TZ.mjs +3437 -0
  16. package/dist/chunk-FA5DJ2TZ.mjs.map +1 -0
  17. package/dist/chunk-HNUNXFTW.mjs +129 -0
  18. package/dist/chunk-HNUNXFTW.mjs.map +1 -0
  19. package/dist/chunk-SFLMY3ES.mjs +80 -0
  20. package/dist/chunk-SFLMY3ES.mjs.map +1 -0
  21. package/dist/chunk-U3V2BCGI.mjs +152 -0
  22. package/dist/chunk-U3V2BCGI.mjs.map +1 -0
  23. package/dist/chunk-ZCU6UPCY.mjs +25 -0
  24. package/dist/chunk-ZCU6UPCY.mjs.map +1 -0
  25. package/dist/copilot-runtime-1a224a0f.d.ts +196 -0
  26. package/dist/graphql/types/base/index.d.ts +6 -0
  27. package/dist/graphql/types/base/index.js +63 -0
  28. package/dist/graphql/types/base/index.js.map +1 -0
  29. package/dist/graphql/types/base/index.mjs +8 -0
  30. package/dist/graphql/types/base/index.mjs.map +1 -0
  31. package/dist/graphql/types/converted/index.d.ts +2 -0
  32. package/dist/graphql/types/converted/index.js +187 -0
  33. package/dist/graphql/types/converted/index.js.map +1 -0
  34. package/dist/graphql/types/converted/index.mjs +17 -0
  35. package/dist/graphql/types/converted/index.mjs.map +1 -0
  36. package/dist/groq-adapter-c35c5374.d.ts +281 -0
  37. package/dist/index-24315d90.d.ts +103 -0
  38. package/dist/index.d.ts +23 -0
  39. package/dist/index.js +5258 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/index.mjs +76 -0
  42. package/dist/index.mjs.map +1 -0
  43. package/dist/langserve-a16ef8f4.d.ts +180 -0
  44. package/dist/lib/cloud/index.d.ts +6 -0
  45. package/dist/lib/cloud/index.js +18 -0
  46. package/dist/lib/cloud/index.js.map +1 -0
  47. package/dist/lib/cloud/index.mjs +1 -0
  48. package/dist/lib/cloud/index.mjs.map +1 -0
  49. package/dist/lib/index.d.ts +20 -0
  50. package/dist/lib/index.js +4906 -0
  51. package/dist/lib/index.js.map +1 -0
  52. package/dist/lib/index.mjs +58 -0
  53. package/dist/lib/index.mjs.map +1 -0
  54. package/dist/lib/integrations/index.d.ts +33 -0
  55. package/dist/lib/integrations/index.js +2229 -0
  56. package/dist/lib/integrations/index.js.map +1 -0
  57. package/dist/lib/integrations/index.mjs +34 -0
  58. package/dist/lib/integrations/index.mjs.map +1 -0
  59. package/dist/lib/integrations/nest/index.d.ts +14 -0
  60. package/dist/lib/integrations/nest/index.js +2138 -0
  61. package/dist/lib/integrations/nest/index.js.map +1 -0
  62. package/dist/lib/integrations/nest/index.mjs +13 -0
  63. package/dist/lib/integrations/nest/index.mjs.map +1 -0
  64. package/dist/lib/integrations/node-express/index.d.ts +14 -0
  65. package/dist/lib/integrations/node-express/index.js +2138 -0
  66. package/dist/lib/integrations/node-express/index.js.map +1 -0
  67. package/dist/lib/integrations/node-express/index.mjs +13 -0
  68. package/dist/lib/integrations/node-express/index.mjs.map +1 -0
  69. package/dist/lib/integrations/node-http/index.d.ts +14 -0
  70. package/dist/lib/integrations/node-http/index.js +2124 -0
  71. package/dist/lib/integrations/node-http/index.js.map +1 -0
  72. package/dist/lib/integrations/node-http/index.mjs +12 -0
  73. package/dist/lib/integrations/node-http/index.mjs.map +1 -0
  74. package/dist/service-adapters/index.d.ts +84 -0
  75. package/dist/service-adapters/index.js +1448 -0
  76. package/dist/service-adapters/index.js.map +1 -0
  77. package/dist/service-adapters/index.mjs +26 -0
  78. package/dist/service-adapters/index.mjs.map +1 -0
  79. package/dist/utils/index.d.ts +49 -0
  80. package/dist/utils/index.js +174 -0
  81. package/dist/utils/index.js.map +1 -0
  82. package/dist/utils/index.mjs +12 -0
  83. package/dist/utils/index.mjs.map +1 -0
  84. package/jest.config.js +5 -0
  85. package/package.json +85 -0
  86. package/scripts/generate-gql-schema.ts +13 -0
  87. package/src/agents/langgraph/event-source.ts +287 -0
  88. package/src/agents/langgraph/events.ts +338 -0
  89. package/src/graphql/inputs/action.input.ts +16 -0
  90. package/src/graphql/inputs/agent-session.input.ts +13 -0
  91. package/src/graphql/inputs/agent-state.input.ts +10 -0
  92. package/src/graphql/inputs/cloud-guardrails.input.ts +16 -0
  93. package/src/graphql/inputs/cloud.input.ts +8 -0
  94. package/src/graphql/inputs/context-property.input.ts +10 -0
  95. package/src/graphql/inputs/custom-property.input.ts +15 -0
  96. package/src/graphql/inputs/forwarded-parameters.input.ts +22 -0
  97. package/src/graphql/inputs/frontend.input.ts +14 -0
  98. package/src/graphql/inputs/generate-copilot-response.input.ts +47 -0
  99. package/src/graphql/inputs/message.input.ts +92 -0
  100. package/src/graphql/resolvers/copilot.resolver.ts +556 -0
  101. package/src/graphql/types/agents-response.type.ts +22 -0
  102. package/src/graphql/types/base/index.ts +10 -0
  103. package/src/graphql/types/converted/index.ts +136 -0
  104. package/src/graphql/types/copilot-response.type.ts +113 -0
  105. package/src/graphql/types/enums.ts +37 -0
  106. package/src/graphql/types/guardrails-result.type.ts +20 -0
  107. package/src/graphql/types/message-status.type.ts +40 -0
  108. package/src/graphql/types/response-status.type.ts +66 -0
  109. package/src/index.ts +4 -0
  110. package/src/lib/cloud/index.ts +4 -0
  111. package/src/lib/index.ts +8 -0
  112. package/src/lib/integrations/index.ts +6 -0
  113. package/src/lib/integrations/nest/index.ts +17 -0
  114. package/src/lib/integrations/nextjs/app-router.ts +40 -0
  115. package/src/lib/integrations/nextjs/pages-router.ts +49 -0
  116. package/src/lib/integrations/node-express/index.ts +17 -0
  117. package/src/lib/integrations/node-http/index.ts +34 -0
  118. package/src/lib/integrations/shared.ts +109 -0
  119. package/src/lib/logger.ts +28 -0
  120. package/src/lib/runtime/copilot-runtime.ts +466 -0
  121. package/src/lib/runtime/remote-action-constructors.ts +304 -0
  122. package/src/lib/runtime/remote-actions.ts +174 -0
  123. package/src/lib/runtime/remote-lg-action.ts +657 -0
  124. package/src/lib/telemetry-client.ts +52 -0
  125. package/src/service-adapters/anthropic/anthropic-adapter.ts +205 -0
  126. package/src/service-adapters/anthropic/utils.ts +144 -0
  127. package/src/service-adapters/conversion.ts +64 -0
  128. package/src/service-adapters/events.ts +419 -0
  129. package/src/service-adapters/experimental/empty/empty-adapter.ts +33 -0
  130. package/src/service-adapters/experimental/ollama/ollama-adapter.ts +79 -0
  131. package/src/service-adapters/google/google-genai-adapter.ts +39 -0
  132. package/src/service-adapters/groq/groq-adapter.ts +173 -0
  133. package/src/service-adapters/index.ts +16 -0
  134. package/src/service-adapters/langchain/langchain-adapter.ts +99 -0
  135. package/src/service-adapters/langchain/langserve.ts +87 -0
  136. package/src/service-adapters/langchain/types.ts +14 -0
  137. package/src/service-adapters/langchain/utils.ts +306 -0
  138. package/src/service-adapters/openai/openai-adapter.ts +210 -0
  139. package/src/service-adapters/openai/openai-assistant-adapter.ts +304 -0
  140. package/src/service-adapters/openai/utils.ts +161 -0
  141. package/src/service-adapters/service-adapter.ts +30 -0
  142. package/src/service-adapters/unify/unify-adapter.ts +145 -0
  143. package/src/utils/failed-response-status-reasons.ts +48 -0
  144. package/src/utils/index.ts +1 -0
  145. package/tsconfig.json +11 -0
  146. package/tsup.config.ts +16 -0
  147. package/typedoc.json +4 -0
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Copilot Runtime adapter for Groq.
3
+ *
4
+ * ## Example
5
+ *
6
+ * ```ts
7
+ * import { CopilotRuntime, GroqAdapter } from "@copilotkit/runtime";
8
+ * import { Groq } from "groq-sdk";
9
+ *
10
+ * const groq = new Groq({ apiKey: process.env["GROQ_API_KEY"] });
11
+ *
12
+ * const copilotKit = new CopilotRuntime();
13
+ *
14
+ * return new GroqAdapter({ groq, model: "<model-name>" });
15
+ * ```
16
+ */
17
+ import { Groq } from "groq-sdk";
18
+ import {
19
+ CopilotServiceAdapter,
20
+ CopilotRuntimeChatCompletionRequest,
21
+ CopilotRuntimeChatCompletionResponse,
22
+ } from "../service-adapter";
23
+ import {
24
+ convertActionInputToOpenAITool,
25
+ convertMessageToOpenAIMessage,
26
+ limitMessagesToTokenCount,
27
+ } from "../openai/utils";
28
+ import { randomId } from "@copilotkit/shared";
29
+
30
+ const DEFAULT_MODEL = "llama3-groq-70b-8192-tool-use-preview";
31
+
32
+ export interface GroqAdapterParams {
33
+ /**
34
+ * An optional Groq instance to use.
35
+ */
36
+ groq?: Groq;
37
+
38
+ /**
39
+ * The model to use.
40
+ */
41
+ model?: string;
42
+
43
+ /**
44
+ * Whether to disable parallel tool calls.
45
+ * You can disable parallel tool calls to force the model to execute tool calls sequentially.
46
+ * This is useful if you want to execute tool calls in a specific order so that the state changes
47
+ * introduced by one tool call are visible to the next tool call. (i.e. new actions or readables)
48
+ *
49
+ * @default false
50
+ */
51
+ disableParallelToolCalls?: boolean;
52
+ }
53
+
54
+ export class GroqAdapter implements CopilotServiceAdapter {
55
+ private model: string = DEFAULT_MODEL;
56
+
57
+ private disableParallelToolCalls: boolean = false;
58
+ private _groq: Groq;
59
+ public get groq(): Groq {
60
+ return this._groq;
61
+ }
62
+
63
+ constructor(params?: GroqAdapterParams) {
64
+ this._groq = params?.groq || new Groq({});
65
+ if (params?.model) {
66
+ this.model = params.model;
67
+ }
68
+ this.disableParallelToolCalls = params?.disableParallelToolCalls || false;
69
+ }
70
+
71
+ async process(
72
+ request: CopilotRuntimeChatCompletionRequest,
73
+ ): Promise<CopilotRuntimeChatCompletionResponse> {
74
+ const {
75
+ threadId,
76
+ model = this.model,
77
+ messages,
78
+ actions,
79
+ eventSource,
80
+ forwardedParameters,
81
+ } = request;
82
+ const tools = actions.map(convertActionInputToOpenAITool);
83
+
84
+ let openaiMessages = messages.map(convertMessageToOpenAIMessage);
85
+ openaiMessages = limitMessagesToTokenCount(openaiMessages, tools, model);
86
+
87
+ let toolChoice: any = forwardedParameters?.toolChoice;
88
+ if (forwardedParameters?.toolChoice === "function") {
89
+ toolChoice = {
90
+ type: "function",
91
+ function: { name: forwardedParameters.toolChoiceFunctionName },
92
+ };
93
+ }
94
+ const stream = await this.groq.chat.completions.create({
95
+ model: model,
96
+ stream: true,
97
+ messages: openaiMessages,
98
+ ...(tools.length > 0 && { tools }),
99
+ ...(forwardedParameters?.maxTokens && {
100
+ max_tokens: forwardedParameters.maxTokens,
101
+ }),
102
+ ...(forwardedParameters?.stop && { stop: forwardedParameters.stop }),
103
+ ...(toolChoice && { tool_choice: toolChoice }),
104
+ ...(this.disableParallelToolCalls && { parallel_tool_calls: false }),
105
+ ...(forwardedParameters?.temperature && { temperature: forwardedParameters.temperature }),
106
+ });
107
+
108
+ eventSource.stream(async (eventStream$) => {
109
+ let mode: "function" | "message" | null = null;
110
+ let currentMessageId: string;
111
+ let currentToolCallId: string;
112
+
113
+ for await (const chunk of stream) {
114
+ const toolCall = chunk.choices[0].delta.tool_calls?.[0];
115
+ const content = chunk.choices[0].delta.content;
116
+
117
+ // When switching from message to function or vice versa,
118
+ // send the respective end event.
119
+ // If toolCall?.id is defined, it means a new tool call starts.
120
+ if (mode === "message" && toolCall?.id) {
121
+ mode = null;
122
+ eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
123
+ } else if (mode === "function" && (toolCall === undefined || toolCall?.id)) {
124
+ mode = null;
125
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
126
+ }
127
+
128
+ // If we send a new message type, send the appropriate start event.
129
+ if (mode === null) {
130
+ if (toolCall?.id) {
131
+ mode = "function";
132
+ currentToolCallId = toolCall!.id;
133
+ eventStream$.sendActionExecutionStart({
134
+ actionExecutionId: currentToolCallId,
135
+ actionName: toolCall!.function!.name,
136
+ parentMessageId: chunk.id,
137
+ });
138
+ } else if (content) {
139
+ mode = "message";
140
+ currentMessageId = chunk.id;
141
+ eventStream$.sendTextMessageStart({ messageId: currentMessageId });
142
+ }
143
+ }
144
+
145
+ // send the content events
146
+ if (mode === "message" && content) {
147
+ eventStream$.sendTextMessageContent({
148
+ messageId: currentMessageId,
149
+ content,
150
+ });
151
+ } else if (mode === "function" && toolCall?.function?.arguments) {
152
+ eventStream$.sendActionExecutionArgs({
153
+ actionExecutionId: currentToolCallId,
154
+ args: toolCall.function.arguments,
155
+ });
156
+ }
157
+ }
158
+
159
+ // send the end events
160
+ if (mode === "message") {
161
+ eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
162
+ } else if (mode === "function") {
163
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: currentToolCallId });
164
+ }
165
+
166
+ eventStream$.complete();
167
+ });
168
+
169
+ return {
170
+ threadId: threadId || randomId(),
171
+ };
172
+ }
173
+ }
@@ -0,0 +1,16 @@
1
+ export type {
2
+ CopilotRuntimeChatCompletionRequest,
3
+ CopilotRuntimeChatCompletionResponse,
4
+ CopilotServiceAdapter,
5
+ } from "./service-adapter";
6
+ export type { RemoteChainParameters } from "./langchain/langserve";
7
+ export { RemoteChain } from "./langchain/langserve";
8
+ export * from "./openai/openai-adapter";
9
+ export * from "./langchain/langchain-adapter";
10
+ export * from "./google/google-genai-adapter";
11
+ export * from "./openai/openai-assistant-adapter";
12
+ export * from "./unify/unify-adapter";
13
+ export * from "./groq/groq-adapter";
14
+ export * from "./anthropic/anthropic-adapter";
15
+ export * from "./experimental/ollama/ollama-adapter";
16
+ export * from "./experimental/empty/empty-adapter";
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Copilot Runtime adapter for LangChain.
3
+ *
4
+ * ## Example
5
+ *
6
+ * ```ts
7
+ * import { CopilotRuntime, LangChainAdapter } from "@copilotkit/runtime";
8
+ * import { ChatOpenAI } from "@langchain/openai";
9
+ *
10
+ * const copilotKit = new CopilotRuntime();
11
+ *
12
+ * const model = new ChatOpenAI({
13
+ * model: "gpt-4o",
14
+ * apiKey: "<your-api-key>",
15
+ * });
16
+ *
17
+ * return new LangChainAdapter({
18
+ * chainFn: async ({ messages, tools }) => {
19
+ * return model.bindTools(tools).stream(messages);
20
+ * // or optionally enable strict mode
21
+ * // return model.bindTools(tools, { strict: true }).stream(messages);
22
+ * }
23
+ * });
24
+ * ```
25
+ *
26
+ * The asynchronous handler function (`chainFn`) can return any of the following:
27
+ *
28
+ * - A simple `string` response
29
+ * - A LangChain stream (`IterableReadableStream`)
30
+ * - A LangChain `BaseMessageChunk` object
31
+ * - A LangChain `AIMessage` object
32
+ */
33
+
34
+ import { BaseMessage } from "@langchain/core/messages";
35
+ import { CopilotServiceAdapter } from "../service-adapter";
36
+ import {
37
+ CopilotRuntimeChatCompletionRequest,
38
+ CopilotRuntimeChatCompletionResponse,
39
+ } from "../service-adapter";
40
+ import {
41
+ convertActionInputToLangChainTool,
42
+ convertMessageToLangChainMessage,
43
+ streamLangChainResponse,
44
+ } from "./utils";
45
+ import { DynamicStructuredTool } from "@langchain/core/tools";
46
+ import { LangChainReturnType } from "./types";
47
+ import { randomId } from "@copilotkit/shared";
48
+ import { awaitAllCallbacks } from "@langchain/core/callbacks/promises";
49
+
50
+ interface ChainFnParameters {
51
+ model: string;
52
+ messages: BaseMessage[];
53
+ tools: DynamicStructuredTool[];
54
+ threadId?: string;
55
+ runId?: string;
56
+ }
57
+
58
+ interface LangChainAdapterOptions {
59
+ /**
60
+ * A function that uses the LangChain API to generate a response.
61
+ */
62
+ chainFn: (parameters: ChainFnParameters) => Promise<LangChainReturnType>;
63
+ }
64
+
65
+ export class LangChainAdapter implements CopilotServiceAdapter {
66
+ /**
67
+ * To use LangChain as a backend, provide a handler function to the adapter with your custom LangChain logic.
68
+ */
69
+ constructor(private options: LangChainAdapterOptions) {}
70
+
71
+ async process(
72
+ request: CopilotRuntimeChatCompletionRequest,
73
+ ): Promise<CopilotRuntimeChatCompletionResponse> {
74
+ try {
75
+ const { eventSource, model, actions, messages, runId } = request;
76
+ const threadId = request.threadId ?? randomId();
77
+ const result = await this.options.chainFn({
78
+ messages: messages.map(convertMessageToLangChainMessage),
79
+ tools: actions.map(convertActionInputToLangChainTool),
80
+ model,
81
+ threadId,
82
+ runId,
83
+ });
84
+
85
+ eventSource.stream(async (eventStream$) => {
86
+ await streamLangChainResponse({
87
+ result,
88
+ eventStream$,
89
+ });
90
+ });
91
+
92
+ return {
93
+ threadId,
94
+ };
95
+ } finally {
96
+ await awaitAllCallbacks();
97
+ }
98
+ }
99
+ }
@@ -0,0 +1,87 @@
1
+ import { Parameter, Action } from "@copilotkit/shared";
2
+ import { RemoteRunnable } from "langchain/runnables/remote";
3
+
4
+ export interface RemoteChainParameters {
5
+ name: string;
6
+ description: string;
7
+ chainUrl: string;
8
+ parameters?: Parameter[];
9
+ parameterType?: "single" | "multi";
10
+ }
11
+
12
+ export class RemoteChain {
13
+ name: string;
14
+ description: string;
15
+ chainUrl: string;
16
+ parameters?: Parameter[];
17
+ parameterType: "single" | "multi";
18
+
19
+ constructor(options: RemoteChainParameters) {
20
+ this.name = options.name;
21
+ this.description = options.description;
22
+ this.chainUrl = options.chainUrl;
23
+ this.parameters = options.parameters;
24
+ this.parameterType = options.parameterType || "multi";
25
+ }
26
+
27
+ async toAction(): Promise<Action<any>> {
28
+ if (!this.parameters) {
29
+ await this.inferLangServeParameters();
30
+ }
31
+
32
+ return {
33
+ name: this.name,
34
+ description: this.description,
35
+ parameters: this.parameters!,
36
+ handler: async (args: any) => {
37
+ const runnable = new RemoteRunnable({ url: this.chainUrl });
38
+ let input: any;
39
+ if (this.parameterType === "single") {
40
+ input = args[Object.keys(args)[0]];
41
+ } else {
42
+ input = args;
43
+ }
44
+ return await runnable.invoke(input);
45
+ },
46
+ };
47
+ }
48
+
49
+ async inferLangServeParameters() {
50
+ const supportedTypes = ["string", "number", "boolean"];
51
+
52
+ let schemaUrl = this.chainUrl.replace(/\/+$/, "") + "/input_schema";
53
+ let schema = await fetch(schemaUrl)
54
+ .then((res) => res.json())
55
+ .catch(() => {
56
+ throw new Error("Failed to fetch langserve schema at " + schemaUrl);
57
+ });
58
+ // for now, don't use json schema, just do a simple conversion
59
+
60
+ if (supportedTypes.includes(schema.type)) {
61
+ this.parameterType = "single";
62
+ this.parameters = [
63
+ {
64
+ name: "input",
65
+ type: schema.type,
66
+ description: "The input to the chain",
67
+ },
68
+ ];
69
+ } else if (schema.type === "object") {
70
+ this.parameterType = "multi";
71
+ this.parameters = Object.keys(schema.properties).map((key) => {
72
+ let property = schema.properties[key];
73
+ if (!supportedTypes.includes(property.type)) {
74
+ throw new Error("Unsupported schema type");
75
+ }
76
+ return {
77
+ name: key,
78
+ type: property.type,
79
+ description: property.description || "",
80
+ required: schema.required?.includes(key) || false,
81
+ };
82
+ });
83
+ } else {
84
+ throw new Error("Unsupported schema type");
85
+ }
86
+ }
87
+ }
@@ -0,0 +1,14 @@
1
+ import { AIMessage, AIMessageChunk, BaseMessageChunk } from "@langchain/core/messages";
2
+ import {
3
+ IterableReadableStream,
4
+ IterableReadableStreamInterface,
5
+ } from "@langchain/core/utils/stream";
6
+
7
+ export type LangChainBaseMessageChunkStream = IterableReadableStream<BaseMessageChunk>;
8
+ export type LangChainAIMessageChunkStream = IterableReadableStreamInterface<AIMessageChunk>;
9
+ export type LangChainReturnType =
10
+ | LangChainBaseMessageChunkStream
11
+ | LangChainAIMessageChunkStream
12
+ | BaseMessageChunk
13
+ | string
14
+ | AIMessage;
@@ -0,0 +1,306 @@
1
+ import {
2
+ ActionExecutionMessage,
3
+ Message,
4
+ ResultMessage,
5
+ TextMessage,
6
+ } from "../../graphql/types/converted";
7
+ import {
8
+ AIMessage,
9
+ AIMessageChunk,
10
+ BaseMessage,
11
+ BaseMessageChunk,
12
+ HumanMessage,
13
+ SystemMessage,
14
+ ToolMessage,
15
+ } from "@langchain/core/messages";
16
+ import { DynamicStructuredTool } from "@langchain/core/tools";
17
+ import { z } from "zod";
18
+ import { ActionInput } from "../../graphql/inputs/action.input";
19
+ import { LangChainReturnType } from "./types";
20
+ import { RuntimeEventSubject } from "../events";
21
+ import { randomId, convertJsonSchemaToZodSchema } from "@copilotkit/shared";
22
+
23
+ export function convertMessageToLangChainMessage(message: Message): BaseMessage {
24
+ if (message.isTextMessage()) {
25
+ if (message.role == "user") {
26
+ return new HumanMessage(message.content);
27
+ } else if (message.role == "assistant") {
28
+ return new AIMessage(message.content);
29
+ } else if (message.role === "system") {
30
+ return new SystemMessage(message.content);
31
+ }
32
+ } else if (message.isActionExecutionMessage()) {
33
+ return new AIMessage({
34
+ content: "",
35
+ tool_calls: [
36
+ {
37
+ id: message.id,
38
+ args: message.arguments,
39
+ name: message.name,
40
+ },
41
+ ],
42
+ });
43
+ } else if (message.isResultMessage()) {
44
+ return new ToolMessage({
45
+ content: message.result,
46
+ tool_call_id: message.actionExecutionId,
47
+ });
48
+ }
49
+ }
50
+
51
+ export function convertActionInputToLangChainTool(actionInput: ActionInput): any {
52
+ return new DynamicStructuredTool({
53
+ name: actionInput.name,
54
+ description: actionInput.description,
55
+ schema: convertJsonSchemaToZodSchema(
56
+ JSON.parse(actionInput.jsonSchema),
57
+ true,
58
+ ) as z.ZodObject<any>,
59
+ func: async () => {
60
+ return "";
61
+ },
62
+ });
63
+ }
64
+
65
+ interface StreamLangChainResponseParams {
66
+ result: LangChainReturnType;
67
+ eventStream$: RuntimeEventSubject;
68
+ actionExecution?: {
69
+ id: string;
70
+ name: string;
71
+ };
72
+ }
73
+
74
+ function getConstructorName(object: any): string {
75
+ if (object && typeof object === "object" && object.constructor && object.constructor.name) {
76
+ return object.constructor.name;
77
+ }
78
+ return "";
79
+ }
80
+
81
+ function isAIMessage(message: any): message is AIMessage {
82
+ return Object.prototype.toString.call(message) === "[object AIMessage]";
83
+ }
84
+
85
+ function isAIMessageChunk(message: any): message is AIMessageChunk {
86
+ return Object.prototype.toString.call(message) === "[object AIMessageChunk]";
87
+ }
88
+
89
+ function isBaseMessageChunk(message: any): message is BaseMessageChunk {
90
+ return Object.prototype.toString.call(message) === "[object BaseMessageChunk]";
91
+ }
92
+
93
+ function maybeSendActionExecutionResultIsMessage(
94
+ eventStream$: RuntimeEventSubject,
95
+ actionExecution?: { id: string; name: string },
96
+ ) {
97
+ // language models need a result after the function call
98
+ // we simply let them know that we are sending a message
99
+ if (actionExecution) {
100
+ eventStream$.sendActionExecutionResult({
101
+ actionExecutionId: actionExecution.id,
102
+ actionName: actionExecution.name,
103
+ result: "Sending a message",
104
+ });
105
+ }
106
+ }
107
+
108
+ export async function streamLangChainResponse({
109
+ result,
110
+ eventStream$,
111
+ actionExecution,
112
+ }: StreamLangChainResponseParams) {
113
+ // We support several types of return values from LangChain functions:
114
+
115
+ // 1. string
116
+
117
+ if (typeof result === "string") {
118
+ if (!actionExecution) {
119
+ // Just send one chunk with the string as the content.
120
+ eventStream$.sendTextMessage(randomId(), result);
121
+ } else {
122
+ // Send as a result
123
+ eventStream$.sendActionExecutionResult({
124
+ actionExecutionId: actionExecution.id,
125
+ actionName: actionExecution.name,
126
+ result: result,
127
+ });
128
+ }
129
+ }
130
+
131
+ // 2. AIMessage
132
+ // Send the content and function call of the AIMessage as the content of the chunk.
133
+ else if (isAIMessage(result)) {
134
+ maybeSendActionExecutionResultIsMessage(eventStream$, actionExecution);
135
+
136
+ if (result.content) {
137
+ eventStream$.sendTextMessage(randomId(), result.content as string);
138
+ }
139
+ for (const toolCall of result.tool_calls) {
140
+ eventStream$.sendActionExecution({
141
+ actionExecutionId: toolCall.id || randomId(),
142
+ actionName: toolCall.name,
143
+ args: JSON.stringify(toolCall.args),
144
+ });
145
+ }
146
+ }
147
+
148
+ // 3. BaseMessageChunk
149
+ // Send the content and function call of the AIMessage as the content of the chunk.
150
+ else if (isBaseMessageChunk(result)) {
151
+ maybeSendActionExecutionResultIsMessage(eventStream$, actionExecution);
152
+
153
+ if (result.lc_kwargs?.content) {
154
+ eventStream$.sendTextMessage(randomId(), result.content as string);
155
+ }
156
+ if (result.lc_kwargs?.tool_calls) {
157
+ for (const toolCall of result.lc_kwargs?.tool_calls) {
158
+ eventStream$.sendActionExecution({
159
+ actionExecutionId: toolCall.id || randomId(),
160
+ actionName: toolCall.name,
161
+ args: JSON.stringify(toolCall.args),
162
+ });
163
+ }
164
+ }
165
+ }
166
+
167
+ // 4. IterableReadableStream
168
+ // Stream the result of the LangChain function.
169
+ else if (result && "getReader" in result) {
170
+ maybeSendActionExecutionResultIsMessage(eventStream$, actionExecution);
171
+
172
+ let reader = result.getReader();
173
+
174
+ let mode: "function" | "message" | null = null;
175
+ let currentMessageId: string;
176
+
177
+ const toolCallDetails = {
178
+ name: null,
179
+ id: null,
180
+ index: null,
181
+ prevIndex: null,
182
+ };
183
+
184
+ while (true) {
185
+ try {
186
+ const { done, value } = await reader.read();
187
+
188
+ let toolCallName: string | undefined = undefined;
189
+ let toolCallId: string | undefined = undefined;
190
+ let toolCallArgs: string | undefined = undefined;
191
+ let hasToolCall: boolean = false;
192
+ let content = "";
193
+ if (value && value.content) {
194
+ content = Array.isArray(value.content)
195
+ ? (((value.content[0] as any)?.text ?? "") as string)
196
+ : value.content;
197
+ }
198
+
199
+ if (isAIMessageChunk(value)) {
200
+ let chunk = value.tool_call_chunks?.[0];
201
+ toolCallArgs = chunk?.args;
202
+ hasToolCall = chunk != undefined;
203
+ if (chunk?.name) toolCallDetails.name = chunk.name;
204
+ // track different index on the same tool cool
205
+ if (chunk?.index != null) {
206
+ toolCallDetails.index = chunk.index; // 1
207
+ if (toolCallDetails.prevIndex == null) toolCallDetails.prevIndex = chunk.index;
208
+ }
209
+ // Differentiate when calling the same tool but with different index
210
+ if (chunk?.id)
211
+ toolCallDetails.id = chunk.index != null ? `${chunk.id}-idx-${chunk.index}` : chunk.id;
212
+
213
+ // Assign to internal variables that the entire script here knows how to work with
214
+ toolCallName = toolCallDetails.name;
215
+ toolCallId = toolCallDetails.id;
216
+ } else if (isBaseMessageChunk(value)) {
217
+ let chunk = value.additional_kwargs?.tool_calls?.[0];
218
+ toolCallName = chunk?.function?.name;
219
+ toolCallId = chunk?.id;
220
+ toolCallArgs = chunk?.function?.arguments;
221
+ hasToolCall = chunk?.function != undefined;
222
+ }
223
+
224
+ // When switching from message to function or vice versa,
225
+ // send the respective end event.
226
+ // If toolCallName is defined, it means a new tool call starts.
227
+ if (mode === "message" && (toolCallId || done)) {
228
+ mode = null;
229
+ eventStream$.sendTextMessageEnd({ messageId: currentMessageId });
230
+ } else if (mode === "function" && (!hasToolCall || done)) {
231
+ mode = null;
232
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: toolCallId });
233
+ }
234
+
235
+ if (done) {
236
+ break;
237
+ }
238
+
239
+ // If we send a new message type, send the appropriate start event.
240
+ if (mode === null) {
241
+ if (hasToolCall && toolCallId && toolCallName) {
242
+ mode = "function";
243
+ eventStream$.sendActionExecutionStart({
244
+ actionExecutionId: toolCallId,
245
+ actionName: toolCallName,
246
+ parentMessageId: value.lc_kwargs?.id,
247
+ });
248
+ } else if (content) {
249
+ mode = "message";
250
+ currentMessageId = value.lc_kwargs?.id || randomId();
251
+ eventStream$.sendTextMessageStart({ messageId: currentMessageId });
252
+ }
253
+ }
254
+
255
+ // send the content events
256
+ if (mode === "message" && content) {
257
+ eventStream$.sendTextMessageContent({
258
+ messageId: currentMessageId,
259
+ content,
260
+ });
261
+ } else if (mode === "function" && toolCallArgs) {
262
+ // For calls of the same tool with different index, we seal last tool call and register a new one
263
+ if (toolCallDetails.index !== toolCallDetails.prevIndex) {
264
+ eventStream$.sendActionExecutionEnd({ actionExecutionId: toolCallId });
265
+ eventStream$.sendActionExecutionStart({
266
+ actionExecutionId: toolCallId,
267
+ actionName: toolCallName,
268
+ parentMessageId: value.lc_kwargs?.id,
269
+ });
270
+ toolCallDetails.prevIndex = toolCallDetails.index;
271
+ }
272
+ eventStream$.sendActionExecutionArgs({
273
+ actionExecutionId: toolCallId,
274
+ args: toolCallArgs,
275
+ });
276
+ }
277
+ } catch (error) {
278
+ console.error("Error reading from stream", error);
279
+ break;
280
+ }
281
+ }
282
+ } else if (actionExecution) {
283
+ eventStream$.sendActionExecutionResult({
284
+ actionExecutionId: actionExecution.id,
285
+ actionName: actionExecution.name,
286
+ result: encodeResult(result),
287
+ });
288
+ }
289
+
290
+ // unsupported type
291
+ else {
292
+ throw new Error("Invalid return type from LangChain function.");
293
+ }
294
+
295
+ eventStream$.complete();
296
+ }
297
+
298
+ function encodeResult(result: any): string {
299
+ if (result === undefined) {
300
+ return "";
301
+ } else if (typeof result === "string") {
302
+ return result;
303
+ } else {
304
+ return JSON.stringify(result);
305
+ }
306
+ }