如何使用go语言的gin库来搭建一个属于自己的网页版GPT

我们将会使用go语言的gin库来搭建一个属于自己的网页版GPT

一、准备工作

我们需要使用到ollama,如何下载和使用[ollama](Ollama完整教程:本地LLM管理、WebUI对话、Python/Java客户端API应用 - 老牛啊 - 博客园)请看这个文档

有过gin环境的直接运行就可以,如果没有就根据文档内容去下载相关配置库

二、使用步骤

git clone https://github.com/yty666zsy/gin_web_ai.git
cd gin_web_ai
ollama run "大模型的名称"

这里需要注意的是要在chat.html文件中修改模型的名称,要不然找不到模型,在这个位置外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后运行代码,如下图所示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

"然后开启一个新的终端"
go run main.go

这里需要注意的是端口号可以适当的进行修改,防止某些端口被占用的情况

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

然后本地访问127.0.0.1:8088就能打开网址进行愉快的聊天啦

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

同时后台同步打印信息以便日志管理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面把代码贴出来
chat.html

<!DOCTYPE html>
<html>
<head>
    <title>Ollama 聊天界面</title>
    <style>
        #chat-container {
            width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        #messages {
            height: 400px;
            border: 1px solid #ccc;
            overflow-y: auto;
            margin-bottom: 20px;
            padding: 10px;
        }
        .message {
            margin: 10px 0;
            padding: 10px;
            border-radius: 5px;
        }
        .user {
            background-color: #e3f2fd;
            text-align: right;
        }
        .assistant {
            background-color: #f5f5f5;
        }
    </style>
