纯JS完成CHATGPT流式输出

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chat with OpenAI</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 600px;
            margin: 50px auto;
        }
        #input-section, #history-section {
            margin-bottom: 20px;
        }
        #output {
            padding: 10px;
            background-color: #f1f1f1;
            min-height: 50px;
            margin-top: 10px;
            white-space: pre-wrap;
        }
        #history {
            padding: 10px;
            background-color: #f9f9f9;
            margin-top: 20px;
            border: 1px solid #ddd;
        }
    </style>
</head>
<body>
    <h2>Chat with OpenAI</h2>
    <div id="input-section">
        <input type="text" id="user-input" placeholder="Type your question..." style="width: 80%;">
        <button onclick="sendMessage()">Send</button>
    </div>
    <div id="output"></div>
    <div id="history-section">
        <h3>Chat History</h3>
        <div id="history"></div>
    </div>

    <script>
        //JS部分详见下面
    </script>
</body>
</html>

完成比较简陋,需要的自己美化界面。流式输出,存储本地列表。

javascript 复制代码
        const API_KEY = '';  // 替换为你的OpenAI API密钥
		const API_URL = '';

        // 获取并展示本地历史记录
        function loadHistory() {
            const historyContainer = document.getElementById('history');
            const chatHistory = JSON.parse(localStorage.getItem('chatHistory')) || [];
            historyContainer.innerHTML = '';
            chatHistory.forEach(entry => {
                const question = document.createElement('p');
                question.textContent = `Q: ${entry.question}`;
                const answer = document.createElement('p');
                answer.textContent = `A: ${entry.answer}`;
                historyContainer.appendChild(question);
                historyContainer.appendChild(answer);
                historyContainer.appendChild(document.createElement('hr'));
            });
        }

        // 发送用户问题并流式获取答案
       async function sendMessage() {
           const userInput = document.getElementById('user-input').value;
           if (!userInput) return;
       
           document.getElementById('output').textContent = 'Thinking...';
           document.getElementById('user-input').value = '';
       
           try {
               const response = await fetch(API_URL, {
                   method: "POST",
                   headers: {
                       "Content-Type": "application/json",
                       "Authorization": `Bearer ${API_KEY}`
                   },
                   body: JSON.stringify({
                       model: "gpt-3.5-turbo",
                       messages: [{"role": "user", "content": userInput}],
                       stream: true
                   })
               });
       
               const reader = response.body.getReader();
               const decoder = new TextDecoder("utf-8");
               let responseText = '';
               document.getElementById('output').textContent = '';
       
               while (true) {
                   const { done, value } = await reader.read();
                   if (done) break;
                   
                   // 将流返回的数据块转为文本
                   const chunk = decoder.decode(value, { stream: true });
                   
                   // 解析流返回的JSON字符串
                   const lines = chunk.trim().split("\n");
                   for (const line of lines) {
                       if (line.startsWith("data:")) {
                           const jsonStr = line.replace("data: ", "").trim();
                           if (jsonStr !== "[DONE]") {
                               const parsedData = JSON.parse(jsonStr);
                               const content = parsedData.choices[0].delta.content || '';
                               responseText += content;
                               document.getElementById('output').textContent = responseText;
                           }
                       }
                   }
               }
       
               saveToHistory(userInput, responseText);
           } catch (error) {
               document.getElementById('output').textContent = 'Error: ' + error.message;
           }
       }

        // 保存对话记录到localStorage
        function saveToHistory(question, answer) {
            const chatHistory = JSON.parse(localStorage.getItem('chatHistory')) || [];
            chatHistory.push({ question, answer });
            localStorage.setItem('chatHistory', JSON.stringify(chatHistory));
            loadHistory();
        }

        // 初次加载历史记录
        loadHistory();
相关推荐
Jagger_3 小时前
整洁架构三连问:是什么,怎么做,为什么要用
前端
一个处女座的程序猿O(∩_∩)O3 小时前
React 完全入门指南:从基础概念到组件协作
前端·react.js·前端框架
前端摸鱼匠4 小时前
Vue 3 的defineEmits编译器宏:详解<script setup>中defineEmits的使用
前端·javascript·vue.js·前端框架·ecmascript
里欧跑得慢4 小时前
Flutter 测试全攻略:从单元测试到集成测试的完整实践
前端·css·flutter·web
Jagger_4 小时前
前端整洁架构详解
前端
徐小夕4 小时前
我花一天时间Vibe Coding的开源AI工具,一键检测你的电脑能跑哪些AI大模型
前端·javascript·github
英俊潇洒美少年4 小时前
Vue3 企业级封装:useEventListener + 终极版 BaseEcharts 组件
前端·javascript·vue.js
嵌入式×边缘AI:打怪升级日志4 小时前
使用JsonRPC实现前后台
前端·后端
小码哥_常5 小时前
深度剖析:为什么Android选择了Binder
前端
方安乐6 小时前
单元测试之helper函数
前端·javascript·单元测试