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