langchain 0.0.183 → 0.0.185

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 (67) hide show
  1. package/agents/format_scratchpad/openai_tools.cjs +1 -0
  2. package/agents/format_scratchpad/openai_tools.d.ts +1 -0
  3. package/agents/format_scratchpad/openai_tools.js +1 -0
  4. package/dist/agents/agent.cjs +19 -13
  5. package/dist/agents/agent.d.ts +16 -17
  6. package/dist/agents/agent.js +17 -11
  7. package/dist/agents/executor.d.ts +10 -16
  8. package/dist/agents/format_scratchpad/openai_tools.cjs +19 -0
  9. package/dist/agents/format_scratchpad/openai_tools.d.ts +3 -0
  10. package/dist/agents/format_scratchpad/openai_tools.js +15 -0
  11. package/dist/agents/openai/output_parser.cjs +66 -1
  12. package/dist/agents/openai/output_parser.d.ts +26 -2
  13. package/dist/agents/openai/output_parser.js +65 -1
  14. package/dist/agents/structured_chat/index.cjs +1 -2
  15. package/dist/agents/structured_chat/index.d.ts +2 -0
  16. package/dist/agents/structured_chat/index.js +1 -2
  17. package/dist/agents/toolkits/aws_sfn.d.ts +1 -4
  18. package/dist/agents/toolkits/conversational_retrieval/openai_functions.d.ts +1 -1
  19. package/dist/agents/toolkits/json/json.d.ts +1 -4
  20. package/dist/agents/toolkits/openapi/openapi.d.ts +1 -4
  21. package/dist/agents/toolkits/sql/sql.d.ts +1 -4
  22. package/dist/agents/toolkits/vectorstore/vectorstore.d.ts +2 -8
  23. package/dist/agents/types.cjs +8 -1
  24. package/dist/agents/types.d.ts +11 -5
  25. package/dist/agents/types.js +6 -0
  26. package/dist/document_loaders/fs/pdf.cjs +17 -3
  27. package/dist/document_loaders/fs/pdf.js +17 -3
  28. package/dist/document_loaders/web/apify_dataset.cjs +12 -6
  29. package/dist/document_loaders/web/apify_dataset.d.ts +9 -6
  30. package/dist/document_loaders/web/apify_dataset.js +12 -6
  31. package/dist/document_loaders/web/pdf.cjs +17 -3
  32. package/dist/document_loaders/web/pdf.js +17 -3
  33. package/dist/document_loaders/web/puppeteer.cjs +37 -0
  34. package/dist/document_loaders/web/puppeteer.d.ts +17 -0
  35. package/dist/document_loaders/web/puppeteer.js +37 -0
  36. package/dist/experimental/openai_assistant/index.cjs +221 -0
  37. package/dist/experimental/openai_assistant/index.d.ts +36 -0
  38. package/dist/experimental/openai_assistant/index.js +217 -0
  39. package/dist/experimental/openai_assistant/schema.cjs +2 -0
  40. package/dist/experimental/openai_assistant/schema.d.ts +12 -0
  41. package/dist/experimental/openai_assistant/schema.js +1 -0
  42. package/dist/experimental/plan_and_execute/agent_executor.cjs +28 -2
  43. package/dist/experimental/plan_and_execute/agent_executor.d.ts +10 -3
  44. package/dist/experimental/plan_and_execute/agent_executor.js +26 -1
  45. package/dist/experimental/plan_and_execute/prompt.d.ts +2 -1
  46. package/dist/load/import_map.cjs +4 -2
  47. package/dist/load/import_map.d.ts +2 -0
  48. package/dist/load/import_map.js +2 -0
  49. package/dist/prompts/chat.cjs +22 -1
  50. package/dist/prompts/chat.d.ts +1 -0
  51. package/dist/prompts/chat.js +22 -1
  52. package/dist/schema/index.d.ts +1 -0
  53. package/dist/tools/convert_to_openai.cjs +14 -2
  54. package/dist/tools/convert_to_openai.d.ts +1 -0
  55. package/dist/tools/convert_to_openai.js +11 -0
  56. package/dist/tools/index.cjs +2 -1
  57. package/dist/tools/index.d.ts +1 -1
  58. package/dist/tools/index.js +1 -1
  59. package/dist/vectorstores/momento_vector_index.cjs +1 -1
  60. package/dist/vectorstores/momento_vector_index.js +1 -1
  61. package/dist/vectorstores/pinecone.cjs +4 -1
  62. package/dist/vectorstores/pinecone.d.ts +2 -1
  63. package/dist/vectorstores/pinecone.js +4 -1
  64. package/experimental/openai_assistant.cjs +1 -0
  65. package/experimental/openai_assistant.d.ts +1 -0
  66. package/experimental/openai_assistant.js +1 -0
  67. package/package.json +24 -8
