@copilotkit/runtime 0.0.0-test-custom-tag-prerelease-1-20250108200215
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 +729 -0
- package/README.md +46 -0
- package/__snapshots__/schema/schema.graphql +262 -0
- package/dist/chunk-2WXVJKUZ.mjs +25 -0
- package/dist/chunk-2WXVJKUZ.mjs.map +1 -0
- package/dist/chunk-44O2JGUY.mjs +12 -0
- package/dist/chunk-44O2JGUY.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-RFF5IIZJ.mjs +66 -0
- package/dist/chunk-RFF5IIZJ.mjs.map +1 -0
- package/dist/chunk-U3V2BCGI.mjs +152 -0
- package/dist/chunk-U3V2BCGI.mjs.map +1 -0
- package/dist/chunk-UYX3NHOI.mjs +25 -0
- package/dist/chunk-UYX3NHOI.mjs.map +1 -0
- package/dist/chunk-W3GSZTZR.mjs +3281 -0
- package/dist/chunk-W3GSZTZR.mjs.map +1 -0
- package/dist/chunk-WPNQ4AMN.mjs +80 -0
- package/dist/chunk-WPNQ4AMN.mjs.map +1 -0
- package/dist/copilot-runtime-6285d897.d.ts +189 -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 +124 -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-15d41154.d.ts +281 -0
- package/dist/index-ff3fbc33.d.ts +87 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +5039 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +76 -0
- package/dist/index.mjs.map +1 -0
- package/dist/langserve-48e976ac.d.ts +176 -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 +4687 -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 +2091 -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 +2000 -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 +2000 -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 +1986 -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 +540 -0
- package/src/graphql/types/base/index.ts +10 -0
- package/src/graphql/types/converted/index.ts +70 -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 +412 -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 +377 -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,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
|
+
}
|