moonshot.ts 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. "use client";
  2. // azure and openai, using same models. so using same LLMApi.
  3. import {
  4. ApiPath,
  5. DEFAULT_API_HOST,
  6. DEFAULT_MODELS,
  7. Moonshot,
  8. REQUEST_TIMEOUT_MS,
  9. ServiceProvider,
  10. } from "@/app/constant";
  11. import {
  12. useAccessStore,
  13. useAppConfig,
  14. useChatStore,
  15. ChatMessageTool,
  16. usePluginStore,
  17. } from "@/app/store";
  18. import { collectModelsWithDefaultModel } from "@/app/utils/model";
  19. import { preProcessImageContent, stream } from "@/app/utils/chat";
  20. import { cloudflareAIGatewayUrl } from "@/app/utils/cloudflare";
  21. import {
  22. ChatOptions,
  23. getHeaders,
  24. LLMApi,
  25. LLMModel,
  26. LLMUsage,
  27. MultimodalContent,
  28. } from "../api";
  29. import Locale from "../../locales";
  30. import {
  31. EventStreamContentType,
  32. fetchEventSource,
  33. } from "@fortaine/fetch-event-source";
  34. import { prettyObject } from "@/app/utils/format";
  35. import { getClientConfig } from "@/app/config/client";
  36. import { getMessageTextContent } from "@/app/utils";
  37. import { OpenAIListModelResponse, RequestPayload } from "./openai";
  38. export class MoonshotApi implements LLMApi {
  39. private disableListModels = true;
  40. path(path: string): string {
  41. const accessStore = useAccessStore.getState();
  42. let baseUrl = "";
  43. if (accessStore.useCustomConfig) {
  44. baseUrl = accessStore.moonshotUrl;
  45. }
  46. if (baseUrl.length === 0) {
  47. const isApp = !!getClientConfig()?.isApp;
  48. const apiPath = ApiPath.Moonshot;
  49. baseUrl = isApp ? DEFAULT_API_HOST + "/proxy" + apiPath : apiPath;
  50. }
  51. if (baseUrl.endsWith("/")) {
  52. baseUrl = baseUrl.slice(0, baseUrl.length - 1);
  53. }
  54. if (!baseUrl.startsWith("http") && !baseUrl.startsWith(ApiPath.Moonshot)) {
  55. baseUrl = "https://" + baseUrl;
  56. }
  57. console.log("[Proxy Endpoint] ", baseUrl, path);
  58. return [baseUrl, path].join("/");
  59. }
  60. extractMessage(res: any) {
  61. return res.choices?.at(0)?.message?.content ?? "";
  62. }
  63. async chat(options: ChatOptions) {
  64. const messages: ChatOptions["messages"] = [];
  65. for (const v of options.messages) {
  66. const content = getMessageTextContent(v);
  67. messages.push({ role: v.role, content });
  68. }
  69. const modelConfig = {
  70. ...useAppConfig.getState().modelConfig,
  71. ...useChatStore.getState().currentSession().mask.modelConfig,
  72. ...{
  73. model: options.config.model,
  74. providerName: options.config.providerName,
  75. },
  76. };
  77. const requestPayload: RequestPayload = {
  78. messages,
  79. stream: options.config.stream,
  80. model: modelConfig.model,
  81. temperature: modelConfig.temperature,
  82. presence_penalty: modelConfig.presence_penalty,
  83. frequency_penalty: modelConfig.frequency_penalty,
  84. top_p: modelConfig.top_p,
  85. // max_tokens: Math.max(modelConfig.max_tokens, 1024),
  86. // Please do not ask me why not send max_tokens, no reason, this param is just shit, I dont want to explain anymore.
  87. };
  88. console.log("[Request] openai payload: ", requestPayload);
  89. const shouldStream = !!options.config.stream;
  90. const controller = new AbortController();
  91. options.onController?.(controller);
  92. try {
  93. const chatPath = this.path(Moonshot.ChatPath);
  94. const chatPayload = {
  95. method: "POST",
  96. body: JSON.stringify(requestPayload),
  97. signal: controller.signal,
  98. headers: getHeaders(),
  99. };
  100. // make a fetch request
  101. const requestTimeoutId = setTimeout(
  102. () => controller.abort(),
  103. REQUEST_TIMEOUT_MS,
  104. );
  105. if (shouldStream) {
  106. const [tools, funcs] = usePluginStore
  107. .getState()
  108. .getAsTools(
  109. useChatStore.getState().currentSession().mask?.plugin as string[],
  110. );
  111. console.log("getAsTools", tools, funcs);
  112. return stream(
  113. chatPath,
  114. requestPayload,
  115. getHeaders(),
  116. tools as any,
  117. funcs,
  118. controller,
  119. // parseSSE
  120. (text: string, runTools: ChatMessageTool[]) => {
  121. // console.log("parseSSE", text, runTools);
  122. const json = JSON.parse(text);
  123. const choices = json.choices as Array<{
  124. delta: {
  125. content: string;
  126. tool_calls: ChatMessageTool[];
  127. };
  128. }>;
  129. const tool_calls = choices[0]?.delta?.tool_calls;
  130. if (tool_calls?.length > 0) {
  131. const index = tool_calls[0]?.index;
  132. const id = tool_calls[0]?.id;
  133. const args = tool_calls[0]?.function?.arguments;
  134. if (id) {
  135. runTools.push({
  136. id,
  137. type: tool_calls[0]?.type,
  138. function: {
  139. name: tool_calls[0]?.function?.name as string,
  140. arguments: args,
  141. },
  142. });
  143. } else {
  144. // @ts-ignore
  145. runTools[index]["function"]["arguments"] += args;
  146. }
  147. }
  148. return choices[0]?.delta?.content;
  149. },
  150. // processToolMessage, include tool_calls message and tool call results
  151. (
  152. requestPayload: RequestPayload,
  153. toolCallMessage: any,
  154. toolCallResult: any[],
  155. ) => {
  156. // @ts-ignore
  157. requestPayload?.messages?.splice(
  158. // @ts-ignore
  159. requestPayload?.messages?.length,
  160. 0,
  161. toolCallMessage,
  162. ...toolCallResult,
  163. );
  164. },
  165. options,
  166. );
  167. } else {
  168. const res = await fetch(chatPath, chatPayload);
  169. clearTimeout(requestTimeoutId);
  170. const resJson = await res.json();
  171. const message = this.extractMessage(resJson);
  172. options.onFinish(message);
  173. }
  174. } catch (e) {
  175. console.log("[Request] failed to make a chat request", e);
  176. options.onError?.(e as Error);
  177. }
  178. }
  179. async usage() {
  180. return {
  181. used: 0,
  182. total: 0,
  183. };
  184. }
  185. async models(): Promise<LLMModel[]> {
  186. return [];
  187. }
  188. }