@@ -59,9 +59,23 @@ class PDFLoader extends buffer_js_1.BufferLoader {
59
59
  if (content.items.length === 0) {
60
60
  continue;
61
61
  }
62
- const text = content.items
63
- .map((item) => item.str)
64
- .join("\n");
62
+ // Eliminate excessive newlines
63
+ // Source: https://github.com/albertcui/pdf-parse/blob/7086fc1cc9058545cdf41dd0646d6ae5832c7107/lib/pdf-parse.js#L16
64
+ let lastY;
65
+ const textItems = [];
66
+ for (const item of content.items) {
67
+ if ("str" in item) {
68
+ if (lastY === item.transform[5] || !lastY) {
69
+ textItems.push(item.str);
70
+ }
71
+ else {
72
+ textItems.push(`\n${item.str}`);
73
+ }
74
+ // eslint-disable-next-line prefer-destructuring
75
+ lastY = item.transform[5];
76
+ }
77
+ }
78
+ const text = textItems.join(" ");
65
79
  documents.push(new document_js_1.Document({
66
80
  pageContent: text,
67
81
  metadata: {
@@ -56,9 +56,23 @@ export class PDFLoader extends BufferLoader {
56
56
  if (content.items.length === 0) {
57
57
  continue;
58
58
  }
59
- const text = content.items
60
- .map((item) => item.str)
61
- .join("\n");
59
+ // Eliminate excessive newlines
60
+ // Source: https://github.com/albertcui/pdf-parse/blob/7086fc1cc9058545cdf41dd0646d6ae5832c7107/lib/pdf-parse.js#L16
61
+ let lastY;
62
+ const textItems = [];
63
+ for (const item of content.items) {
64
+ if ("str" in item) {
65
+ if (lastY === item.transform[5] || !lastY) {
66
+ textItems.push(item.str);
67
+ }
68
+ else {
69
+ textItems.push(`\n${item.str}`);
70
+ }
71
+ // eslint-disable-next-line prefer-destructuring
72
+ lastY = item.transform[5];
73
+ }
74
+ }
75
+ const text = textItems.join(" ");
62
76
  documents.push(new Document({
63
77
  pageContent: text,
64
78
  metadata: {
@@ -3,6 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.ApifyDatasetLoader = void 0;
5
5
  const apify_client_1 = require("apify-client");
6
+ const async_caller_js_1 = require("../../util/async_caller.cjs");
6
7
  const base_js_1 = require("../base.cjs");
7
8
  const env_js_1 = require("../../util/env.cjs");
8
9
  /**
@@ -31,13 +32,18 @@ class ApifyDatasetLoader extends base_js_1.BaseDocumentLoader {
31
32
  writable: true,
32
33
  value: void 0
33
34
  });
34
- const apifyApiToken = ApifyDatasetLoader._getApifyApiToken(config.clientOptions);
35
- this.apifyClient = new apify_client_1.ApifyClient({
36
- ...config.clientOptions,
37
- token: apifyApiToken,
35
+ Object.defineProperty(this, "caller", {
36
+ enumerable: true,
37
+ configurable: true,
38
+ writable: true,
39
+ value: void 0
38
40
  });
41
+ const { clientOptions, datasetMappingFunction, ...asyncCallerParams } = config;
42
+ const token = ApifyDatasetLoader._getApifyApiToken(clientOptions);
43
+ this.apifyClient = new apify_client_1.ApifyClient({ ...clientOptions, token });
39
44
  this.datasetId = datasetId;
40
- this.datasetMappingFunction = config.datasetMappingFunction;
45
+ this.datasetMappingFunction = datasetMappingFunction;
46
+ this.caller = new async_caller_js_1.AsyncCaller(asyncCallerParams);
41
47
  }
42
48
  static _getApifyApiToken(config) {
43
49
  return config?.token ?? (0, env_js_1.getEnvironmentVariable)("APIFY_API_TOKEN");
@@ -50,7 +56,7 @@ class ApifyDatasetLoader extends base_js_1.BaseDocumentLoader {
50
56
  */
51
57
  async load() {
52
58
  const datasetItems = (await this.apifyClient.dataset(this.datasetId).listItems({ clean: true })).items;
53
- return datasetItems.map(this.datasetMappingFunction);
59
+ return await Promise.all(datasetItems.map((item) => this.caller.call(async () => this.datasetMappingFunction(item))));
54
60
  }
55
61
  /**
56
62
  * Create an ApifyDatasetLoader by calling an Actor on the Apify platform and waiting for its results to be ready.
@@ -1,11 +1,16 @@
1
1
  import { ActorCallOptions, ApifyClient, ApifyClientOptions, TaskCallOptions } from "apify-client";
2
+ import { AsyncCaller, AsyncCallerParams } from "../../util/async_caller.js";
2
3
  import { BaseDocumentLoader, DocumentLoader } from "../base.js";
3
4
  import { Document } from "../../document.js";
4
5
  /**
5
6
  * A type that represents a function that takes a single object (an Apify
6
7
  * dataset item) and converts it to an instance of the Document class.
7
8
  */
8
- export type ApifyDatasetMappingFunction<Metadata extends Record<string, any>> = (item: Record<string | number, unknown>) => Document<Metadata>;
9
+ export type ApifyDatasetMappingFunction<Metadata extends Record<string, any>> = (item: Record<string | number, unknown>) => Document<Metadata> | Promise<Document<Metadata>>;
10
+ export interface ApifyDatasetLoaderConfig<Metadata extends Record<string, any>> extends AsyncCallerParams {
11
+ datasetMappingFunction: ApifyDatasetMappingFunction<Metadata>;
12
+ clientOptions?: ApifyClientOptions;
13
+ }
9
14
  /**
10
15
  * A class that extends the BaseDocumentLoader and implements the
11
16
  * DocumentLoader interface. It represents a document loader that loads
@@ -14,11 +19,9 @@ export type ApifyDatasetMappingFunction<Metadata extends Record<string, any>> =
14
19
  export declare class ApifyDatasetLoader<Metadata extends Record<string, any>> extends BaseDocumentLoader implements DocumentLoader {
15
20
  protected apifyClient: ApifyClient;
16
21
  protected datasetId: string;
17
- protected datasetMappingFunction: (item: Record<string | number, unknown>) => Document<Metadata>;
18
- constructor(datasetId: string, config: {
19
- datasetMappingFunction: ApifyDatasetMappingFunction<Metadata>;
20
- clientOptions?: ApifyClientOptions;
21
- });
22
+ protected datasetMappingFunction: ApifyDatasetMappingFunction<Metadata>;
23
+ protected caller: AsyncCaller;
24
+ constructor(datasetId: string, config: ApifyDatasetLoaderConfig<Metadata>);
22
25
  private static _getApifyApiToken;
23
26
  /**
24
27
  * Retrieves the dataset items from the Apify platform and applies the
@@ -1,5 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { ApifyClient, } from "apify-client";
3
+ import { AsyncCaller } from "../../util/async_caller.js";
3
4
  import { BaseDocumentLoader } from "../base.js";
4
5
  import { getEnvironmentVariable } from "../../util/env.js";
5
6
  /**
@@ -28,13 +29,18 @@ export class ApifyDatasetLoader extends BaseDocumentLoader {
28
29
  writable: true,
29
30
  value: void 0
30
31
  });
31
- const apifyApiToken = ApifyDatasetLoader._getApifyApiToken(config.clientOptions);
32
- this.apifyClient = new ApifyClient({
33
- ...config.clientOptions,
34
- token: apifyApiToken,
32
+ Object.defineProperty(this, "caller", {
33
+ enumerable: true,
34
+ configurable: true,
35
+ writable: true,
36
+ value: void 0
35
37
  });
38
+ const { clientOptions, datasetMappingFunction, ...asyncCallerParams } = config;
39
+ const token = ApifyDatasetLoader._getApifyApiToken(clientOptions);
40
+ this.apifyClient = new ApifyClient({ ...clientOptions, token });
36
41
  this.datasetId = datasetId;
37
- this.datasetMappingFunction = config.datasetMappingFunction;
42
+ this.datasetMappingFunction = datasetMappingFunction;
43
+ this.caller = new AsyncCaller(asyncCallerParams);
38
44
  }
39
45
  static _getApifyApiToken(config) {
40
46
  return config?.token ?? getEnvironmentVariable("APIFY_API_TOKEN");
@@ -47,7 +53,7 @@ export class ApifyDatasetLoader extends BaseDocumentLoader {
47
53
  */
48
54
  async load() {
49
55
  const datasetItems = (await this.apifyClient.dataset(this.datasetId).listItems({ clean: true })).items;
50
- return datasetItems.map(this.datasetMappingFunction);
56
+ return await Promise.all(datasetItems.map((item) => this.caller.call(async () => this.datasetMappingFunction(item))));
51
57
  }
52
58
  /**
53
59
  * Create an ApifyDatasetLoader by calling an Actor on the Apify platform and waiting for its results to be ready.
@@ -52,9 +52,23 @@ class WebPDFLoader extends base_js_1.BaseDocumentLoader {
52
52
  if (content.items.length === 0) {
53
53
  continue;
54
54
  }
55
- const text = content.items
56
- .map((item) => item.str)
57
- .join("\n");
55
+ // Eliminate excessive newlines
56
+ // Source: https://github.com/albertcui/pdf-parse/blob/7086fc1cc9058545cdf41dd0646d6ae5832c7107/lib/pdf-parse.js#L16
57
+ let lastY;
58
+ const textItems = [];
59
+ for (const item of content.items) {
60
+ if ("str" in item) {
61
+ if (lastY === item.transform[5] || !lastY) {
62
+ textItems.push(item.str);
63
+ }
64
+ else {
65
+ textItems.push(`\n${item.str}`);
66
+ }
67
+ // eslint-disable-next-line prefer-destructuring
68
+ lastY = item.transform[5];
69
+ }
70
+ }
71
+ const text = textItems.join(" ");
58
72
  documents.push(new document_js_1.Document({
59
73
  pageContent: text,
60
74
  metadata: {
@@ -49,9 +49,23 @@ export class WebPDFLoader extends BaseDocumentLoader {
49
49
  if (content.items.length === 0) {
50
50
  continue;
51
51
  }
52
- const text = content.items
53
- .map((item) => item.str)
54
- .join("\n");
52
+ // Eliminate excessive newlines
53
+ // Source: https://github.com/albertcui/pdf-parse/blob/7086fc1cc9058545cdf41dd0646d6ae5832c7107/lib/pdf-parse.js#L16
54
+ let lastY;
55
+ const textItems = [];
56
+ for (const item of content.items) {
57
+ if ("str" in item) {
58
+ if (lastY === item.transform[5] || !lastY) {
59
+ textItems.push(item.str);
60
+ }
61
+ else {
62
+ textItems.push(`\n${item.str}`);
63
+ }
64
+ // eslint-disable-next-line prefer-destructuring
65
+ lastY = item.transform[5];
66
+ }
67
+ }
68
+ const text = textItems.join(" ");
55
69
  documents.push(new Document({
56
70
  pageContent: text,
57
71
  metadata: {
@@ -63,6 +63,43 @@ class PuppeteerWebBaseLoader extends base_js_1.BaseDocumentLoader {
63
63
  const metadata = { source: this.webPath };
64
64
  return [new document_js_1.Document({ pageContent: text, metadata })];
65
65
  }
66
+ /**
67
+ * Static class method used to screenshot a web page and return
68
+ * it as a {@link Document} object where the pageContent property
69
+ * is the screenshot encoded in base64.
70
+ *
71
+ * @param {string} url
72
+ * @param {PuppeteerWebBaseLoaderOptions} options
73
+ * @returns {Document} A document object containing the screenshot of the page encoded in base64.
74
+ */
75
+ static async _screenshot(url, options) {
76
+ const { launch } = await PuppeteerWebBaseLoader.imports();
77
+ const browser = await launch({
78
+ headless: true,
79
+ defaultViewport: null,
80
+ ignoreDefaultArgs: ["--disable-extensions"],
81
+ ...options?.launchOptions,
82
+ });
83
+ const page = await browser.newPage();
84
+ await page.goto(url, {
85
+ timeout: 180000,
86
+ waitUntil: "domcontentloaded",
87
+ ...options?.gotoOptions,
88
+ });
89
+ const screenshot = await page.screenshot();
90
+ const base64 = screenshot.toString("base64");
91
+ const metadata = { source: url };
92
+ return new document_js_1.Document({ pageContent: base64, metadata });
93
+ }
94
+ /**
95
+ * Screenshot a web page and return it as a {@link Document} object where
96
+ * the pageContent property is the screenshot encoded in base64.
97
+ *
98
+ * @returns {Promise<Document>} A document object containing the screenshot of the page encoded in base64.
99
+ */
100
+ async screenshot() {
101
+ return PuppeteerWebBaseLoader._screenshot(this.webPath, this.options);
102
+ }
66
103
  /**
67
104
  * Static method that imports the necessary Puppeteer modules. It returns
68
105
  * a Promise that resolves to an object containing the imported modules.
@@ -40,6 +40,23 @@ export declare class PuppeteerWebBaseLoader extends BaseDocumentLoader implement
40
40
  * @returns Promise that resolves to an array of Document objects.
41
41
  */
42
42
  load(): Promise<Document[]>;
43
+ /**
44
+ * Static class method used to screenshot a web page and return
45
+ * it as a {@link Document} object where the pageContent property
46
+ * is the screenshot encoded in base64.
47
+ *
48
+ * @param {string} url
49
+ * @param {PuppeteerWebBaseLoaderOptions} options
50
+ * @returns {Document} A document object containing the screenshot of the page encoded in base64.
51
+ */
52
+ static _screenshot(url: string, options?: PuppeteerWebBaseLoaderOptions): Promise<Document>;
53
+ /**
54
+ * Screenshot a web page and return it as a {@link Document} object where
55
+ * the pageContent property is the screenshot encoded in base64.
56
+ *
57
+ * @returns {Promise<Document>} A document object containing the screenshot of the page encoded in base64.
58
+ */
59
+ screenshot(): Promise<Document>;
43
60
  /**
44
61
  * Static method that imports the necessary Puppeteer modules. It returns
45
62
  * a Promise that resolves to an object containing the imported modules.
@@ -60,6 +60,43 @@ export class PuppeteerWebBaseLoader extends BaseDocumentLoader {
60
60
  const metadata = { source: this.webPath };
61
61
  return [new Document({ pageContent: text, metadata })];
62
62
  }
63
+ /**
64
+ * Static class method used to screenshot a web page and return
65
+ * it as a {@link Document} object where the pageContent property
66
+ * is the screenshot encoded in base64.
67
+ *
68
+ * @param {string} url
69
+ * @param {PuppeteerWebBaseLoaderOptions} options
70
+ * @returns {Document} A document object containing the screenshot of the page encoded in base64.
71
+ */
72
+ static async _screenshot(url, options) {
73
+ const { launch } = await PuppeteerWebBaseLoader.imports();
74
+ const browser = await launch({
75
+ headless: true,
76
+ defaultViewport: null,
77
+ ignoreDefaultArgs: ["--disable-extensions"],
78
+ ...options?.launchOptions,
79
+ });
80
+ const page = await browser.newPage();
81
+ await page.goto(url, {
82
+ timeout: 180000,
83
+ waitUntil: "domcontentloaded",
84
+ ...options?.gotoOptions,
85
+ });
86
+ const screenshot = await page.screenshot();
87
+ const base64 = screenshot.toString("base64");
88
+ const metadata = { source: url };
89
+ return new Document({ pageContent: base64, metadata });
90
+ }
91
+ /**
92
+ * Screenshot a web page and return it as a {@link Document} object where
93
+ * the pageContent property is the screenshot encoded in base64.
94
+ *
95
+ * @returns {Promise<Document>} A document object containing the screenshot of the page encoded in base64.
96
+ */
97
+ async screenshot() {
98
+ return PuppeteerWebBaseLoader._screenshot(this.webPath, this.options);
99
+ }
63
100
  /**
64
101
  * Static method that imports the necessary Puppeteer modules. It returns
65
102
  * a Promise that resolves to an object containing the imported modules.
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenAIAssistantRunnable = void 0;
4
+ const openai_1 = require("openai");
5
+ const base_js_1 = require("../../schema/runnable/base.cjs");
6
+ const time_js_1 = require("../../util/time.cjs");
7
+ const base_js_2 = require("../../tools/base.cjs");
8
+ const convert_to_openai_js_1 = require("../../tools/convert_to_openai.cjs");
9
+ class OpenAIAssistantRunnable extends base_js_1.Runnable {
10
+ constructor(fields) {
11
+ super(fields);
12
+ Object.defineProperty(this, "lc_namespace", {
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true,
16
+ value: ["langchain", "experimental", "openai_assistant"]
17
+ });
18
+ Object.defineProperty(this, "client", {
19
+ enumerable: true,
20
+ configurable: true,
21
+ writable: true,
22
+ value: void 0
23
+ });
24
+ Object.defineProperty(this, "assistantId", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: void 0
29
+ });
30
+ Object.defineProperty(this, "pollIntervalMs", {
31
+ enumerable: true,
32
+ configurable: true,
33
+ writable: true,
34
+ value: 1000
35
+ });
36
+ Object.defineProperty(this, "asAgent", {
37
+ enumerable: true,
38
+ configurable: true,
39
+ writable: true,
40
+ value: void 0
41
+ });
42
+ this.client = fields.client ?? new openai_1.OpenAI(fields?.clientOptions);
43
+ this.assistantId = fields.assistantId;
44
+ this.asAgent = fields.asAgent ?? this.asAgent;
45
+ }
46
+ static async createAssistant({ model, name, instructions, tools, client, clientOptions, asAgent, pollIntervalMs, }) {
47
+ const formattedTools = tools?.map((tool) => {
48
+ // eslint-disable-next-line no-instanceof/no-instanceof
49
+ if (tool instanceof base_js_2.StructuredTool) {
50
+ return (0, convert_to_openai_js_1.formatToOpenAIAssistantTool)(tool);
51
+ }
52
+ return tool;
53
+ }) ?? [];
54
+ const oaiClient = client ?? new openai_1.OpenAI(clientOptions);
55
+ const assistant = await oaiClient.beta.assistants.create({
56
+ name,
57
+ instructions,
58
+ tools: formattedTools,
59
+ model,
60
+ });
61
+ return new this({
62
+ client: oaiClient,
63
+ assistantId: assistant.id,
64
+ asAgent,
65
+ pollIntervalMs,
66
+ });
67
+ }
68
+ async invoke(input, _options) {
69
+ let run;
70
+ if (this.asAgent && input.steps && input.steps.length > 0) {
71
+ const parsedStepsInput = await this._parseStepsInput(input);
72
+ run = await this.client.beta.threads.runs.submitToolOutputs(parsedStepsInput.threadId, parsedStepsInput.runId, {
73
+ tool_outputs: parsedStepsInput.toolOutputs,
74
+ });
75
+ }
76
+ else if (!("threadId" in input)) {
77
+ const thread = {
78
+ messages: [
79
+ {
80
+ role: "user",
81
+ content: input.content,
82
+ file_ids: input.fileIds,
83
+ metadata: input.messagesMetadata,
84
+ },
85
+ ],
86
+ metadata: input.threadMetadata,
87
+ };
88
+ run = await this._createThreadAndRun({
89
+ ...input,
90
+ thread,
91
+ });
92
+ }
93
+ else if (!("runId" in input)) {
94
+ await this.client.beta.threads.messages.create(input.threadId, {
95
+ content: input.content,
96
+ role: "user",
97
+ file_ids: input.file_ids,
98
+ metadata: input.messagesMetadata,
99
+ });
100
+ run = await this._createRun(input);
101
+ }
102
+ else {
103
+ // Submitting tool outputs to an existing run, outside the AgentExecutor
104
+ // framework.
105
+ run = await this.client.beta.threads.runs.submitToolOutputs(input.runId, input.threadId, {
106
+ tool_outputs: input.toolOutputs,
107
+ });
108
+ }
109
+ return this._getResponse(run.id, run.thread_id);
110
+ }
111
+ async _parseStepsInput(input) {
112
+ const { action: { runId, threadId }, } = input.steps[input.steps.length - 1];
113
+ const run = await this._waitForRun(runId, threadId);
114
+ const toolCalls = run.required_action?.submit_tool_outputs.tool_calls;
115
+ if (!toolCalls) {
116
+ return input;
117
+ }
118
+ const toolOutputs = toolCalls.flatMap((toolCall) => {
119
+ const matchedAction = input.steps.find((step) => step.action.toolCallId === toolCall.id);
120
+ return matchedAction
121
+ ? [
122
+ {
123
+ output: matchedAction.observation,
124
+ tool_call_id: matchedAction.action.toolCallId,
125
+ },
126
+ ]
127
+ : [];
128
+ });
129
+ return { toolOutputs, runId, threadId };
130
+ }
131
+ async _createRun({ instructions, model, tools, metadata, threadId, }) {
132
+ const run = this.client.beta.threads.runs.create(threadId, {
133
+ assistant_id: this.assistantId,
134
+ instructions,
135
+ model,
136
+ tools,
137
+ metadata,
138
+ });
139
+ return run;
140
+ }
141
+ async _createThreadAndRun(input) {
142
+ const params = [
143
+ "instructions",
144
+ "model",
145
+ "tools",
146
+ "run_metadata",
147
+ ]
148
+ .filter((key) => key in input)
149
+ .reduce((obj, key) => {
150
+ const newObj = obj;
151
+ newObj[key] = input[key];
152
+ return newObj;
153
+ }, {});
154
+ const run = this.client.beta.threads.createAndRun({
155
+ ...params,
156
+ thread: input.thread,
157
+ assistant_id: this.assistantId,
158
+ });
159
+ return run;
160
+ }
161
+ async _waitForRun(runId, threadId) {
162
+ let inProgress = true;
163
+ let run = {};
164
+ while (inProgress) {
165
+ run = await this.client.beta.threads.runs.retrieve(threadId, runId);
166
+ inProgress = ["in_progress", "queued"].includes(run.status);
167
+ if (inProgress) {
168
+ await (0, time_js_1.sleep)(this.pollIntervalMs);
169
+ }
170
+ }
171
+ return run;
172
+ }
173
+ async _getResponse(runId, threadId) {
174
+ const run = await this._waitForRun(runId, threadId);
175
+ if (run.status === "completed") {
176
+ const messages = await this.client.beta.threads.messages.list(threadId, {
177
+ order: "asc",
178
+ });
179
+ const newMessages = messages.data.filter((msg) => msg.run_id === runId);
180
+ if (!this.asAgent) {
181
+ return newMessages;
182
+ }
183
+ const answer = newMessages.flatMap((msg) => msg.content);
184
+ if (answer.every((item) => item.type === "text")) {
185
+ const answerString = answer
186
+ .map((item) => item.type === "text" && item.text.value)
187
+ .join("\n");
188
+ return {
189
+ returnValues: {
190
+ output: answerString,
191
+ },
192
+ log: "",
193
+ runId,
194
+ threadId,
195
+ };
196
+ }
197
+ }
198
+ else if (run.status === "requires_action") {
199
+ if (!this.asAgent) {
200
+ return run.required_action?.submit_tool_outputs.tool_calls ?? [];
201
+ }
202
+ const actions = [];
203
+ run.required_action?.submit_tool_outputs.tool_calls.forEach((item) => {
204
+ const functionCall = item.function;
205
+ const args = JSON.parse(functionCall.arguments);
206
+ actions.push({
207
+ tool: functionCall.name,
208
+ toolInput: args,
209
+ toolCallId: item.id,
210
+ log: "",
211
+ runId,
212
+ threadId,
213
+ });
214
+ });
215
+ return actions;
216
+ }
217
+ const runInfo = JSON.stringify(run, null, 2);
218
+ throw new Error(`Unexpected run status ${run.status}.\nFull run info:\n\n${runInfo}`);
219
+ }
220
+ }
221
+ exports.OpenAIAssistantRunnable = OpenAIAssistantRunnable;
@@ -0,0 +1,36 @@
1
+ import { type ClientOptions, OpenAI as OpenAIClient } from "openai";
2
+ import { Runnable } from "../../schema/runnable/base.js";
3
+ import type { RunnableConfig } from "../../schema/runnable/config.js";
4
+ import type { OpenAIAssistantFinish, OpenAIAssistantAction, OpenAIToolType } from "./schema.js";
5
+ import { StructuredTool } from "../../tools/base.js";
6
+ type ThreadMessage = OpenAIClient.Beta.Threads.ThreadMessage;
7
+ type RequiredActionFunctionToolCall = OpenAIClient.Beta.Threads.RequiredActionFunctionToolCall;
8
+ type ExtractRunOutput<AsAgent extends boolean | undefined> = AsAgent extends true ? OpenAIAssistantFinish | OpenAIAssistantAction[] : ThreadMessage[] | RequiredActionFunctionToolCall[];
9
+ export type OpenAIAssistantRunnableInput<AsAgent extends boolean | undefined = undefined> = {
10
+ client?: OpenAIClient;
11
+ clientOptions?: ClientOptions;
12
+ assistantId: string;
13
+ pollIntervalMs?: number;
14
+ asAgent?: AsAgent;
15
+ };
16
+ export declare class OpenAIAssistantRunnable<AsAgent extends boolean | undefined, RunInput extends Record<string, any> = Record<string, any>> extends Runnable<RunInput, ExtractRunOutput<AsAgent>> {
17
+ lc_namespace: string[];
18
+ private client;
19
+ assistantId: string;
20
+ pollIntervalMs: number;
21
+ asAgent?: AsAgent;
22
+ constructor(fields: OpenAIAssistantRunnableInput<AsAgent>);
23
+ static createAssistant<AsAgent extends boolean>({ model, name, instructions, tools, client, clientOptions, asAgent, pollIntervalMs, }: Omit<OpenAIAssistantRunnableInput<AsAgent>, "assistantId"> & {
24
+ model: string;
25
+ name?: string;
26
+ instructions?: string;
27
+ tools?: OpenAIToolType | Array<StructuredTool>;
28
+ }): Promise<OpenAIAssistantRunnable<AsAgent, Record<string, any>>>;
29
+ invoke(input: RunInput, _options?: RunnableConfig): Promise<ExtractRunOutput<AsAgent>>;
30
+ private _parseStepsInput;
31
+ private _createRun;
32
+ private _createThreadAndRun;
33
+ private _waitForRun;
34
+ private _getResponse;
35
+ }
36
+ export {};