remote.cjs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.RemoteRunnable = void 0;
  4. const base_js_1 = require("./base.cjs");
  5. const config_js_1 = require("./config.cjs");
  6. const index_js_1 = require("../documents/index.cjs");
  7. const prompt_values_js_1 = require("../prompt_values.cjs");
  8. const log_stream_js_1 = require("../tracers/log_stream.cjs");
  9. const index_js_2 = require("../messages/index.cjs");
  10. const outputs_js_1 = require("../outputs.cjs");
  11. const event_source_parse_js_1 = require("../utils/event_source_parse.cjs");
  12. const stream_js_1 = require("../utils/stream.cjs");
  13. function isSuperset(set, subset) {
  14. for (const elem of subset) {
  15. if (!set.has(elem)) {
  16. return false;
  17. }
  18. }
  19. return true;
  20. }
  21. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  22. function revive(obj) {
  23. if (Array.isArray(obj))
  24. return obj.map(revive);
  25. if (typeof obj === "object") {
  26. // eslint-disable-next-line no-instanceof/no-instanceof
  27. if (!obj || obj instanceof Date) {
  28. return obj;
  29. }
  30. const keysArr = Object.keys(obj);
  31. const keys = new Set(keysArr);
  32. if (isSuperset(keys, new Set(["page_content", "metadata"]))) {
  33. return new index_js_1.Document({
  34. pageContent: obj.page_content,
  35. metadata: obj.metadata,
  36. });
  37. }
  38. if (isSuperset(keys, new Set(["content", "type", "additional_kwargs"]))) {
  39. if (obj.type === "HumanMessage" || obj.type === "human") {
  40. return new index_js_2.HumanMessage({
  41. content: obj.content,
  42. });
  43. }
  44. if (obj.type === "SystemMessage" || obj.type === "system") {
  45. return new index_js_2.SystemMessage({
  46. content: obj.content,
  47. });
  48. }
  49. if (obj.type === "ChatMessage" || obj.type === "generic") {
  50. return new index_js_2.ChatMessage({
  51. content: obj.content,
  52. role: obj.role,
  53. });
  54. }
  55. if (obj.type === "FunctionMessage" || obj.type === "function") {
  56. return new index_js_2.FunctionMessage({
  57. content: obj.content,
  58. name: obj.name,
  59. });
  60. }
  61. if (obj.type === "ToolMessage" || obj.type === "tool") {
  62. return new index_js_2.ToolMessage({
  63. content: obj.content,
  64. tool_call_id: obj.tool_call_id,
  65. status: obj.status,
  66. artifact: obj.artifact,
  67. });
  68. }
  69. if (obj.type === "AIMessage" || obj.type === "ai") {
  70. return new index_js_2.AIMessage({
  71. content: obj.content,
  72. });
  73. }
  74. if (obj.type === "HumanMessageChunk") {
  75. return new index_js_2.HumanMessageChunk({
  76. content: obj.content,
  77. });
  78. }
  79. if (obj.type === "SystemMessageChunk") {
  80. return new index_js_2.SystemMessageChunk({
  81. content: obj.content,
  82. });
  83. }
  84. if (obj.type === "ChatMessageChunk") {
  85. return new index_js_2.ChatMessageChunk({
  86. content: obj.content,
  87. role: obj.role,
  88. });
  89. }
  90. if (obj.type === "FunctionMessageChunk") {
  91. return new index_js_2.FunctionMessageChunk({
  92. content: obj.content,
  93. name: obj.name,
  94. });
  95. }
  96. if (obj.type === "ToolMessageChunk") {
  97. return new index_js_2.ToolMessageChunk({
  98. content: obj.content,
  99. tool_call_id: obj.tool_call_id,
  100. status: obj.status,
  101. artifact: obj.artifact,
  102. });
  103. }
  104. if (obj.type === "AIMessageChunk") {
  105. return new index_js_2.AIMessageChunk({
  106. content: obj.content,
  107. });
  108. }
  109. }
  110. if (isSuperset(keys, new Set(["text", "generation_info", "type"]))) {
  111. if (obj.type === "ChatGenerationChunk") {
  112. return new outputs_js_1.ChatGenerationChunk({
  113. message: revive(obj.message),
  114. text: obj.text,
  115. generationInfo: obj.generation_info,
  116. });
  117. }
  118. else if (obj.type === "ChatGeneration") {
  119. return {
  120. message: revive(obj.message),
  121. text: obj.text,
  122. generationInfo: obj.generation_info,
  123. };
  124. }
  125. else if (obj.type === "GenerationChunk") {
  126. return new outputs_js_1.GenerationChunk({
  127. text: obj.text,
  128. generationInfo: obj.generation_info,
  129. });
  130. }
  131. else if (obj.type === "Generation") {
  132. return {
  133. text: obj.text,
  134. generationInfo: obj.generation_info,
  135. };
  136. }
  137. }
  138. if (isSuperset(keys, new Set(["tool", "tool_input", "log", "type"]))) {
  139. if (obj.type === "AgentAction") {
  140. return {
  141. tool: obj.tool,
  142. toolInput: obj.tool_input,
  143. log: obj.log,
  144. };
  145. }
  146. }
  147. if (isSuperset(keys, new Set(["return_values", "log", "type"]))) {
  148. if (obj.type === "AgentFinish") {
  149. return {
  150. returnValues: obj.return_values,
  151. log: obj.log,
  152. };
  153. }
  154. }
  155. if (isSuperset(keys, new Set(["generations", "run", "type"]))) {
  156. if (obj.type === "LLMResult") {
  157. return {
  158. generations: revive(obj.generations),
  159. llmOutput: obj.llm_output,
  160. [outputs_js_1.RUN_KEY]: obj.run,
  161. };
  162. }
  163. }
  164. if (isSuperset(keys, new Set(["messages"]))) {
  165. // TODO: Start checking for type: ChatPromptValue and ChatPromptValueConcrete
  166. // when LangServe bug is fixed
  167. return new prompt_values_js_1.ChatPromptValue({
  168. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  169. messages: obj.messages.map((msg) => revive(msg)),
  170. });
  171. }
  172. if (isSuperset(keys, new Set(["text"]))) {
  173. // TODO: Start checking for type: StringPromptValue
  174. // when LangServe bug is fixed
  175. return new prompt_values_js_1.StringPromptValue(obj.text);
  176. }
  177. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  178. const innerRevive = (key) => [
  179. key,
  180. revive(obj[key]),
  181. ];
  182. const rtn = Object.fromEntries(keysArr.map(innerRevive));
  183. return rtn;
  184. }
  185. return obj;
  186. }
  187. function deserialize(str) {
  188. const obj = JSON.parse(str);
  189. return revive(obj);
  190. }
  191. function removeCallbacksAndSignal(options) {
  192. const rest = { ...options };
  193. delete rest.callbacks;
  194. delete rest.signal;
  195. return rest;
  196. }
  197. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  198. function serialize(input) {
  199. if (Array.isArray(input))
  200. return input.map(serialize);
  201. if ((0, index_js_2.isBaseMessage)(input)) {
  202. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  203. const serializedMessage = {
  204. content: input.content,
  205. type: input._getType(),
  206. additional_kwargs: input.additional_kwargs,
  207. name: input.name,
  208. example: false,
  209. };
  210. if (index_js_2.ToolMessage.isInstance(input)) {
  211. serializedMessage.tool_call_id = input.tool_call_id;
  212. }
  213. else if (index_js_2.ChatMessage.isInstance(input)) {
  214. serializedMessage.role = input.role;
  215. }
  216. return serializedMessage;
  217. }
  218. if (typeof input === "object") {
  219. // eslint-disable-next-line no-instanceof/no-instanceof
  220. if (!input || input instanceof Date) {
  221. return input;
  222. }
  223. const keysArr = Object.keys(input);
  224. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  225. const innerSerialize = (key) => [
  226. key,
  227. serialize(input[key]),
  228. ];
  229. const rtn = Object.fromEntries(keysArr.map(innerSerialize));
  230. return rtn;
  231. }
  232. return input;
  233. }
  234. /**
  235. * Client for interacting with LangChain runnables
  236. * that are hosted as LangServe endpoints.
  237. *
  238. * Allows you to interact with hosted runnables using the standard
  239. * `.invoke()`, `.stream()`, `.streamEvents()`, etc. methods that
  240. * other runnables support.
  241. *
  242. * @deprecated LangServe is no longer actively developed - please consider using LangGraph Platform.
  243. *
  244. * @param url - The base URL of the LangServe endpoint.
  245. * @param options - Optional configuration for the remote runnable, including timeout and headers.
  246. * @param fetch - Optional custom fetch implementation.
  247. * @param fetchRequestOptions - Optional additional options for fetch requests.
  248. */
  249. class RemoteRunnable extends base_js_1.Runnable {
  250. constructor(fields) {
  251. super(fields);
  252. Object.defineProperty(this, "url", {
  253. enumerable: true,
  254. configurable: true,
  255. writable: true,
  256. value: void 0
  257. });
  258. Object.defineProperty(this, "options", {
  259. enumerable: true,
  260. configurable: true,
  261. writable: true,
  262. value: void 0
  263. });
  264. // Wrap the default fetch call due to issues with illegal invocations
  265. // from the browser:
  266. // https://stackoverflow.com/questions/69876859/why-does-bind-fix-failed-to-execute-fetch-on-window-illegal-invocation-err
  267. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  268. Object.defineProperty(this, "fetchImplementation", {
  269. enumerable: true,
  270. configurable: true,
  271. writable: true,
  272. value: (...args) =>
  273. // @ts-expect-error Broad typing to support a range of fetch implementations
  274. fetch(...args)
  275. });
  276. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  277. Object.defineProperty(this, "fetchRequestOptions", {
  278. enumerable: true,
  279. configurable: true,
  280. writable: true,
  281. value: void 0
  282. });
  283. Object.defineProperty(this, "lc_namespace", {
  284. enumerable: true,
  285. configurable: true,
  286. writable: true,
  287. value: ["langchain", "schema", "runnable", "remote"]
  288. });
  289. const { url, options, fetch: fetchImplementation, fetchRequestOptions, } = fields;
  290. this.url = url.replace(/\/$/, ""); // remove trailing slash
  291. this.options = options;
  292. this.fetchImplementation = fetchImplementation ?? this.fetchImplementation;
  293. this.fetchRequestOptions = fetchRequestOptions;
  294. }
  295. async post(path, body, signal) {
  296. return this.fetchImplementation(`${this.url}${path}`, {
  297. method: "POST",
  298. body: JSON.stringify(serialize(body)),
  299. signal: signal ?? AbortSignal.timeout(this.options?.timeout ?? 60000),
  300. ...this.fetchRequestOptions,
  301. headers: {
  302. "Content-Type": "application/json",
  303. ...this.fetchRequestOptions?.headers,
  304. ...this.options?.headers,
  305. },
  306. });
  307. }
  308. async _invoke(input, options, _) {
  309. const [config, kwargs] = this._separateRunnableConfigFromCallOptions(options);
  310. const response = await this.post("/invoke", {
  311. input,
  312. config: removeCallbacksAndSignal(config),
  313. kwargs: kwargs ?? {},
  314. }, config.signal);
  315. if (!response.ok) {
  316. throw new Error(`${response.status} Error: ${await response.text()}`);
  317. }
  318. return revive((await response.json()).output);
  319. }
  320. async invoke(input, options) {
  321. return this._callWithConfig(this._invoke, input, options);
  322. }
  323. async _batch(inputs, options, _, batchOptions) {
  324. if (batchOptions?.returnExceptions) {
  325. throw new Error("returnExceptions is not supported for remote clients");
  326. }
  327. const configsAndKwargsArray = options?.map((opts) => this._separateRunnableConfigFromCallOptions(opts));
  328. const [configs, kwargs] = configsAndKwargsArray?.reduce(([pc, pk], [c, k]) => [
  329. [...pc, c],
  330. [...pk, k],
  331. ], [[], []]) ?? [undefined, undefined];
  332. const response = await this.post("/batch", {
  333. inputs,
  334. config: (configs ?? [])
  335. .map(removeCallbacksAndSignal)
  336. .map((config) => ({ ...config, ...batchOptions })),
  337. kwargs,
  338. }, options?.[0]?.signal);
  339. if (!response.ok) {
  340. throw new Error(`${response.status} Error: ${await response.text()}`);
  341. }
  342. const body = await response.json();
  343. if (!body.output)
  344. throw new Error("Invalid response from remote runnable");
  345. return revive(body.output);
  346. }
  347. async batch(inputs, options, batchOptions) {
  348. if (batchOptions?.returnExceptions) {
  349. throw Error("returnExceptions is not supported for remote clients");
  350. }
  351. return this._batchWithConfig(this._batch.bind(this), inputs, options, batchOptions);
  352. }
  353. async *_streamIterator(input, options) {
  354. const [config, kwargs] = this._separateRunnableConfigFromCallOptions(options);
  355. const callbackManager_ = await (0, config_js_1.getCallbackManagerForConfig)(options);
  356. const runManager = await callbackManager_?.handleChainStart(this.toJSON(), (0, base_js_1._coerceToDict)(input, "input"), config.runId, undefined, undefined, undefined, config.runName);
  357. delete config.runId;
  358. let finalOutput;
  359. let finalOutputSupported = true;
  360. try {
  361. const response = await this.post("/stream", {
  362. input,
  363. config: removeCallbacksAndSignal(config),
  364. kwargs,
  365. }, config.signal);
  366. if (!response.ok) {
  367. const json = await response.json();
  368. const error = new Error(`RemoteRunnable call failed with status code ${response.status}: ${json.message}`);
  369. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  370. error.response = response;
  371. throw error;
  372. }
  373. const { body } = response;
  374. if (!body) {
  375. throw new Error("Could not begin remote stream. Please check the given URL and try again.");
  376. }
  377. const runnableStream = (0, event_source_parse_js_1.convertEventStreamToIterableReadableDataStream)(body);
  378. for await (const chunk of runnableStream) {
  379. const deserializedChunk = deserialize(chunk);
  380. yield deserializedChunk;
  381. if (finalOutputSupported) {
  382. if (finalOutput === undefined) {
  383. finalOutput = deserializedChunk;
  384. }
  385. else {
  386. try {
  387. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  388. finalOutput = (0, stream_js_1.concat)(finalOutput, deserializedChunk);
  389. }
  390. catch {
  391. finalOutput = undefined;
  392. finalOutputSupported = false;
  393. }
  394. }
  395. }
  396. }
  397. }
  398. catch (err) {
  399. await runManager?.handleChainError(err);
  400. throw err;
  401. }
  402. await runManager?.handleChainEnd(finalOutput ?? {});
  403. }
  404. async *streamLog(input, options, streamOptions) {
  405. const [config, kwargs] = this._separateRunnableConfigFromCallOptions(options);
  406. const callbackManager_ = await (0, config_js_1.getCallbackManagerForConfig)(options);
  407. const runManager = await callbackManager_?.handleChainStart(this.toJSON(), (0, base_js_1._coerceToDict)(input, "input"), config.runId, undefined, undefined, undefined, config.runName);
  408. delete config.runId;
  409. // The type is in camelCase but the API only accepts snake_case.
  410. const camelCaseStreamOptions = {
  411. include_names: streamOptions?.includeNames,
  412. include_types: streamOptions?.includeTypes,
  413. include_tags: streamOptions?.includeTags,
  414. exclude_names: streamOptions?.excludeNames,
  415. exclude_types: streamOptions?.excludeTypes,
  416. exclude_tags: streamOptions?.excludeTags,
  417. };
  418. let runLog;
  419. try {
  420. const response = await this.post("/stream_log", {
  421. input,
  422. config: removeCallbacksAndSignal(config),
  423. kwargs,
  424. ...camelCaseStreamOptions,
  425. diff: false,
  426. }, config.signal);
  427. const { body, ok } = response;
  428. if (!ok) {
  429. throw new Error(`${response.status} Error: ${await response.text()}`);
  430. }
  431. if (!body) {
  432. throw new Error("Could not begin remote stream log. Please check the given URL and try again.");
  433. }
  434. const runnableStream = (0, event_source_parse_js_1.convertEventStreamToIterableReadableDataStream)(body);
  435. for await (const log of runnableStream) {
  436. const chunk = revive(JSON.parse(log));
  437. const logPatch = new log_stream_js_1.RunLogPatch({ ops: chunk.ops });
  438. yield logPatch;
  439. if (runLog === undefined) {
  440. runLog = log_stream_js_1.RunLog.fromRunLogPatch(logPatch);
  441. }
  442. else {
  443. runLog = runLog.concat(logPatch);
  444. }
  445. }
  446. }
  447. catch (err) {
  448. await runManager?.handleChainError(err);
  449. throw err;
  450. }
  451. await runManager?.handleChainEnd(runLog?.state.final_output);
  452. }
  453. _streamEvents(input, options, streamOptions) {
  454. // eslint-disable-next-line @typescript-eslint/no-this-alias
  455. const outerThis = this;
  456. const generator = async function* () {
  457. const [config, kwargs] = outerThis._separateRunnableConfigFromCallOptions(options);
  458. const callbackManager_ = await (0, config_js_1.getCallbackManagerForConfig)(options);
  459. const runManager = await callbackManager_?.handleChainStart(outerThis.toJSON(), (0, base_js_1._coerceToDict)(input, "input"), config.runId, undefined, undefined, undefined, config.runName);
  460. delete config.runId;
  461. // The type is in camelCase but the API only accepts snake_case.
  462. const camelCaseStreamOptions = {
  463. include_names: streamOptions?.includeNames,
  464. include_types: streamOptions?.includeTypes,
  465. include_tags: streamOptions?.includeTags,
  466. exclude_names: streamOptions?.excludeNames,
  467. exclude_types: streamOptions?.excludeTypes,
  468. exclude_tags: streamOptions?.excludeTags,
  469. };
  470. const events = [];
  471. try {
  472. const response = await outerThis.post("/stream_events", {
  473. input,
  474. config: removeCallbacksAndSignal(config),
  475. kwargs,
  476. ...camelCaseStreamOptions,
  477. diff: false,
  478. }, config.signal);
  479. const { body, ok } = response;
  480. if (!ok) {
  481. throw new Error(`${response.status} Error: ${await response.text()}`);
  482. }
  483. if (!body) {
  484. throw new Error("Could not begin remote stream events. Please check the given URL and try again.");
  485. }
  486. const runnableStream = (0, event_source_parse_js_1.convertEventStreamToIterableReadableDataStream)(body);
  487. for await (const log of runnableStream) {
  488. const chunk = revive(JSON.parse(log));
  489. const event = {
  490. event: chunk.event,
  491. name: chunk.name,
  492. run_id: chunk.run_id,
  493. tags: chunk.tags,
  494. metadata: chunk.metadata,
  495. data: chunk.data,
  496. };
  497. yield event;
  498. events.push(event);
  499. }
  500. }
  501. catch (err) {
  502. await runManager?.handleChainError(err);
  503. throw err;
  504. }
  505. await runManager?.handleChainEnd(events);
  506. };
  507. return generator();
  508. }
  509. streamEvents(input, options, streamOptions) {
  510. if (options.version !== "v1" && options.version !== "v2") {
  511. throw new Error(`Only versions "v1" and "v2" of the events schema is currently supported.`);
  512. }
  513. if (options.encoding !== undefined) {
  514. throw new Error("Special encodings are not supported for this runnable.");
  515. }
  516. const eventStream = this._streamEvents(input, options, streamOptions);
  517. return stream_js_1.IterableReadableStream.fromAsyncGenerator(eventStream);
  518. }
  519. }
  520. exports.RemoteRunnable = RemoteRunnable;