</head>
<body>
    <div id="chat-container">
        <div id="messages"></div>
        <div>
            <select id="model">
                <option value="llama3-cn">Llama 3 中文</option>
            </select>
            <input type="text" id="message" style="width: 80%;" placeholder="输入消息...">
            <button onclick="sendMessage()">发送</button>
        </div>
    </div>

    <script>
        const messagesDiv = document.getElementById('messages');
        const messageInput = document.getElementById('message');
        const modelSelect = document.getElementById('model');
        let chatHistory = [];

        function addMessage(role, content) {
            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${role}`;
            messageDiv.textContent = content;
            messagesDiv.appendChild(messageDiv);
            messagesDiv.scrollTop = messagesDiv.scrollHeight;
        }

        async function sendMessage() {
            const content = messageInput.value.trim();
            if (!content) return;

            const requestData = {
                model: modelSelect.value,
                messages: chatHistory
            };
            
            console.log('发送请求:', requestData);

            addMessage('user', content);
            chatHistory.push({role: 'user', content: content});
            messageInput.value = '';

            try {
                const response = await fetch('/chat', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        model: modelSelect.value,
                        messages: chatHistory
                    })
                });

                const data = await response.json();
                chatHistory.push(data.message);
                addMessage('assistant', data.message.content);
            } catch (error) {
                console.error('Error:', error);
                addMessage('assistant', '发生错误,请重试。');
            }
        }

        messageInput.addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                sendMessage();
            }
        });
    </script>
</body>
</html> 

main.go

package main

import (
	"bytes"
	"encoding/json"
	"fmt"

	//"io/ioutil"
	"bufio"
	"net"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
)

type ChatRequest struct {
	Model    string    `json:"model"`
	Messages []Message `json:"messages"`
}

type Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type ChatResponse struct {
	Message Message `json:"message"`
}

// 添加新的结构体用于处理流式响应
type StreamResponse struct {
	Model      string  `json:"model"`
	CreatedAt  string  `json:"created_at"`
	Message    Message `json:"message"`
	Done       bool    `json:"done"`
	DoneReason string  `json:"done_reason,omitempty"`
}

// 在main函数前添加一个新的函数
func findAvailablePort(startPort int) int {
	for port := startPort; port < startPort+100; port++ {
		listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
		if err == nil {
			listener.Close()
			return port
		}
	}
	return startPort // 如果没找到可用端口,返回初始端口
}

func main() {
	r := gin.Default()

	// 加载模板
	r.LoadHTMLGlob("templates/*")

	// 设置静态文件路径
	r.Static("/static", "./static")

	// 首页路由
	r.GET("/", func(c *gin.Context) {
		c.HTML(http.StatusOK, "chat.html", nil)
	})

	// 处理聊天请求的API
	r.POST("/chat", func(c *gin.Context) {
		var req ChatRequest
		if err := c.BindJSON(&req); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		prettyJSON, _ := json.MarshalIndent(req, "", "    ")
		fmt.Printf("发送到Ollama的请求:\n%s\n", string(prettyJSON))

		jsonData, _ := json.Marshal(req)
		resp, err := http.Post("http://localhost:11434/api/chat", "application/json", bytes.NewBuffer(jsonData))
		if err != nil {
			fmt.Printf("调用Ollama API错误: %v\n", err)
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		defer resp.Body.Close()

		// 使用scanner来读取流式响应
		scanner := bufio.NewScanner(resp.Body)
		var fullContent string

		for scanner.Scan() {
			line := scanner.Text()
			var streamResp StreamResponse
			if err := json.Unmarshal([]byte(line), &streamResp); err != nil {
				fmt.Printf("解析流式响应行错误: %v\n", err)
				continue
			}

			// 累积内容
			fullContent += streamResp.Message.Content

			// 如果是最后一条消息
			if streamResp.Done {
				response := ChatResponse{
					Message: Message{
						Role:    "assistant",
						Content: fullContent,
					},
				}
				c.JSON(http.StatusOK, response)
				return
			}
		}

		if err := scanner.Err(); err != nil {
			fmt.Printf("读取流式响应错误: %v\n", err)
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
	})

	// 从环境变量获取端口,如果未设置则使用默认值
	port := os.Getenv("PORT")
	if port == "" {
		port = "8088"
	}
	r.Run(":" + port)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/934277.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用户登录流程详解

目录 前言1. 登录请求的发起1.1 表单设计与数据收集1.2 请求发送与状态反馈 2. 验证码校验2.1 验证码的生成与展示2.2 验证码的校验机制 3. 登录前置校验3.1 检查账户状态3.2 登录频率限制 4. SS认证管理器的用户校验4.1 密码校验机制4.2 用户角色与权限检查 5. 登录成功后的处…

虚拟机与Xshell5和Xftp4连接与虚拟机克隆

虚拟机与Xshell5和Xftp4连接与虚拟机克隆 虚拟机与Xshell5和Xftp4连接 虚拟机与Xshell5连接 下载Xshell5后启动出现如下界面&#xff0c;点击新建 新建会话输入虚拟机命名&#xff0c;如master&#xff0c;主机输入虚拟机IP&#xff0c;xxx.xxx.xxx.xxx然后确认&#xff0c;…

【大模型系列篇】LLaMA-Factory大模型微调实践 - 从零开始

前一次我们使用了NVIDIA TensorRT-LLM 大模型推理框架对智谱chatglm3-6b模型格式进行了转换和量化压缩&#xff0c;并成功部署了推理服务&#xff0c;有兴趣的同学可以翻阅《NVIDIA TensorRT-LLM 大模型推理框架实践》&#xff0c;今天我们来实践如何通过LLaMA-Factory对大模型…

最大值和最小值的差

最大值和最小值的差 C语言代码C 语言代码Java语言代码Python语言代码 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 输出一个整数序列中最大的数和最小的数的差。 输入 第一行为M&#xff0c;表示整数个数&#xff0c;整数个数不会大于1…

【SH】微信小程序调用EasyDL零门槛AI开发平台的图像分类研发笔记

文章目录 微信小程序字符串字符串模板字符串拼接 上传图片GET请求测试编写测试代码域名不合法问题 GET和POST请求测试 微信小程序字符串 字符串模板 这是ES6引入的特性&#xff0c;允许你通过反引号&#xff08;&#xff09;创建模板字符串&#xff0c;并在其中嵌入变量或表达…

Certimate自动化SSL证书部署至IIS服务器

前言&#xff1a;笔者上一篇内容已经部署好了Certimate开源系统&#xff0c;于是开始搭建部署至Linux和Windows服务器&#xff0c;Linux服务器十分的顺利&#xff0c;申请证书-部署证书很快的完成了&#xff0c;但是部署至Windows Server的IIS服务时&#xff0c;遇到一些阻碍&a…

【C++算法】38.模拟_替换所有的问号

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a; 题目链接&#xff1a; 1576. 替换所有的问号 题目描述&#xff1a; 解法 模拟算法就是依葫芦画瓢 特点是思路简单&#xff0c;主要考察代码能力 模拟算法流程&#xff08;一定要在草稿纸上过一遍…

三菱FX3uPLC输入接线注意事项

FX3u微型控制器(DC输入型)的输入根据外部接线&#xff0c;漏型输入和源型输入都可使用。 但是,一定要连接S/S端子的接线。 详细事宜请参考“FX3U系列微型控制器硬件说明手册 AC电源型的输入接线事例(FX3U-囗MR/UA1除外) DC电源型的输入接线事例 *请不要与(0V)、(24V)端子接线…

Milvus向量数据库03-搜索理论

Milvus向量数据库03-搜索理论 1-ANN搜索 通过 k-最近邻&#xff08;kNN&#xff09;搜索可以找到一个查询向量的 k 个最近向量。kNN 算法将查询向量与向量空间中的每个向量进行比较&#xff0c;直到出现 k 个完全匹配的结果。尽管 kNN 搜索可以确保准确性&#xff0c;但十分耗…

链表刷题笔记(题解出自灵茶山)

反转链表 class Solution { public:ListNode* reverseList(ListNode* head){ListNode* cur head;ListNode* prv nullptr;while (cur){ ListNode* nxt cur->next; cur->next prv;prv cur;cur nxt; }return prv;} };反转倒数第n个链表 难点在于怎么找到要反转的头…

【JavaEE初阶】HTML

&#x1f334;什么是HTML&#xff1f; HTML 是用来描述网页的一种语言。 HTML 指的是超文本标记语言: HyperText Markup LanguageHTML 不是一种编程语言&#xff0c;而是一种标记语言标记语言是一套标记标签 (markup tag)HTML 使用标记标签来描述网页HTML 文档包含了HTML 标签…

一文理解 “Bootstrap“ 在统计学背景下的含义

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一文理解 “Bootstrap“ 在统计学背景下的含义 类比&#xff1a;重新抽样 假设我参加了班级的考试&#xff0c;每位同学都获得了一个成绩。现在&#xff0c;我想了解整个班级的平均成绩&#xff0c;但…

2024年下半年郑州大学ACM招新赛题解(ABCDEFGHIJKL)

A n-th 题意 已知公式 π ∑ k 0 ∞ 1 1 6 k ( 4 8 k 1 − 2 8 k 4 − 1 8 k 5 − 1 8 k 6 ) \pi \sum_{k0}^{\infty} \frac{1}{16^k} (\frac{4}{8k1} - \frac{2}{8k4} - \frac{1}{8k5} - \frac{1}{8k6}) π∑k0∞​16k1​(8k14​−8k42​−8k51​−8k61​) 请你求出…

Flutter:开发环境搭建和Android Studio创建Flutter Project

一、系统要求 在安装和运行 Flutter 前&#xff0c;你的 macOS 或者 Windows 环境必须满足以下要求&#xff1a; 二、硬件要求 macOS Flutter 开发环境必须满足以下最低硬件要求。 Windows Flutter 开发环境必须满足以下最低硬件要求。 三、软件要求 要为 Android 编写和编译…

观察者模式的理解和实践

引言 在软件开发中&#xff0c;设计模式是开发者们为了解决常见的设计问题而总结出来的一系列最佳实践。观察者模式&#xff08;Observer Pattern&#xff09;是其中一种非常经典且使用率极高的设计模式。它主要用于定义对象之间的一对多关系&#xff0c;使得当一个对象的状态发…

音视频入门基础:MPEG2-TS专题(16)——PMT简介

一、引言 PMT&#xff08;Program Map Table&#xff09;与PAT表成对出现&#xff0c;其PID由PAT表给出。通过PMT表可以得到该节目包含的视频和音频信息&#xff0c;从而找到音视频流&#xff1a; 二、PMT表中的属性 根据《T-REC-H.222.0-202106-S!!PDF-E.pdf》第79页&#x…

嘉誉府5区共有产权看房记

特地工作日来看下嘉誉府5区的网红共有产权的房子&#xff0c;主要是冲着均价2.1万/平才来看。说实话从塘尾地铁步行到嘉誉府5区还挺需要时间的哈。可能以后需要电驴代步到地铁&#xff1f;确实楼盘现在是现楼&#xff0c;今年买明年住。鸿荣源确实很666哈。 今天来不需要排队&a…

ios上架构建版本没苹果电脑怎么上传

在app store上架的时候&#xff0c;遇到下图的问题&#xff1a; 点击蓝色加号的时候&#xff0c;并没有构建版本可以选择 从图中可以看出&#xff0c;它给我们推荐了很多上传工具&#xff0c;比如xcode、transporter或命令行工具之类的&#xff0c;但是这些工具都是只能在苹果…

提升网站流量的关键:AI在SEO关键词优化中的应用

内容概要 在当今数字时代&#xff0c;提升网站流量已成为每个网站管理员的首要任务。而人工智能的技术进步&#xff0c;为搜索引擎优化&#xff08;SEO&#xff09;提供了强有力的支持&#xff0c;尤其是在关键词优化方面。关键词是连接用户需求与网站内容的桥梁&#xff0c;其…

低代码云组态支持draw.io导入导出

支持draw.io 官网&#xff1a;draw.io 绘图 进入官网绘制模型&#xff0c;完成后导出 导出 选择“文件“ > “导出“ > “SVG“,完成后即可进行导入 新建 在低代码平台新建一个“网络拓扑”模型&#xff0c;如下图所示&#xff1a; 设计 新建的“网络拓扑”模型进行…