当前位置: 首页 > news >正文

aichat-core简化 LLM 与 MCP 集成的前端核心库(TypeScript)

aichat-core

B站视频:【实现】仿CherryStudio做一个Web端全能AI助手(!!!全网第一个讲思路和实现细节的UP!!!)

在这里插入图片描述

npm仓库如下:

在这里插入图片描述

1. 介绍

NPM仓库地址:https://www.npmjs.com/package/aichat-core
简化 LLM 与 MCP 集成的前端核心库(TypeScript)

aichat-core 是一个前端核心库,旨在显著降低在项目中集成 OpenAI 和 MCP 服务的复杂度。它封装了 openai-sdkmcp-sdk,提供了:

  1. 核心业务模型与流程抽象:预定义了关键业务模型,并实现了结合 MCP 协议的流式聊天核心逻辑。

  2. 开箱即用的 UI 组件:封装了常用交互组件,包括:

    • HTML 代码的实时编辑与预览

    • Markdown 内容的优雅渲染

    • 支持多轮对话展示的消息项组件

核心价值:开发者只需专注于后端业务逻辑实现前端 UI 定制,而无需深入处理 LLM 与 MCP 之间复杂的交互细节。aichat-core 为您处理了底层的复杂性,让您更快地构建基于 LLM 和 MCP 的智能对话应用!

2. 使用

2.1 依赖项

install该模块,请注意安装以下对应版本的npm包

"@modelcontextprotocol/sdk": "^1.15.0","codemirror": "5","github-markdown-css": "^5.8.1","openai": "^5.3.0","react-codemirror2": "^8.0.1","react-json-view": "^1.21.3","react-markdown": "^9.1.0","react-syntax-highlighter": "^15.6.1","rehype-katex": "^7.0.1","rehype-raw": "^7.0.0","remark-gfm": "^4.0.1","remark-math": "^6.0.0"

暴露出来的核心功能:

/** TS类型及核心业务实现客户端类封装 */
import LLMCallBackMessage from "./core/response/LLMCallBackMessage";
import LLMCallBackMessageChoice from "./core/response/LLMCallBackMessageChoice";
import LLMCallBackToolMessage from "./core/response/LLMCallBackToolMessage";
import LLMUsage from "./core/response/LLMUsage";
import LLMThinkUsage from "./core/response/LLMThinkUsage";
import LLMClient from "./core/LLMClient";
import McpClient from "./core/McpClient";
import McpTool from "./core/McpTool";
import LLMUtil from "./core/LLMUtil";
import ChatMarkDown from "./ui/chat-markdown";
import CodeEditorPreview from "./ui/code-editor-preview";
import ChatBubbleItem from "./ui/chat-bubble-item";
export {LLMCallBackMessage,LLMCallBackMessageChoice,LLMCallBackToolMessage,LLMThinkUsage,LLMUsage,LLMClient,McpClient,McpTool,LLMUtil,ChatMarkDown,CodeEditorPreview,ChatBubbleItem,
};

2.2 安装

npm install aichat-core  && yarn add aichat-core

2.3 核心业务模型

LLMCallBackMessage.ts


/** AI响应消息回调对象 */export default interface LLMCallBackMessage {/** 消息ID,一定是有的 */id: string | number;/** 消息创建时间,一定有的,只不过初始化的时候无,UI回显需要, 案例:1750923621 */timed?: number;/** 使用的llm模型,一定是有的 */model: string;/** 消息角色(system、assistant、user、tool)*/role: string;/** 集成antd design x Bubble的typing属性,true表示设置聊天内容打字动画,false则不使用 */typing?: boolean;/** 集成antd design x Bubble的loading属性,true表示聊天内容加载,false表示不加载 */loading?: boolean;/** 消息组,一次LLM响应可能包含多组消息,初始化的时候可空 */choices?: LLMCallBackMessageChoice[];}

LLMCallBackToolMessage


/** 回调消息选择对象 */export default interface LLMCallBackMessageChoice {/** 消息索引,从1开始(多轮对话,index是累加的) */index: number;/** 正常响应内容,一定有,就算LLM择取工具时,content也是有值的,只不过是空串 */content: string;/** 推理内容,不一定有,要看LLM是否具备 */reasoning_content?: string;/*** -1 或 undefined :不带推理*  0:思考中*  1:思考结束*/thinking?: number;/** 回调工具消息数组 */tools?: LLMCallBackToolMessage[];/** 消耗的token统计 */usage?: LLMMessageUsage;}

LLMCallBackToolMessage


/** 工具调用消息回调对象 */export default interface LLMCallBackToolMessage {/** 调用id */id:string;/** 函数的索引 */index: number;/** 函数的名称 */name: string;/** 函数的参数,这个选填,如果能弄过来更好,便于后续错误调试 */arguments?: any;/** 函数调用的结果内容 */content?: string;}

LLMClient.ts


