AbstractChatCompletionRunner.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. "use strict";
  2. var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
  3. if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
  4. if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
  5. return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
  6. };
  7. var _AbstractChatCompletionRunner_instances, _AbstractChatCompletionRunner_getFinalContent, _AbstractChatCompletionRunner_getFinalMessage, _AbstractChatCompletionRunner_getFinalFunctionCall, _AbstractChatCompletionRunner_getFinalFunctionCallResult, _AbstractChatCompletionRunner_calculateTotalUsage, _AbstractChatCompletionRunner_validateParams, _AbstractChatCompletionRunner_stringifyFunctionCallResult;
  8. Object.defineProperty(exports, "__esModule", { value: true });
  9. exports.AbstractChatCompletionRunner = void 0;
  10. const error_1 = require("../error.js");
  11. const RunnableFunction_1 = require("./RunnableFunction.js");
  12. const chatCompletionUtils_1 = require("./chatCompletionUtils.js");
  13. const EventStream_1 = require("./EventStream.js");
  14. const parser_1 = require("../lib/parser.js");
  15. const DEFAULT_MAX_CHAT_COMPLETIONS = 10;
  16. class AbstractChatCompletionRunner extends EventStream_1.EventStream {
  17. constructor() {
  18. super(...arguments);
  19. _AbstractChatCompletionRunner_instances.add(this);
  20. this._chatCompletions = [];
  21. this.messages = [];
  22. }
  23. _addChatCompletion(chatCompletion) {
  24. this._chatCompletions.push(chatCompletion);
  25. this._emit('chatCompletion', chatCompletion);
  26. const message = chatCompletion.choices[0]?.message;
  27. if (message)
  28. this._addMessage(message);
  29. return chatCompletion;
  30. }
  31. _addMessage(message, emit = true) {
  32. if (!('content' in message))
  33. message.content = null;
  34. this.messages.push(message);
  35. if (emit) {
  36. this._emit('message', message);
  37. if (((0, chatCompletionUtils_1.isFunctionMessage)(message) || (0, chatCompletionUtils_1.isToolMessage)(message)) && message.content) {
  38. // Note, this assumes that {role: 'tool', content: …} is always the result of a call of tool of type=function.
  39. this._emit('functionCallResult', message.content);
  40. }
  41. else if ((0, chatCompletionUtils_1.isAssistantMessage)(message) && message.function_call) {
  42. this._emit('functionCall', message.function_call);
  43. }
  44. else if ((0, chatCompletionUtils_1.isAssistantMessage)(message) && message.tool_calls) {
  45. for (const tool_call of message.tool_calls) {
  46. if (tool_call.type === 'function') {
  47. this._emit('functionCall', tool_call.function);
  48. }
  49. }
  50. }
  51. }
  52. }
  53. /**
  54. * @returns a promise that resolves with the final ChatCompletion, or rejects
  55. * if an error occurred or the stream ended prematurely without producing a ChatCompletion.
  56. */
  57. async finalChatCompletion() {
  58. await this.done();
  59. const completion = this._chatCompletions[this._chatCompletions.length - 1];
  60. if (!completion)
  61. throw new error_1.OpenAIError('stream ended without producing a ChatCompletion');
  62. return completion;
  63. }
  64. /**
  65. * @returns a promise that resolves with the content of the final ChatCompletionMessage, or rejects
  66. * if an error occurred or the stream ended prematurely without producing a ChatCompletionMessage.
  67. */
  68. async finalContent() {
  69. await this.done();
  70. return __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_getFinalContent).call(this);
  71. }
  72. /**
  73. * @returns a promise that resolves with the the final assistant ChatCompletionMessage response,
  74. * or rejects if an error occurred or the stream ended prematurely without producing a ChatCompletionMessage.
  75. */
  76. async finalMessage() {
  77. await this.done();
  78. return __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_getFinalMessage).call(this);
  79. }
  80. /**
  81. * @returns a promise that resolves with the content of the final FunctionCall, or rejects
  82. * if an error occurred or the stream ended prematurely without producing a ChatCompletionMessage.
  83. */
  84. async finalFunctionCall() {
  85. await this.done();
  86. return __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_getFinalFunctionCall).call(this);
  87. }
  88. async finalFunctionCallResult() {
  89. await this.done();
  90. return __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_getFinalFunctionCallResult).call(this);
  91. }
  92. async totalUsage() {
  93. await this.done();
  94. return __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_calculateTotalUsage).call(this);
  95. }
  96. allChatCompletions() {
  97. return [...this._chatCompletions];
  98. }
  99. _emitFinal() {
  100. const completion = this._chatCompletions[this._chatCompletions.length - 1];
  101. if (completion)
  102. this._emit('finalChatCompletion', completion);
  103. const finalMessage = __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_getFinalMessage).call(this);
  104. if (finalMessage)
  105. this._emit('finalMessage', finalMessage);
  106. const finalContent = __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_getFinalContent).call(this);
  107. if (finalContent)
  108. this._emit('finalContent', finalContent);
  109. const finalFunctionCall = __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_getFinalFunctionCall).call(this);
  110. if (finalFunctionCall)
  111. this._emit('finalFunctionCall', finalFunctionCall);
  112. const finalFunctionCallResult = __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_getFinalFunctionCallResult).call(this);
  113. if (finalFunctionCallResult != null)
  114. this._emit('finalFunctionCallResult', finalFunctionCallResult);
  115. if (this._chatCompletions.some((c) => c.usage)) {
  116. this._emit('totalUsage', __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_calculateTotalUsage).call(this));
  117. }
  118. }
  119. async _createChatCompletion(client, params, options) {
  120. const signal = options?.signal;
  121. if (signal) {
  122. if (signal.aborted)
  123. this.controller.abort();
  124. signal.addEventListener('abort', () => this.controller.abort());
  125. }
  126. __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_validateParams).call(this, params);
  127. const chatCompletion = await client.chat.completions.create({ ...params, stream: false }, { ...options, signal: this.controller.signal });
  128. this._connected();
  129. return this._addChatCompletion((0, parser_1.parseChatCompletion)(chatCompletion, params));
  130. }
  131. async _runChatCompletion(client, params, options) {
  132. for (const message of params.messages) {
  133. this._addMessage(message, false);
  134. }
  135. return await this._createChatCompletion(client, params, options);
  136. }
  137. async _runFunctions(client, params, options) {
  138. const role = 'function';
  139. const { function_call = 'auto', stream, ...restParams } = params;
  140. const singleFunctionToCall = typeof function_call !== 'string' && function_call?.name;
  141. const { maxChatCompletions = DEFAULT_MAX_CHAT_COMPLETIONS } = options || {};
  142. const functionsByName = {};
  143. for (const f of params.functions) {
  144. functionsByName[f.name || f.function.name] = f;
  145. }
  146. const functions = params.functions.map((f) => ({
  147. name: f.name || f.function.name,
  148. parameters: f.parameters,
  149. description: f.description,
  150. }));
  151. for (const message of params.messages) {
  152. this._addMessage(message, false);
  153. }
  154. for (let i = 0; i < maxChatCompletions; ++i) {
  155. const chatCompletion = await this._createChatCompletion(client, {
  156. ...restParams,
  157. function_call,
  158. functions,
  159. messages: [...this.messages],
  160. }, options);
  161. const message = chatCompletion.choices[0]?.message;
  162. if (!message) {
  163. throw new error_1.OpenAIError(`missing message in ChatCompletion response`);
  164. }
  165. if (!message.function_call)
  166. return;
  167. const { name, arguments: args } = message.function_call;
  168. const fn = functionsByName[name];
  169. if (!fn) {
  170. const content = `Invalid function_call: ${JSON.stringify(name)}. Available options are: ${functions
  171. .map((f) => JSON.stringify(f.name))
  172. .join(', ')}. Please try again`;
  173. this._addMessage({ role, name, content });
  174. continue;
  175. }
  176. else if (singleFunctionToCall && singleFunctionToCall !== name) {
  177. const content = `Invalid function_call: ${JSON.stringify(name)}. ${JSON.stringify(singleFunctionToCall)} requested. Please try again`;
  178. this._addMessage({ role, name, content });
  179. continue;
  180. }
  181. let parsed;
  182. try {
  183. parsed = (0, RunnableFunction_1.isRunnableFunctionWithParse)(fn) ? await fn.parse(args) : args;
  184. }
  185. catch (error) {
  186. this._addMessage({
  187. role,
  188. name,
  189. content: error instanceof Error ? error.message : String(error),
  190. });
  191. continue;
  192. }
  193. // @ts-expect-error it can't rule out `never` type.
  194. const rawContent = await fn.function(parsed, this);
  195. const content = __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_stringifyFunctionCallResult).call(this, rawContent);
  196. this._addMessage({ role, name, content });
  197. if (singleFunctionToCall)
  198. return;
  199. }
  200. }
  201. async _runTools(client, params, options) {
  202. const role = 'tool';
  203. const { tool_choice = 'auto', stream, ...restParams } = params;
  204. const singleFunctionToCall = typeof tool_choice !== 'string' && tool_choice?.function?.name;
  205. const { maxChatCompletions = DEFAULT_MAX_CHAT_COMPLETIONS } = options || {};
  206. // TODO(someday): clean this logic up
  207. const inputTools = params.tools.map((tool) => {
  208. if ((0, parser_1.isAutoParsableTool)(tool)) {
  209. if (!tool.$callback) {
  210. throw new error_1.OpenAIError('Tool given to `.runTools()` that does not have an associated function');
  211. }
  212. return {
  213. type: 'function',
  214. function: {
  215. function: tool.$callback,
  216. name: tool.function.name,
  217. description: tool.function.description || '',
  218. parameters: tool.function.parameters,
  219. parse: tool.$parseRaw,
  220. strict: true,
  221. },
  222. };
  223. }
  224. return tool;
  225. });
  226. const functionsByName = {};
  227. for (const f of inputTools) {
  228. if (f.type === 'function') {
  229. functionsByName[f.function.name || f.function.function.name] = f.function;
  230. }
  231. }
  232. const tools = 'tools' in params ?
  233. inputTools.map((t) => t.type === 'function' ?
  234. {
  235. type: 'function',
  236. function: {
  237. name: t.function.name || t.function.function.name,
  238. parameters: t.function.parameters,
  239. description: t.function.description,
  240. strict: t.function.strict,
  241. },
  242. }
  243. : t)
  244. : undefined;
  245. for (const message of params.messages) {
  246. this._addMessage(message, false);
  247. }
  248. for (let i = 0; i < maxChatCompletions; ++i) {
  249. const chatCompletion = await this._createChatCompletion(client, {
  250. ...restParams,
  251. tool_choice,
  252. tools,
  253. messages: [...this.messages],
  254. }, options);
  255. const message = chatCompletion.choices[0]?.message;
  256. if (!message) {
  257. throw new error_1.OpenAIError(`missing message in ChatCompletion response`);
  258. }
  259. if (!message.tool_calls?.length) {
  260. return;
  261. }
  262. for (const tool_call of message.tool_calls) {
  263. if (tool_call.type !== 'function')
  264. continue;
  265. const tool_call_id = tool_call.id;
  266. const { name, arguments: args } = tool_call.function;
  267. const fn = functionsByName[name];
  268. if (!fn) {
  269. const content = `Invalid tool_call: ${JSON.stringify(name)}. Available options are: ${Object.keys(functionsByName)
  270. .map((name) => JSON.stringify(name))
  271. .join(', ')}. Please try again`;
  272. this._addMessage({ role, tool_call_id, content });
  273. continue;
  274. }
  275. else if (singleFunctionToCall && singleFunctionToCall !== name) {
  276. const content = `Invalid tool_call: ${JSON.stringify(name)}. ${JSON.stringify(singleFunctionToCall)} requested. Please try again`;
  277. this._addMessage({ role, tool_call_id, content });
  278. continue;
  279. }
  280. let parsed;
  281. try {
  282. parsed = (0, RunnableFunction_1.isRunnableFunctionWithParse)(fn) ? await fn.parse(args) : args;
  283. }
  284. catch (error) {
  285. const content = error instanceof Error ? error.message : String(error);
  286. this._addMessage({ role, tool_call_id, content });
  287. continue;
  288. }
  289. // @ts-expect-error it can't rule out `never` type.
  290. const rawContent = await fn.function(parsed, this);
  291. const content = __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_stringifyFunctionCallResult).call(this, rawContent);
  292. this._addMessage({ role, tool_call_id, content });
  293. if (singleFunctionToCall) {
  294. return;
  295. }
  296. }
  297. }
  298. return;
  299. }
  300. }
  301. exports.AbstractChatCompletionRunner = AbstractChatCompletionRunner;
  302. _AbstractChatCompletionRunner_instances = new WeakSet(), _AbstractChatCompletionRunner_getFinalContent = function _AbstractChatCompletionRunner_getFinalContent() {
  303. return __classPrivateFieldGet(this, _AbstractChatCompletionRunner_instances, "m", _AbstractChatCompletionRunner_getFinalMessage).call(this).content ?? null;
  304. }, _AbstractChatCompletionRunner_getFinalMessage = function _AbstractChatCompletionRunner_getFinalMessage() {
  305. let i = this.messages.length;
  306. while (i-- > 0) {
  307. const message = this.messages[i];
  308. if ((0, chatCompletionUtils_1.isAssistantMessage)(message)) {
  309. const { function_call, ...rest } = message;
  310. // TODO: support audio here
  311. const ret = {
  312. ...rest,
  313. content: message.content ?? null,
  314. refusal: message.refusal ?? null,
  315. };
  316. if (function_call) {
  317. ret.function_call = function_call;
  318. }
  319. return ret;
  320. }
  321. }
  322. throw new error_1.OpenAIError('stream ended without producing a ChatCompletionMessage with role=assistant');
  323. }, _AbstractChatCompletionRunner_getFinalFunctionCall = function _AbstractChatCompletionRunner_getFinalFunctionCall() {
  324. for (let i = this.messages.length - 1; i >= 0; i--) {
  325. const message = this.messages[i];
  326. if ((0, chatCompletionUtils_1.isAssistantMessage)(message) && message?.function_call) {
  327. return message.function_call;
  328. }
  329. if ((0, chatCompletionUtils_1.isAssistantMessage)(message) && message?.tool_calls?.length) {
  330. return message.tool_calls.at(-1)?.function;
  331. }
  332. }
  333. return;
  334. }, _AbstractChatCompletionRunner_getFinalFunctionCallResult = function _AbstractChatCompletionRunner_getFinalFunctionCallResult() {
  335. for (let i = this.messages.length - 1; i >= 0; i--) {
  336. const message = this.messages[i];
  337. if ((0, chatCompletionUtils_1.isFunctionMessage)(message) && message.content != null) {
  338. return message.content;
  339. }
  340. if ((0, chatCompletionUtils_1.isToolMessage)(message) &&
  341. message.content != null &&
  342. typeof message.content === 'string' &&
  343. this.messages.some((x) => x.role === 'assistant' &&
  344. x.tool_calls?.some((y) => y.type === 'function' && y.id === message.tool_call_id))) {
  345. return message.content;
  346. }
  347. }
  348. return;
  349. }, _AbstractChatCompletionRunner_calculateTotalUsage = function _AbstractChatCompletionRunner_calculateTotalUsage() {
  350. const total = {
  351. completion_tokens: 0,
  352. prompt_tokens: 0,
  353. total_tokens: 0,
  354. };
  355. for (const { usage } of this._chatCompletions) {
  356. if (usage) {
  357. total.completion_tokens += usage.completion_tokens;
  358. total.prompt_tokens += usage.prompt_tokens;
  359. total.total_tokens += usage.total_tokens;
  360. }
  361. }
  362. return total;
  363. }, _AbstractChatCompletionRunner_validateParams = function _AbstractChatCompletionRunner_validateParams(params) {
  364. if (params.n != null && params.n > 1) {
  365. throw new error_1.OpenAIError('ChatCompletion convenience helpers only support n=1 at this time. To use n>1, please use chat.completions.create() directly.');
  366. }
  367. }, _AbstractChatCompletionRunner_stringifyFunctionCallResult = function _AbstractChatCompletionRunner_stringifyFunctionCallResult(rawContent) {
  368. return (typeof rawContent === 'string' ? rawContent
  369. : rawContent === undefined ? 'undefined'
  370. : JSON.stringify(rawContent));
  371. };
  372. //# sourceMappingURL=AbstractChatCompletionRunner.js.map