基于React开发的聊天窗口,集成了deepseek和gpt-4o的api

本文将介绍如何基于 React / Nextjs框架开发一个功能强大的聊天窗口,并集成 deepseek 和 gpt-4o 的 API,实现智能对话功能。免登录。

源码地址:https://github.com/geeeeeeeek/ai-starter

一、技术栈

  • 前端框架: React / nextjs
  • UI 库: tailwindcss/shadcn
  • 状态管理: Redux
  • HTTP 请求: fetch
  • API 集成: deepseek API, gpt-4o API

二、功能实现

  1. 聊天窗口界面

使用 React 组件构建聊天窗口界面,包括消息列表、输入框、发送按钮等。
使用 UI 库美化界面,例如 Material-UI 的卡片、按钮等组件。
实现消息的发送和接收功能,并将消息实时显示在消息列表中。
将聊天窗口封装到chatDialog.jsx中(具体代码可参考github里)

    return (
        <div className="flex-1 flex flex-col h-screen bg-white p-0">
            <div className="relative bg-white h-12 shadow">

                <SidebarTrigger className="absolute h-12 w-12" />
                <div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 ">AI Chat</div>
            </div>
            <div className="flex-1 overflow-y-auto mt-4 mb-4 space-y-4 ">
                {messages.map((msg, index) => (
                    <div
                        key={index} className="w-full max-w-4xl m-auto"
                    >
                        {
                            msg.role === 'user' ? (
                                <div className="flex justify-end">
                                    <div className="bg-blue-200 p-3 rounded inline-block">{msg.content}</div>
                                </div>
                            ) : (
                                <div className="flex gap-4">
                                    <div>
                                        <svg t="1735719680744" className="icon" viewBox="0 0 1024 1024" version="1.1"
                                            xmlns="http://www.w3.org/2000/svg" p-id="20857" width="32" height="32">
                                            <path
                                                d="M509.44 403.2c-26.88 0-48.64 21.76-48.64 48.64s21.76 48.64 48.64 48.64 48.64-21.76 48.64-48.64c1.28-26.88-20.48-48.64-48.64-48.64z m104.96 53.76c0 26.88 21.76 48.64 48.64 48.64s48.64-21.76 48.64-48.64-21.76-48.64-48.64-48.64c-26.88-1.28-48.64 20.48-48.64 48.64zM512 0C229.12 0 0 229.12 0 512s229.12 512 512 512 512-229.12 512-512S794.88 0 512 0z m243.2 509.44c-14.08 117.76-138.24 200.96-267.52 192-14.08-1.28-29.44 1.28-42.24 7.68l-87.04 47.36c-12.8 6.4-23.04 1.28-26.88-1.28s-12.8-10.24-12.8-24.32l2.56-70.4c1.28-19.2 3.84-46.08-12.8-58.88-56.32-44.8-57.6-97.28-51.2-152.32 12.8-111.36 115.2-195.84 234.24-195.84 10.24 0 21.76 1.28 32 2.56 65.28 7.68 128 34.56 167.68 83.2 44.8 46.08 70.4 111.36 64 170.24zM353.28 403.2c-26.88 0-48.64 21.76-48.64 48.64s21.76 48.64 48.64 48.64 48.64-21.76 48.64-48.64-21.76-48.64-48.64-48.64z"
                                                p-id="20858" fill="#1296db"></path>
                                        </svg>
                                    </div>
                                    <div className="pt-1">
                                        <ReactMarkdown components={{
                                            code({ node, inline, className, children, ...props }) {
                                                const match = /language-(\w+)/.exec(className || '');
                                                return !inline && match ? (
                                                    <SyntaxHighlighter
                                                        style={materialDark}
                                                        language={match[1]}
                                                        PreTag="div"
                                                        {...props}
                                                    >
                                                        {String(children).replace(/\n$/, '')}
                                                    </SyntaxHighlighter>
                                                ) : (
                                                    <code className={className} {...props}>
                                                        {children}
                                                    </code>
                                                );
                                            },
                                        }}>{msg.content}</ReactMarkdown>
                                    </div>
                                </div>
                            )
                        }
                    </div>
                ))}
                {/* 用于自动滚动的空 div */}
                <div ref={messagesEndRef} />
            </div>
            <div className="flex w-full max-w-4xl mb-4 m-auto " >
                <input
                    type="text"
                    value={input}
                    onChange={(e) => setInput(e.target.value)}
                    onKeyPress={(e) => e.key === 'Enter' && handleSend()}
                    className="flex-1 h-12 p-4 border border-gray-300 rounded-l-lg focus:outline-none"
                    placeholder="Send a message to AI...."
                />

                <Button onClick={handleSend} disabled={isFetching} className="bg-blue-500 text-white h-12 py-4 px-4 rounded-l-none rounded-r-lg hover:bg-blue-600">
                    Send
                </Button>
            </div>
        </div>
    );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  1. API 集成

使用了nextjs的api路由功能,提供的服务渲染,创建route.js文件编写api请求方法,并调用openai的sdk。实现了流式获取。

// app/api/chat/route.js
import OpenAI from 'openai';

export async function POST(request) {
  const { messages, model } = await request.json();

  let baseURL = 'https://api.deepseek.com';
  let apiKey = process.env.OPENAI_API_KEY;

  if (model === 'gpt-4o-mini' || model === 'gpt-4o') {
    baseURL = 'https://models.inference.ai.azure.com';
    apiKey = process.env.OPENAI_API_KEY_GPT;
  } else {
    baseURL = 'https://api.deepseek.com';
    apiKey = process.env.OPENAI_API_KEY;
  }

  const openai = new OpenAI({
    baseURL: baseURL,
    apiKey: apiKey, // 从环境变量中获取 API Key
  });

  try {
    const response = await openai.chat.completions.create({
      model: model,
      messages,
      stream: true, // 启用流式响应
    });

    // 创建可读流
    const stream = new ReadableStream({
      async start(controller) {
        for await (const chunk of response) {
          const content = chunk.choices[0]?.delta?.content || '';
          controller.enqueue(`data: ${JSON.stringify({ content })}\n\n`);
        }
        controller.close();
      },
    });

    // 返回流式响应
    return new Response(stream, {
      headers: {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        Connection: 'keep-alive',
      },
    });
  } catch (error) {
    console.error('Error calling OpenAI API:', error);
    return new Response(JSON.stringify({ error: 'Internal server error' }), {
      status: 500,
      headers: { 'Content-Type': 'application/json' },
    });
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

注意:需要开发者去deepseek申请api_key,另外还需要去github申请gpt-4o的api_key

预览结果如下