/*** @description 用于与 OpenAI 和 MCP 服务进行交互,支持流式聊天和工具调用。* @author appleyk* @github https://github.com/kobeyk* @date 2025年7月9日21:44:02*/export default class LLMClient {// 定义常量类型(防止魔法值)public static readonly CONTENT_THINKING = "thinking";public static readonly TYPE_FUNCTION = "function";public static readonly TYPE_STRING = "string";public static readonly REASON_STOP = "stop";public static readonly REASON_TOO_CALLS = "tool_calls";public static readonly ROLE_AI = "assistant";public static readonly ROLE_SYSTEM = "system";public static readonly ROLE_USER = "user";public static readonly ROLE_TOOL = "tool";public static readonly THINGKING_START_TAG = "<think>";public static readonly THINGKING_END_TAG = "</think>";// llm大模型对象llm: OpenAI;// mcp客户端对象(二次封装),不一定有,一旦有,必须初始化和进行"三次握手"后才可以正常使用mcp服务端提供的能力mcpClient?: McpClient;// 模型名称modelName: string;// mcpServer服务器连接地址mcpServer = "";// mcp工具列表tools: McpTool[] = [];// 初始化mcp的状态,默认false,如果开启llm流式聊天前,这个状态为false,则报错initMcpState = false;// 思考状态(每一次消息问答结束后,thinkState要回归false)thinkState = false;/*** 构造器* @param mcpServer mcp服务器连接地址(目前仅支持streamable http,后续再放开stdio和sse),如果空的话,则表示外部不使用mcp* @param mcpClientName mcpClient名称* @param apiKey llm访问接口对应的key,如果没有,填写"EMPTY",默认值走系统配置,如需要指定外部传进来* @param baseUrl llm访问接口的地址,目前走的是openai标准,大部分llm都支持,除了少数,后续可能要支持下(再说),默认值走系统配置,如需要指定外部传进来* @param modelName llm模型的名称,这个需要发起llm聊天的时候指定,默认值走系统配置,如需要指定外部传进来*/constructor(mcpServer = "",mcpClientName = "",apiKey:string,baseUrl:string,modelName:string) {// 使用llm,必须要实例化OpenAI实例对象this.llm = new OpenAI({apiKey: apiKey,baseURL: baseUrl,dangerouslyAllowBrowser: true, // web端直接调用openai是不安全的,这个要开启});// 使用llm,必须要指定模型的名称this.modelName = modelName;// 表示使用mcp来为llm提供外部工具调用参考if (mcpServer !== "") {this.mcpServer = mcpServer;this.mcpClient = new McpClient(mcpClientName);}}/*** 流式聊天升级版(支持多轮对话)* @param messages  消息上下文(多轮多话要把上一轮对话的结果加进去)* @param onContentCallBack 文本内容回调函数* @param onCallToolsCallBack 工具回调函数,如果传入空列表,则表示一轮工具调用结束* @param onCallToolResultCallBack 工具调用执行结果回调函数,name->result键值对* @param callBackMessage 回调消息大对象(包含的信息相当全)* @param controllerRef 请求中断控制器* @param loop 对话轮数*/async chatStreamLLMV2(messages: ChatCompletionMessageParam[],onContentCallBack: (callBackMessage: LLMCallBackMessage) => void,onCallToolsCallBack: (_toolCalls: LLMStreamChoiceDeltaTooCall[]) => void,onCallToolResultCallBack: (name: string, result: any) => void,callBackMessage: LLMCallBackMessage,controllerRef: AbortController | null,loop: number = 1 // 轮数) {const stream = await this.genLLMStream(messages, controllerRef);if (stream && typeof stream[Symbol.asyncIterator] === LLMClient.TYPE_FUNCTION) {let _toolCalls: LLMStreamChoiceDeltaTooCall[] = [];for await (const chunk of stream) {/** 如果usage包含且内容不等于null,则说明本次结束 */callBackMessage.timed = chunk.created;callBackMessage.model = chunk.model;let usage = chunk.usage;if (usage) {let _choices = callBackMessage.choicesif (_choices && _choices.length > 0) {let _choiceMessage = this.filterChoiceMessage(callBackMessage, loop);_choiceMessage.index = loop;_choiceMessage.thinking = 2;_choiceMessage.reasoning_content = "";_choiceMessage.content = "";_choiceMessage.usage = {completion_tokens: usage.completion_tokens,prompt_tokens: usage.prompt_tokens,total_tokens: usage.total_tokens}let newCallBackMessage = JSON.parse(JSON.stringify(callBackMessage))onContentCallBack(newCallBackMessage);}break;}/** 判断chunk.choices[0]是否有finish_reason字段,如果有赋值 */let finishReason = chunk.choices[0].finish_reason ?? "";if (finishReason !== "") {await this.dealFinishReason(finishReason,_toolCalls,messages,onContentCallBack,onCallToolsCallBack,onCallToolResultCallBack,callBackMessage,controllerRef,loop);}let toolCalls = chunk.choices[0].delta.tool_calls;/** 如果工具有值,则构建工具列表 */if (toolCalls && toolCalls.length > 0) {this.dealToolCalls(toolCalls, _toolCalls);} else {/** 否则流式展示消息内容 */const { choices } = chunk;await this.dealTextContent(choices, onContentCallBack, callBackMessage, loop);}}} else {console.error("Stream is not async iterable.");}}}

2.4 aichat-core使用案例

核心集成代码片段如下:


// 存储会话列表(默认default-0)const [conversations, setConversations] = useState(DEFAULT_CONVERSATIONS_ITEMS);// 历史消息,一个对话对应一组历史消息const [messageHistory, setMessageHistory] = useState<Record<string, any>>(useMockData > 0 ? mockHistoryMessages : []);...// 实例化llmClient及初始化mcp服务(如果有mcp server 服务支撑的话)llmClient = new LLMClient(mcpServer, "demo", apiKey, baseUrl, modelName);await llmClient.initMcpServer();.../** 消息回调 (callBackMessage对象已经经过深拷贝)*/const onMessageContentCallBack = (callBackMessage: LLMCallBackMessage) => {const _currentConversation = curConversation.current;let _llmChoices = callBackMessage.choices;let _messageId = callBackMessage.id;if (!_llmChoices || _llmChoices.length == 0) {return [];}setMessageHistory((sPrev) => ({...sPrev,[_currentConversation]: sPrev[_currentConversation].map((message: LLMCallBackMessage) => {if (_messageId != message.id) {return message;}let _message: LLMCallBackMessage = {id: _messageId,timed: callBackMessage.timed,model: callBackMessage.model,role: callBackMessage.role,typing: true,loading: false,}/** 拿到原来的 */let preMessageChoices = message.choices ?? [];_llmChoices.forEach((updateChoice: LLMCallBackMessageChoice) => {let _targets = preMessageChoices.filter((preChoice: any) => updateChoice.index == preChoice.index) ?? []// 有值就动态修改值if (_targets.length > 0) {let _target = _targets[0];let _reason_content = _target.reasoning_content ?? "";let _content = _target.content ?? "";_target.thinking = updateChoice.thinking;if (updateChoice.reasoning_content && updateChoice.reasoning_content != "") {_target.reasoning_content = _reason_content + updateChoice.reasoning_content;}if (updateChoice.content && updateChoice.content != "") {_target.content = _content + updateChoice.content;}_target.tools = updateChoice.tools;_target.usage = updateChoice.usage;} else {// 否则的话就添加一个preMessageChoices.push(updateChoice)}})// 最后别忘了给值(这个地方要用深拷贝)_message.choices = JSON.parse(JSON.stringify(preMessageChoices));return _message;}),}));};...// 发起流式聊天await llmClient.current.chatStreamLLMV2(messages,onMessageContentCallBack,onCallToolCallBack,onCallToolResultCallBack,onInitCallBackMessage(messageId),abortControllerRef);...

3. 最终效果图

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

http://www.dtcms.com/a/271376.html

相关文章:

  • 前端开发流程设计详解
  • 【leetcode】2235. 两整数相加
  • 【LeetCode 热题 100】21. 合并两个有序链表——(解法二)递归法
  • 仓颉语言 1.0.0 升级指南:工具链适配、collection 操作重构与 Map 遍历删除避坑
  • 深度学习12(卷积神经网络)
  • java idea 本地debug linux服务
  • Vue响应式原理四:响应式-监听属性变化
  • 国密算法(SM2/SM3/SM4)
  • 【MySQL】一些操作:修改MySQL root密码等等
  • Java 多线程编程:原理与实践
  • UI前端与数字孪生结合实践探索:智慧物流的仓储优化与管理系统
  • 供应链管理:定量分析中的无量纲化处理
  • Java 各集合接口常用方法对照表
  • 虚拟化技术,容器技术和Docker
  • Android View 绘制流程 简述 (无限递归+BitMap问题)
  • android activity生命周期温习
  • Java HashMap 的 get 和 put 方法的实现流程
  • android——热修复(补丁)
  • 微软官方C++构建工具:历史演变、核心组件与现代实践指南
  • SQL注入:现象、本质与防御详解
  • 文本标签提取与大模型理解:方法论深度指南
  • Kubernetes 集群部署、配置和验证-使用kubeadm快速部署一个K8s集群_笔记
  • 【K8S】在 Kubernetes 上配置安装 Nginx Ingress 控制器指南
  • 使用LLaMA-Factory微调Qwen2.5-VL-3B 的目标检测任务-LLaMA-Factory训练数据配置
  • 图像处理中的霍夫变换:直线检测与圆检测
  • 【软件运维】前后端部署启动的几种方式
  • 区块链系统开发技术应用构建可信数字生态链
  • 股指期货交割日避坑指南
  • 【MkDocs踩坑】图片路径问题的排查与解决
  • 由 DB_FILES 参数导致的 dg 服务器无法同步问题