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()
				})

			}
相关推荐
社会底层无业大学生2 小时前
uniapp微信小程序简单表格展示
微信小程序·小程序·uni-app·vue·1024程序员节
2501_915918416 小时前
iOS 26 查看电池容量与健康状态 多工具组合的工程实践
android·ios·小程序·https·uni-app·iphone·webview
2501_915909067 小时前
iOS 架构设计全解析 从MVC到MVVM与使用 开心上架 跨平台发布 免Mac
android·ios·小程序·https·uni-app·iphone·webview
2501_9160088917 小时前
Web 前端开发常用工具推荐与团队实践分享
android·前端·ios·小程序·uni-app·iphone·webview
2501_9159214317 小时前
“HTTPS 个人化”实战,个人站点与设备调试的部署、验证与抓包排查方法
网络协议·http·ios·小程序·https·uni-app·iphone
技术小丁21 小时前
uni-app 广告弹窗最佳实践:不扰民、可控制频次、含完整源码
前端·uni-app·1024程序员节
敲敲了个代码1 天前
UniApp 多页面编译优化:编译时间从10分钟到1分钟
开发语言·前端·javascript·学习·uni-app
00后程序员张1 天前
iOS 26 App 运行状况全面解析 多工具协同监控与调试实战指南
android·ios·小程序·https·uni-app·iphone·webview
2501_916007471 天前
iOS 混淆实战,多工具组合完成 IPA 混淆、加固与发布治理(iOS混淆|IPA加固|无源码混淆|App 防反编译)
android·ios·小程序·https·uni-app·iphone·webview
2501_915918411 天前
怎么上架 App?iOS 应用上架完整流程详解与跨平台发布实战指南
android·ios·小程序·https·uni-app·iphone·webview