Explorar el Código

对接智谱AI

李富豪 hace 1 año
padre
commit
4e8ef2887f

+ 0 - 6
.lintstagedrc.json

@@ -1,6 +0,0 @@
-{
-  "./app/**/*.{js,ts,jsx,tsx,json,html,css,md}": [
-    "eslint --fix",
-    "prettier --write"
-  ]
-}

+ 0 - 128
CODE_OF_CONDUCT.md

@@ -1,128 +0,0 @@
-# Contributor Covenant Code of Conduct
-
-## Our Pledge
-
-We as members, contributors, and leaders pledge to make participation in our
-community a harassment-free experience for everyone, regardless of age, body
-size, visible or invisible disability, ethnicity, sex characteristics, gender
-identity and expression, level of experience, education, socio-economic status,
-nationality, personal appearance, race, religion, or sexual identity
-and orientation.
-
-We pledge to act and interact in ways that contribute to an open, welcoming,
-diverse, inclusive, and healthy community.
-
-## Our Standards
-
-Examples of behavior that contributes to a positive environment for our
-community include:
-
-* Demonstrating empathy and kindness toward other people
-* Being respectful of differing opinions, viewpoints, and experiences
-* Giving and gracefully accepting constructive feedback
-* Accepting responsibility and apologizing to those affected by our mistakes,
-  and learning from the experience
-* Focusing on what is best not just for us as individuals, but for the
-  overall community
-
-Examples of unacceptable behavior include:
-
-* The use of sexualized language or imagery, and sexual attention or
-  advances of any kind
-* Trolling, insulting or derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or email
-  address, without their explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
-  professional setting
-
-## Enforcement Responsibilities
-
-Community leaders are responsible for clarifying and enforcing our standards of
-acceptable behavior and will take appropriate and fair corrective action in
-response to any behavior that they deem inappropriate, threatening, offensive,
-or harmful.
-
-Community leaders have the right and responsibility to remove, edit, or reject
-comments, commits, code, wiki edits, issues, and other contributions that are
-not aligned to this Code of Conduct, and will communicate reasons for moderation
-decisions when appropriate.
-
-## Scope
-
-This Code of Conduct applies within all community spaces, and also applies when
-an individual is officially representing the community in public spaces.
-Examples of representing our community include using an official e-mail address,
-posting via an official social media account, or acting as an appointed
-representative at an online or offline event.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported to the community leaders responsible for enforcement at
-flynn.zhang@foxmail.com.
-All complaints will be reviewed and investigated promptly and fairly.
-
-All community leaders are obligated to respect the privacy and security of the
-reporter of any incident.
-
-## Enforcement Guidelines
-
-Community leaders will follow these Community Impact Guidelines in determining
-the consequences for any action they deem in violation of this Code of Conduct:
-
-### 1. Correction
-
-**Community Impact**: Use of inappropriate language or other behavior deemed
-unprofessional or unwelcome in the community.
-
-**Consequence**: A private, written warning from community leaders, providing
-clarity around the nature of the violation and an explanation of why the
-behavior was inappropriate. A public apology may be requested.
-
-### 2. Warning
-
-**Community Impact**: A violation through a single incident or series
-of actions.
-
-**Consequence**: A warning with consequences for continued behavior. No
-interaction with the people involved, including unsolicited interaction with
-those enforcing the Code of Conduct, for a specified period of time. This
-includes avoiding interactions in community spaces as well as external channels
-like social media. Violating these terms may lead to a temporary or
-permanent ban.
-
-### 3. Temporary Ban
-
-**Community Impact**: A serious violation of community standards, including
-sustained inappropriate behavior.
-
-**Consequence**: A temporary ban from any sort of interaction or public
-communication with the community for a specified period of time. No public or
-private interaction with the people involved, including unsolicited interaction
-with those enforcing the Code of Conduct, is allowed during this period.
-Violating these terms may lead to a permanent ban.
-
-### 4. Permanent Ban
-
-**Community Impact**: Demonstrating a pattern of violation of community
-standards, including sustained inappropriate behavior,  harassment of an
-individual, or aggression toward or disparagement of classes of individuals.
-
-**Consequence**: A permanent ban from any sort of public interaction within
-the community.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage],
-version 2.0, available at
-https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
-
-Community Impact Guidelines were inspired by [Mozilla's code of conduct
-enforcement ladder](https://github.com/mozilla/diversity).
-
-[homepage]: https://www.contributor-covenant.org
-
-For answers to common questions about this code of conduct, see the FAQ at
-https://www.contributor-covenant.org/faq. Translations are available at
-https://www.contributor-covenant.org/translations.

+ 0 - 21
LICENSE

@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2023-2024 Zhang Yifei
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.

+ 24 - 19
app/client/api.ts

@@ -1,11 +1,11 @@
 import { getClientConfig } from "../config/client";
 import { getClientConfig } from "../config/client";
 import {
 import {
   ACCESS_CODE_PREFIX,
   ACCESS_CODE_PREFIX,
-  Azure,
   ModelProvider,
   ModelProvider,
   ServiceProvider,
   ServiceProvider,
 } from "../constant";
 } from "../constant";
 import { ChatMessage, ModelType, useAccessStore, useChatStore } from "../store";
 import { ChatMessage, ModelType, useAccessStore, useChatStore } from "../store";
+import { BigModelApi } from "./platforms/bigmodel";
 import { ChatGPTApi, DalleRequestPayload } from "./platforms/openai";
 import { ChatGPTApi, DalleRequestPayload } from "./platforms/openai";
 import { GeminiProApi } from "./platforms/google";
 import { GeminiProApi } from "./platforms/google";
 import { ClaudeApi } from "./platforms/anthropic";
 import { ClaudeApi } from "./platforms/anthropic";
@@ -108,6 +108,9 @@ export class ClientApi {
 
 
   constructor(provider: ModelProvider = ModelProvider.GPT) {
   constructor(provider: ModelProvider = ModelProvider.GPT) {
     switch (provider) {
     switch (provider) {
+      case ModelProvider.GLM:
+        this.llm = new BigModelApi();
+        break;
       case ModelProvider.GeminiPro:
       case ModelProvider.GeminiPro:
         this.llm = new GeminiProApi();
         this.llm = new GeminiProApi();
         break;
         break;
@@ -137,11 +140,11 @@ export class ClientApi {
     }
     }
   }
   }
 
 
-  config() {}
+  config() { }
 
 
-  prompts() {}
+  prompts() { }
 
 
-  masks() {}
+  masks() { }
 
 
   async share(messages: ChatMessage[], avatarUrl: string | null = null) {
   async share(messages: ChatMessage[], avatarUrl: string | null = null) {
     const msgs = messages
     const msgs = messages
@@ -220,20 +223,20 @@ export function getHeaders() {
     const apiKey = isGoogle
     const apiKey = isGoogle
       ? accessStore.googleApiKey
       ? accessStore.googleApiKey
       : isAzure
       : isAzure
-      ? accessStore.azureApiKey
-      : isAnthropic
-      ? accessStore.anthropicApiKey
-      : isByteDance
-      ? accessStore.bytedanceApiKey
-      : isAlibaba
-      ? accessStore.alibabaApiKey
-      : isMoonshot
-      ? accessStore.moonshotApiKey
-      : isIflytek
-      ? accessStore.iflytekApiKey && accessStore.iflytekApiSecret
-        ? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret
-        : ""
-      : accessStore.openaiApiKey;
+        ? accessStore.azureApiKey
+        : isAnthropic
+          ? accessStore.anthropicApiKey
+          : isByteDance
+            ? accessStore.bytedanceApiKey
+            : isAlibaba
+              ? accessStore.alibabaApiKey
+              : isMoonshot
+                ? accessStore.moonshotApiKey
+                : isIflytek
+                  ? accessStore.iflytekApiKey && accessStore.iflytekApiSecret
+                    ? accessStore.iflytekApiKey + ":" + accessStore.iflytekApiSecret
+                    : ""
+                  : accessStore.openaiApiKey;
     return {
     return {
       isGoogle,
       isGoogle,
       isAzure,
       isAzure,
@@ -282,6 +285,8 @@ export function getHeaders() {
 
 
 export function getClientApi(provider: ServiceProvider): ClientApi {
 export function getClientApi(provider: ServiceProvider): ClientApi {
   switch (provider) {
   switch (provider) {
+    case ServiceProvider.BigModel:
+      return new ClientApi(ModelProvider.GLM);
     case ServiceProvider.Google:
     case ServiceProvider.Google:
       return new ClientApi(ModelProvider.GeminiPro);
       return new ClientApi(ModelProvider.GeminiPro);
     case ServiceProvider.Anthropic:
     case ServiceProvider.Anthropic:
@@ -301,4 +306,4 @@ export function getClientApi(provider: ServiceProvider): ClientApi {
     default:
     default:
       return new ClientApi(ModelProvider.GPT);
       return new ClientApi(ModelProvider.GPT);
   }
   }
-}
+}

+ 203 - 0
app/client/platforms/bigmodel.ts

@@ -0,0 +1,203 @@
+"use client";
+import { REQUEST_TIMEOUT_MS } from "@/app/constant";
+import { useAppConfig, useChatStore } from "@/app/store";
+import {
+  ChatOptions,
+  LLMApi,
+  LLMModel,
+} from "../api";
+import Locale from "../../locales";
+import {
+  EventStreamContentType,
+  fetchEventSource,
+} from "@fortaine/fetch-event-source";
+import { prettyObject } from "@/app/utils/format";
+import { getMessageTextContent } from "@/app/utils";
+
+export class BigModelApi implements LLMApi {
+  path(): string {
+    return '/api/paas/v4/chat/completions';
+  }
+
+  async chat(options: ChatOptions) {
+    const messages = options.messages.map((v) => ({
+      role: v.role,
+      content: getMessageTextContent(v),
+    }));
+
+    if (messages.length % 2 === 0) {
+      messages.unshift({
+        role: "user",
+        content: " ",
+      });
+    }
+
+    const modelConfig = {
+      ...useAppConfig.getState().modelConfig,
+      ...useChatStore.getState().currentSession().mask.modelConfig,
+      ...{
+        model: options.config.model,
+      },
+    };
+
+    const shouldStream = !!options.config.stream;
+
+    // 通用大模型参数
+    const requestPayload: any = {
+      messages,
+      stream: shouldStream,
+      model: 'glm-4-flash',
+      temperature: modelConfig.temperature,
+      top_p: modelConfig.top_p,
+    };
+
+    const controller = new AbortController();
+
+    options.onController?.(controller);
+
+    try {
+      let chatPath = this.path();
+      const chatPayload = {
+        method: "POST",
+        body: JSON.stringify(requestPayload),
+        signal: controller.signal,
+        headers: {
+          'Content-Type': 'application/json',
+          // APIKey
+          Authorization: '20480a1ad76c4d9e0a168206a25f9614.bUjEVNXHpgY0H0GH'
+        },
+      };
+
+      // make a fetch request
+      const requestTimeoutId = setTimeout(
+        () => controller.abort(),
+        REQUEST_TIMEOUT_MS,
+      );
+
+      if (shouldStream) {
+        let responseText = "";
+        let remainText = "";
+        let finished = false;
+
+        // animate response to make it looks smooth
+        function animateResponseText() {
+          if (finished || controller.signal.aborted) {
+            responseText += remainText;
+            console.log("[Response Animation] finished");
+            if (responseText?.length === 0) {
+              options.onError?.(new Error("empty response from server"));
+            }
+            return;
+          }
+
+          if (remainText.length > 0) {
+            const fetchCount = Math.max(1, Math.round(remainText.length / 60));
+            const fetchText = remainText.slice(0, fetchCount);
+            responseText += fetchText;
+            remainText = remainText.slice(fetchCount);
+            options.onUpdate?.(responseText, fetchText);
+          }
+
+          requestAnimationFrame(animateResponseText);
+        }
+
+        // start animaion
+        animateResponseText();
+
+        const finish = () => {
+          if (!finished) {
+            finished = true;
+            options.onFinish(responseText + remainText);
+          }
+        };
+
+        controller.signal.onabort = finish;
+
+        fetchEventSource(chatPath, {
+          ...chatPayload,
+          async onopen(res) {
+            clearTimeout(requestTimeoutId);
+            const contentType = res.headers.get("content-type");
+            console.log("[Baidu] request response content type: ", contentType);
+
+            if (contentType?.startsWith("text/plain")) {
+              responseText = await res.clone().text();
+              return finish();
+            }
+
+            if (
+              !res.ok ||
+              !res.headers
+                .get("content-type")
+                ?.startsWith(EventStreamContentType) ||
+              res.status !== 200
+            ) {
+              const responseTexts = [responseText];
+              let extraInfo = await res.clone().text();
+              try {
+                const resJson = await res.clone().json();
+                extraInfo = prettyObject(resJson);
+              } catch { }
+
+              if (res.status === 401) {
+                responseTexts.push(Locale.Error.Unauthorized);
+              }
+
+              if (extraInfo) {
+                responseTexts.push(extraInfo);
+              }
+
+              responseText = responseTexts.join("\n\n");
+
+              return finish();
+            }
+          },
+          onmessage(msg) {
+            if (msg.data === "[DONE]" || finished) {
+              return finish();
+            }
+            const text = msg.data;
+            try {
+              const json = JSON.parse(text);
+              const choices = json.choices as Array<{
+                delta: { content: string };
+              }>;
+              const delta = choices[0]?.delta?.content;
+              if (delta) {
+                remainText += delta;
+              }
+            } catch (e) {
+              console.error("[Request] parse error", text, msg);
+            }
+          },
+          onclose() {
+            finish();
+          },
+          onerror(e) {
+            options.onError?.(e);
+            throw e;
+          },
+          openWhenHidden: true,
+        });
+      } else {
+        const res = await fetch(chatPath, chatPayload);
+        clearTimeout(requestTimeoutId);
+        const resJson = await res.json();
+        const message = resJson?.result;
+        options.onFinish(message);
+      }
+    } catch (e) {
+      options.onError?.(e as Error);
+    }
+  }
+  async usage() {
+    return {
+      used: 0,
+      total: 0,
+    };
+  }
+
+  async models(): Promise<LLMModel[]> {
+    return [];
+  }
+}

+ 124 - 55
app/components/chat.tsx

@@ -173,6 +173,66 @@ export function SessionConfigModel(props: { onClose: () => void }) {
   );
   );
 }
 }
 
 
+// 提示词
+const CallWord: React.FC = () => {
+  const list = [
+    {
+      title: '信息公布',
+      text: '在哪里查看招聘信息?',
+    },
+    {
+      title: '招聘岗位',
+      text: '今年招聘的岗位有哪些?',
+    },
+    {
+      title: '专业要求',
+      text: '招聘的岗位有什么专业要求?',
+    },
+    {
+      title: '工作地点',
+      text: '全国都有工作地点吗?',
+    },
+    {
+      title: '薪资待遇',
+      text: '企业的工资水平怎么样?',
+    },
+    {
+      title: '人才计划',
+      text: '企业有什么人才培养计划?',
+    },
+    {
+      title: '落户政策',
+      text: '公司是否能协助我落户?',
+    }
+  ]
+
+  return (
+    <>
+      {
+        list.map((item, index) => {
+          return <span
+            key={index}
+            style={{
+              padding: '5px 10px',
+              background: '#f6f7f8',
+              color: '#5e5e66',
+              borderRadius: 4,
+              margin: '0 5px 10px 0',
+              cursor: 'pointer',
+              fontSize: 12
+            }}
+            onClick={() => {
+
+            }}
+          >
+            {item.title}
+          </span>
+        })
+      }
+    </>
+  )
+}
+
 function PromptToast(props: {
 function PromptToast(props: {
   showToast?: boolean;
   showToast?: boolean;
   showModal?: boolean;
   showModal?: boolean;
@@ -525,6 +585,7 @@ export function ChatActions(props: {
           icon={<StopIcon />}
           icon={<StopIcon />}
         />
         />
       )}
       )}
+
       {!props.hitBottom && (
       {!props.hitBottom && (
         <ChatAction
         <ChatAction
           onClick={props.scrollToBottom}
           onClick={props.scrollToBottom}
@@ -532,13 +593,20 @@ export function ChatActions(props: {
           icon={<BottomIcon />}
           icon={<BottomIcon />}
         />
         />
       )}
       )}
-      {props.hitBottom && (
+
+      <ChatAction
+        onClick={() => { }}
+        text='新建对话'
+        icon={<RobotIcon />}
+      />
+
+      {/* {props.hitBottom && (
         <ChatAction
         <ChatAction
           onClick={props.showPromptModal}
           onClick={props.showPromptModal}
           text={Locale.Chat.InputActions.Settings}
           text={Locale.Chat.InputActions.Settings}
           icon={<SettingsIcon />}
           icon={<SettingsIcon />}
         />
         />
-      )}
+      )} */}
 
 
       {showUploadImage && (
       {showUploadImage && (
         <ChatAction
         <ChatAction
@@ -547,7 +615,8 @@ export function ChatActions(props: {
           icon={props.uploading ? <LoadingButtonIcon /> : <ImageIcon />}
           icon={props.uploading ? <LoadingButtonIcon /> : <ImageIcon />}
         />
         />
       )}
       )}
-      <ChatAction
+
+      {/* <ChatAction
         onClick={nextTheme}
         onClick={nextTheme}
         text={Locale.Chat.InputActions.Theme[theme]}
         text={Locale.Chat.InputActions.Theme[theme]}
         icon={
         icon={
@@ -561,7 +630,9 @@ export function ChatActions(props: {
             ) : null}
             ) : null}
           </>
           </>
         }
         }
-      />
+      /> */}
+
+      <CallWord />
 
 
       <ChatAction
       <ChatAction
         onClick={props.showPromptHints}
         onClick={props.showPromptHints}
@@ -569,15 +640,15 @@ export function ChatActions(props: {
         icon={<PromptIcon />}
         icon={<PromptIcon />}
       />
       />
 
 
-      <ChatAction
+      {/* <ChatAction
         onClick={() => {
         onClick={() => {
           navigate(Path.Masks);
           navigate(Path.Masks);
         }}
         }}
         text={Locale.Chat.InputActions.Masks}
         text={Locale.Chat.InputActions.Masks}
         icon={<MaskIcon />}
         icon={<MaskIcon />}
-      />
+      /> */}
 
 
-      <ChatAction
+      {/* <ChatAction
         text={Locale.Chat.InputActions.Clear}
         text={Locale.Chat.InputActions.Clear}
         icon={<BreakIcon />}
         icon={<BreakIcon />}
         onClick={() => {
         onClick={() => {
@@ -590,23 +661,22 @@ export function ChatActions(props: {
             }
             }
           });
           });
         }}
         }}
-      />
+      /> */}
 
 
-      <ChatAction
+      {/* <ChatAction
         onClick={() => setShowModelSelector(true)}
         onClick={() => setShowModelSelector(true)}
         text={currentModelName}
         text={currentModelName}
         icon={<RobotIcon />}
         icon={<RobotIcon />}
-      />
+      /> */}
 
 
       {showModelSelector && (
       {showModelSelector && (
         <Selector
         <Selector
           defaultSelectedValue={`${currentModel}@${currentProviderName}`}
           defaultSelectedValue={`${currentModel}@${currentProviderName}`}
           items={models.map((m) => ({
           items={models.map((m) => ({
-            title: `${m.displayName}${
-              m?.provider?.providerName
-                ? "(" + m?.provider?.providerName + ")"
-                : ""
-            }`,
+            title: `${m.displayName}${m?.provider?.providerName
+              ? "(" + m?.provider?.providerName + ")"
+              : ""
+              }`,
             value: `${m.name}@${m?.provider?.providerName}`,
             value: `${m.name}@${m?.provider?.providerName}`,
           }))}
           }))}
           onClose={() => setShowModelSelector(false)}
           onClose={() => setShowModelSelector(false)}
@@ -659,11 +729,12 @@ export function ChatActions(props: {
         />
         />
       )}
       )}
 
 
-      <ChatAction
+      {/* <ChatAction
         onClick={() => setShowPluginSelector(true)}
         onClick={() => setShowPluginSelector(true)}
         text={Locale.Plugin.Name}
         text={Locale.Plugin.Name}
         icon={<PluginIcon />}
         icon={<PluginIcon />}
-      />
+      /> */}
+
       {showPluginSelector && (
       {showPluginSelector && (
         <Selector
         <Selector
           multiple
           multiple
@@ -778,9 +849,9 @@ function _Chat() {
   const scrollRef = useRef<HTMLDivElement>(null);
   const scrollRef = useRef<HTMLDivElement>(null);
   const isScrolledToBottom = scrollRef?.current
   const isScrolledToBottom = scrollRef?.current
     ? Math.abs(
     ? Math.abs(
-        scrollRef.current.scrollHeight -
-          (scrollRef.current.scrollTop + scrollRef.current.clientHeight),
-      ) <= 1
+      scrollRef.current.scrollHeight -
+      (scrollRef.current.scrollTop + scrollRef.current.clientHeight),
+    ) <= 1
     : false;
     : false;
   const { setAutoScroll, scrollDomToBottom } = useScrollToBottom(
   const { setAutoScroll, scrollDomToBottom } = useScrollToBottom(
     scrollRef,
     scrollRef,
@@ -1061,27 +1132,27 @@ function _Chat() {
       .concat(
       .concat(
         isLoading
         isLoading
           ? [
           ? [
-              {
-                ...createMessage({
-                  role: "assistant",
-                  content: "……",
-                }),
-                preview: true,
-              },
-            ]
+            {
+              ...createMessage({
+                role: "assistant",
+                content: "……",
+              }),
+              preview: true,
+            },
+          ]
           : [],
           : [],
       )
       )
       .concat(
       .concat(
         userInput.length > 0 && config.sendPreviewBubble
         userInput.length > 0 && config.sendPreviewBubble
           ? [
           ? [
-              {
-                ...createMessage({
-                  role: "user",
-                  content: userInput,
-                }),
-                preview: true,
-              },
-            ]
+            {
+              ...createMessage({
+                role: "user",
+                content: userInput,
+              }),
+              preview: true,
+            },
+          ]
           : [],
           : [],
       );
       );
   }, [
   }, [
@@ -1176,7 +1247,7 @@ function _Chat() {
         if (payload.key || payload.url) {
         if (payload.key || payload.url) {
           showConfirm(
           showConfirm(
             Locale.URLCommand.Settings +
             Locale.URLCommand.Settings +
-              `\n${JSON.stringify(payload, null, 4)}`,
+            `\n${JSON.stringify(payload, null, 4)}`,
           ).then((res) => {
           ).then((res) => {
             if (!res) return;
             if (!res) return;
             if (payload.key) {
             if (payload.key) {
@@ -1325,14 +1396,14 @@ function _Chat() {
             className={`window-header-main-title ${styles["chat-body-main-title"]}`}
             className={`window-header-main-title ${styles["chat-body-main-title"]}`}
             onClickCapture={() => setIsEditingMessage(true)}
             onClickCapture={() => setIsEditingMessage(true)}
           >
           >
-            {!session.topic ? DEFAULT_TOPIC : session.topic}
+            建科招聘智能助理
           </div>
           </div>
           <div className="window-header-sub-title">
           <div className="window-header-sub-title">
             {Locale.Chat.SubTitle(session.messages.length)}
             {Locale.Chat.SubTitle(session.messages.length)}
           </div>
           </div>
         </div>
         </div>
         <div className="window-actions">
         <div className="window-actions">
-          {!isMobileScreen && (
+          {/* {!isMobileScreen && (
             <div className="window-action-button">
             <div className="window-action-button">
               <IconButton
               <IconButton
                 icon={<RenameIcon />}
                 icon={<RenameIcon />}
@@ -1342,8 +1413,8 @@ function _Chat() {
                 onClick={() => setIsEditingMessage(true)}
                 onClick={() => setIsEditingMessage(true)}
               />
               />
             </div>
             </div>
-          )}
-          <div className="window-action-button">
+          )} */}
+          {/* <div className="window-action-button">
             <IconButton
             <IconButton
               icon={<ExportIcon />}
               icon={<ExportIcon />}
               bordered
               bordered
@@ -1352,7 +1423,7 @@ function _Chat() {
                 setShowExport(true);
                 setShowExport(true);
               }}
               }}
             />
             />
-          </div>
+          </div> */}
           {showMaxIcon && (
           {showMaxIcon && (
             <div className="window-action-button">
             <div className="window-action-button">
               <IconButton
               <IconButton
@@ -1370,11 +1441,11 @@ function _Chat() {
           )}
           )}
         </div>
         </div>
 
 
-        <PromptToast
+        {/* <PromptToast
           showToast={!hitBottom}
           showToast={!hitBottom}
           showModal={showPromptModal}
           showModal={showPromptModal}
           setShowModal={setShowPromptModal}
           setShowModal={setShowPromptModal}
-        />
+        /> */}
       </div>
       </div>
 
 
       <div
       <div
@@ -1408,7 +1479,7 @@ function _Chat() {
                 <div className={styles["chat-message-container"]}>
                 <div className={styles["chat-message-container"]}>
                   <div className={styles["chat-message-header"]}>
                   <div className={styles["chat-message-header"]}>
                     <div className={styles["chat-message-avatar"]}>
                     <div className={styles["chat-message-avatar"]}>
-                      <div className={styles["chat-message-edit"]}>
+                      {/* <div className={styles["chat-message-edit"]}>
                         <IconButton
                         <IconButton
                           icon={<EditIcon />}
                           icon={<EditIcon />}
                           aria={Locale.Chat.Actions.Edit}
                           aria={Locale.Chat.Actions.Edit}
@@ -1442,8 +1513,9 @@ function _Chat() {
                             });
                             });
                           }}
                           }}
                         ></IconButton>
                         ></IconButton>
-                      </div>
+                      </div> */}
                       {isUser ? (
                       {isUser ? (
+                        // 在这里换头像
                         <Avatar avatar={config.avatar} />
                         <Avatar avatar={config.avatar} />
                       ) : (
                       ) : (
                         <>
                         <>
@@ -1460,7 +1532,6 @@ function _Chat() {
                         </>
                         </>
                       )}
                       )}
                     </div>
                     </div>
-
                     {showActions && (
                     {showActions && (
                       <div className={styles["chat-message-actions"]}>
                       <div className={styles["chat-message-actions"]}>
                         <div className={styles["chat-input-actions"]}>
                         <div className={styles["chat-input-actions"]}>
@@ -1559,12 +1630,11 @@ function _Chat() {
                       </div>
                       </div>
                     )}
                     )}
                   </div>
                   </div>
-
-                  <div className={styles["chat-message-action-date"]}>
+                  {/* <div className={styles["chat-message-action-date"]}>
                     {isContext
                     {isContext
                       ? Locale.Chat.IsContext
                       ? Locale.Chat.IsContext
                       : message.date.toLocaleString()}
                       : message.date.toLocaleString()}
-                  </div>
+                  </div> */}
                 </div>
                 </div>
               </div>
               </div>
               {shouldShowClearContextDivider && <ClearContextDivider />}
               {shouldShowClearContextDivider && <ClearContextDivider />}
@@ -1597,11 +1667,10 @@ function _Chat() {
           }}
           }}
         />
         />
         <label
         <label
-          className={`${styles["chat-input-panel-inner"]} ${
-            attachImages.length != 0
-              ? styles["chat-input-panel-inner-attach"]
-              : ""
-          }`}
+          className={`${styles["chat-input-panel-inner"]} ${attachImages.length != 0
+            ? styles["chat-input-panel-inner-attach"]
+            : ""
+            }`}
           htmlFor="chat-input"
           htmlFor="chat-input"
         >
         >
           <textarea
           <textarea

+ 6 - 4
app/components/home.module.scss

@@ -95,7 +95,7 @@
 }
 }
 
 
 .window-content {
 .window-content {
-  width: var(--window-content-width);
+  width: 100%;
   height: 100%;
   height: 100%;
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
@@ -211,12 +211,12 @@
   cursor: pointer;
   cursor: pointer;
 }
 }
 
 
-.chat-item:hover > .chat-item-delete {
+.chat-item:hover>.chat-item-delete {
   opacity: 0.5;
   opacity: 0.5;
   transform: translateX(-4px);
   transform: translateX(-4px);
 }
 }
 
 
-.chat-item:hover > .chat-item-delete:hover {
+.chat-item:hover>.chat-item-delete:hover {
   opacity: 1;
   opacity: 1;
 }
 }
 
 
@@ -237,10 +237,12 @@
 }
 }
 
 
 .narrow-sidebar {
 .narrow-sidebar {
+
   .sidebar-title,
   .sidebar-title,
   .sidebar-sub-title {
   .sidebar-sub-title {
     display: none;
     display: none;
   }
   }
+
   .sidebar-logo {
   .sidebar-logo {
     position: relative;
     position: relative;
     display: flex;
     display: flex;
@@ -343,4 +345,4 @@
 
 
 .rtl-screen {
 .rtl-screen {
   direction: rtl;
   direction: rtl;
-}
+}

+ 3 - 4
app/components/home.tsx

@@ -168,7 +168,7 @@ function Screen() {
     if (isSdNew) return <Sd />;
     if (isSdNew) return <Sd />;
     return (
     return (
       <>
       <>
-        <SideBar className={isHome ? styles["sidebar-show"] : ""} />
+        {/* <SideBar className={isHome ? styles["sidebar-show"] : ""} /> */}
         <WindowContent>
         <WindowContent>
           <Routes>
           <Routes>
             <Route path={Path.Home} element={<Chat />} />
             <Route path={Path.Home} element={<Chat />} />
@@ -184,9 +184,8 @@ function Screen() {
 
 
   return (
   return (
     <div
     <div
-      className={`${styles.container} ${
-        shouldTightBorder ? styles["tight-container"] : styles.container
-      } ${getLang() === "ar" ? styles["rtl-screen"] : ""}`}
+      className={`${styles.container} ${shouldTightBorder ? styles["tight-container"] : styles.container
+        } ${getLang() === "ar" ? styles["rtl-screen"] : ""}`}
     >
     >
       {renderContent()}
       {renderContent()}
     </div>
     </div>

+ 2 - 3
app/components/sidebar.tsx

@@ -142,9 +142,8 @@ export function SideBarContainer(props: {
   const { children, className, onDragStart, shouldNarrow } = props;
   const { children, className, onDragStart, shouldNarrow } = props;
   return (
   return (
     <div
     <div
-      className={`${styles.sidebar} ${className} ${
-        shouldNarrow && styles["narrow-sidebar"]
-      }`}
+      className={`${styles.sidebar} ${className} ${shouldNarrow && styles["narrow-sidebar"]
+        }`}
       style={{
       style={{
         // #3016 disable transition on ios mobile screen
         // #3016 disable transition on ios mobile screen
         transition: isMobileScreen && isIOSMobile ? "none" : undefined,
         transition: isMobileScreen && isIOSMobile ? "none" : undefined,

+ 2 - 2
app/constant.ts

@@ -101,6 +101,7 @@ export const REQUEST_TIMEOUT_MS = 60000;
 export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown";
 export const EXPORT_MESSAGE_CLASS_NAME = "export-markdown";
 
 
 export enum ServiceProvider {
 export enum ServiceProvider {
+  BigModel = "BigModel",
   OpenAI = "OpenAI",
   OpenAI = "OpenAI",
   Azure = "Azure",
   Azure = "Azure",
   Google = "Google",
   Google = "Google",
@@ -114,8 +115,6 @@ export enum ServiceProvider {
   Iflytek = "Iflytek",
   Iflytek = "Iflytek",
 }
 }
 
 
-// Google API safety settings, see https://ai.google.dev/gemini-api/docs/safety-settings
-// BLOCK_NONE will not block any content, and BLOCK_ONLY_HIGH will block only high-risk content.
 export enum GoogleSafetySettingsThreshold {
 export enum GoogleSafetySettingsThreshold {
   BLOCK_NONE = "BLOCK_NONE",
   BLOCK_NONE = "BLOCK_NONE",
   BLOCK_ONLY_HIGH = "BLOCK_ONLY_HIGH",
   BLOCK_ONLY_HIGH = "BLOCK_ONLY_HIGH",
@@ -124,6 +123,7 @@ export enum GoogleSafetySettingsThreshold {
 }
 }
 
 
 export enum ModelProvider {
 export enum ModelProvider {
+  GLM = "GLM",
   Stability = "Stability",
   Stability = "Stability",
   GPT = "GPT",
   GPT = "GPT",
   GeminiPro = "GeminiPro",
   GeminiPro = "GeminiPro",

+ 1 - 1
app/layout.tsx

@@ -10,7 +10,7 @@ import { GoogleTagManager } from "@next/third-parties/google";
 const serverConfig = getServerSideConfig();
 const serverConfig = getServerSideConfig();
 
 
 export const metadata: Metadata = {
 export const metadata: Metadata = {
-  title: "NextChat",
+  title: "智能助理",
   description: "Your personal ChatGPT Chat Bot.",
   description: "Your personal ChatGPT Chat Bot.",
   appleWebApp: {
   appleWebApp: {
     title: "NextChat",
     title: "NextChat",

+ 14 - 13
app/store/chat.ts

@@ -12,6 +12,7 @@ import {
   StoreKey,
   StoreKey,
   SUMMARIZE_MODEL,
   SUMMARIZE_MODEL,
   GEMINI_SUMMARIZE_MODEL,
   GEMINI_SUMMARIZE_MODEL,
+  ServiceProvider,
 } from "../constant";
 } from "../constant";
 import { getClientApi } from "../client/api";
 import { getClientApi } from "../client/api";
 import type {
 import type {
@@ -69,7 +70,7 @@ export interface ChatSession {
 export const DEFAULT_TOPIC = Locale.Store.DefaultTopic;
 export const DEFAULT_TOPIC = Locale.Store.DefaultTopic;
 export const BOT_HELLO: ChatMessage = createMessage({
 export const BOT_HELLO: ChatMessage = createMessage({
   role: "assistant",
   role: "assistant",
-  content: Locale.Store.BotHello,
+  content: '我是「建科招聘智能助理」,请问您有什么招聘、薪酬或工作相关的问题需要咨询?我会尽力为您提供帮助。',
 });
 });
 
 
 function createEmptySession(): ChatSession {
 function createEmptySession(): ChatSession {
@@ -366,9 +367,9 @@ export const useChatStore = createPersistStore(
             botMessage,
             botMessage,
           ]);
           ]);
         });
         });
-
-        const api: ClientApi = getClientApi(modelConfig.providerName);
-        // make request
+        // 固定使用大模型
+        const providerName = 'BigModel' as ServiceProvider;
+        const api: ClientApi = getClientApi(providerName);
         api.llm.chat({
         api.llm.chat({
           messages: sendMessages,
           messages: sendMessages,
           config: { ...modelConfig, stream: true },
           config: { ...modelConfig, stream: true },
@@ -451,14 +452,14 @@ export const useChatStore = createPersistStore(
         var systemPrompts: ChatMessage[] = [];
         var systemPrompts: ChatMessage[] = [];
         systemPrompts = shouldInjectSystemPrompts
         systemPrompts = shouldInjectSystemPrompts
           ? [
           ? [
-              createMessage({
-                role: "system",
-                content: fillTemplateWith("", {
-                  ...modelConfig,
-                  template: DEFAULT_SYSTEM_TEMPLATE,
-                }),
+            createMessage({
+              role: "system",
+              content: fillTemplateWith("", {
+                ...modelConfig,
+                template: DEFAULT_SYSTEM_TEMPLATE,
               }),
               }),
-            ]
+            }),
+          ]
           : [];
           : [];
         if (shouldInjectSystemPrompts) {
         if (shouldInjectSystemPrompts) {
           console.log(
           console.log(
@@ -576,8 +577,8 @@ export const useChatStore = createPersistStore(
             onFinish(message) {
             onFinish(message) {
               get().updateCurrentSession(
               get().updateCurrentSession(
                 (session) =>
                 (session) =>
-                  (session.topic =
-                    message.length > 0 ? trimTopic(message) : DEFAULT_TOPIC),
+                (session.topic =
+                  message.length > 0 ? trimTopic(message) : DEFAULT_TOPIC),
               );
               );
             },
             },
           });
           });

+ 5 - 5
next.config.mjs

@@ -1,10 +1,8 @@
 import webpack from "webpack";
 import webpack from "webpack";
 
 
 const mode = process.env.BUILD_MODE ?? "standalone";
 const mode = process.env.BUILD_MODE ?? "standalone";
-console.log("[Next] build mode", mode);
 
 
 const disableChunk = !!process.env.DISABLE_CHUNK || mode === "export";
 const disableChunk = !!process.env.DISABLE_CHUNK || mode === "export";
-console.log("[Next] build with chunk: ", !disableChunk);
 
 
 /** @type {import('next').NextConfig} */
 /** @type {import('next').NextConfig} */
 const nextConfig = {
 const nextConfig = {
@@ -64,13 +62,15 @@ if (mode !== "export") {
 
 
   nextConfig.rewrites = async () => {
   nextConfig.rewrites = async () => {
     const ret = [
     const ret = [
-      // adjust for previous version directly using "/api/proxy/" as proxy base route
+      {
+        source: '/api/paas/v4/chat/completions',
+        destination: 'https://open.bigmodel.cn/api/paas/v4/chat/completions',
+      },
       {
       {
         source: "/api/proxy/v1/:path*",
         source: "/api/proxy/v1/:path*",
         destination: "https://api.openai.com/v1/:path*",
         destination: "https://api.openai.com/v1/:path*",
       },
       },
       {
       {
-        // https://{resource_name}.openai.azure.com/openai/deployments/{deploy_name}/chat/completions
         source: "/api/proxy/azure/:resource_name/deployments/:deploy_name/:path*",
         source: "/api/proxy/azure/:resource_name/deployments/:deploy_name/:path*",
         destination: "https://:resource_name.openai.azure.com/openai/deployments/:deploy_name/:path*",
         destination: "https://:resource_name.openai.azure.com/openai/deployments/:deploy_name/:path*",
       },
       },
@@ -102,4 +102,4 @@ if (mode !== "export") {
   };
   };
 }
 }
 
 
-export default nextConfig;
+export default nextConfig;

+ 1 - 6
package.json

@@ -57,13 +57,8 @@
     "@types/spark-md5": "^3.0.4",
     "@types/spark-md5": "^3.0.4",
     "concurrently": "^8.2.2",
     "concurrently": "^8.2.2",
     "cross-env": "^7.0.3",
     "cross-env": "^7.0.3",
-    "eslint": "^8.49.0",
-    "eslint-config-next": "13.4.19",
-    "eslint-config-prettier": "^8.8.0",
-    "eslint-plugin-prettier": "^5.1.3",
     "husky": "^8.0.0",
     "husky": "^8.0.0",
     "lint-staged": "^13.2.2",
     "lint-staged": "^13.2.2",
-    "prettier": "^3.0.2",
     "tsx": "^4.16.0",
     "tsx": "^4.16.0",
     "typescript": "5.2.2",
     "typescript": "5.2.2",
     "watch": "^1.0.2",
     "watch": "^1.0.2",
@@ -73,4 +68,4 @@
     "lint-staged/yaml": "^2.2.2"
     "lint-staged/yaml": "^2.2.2"
   },
   },
   "packageManager": "yarn@1.22.19"
   "packageManager": "yarn@1.22.19"
-}
+}

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 646 - 702
yarn.lock


Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio