Electron Forge【实战】百度智能云千帆大模型 —— AI聊天

1. 获取 Access Key 与 Secret Key

登录百度智能云
https://login.bce.baidu.com/



2. 安装node.js sdk

ts 复制代码
npm install @baiducloud/qianfan

3. src/main.ts

ts 复制代码
import { setupIPC } from "./ipc";

在 const mainWindow 之后

ts 复制代码
 setupIPC(mainWindow);

4. src/ipc.ts

ts 复制代码
import { ipcMain, BrowserWindow } from "electron";
import { createProvider } from "./providers/createProvider";

interface CreateChatProps {
  messages: {
    role: string;
    content: string;
    imagePath?: string;
  }[];
  providerName: string;
  selectedModel: string;
  messageId: number;
}

export function setupIPC(mainWindow: BrowserWindow) {
  ipcMain.on("start-chat", async (event, data: CreateChatProps) => {
    const { providerName, messages, messageId, selectedModel } = data;

    try {
      const provider = createProvider(providerName);
      const stream = await provider.chat(messages, selectedModel);
      for await (const chunk of stream) {
        const content = {
          messageId,
          data: chunk,
        };
        mainWindow.webContents.send("update-message", content);
      }
    } catch (error) {
      const errorContent = {
        messageId,
        data: {
          is_end: true,
          result:
            error instanceof Error ? error.message : "与AI服务通信时发生错误",
          is_error: true,
        },
      };
      mainWindow.webContents.send("update-message", errorContent);
    }
  });
}

5. src/providers/createProvider.ts

下方代码中,留意更改为第1步得到的 accessKey 和 secretKey

ts 复制代码
import { QianfanProvider } from "./QianfanProvider";

export function createProvider(providerName: string) {
  // 定义 QianfanConfig 接口
  interface QianfanConfig {
    accessKey: string;
    secretKey: string;
  }

  // 定义完整的配置接口
  interface Config {
    qianfan: QianfanConfig;
  }

  const providerConfigs: Config = {
    qianfan: {
      accessKey: "填第1步获取的accessKey",
      secretKey: "填第1步获取的secretKey",
    },
  };

  const providerConfig = providerConfigs[providerName as keyof Config] || {};
  switch (providerName) {
    case "qianfan":
      if (!providerConfig.accessKey || !providerConfig.secretKey) {
        throw new Error(
          "缺少千帆API配置:请在设置中配置 accessKey 和 secretKey"
        );
      }
      return new QianfanProvider(
        providerConfig.accessKey,
        providerConfig.secretKey
      );
    default:
      throw new Error(`不支持的AI服务提供商: ${providerName}`);
  }
}

6. src/providers/QianfanProvider.ts

ts 复制代码
import { ChatCompletion } from "@baiducloud/qianfan";

interface ChatMessageProps {
  role: string;
  content: string;
  imagePath?: string;
}

export class QianfanProvider {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  private client: any;
  constructor(accessKey: string, secretKey: string) {
    this.client = new ChatCompletion({
      QIANFAN_ACCESS_KEY: accessKey,
      QIANFAN_SECRET_KEY: secretKey,
      ENABLE_OAUTH: true,
    });
  }
  async chat(messages: ChatMessageProps[], model: string) {
    const stream = await this.client.chat(
      {
        messages,
        stream: true,
      },
      model
    );
    return {
      async *[Symbol.asyncIterator]() {
        for await (const chunk of stream) {
          yield chunk;
        }
      },
    };
  }
}

7. src/preload.ts

ts 复制代码
import { ipcRenderer, contextBridge } from "electron";

contextBridge.exposeInMainWorld("electronAPI", {
  startChat: (data: CreateChatProps) => ipcRenderer.send("start-chat", data),
  onUpdateMessage: (callback: OnUpdatedCallback) =>
    ipcRenderer.on("update-message", (_event, value) => callback(value)),
});

interface CreateChatProps {
  messages: {
    role: string;
    content: string;
    imagePath?: string;
  }[];
  providerName: string;
  selectedModel: string;
  messageId: number;
}

interface UpdatgedStreamData {
  messageId: number;
  data: {
    is_end: boolean;
    result: string;
    is_error?: boolean;
  };
}

type OnUpdatedCallback = (data: UpdatgedStreamData) => void;

8. 页面中使用

ts 复制代码
// 访问 AI 模型,获取答案
const get_AI_answer = async (answerIndex: number) => {
  await (window as any).electronAPI.startChat({
    messageId: answerIndex,
    providerName: convsersation.value!.AI_providerName,
    selectedModel: convsersation.value!.AI_modelName,
    // 发给AI模型的消息需移除最后一条加载状态的消息,使最后一条消息为用户的提问
    messages: convsersation
      .value!.msgList.map((message) => ({
        role: message.type === "question" ? "user" : "assistant",
        content: message.content,
      }))
      .slice(0, -1),
  });
};

完整使用范例见
https://blog.csdn.net/weixin_41192489/article/details/146472170

相关推荐
Patrick_Wilson1 天前
桌面端发布的真实成本:签名、公证和那支不能共享的 USB Key
ci/cd·electron·客户端
爱丶不疚1 天前
Electron: 你是否需要对 Preload 开启 nodeIntegration?
安全·性能优化·electron
爱丶不疚1 天前
Electron:Module 与 Service 的职责边界与加载时序编排
前端·electron·nestjs
爱丶不疚1 天前
Electron: 缓存机制有哪些?能控制的又有哪些
前端·electron·v8
忆凡_3 天前
Dyad 鸿蒙 PC 适配全记录:让本地 AI 应用构建器进入 HarmonyOS PC
electron·鸿蒙
心态还需努力呀4 天前
把 Zettlr 搬上鸿蒙 PC:一次 Electron 应用移植实战
华为·electron·harmonyos
web打印社区5 天前
Vue3 发票静默打印,我这边能跑通的一版
前端·javascript·vue.js·chrome·electron
不可能片场5 天前
Electron 主进程热补丁:单行 JS 安全打法
前端·electron
web打印社区6 天前
浏览器静默打印?先别装第三个库了
前端·vue.js·chrome·electron·pdf
不可能片场6 天前
Electron 退化成 node:一个环境变量的锅
前端·electron