uniapp 对接deepseek

废话不多说直接上代码

javascript 复制代码
// 小程序专用流式服务 
export const streamChatMiniProgram = (messages, options = {
	secret: ""
}) => {

	return new Promise((resolve, reject) => {

	
		// 构建请求数据 
		const requestData = {
			model: 'deepseek-chat',
			messages,
			stream: true,
			max_tokens: 2048,
			temperature: 0.7
		};

		// 平台特定配置
		const requestConfig = {
			url: 'https://api.deepseek.com/v1/chat/completions',
			method: 'POST',
			header: {
				'Accept-Charset': 'utf-8',
				'Content-Type': 'application/json',
				'Authorization': `Bearer ${options.secret}`
			},
			data: JSON.stringify(requestData),
			// responseType: 'text',
			enableChunked: true, // 关键配置:启用分块传输
			// enableHttp2: true,
			timeout: 30000
		};

		// 跨平台适配 
		// #ifdef MP-WEIXIN || MP-QQ 
		requestConfig.enableChunked = true;
		// #endif 

		// #ifdef MP-ALIPAY || MP-BAIDU 
		requestConfig.enableChunked = false;
		// #endif 

		// 发起请求 
		const requestTask = wx.request({
			...requestConfig,

			// 分块数据接收处理 
			chunked: requestConfig.enableChunked,

			success: (res) => {

				if (res.statusCode !== 200) {
					reject(new Error(`API错误: ${res.statusCode}`));
				}
			},

			fail: (err) => {
				reject(new Error(`请求失败: ${err.errMsg}`));
			}
		});
	
		try {
			
			// requestTask.onHeadersReceived((chunk)=>{
			// 	console.log("onHeadersReceived")
			// })
			
			// requestTask.onProgressUpdate((chunk)=>{
			// 	console.log("onProgressUpdate")
			// })
			
			// const decoder = new TextDecoder('utf-8') // 显式指定UTF-8
			requestTask.onChunkReceived((chunk) => {
				
				try {
					if (!requestTaskMap.get(requestTask.uniqueId)) {
						return;
					}
					// 缓冲区初始化为空字符串 
					let buffer = '';
					buffer += utf8Decode(chunk
						.data
					) //decoder.decode(chunk.data,  { stream: true });// String.fromCharCode.apply(null, new Uint8Array(chunk.data));

					// SSE格式解析 
					const lines = buffer.split('\n');
					buffer = '';

					for (const line of lines) {
						if (line.trim() === '') continue;
						if (line.startsWith('data:')) {
							const dataStr = line.replace('data:', '').trim();

							// 结束标记处理 
							if (dataStr === '[DONE]') {
								resolve(fullResponse);
								return;
							}

							// 解析JSON内容 
							try {
								const data = JSON.parse(dataStr);
								if (data.choices?.[0]?.delta?.content) {
									const content = data.choices[0].delta.content;
									fullResponse += content;

									// 实时事件通知 
									uni.$emit('deepseek_stream_update', {
										partial: content,
										full: fullResponse
									});
								}
							} catch (e) {
								console.error('JSON 解析错误', e);
								uni.$emit('deepseek_stream_update', {
									partial: 'JSON 解析错误',
									e,
									full: 'JSON 解析错误',
									e
								});

							}
						}
					}
				} catch (err) {
					uni.$emit('deepseek_stream_update', {
						partial: JSON.stringify(err),
						full: JSON.stringify(err)
					});
				}
			});

		} catch (err) {
			uni.$emit('deepseek_stream_update', {
				partial: JSON.stringify(err),
				full: JSON.stringify(err)
			});
		}



		// 存储任务引用以便中断 
		console.log("requestTask=", requestTask)
		requestTaskMap.set(requestTask.uniqueId, requestTask);

		let fullResponse = '';
	});
};


export function utf8Decode(buffer) {
	let uint8 = new Uint8Array(buffer);
	let str = '';
	let i = 0;

	while (i < uint8.length) {
		const byte = uint8[i++];

		// 单字节字符 (0-127)
		if (byte < 0x80) {
			str += String.fromCharCode(byte);
		}
		// 双字节字符 
		else if ((byte & 0xE0) === 0xC0) {
			const byte2 = uint8[i++];
			str += String.fromCharCode(
				((byte & 0x1F) << 6) | (byte2 & 0x3F)
			);
		}
		// 三字节字符(支持中文)
		else if ((byte & 0xF0) === 0xE0) {
			const byte2 = uint8[i++];
			const byte3 = uint8[i++];
			str += String.fromCharCode(
				((byte & 0x0F) << 12) |
				((byte2 & 0x3F) << 6) |
				(byte3 & 0x3F)
			);
		}
		// 四字节字符(简单兼容)
		else if ((byte & 0xF8) === 0xF0) {
			i += 3; // 跳过后续字节
			str += ''; // 替换字符占位
		}
	}

	return str;
}
// 请求任务管理器 
const requestTaskMap = new Map();
// 中断指定请求
export const abortStreamRequest = (requestId) => {
	const task = requestTaskMap.get(requestId);
	if (task) {
		task.abort();
		requestTaskMap.delete(requestId);
	}
};
// 中断所有请求
export const abortAllRequests = () => {
	requestTaskMap.forEach(task => {
		task.abort()
	});
	requestTaskMap.clear();
};

调用

javascript 复制代码
async startStream() {
				if (!this.message) {
					return;
				}
				const userMessage = {
					id: Date.now(),
					role: 'user',
					content: this.message
				};
				this.messages.push(userMessage);
				this.message = ""

				// 构建对话历史 
				const messages = this.messages.map(m => ({
					role: m.role,
					content: m.content
				}));

				this.currMessage = {
					content: "",
					role: "assistant",
					thinking: true
				}
				this.messages.push(this.currMessage)
				// 发起流式请求
				this.loading = true

				const response = await streamChatMiniProgram(messages, {
					secret: this.deepSeekSecret
				});
				this.loading = false
				this.currMessage = undefined
				this.$nextTick(()=>{
					this.scrollBottom()
				})

			}
相关推荐
2501_915106322 小时前
iOS混淆工具实战 金融支付类 App 的安全防护与合规落地
android·ios·小程序·https·uni-app·iphone·webview
TellMeha6 小时前
uniapp打包app关于获取本机相机、相册、麦克风等权限问题(ios第一次安装权限列表里没有对应权限问题)
ios·uni-app
zheshiyangyang14 小时前
uni-app学习【pages】
前端·学习·uni-app
小周同学:1 天前
【UniApp打包鸿蒙APP全流程】如何配置并添加UniApp API所需的鸿蒙系统权限
华为·uni-app·harmonyos
初遇你时动了情2 天前
uniapp vue3 ts自定义底部 tabbar菜单
前端·javascript·uni-app
韩沛晓2 天前
uniapp跨域怎么解决
前端·javascript·uni-app
咸虾米2 天前
微信小程序服务端api签名,安全鉴权模式介绍,通过封装方法实现请求内容加密与签名
vue.js·微信小程序·uni-app
Ratten2 天前
使用 uniapp 实现的扫雷游戏
uni-app
YuShiYue2 天前
【uni-app】自定义导航栏以及状态栏,胶囊按钮位置信息的获取
uni-app·notepad++
2501_915921432 天前
iOS 应用上架多环境实战,Windows、Linux 与 Mac 的不同路径
android·ios·小程序·https·uni-app·iphone·webview