New file |
| | |
| | | > 1% |
| | | last 2 versions |
| | | not dead |
New file |
| | |
| | | module.exports = { |
| | | root: true, |
| | | env: { |
| | | node: true |
| | | }, |
| | | 'extends': [ |
| | | 'plugin:vue/essential', |
| | | 'eslint:recommended' |
| | | ], |
| | | parserOptions: { |
| | | parser: '@babel/eslint-parser' |
| | | }, |
| | | rules: { |
| | | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', |
| | | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' |
| | | } |
| | | } |
New file |
| | |
| | | .DS_Store |
| | | node_modules |
| | | /dist |
| | | |
| | | |
| | | # local env files |
| | | .env.local |
| | | .env.*.local |
| | | |
| | | # Log files |
| | | npm-debug.log* |
| | | yarn-debug.log* |
| | | yarn-error.log* |
| | | pnpm-debug.log* |
| | | |
| | | # Editor directories and files |
| | | .idea |
| | | .vscode |
| | | *.suo |
| | | *.ntvs* |
| | | *.njsproj |
| | | *.sln |
| | | *.sw? |
| | |
| | | ## knowledge-base-ai-client |
| | | # ai-client |
| | | |
| | | 核工业西南物理研究院知识库AI客户端 |
| | | ## Project setup |
| | | ``` |
| | | npm install |
| | | ``` |
| | | |
| | | ### Compiles and hot-reloads for development |
| | | ``` |
| | | npm run serve |
| | | ``` |
| | | |
| | | ### Compiles and minifies for production |
| | | ``` |
| | | npm run build |
| | | ``` |
| | | |
| | | ### Lints and fixes files |
| | | ``` |
| | | npm run lint |
| | | ``` |
| | | |
| | | ### Customize configuration |
| | | See [Configuration Reference](https://cli.vuejs.org/config/). |
New file |
| | |
| | | module.exports = { |
| | | presets: [ |
| | | '@vue/cli-plugin-babel/preset' |
| | | ] |
| | | } |
New file |
| | |
| | | { |
| | | "compilerOptions": { |
| | | "target": "es5", |
| | | "module": "esnext", |
| | | "baseUrl": "./", |
| | | "moduleResolution": "node", |
| | | "paths": { |
| | | "@/*": [ |
| | | "src/*" |
| | | ] |
| | | }, |
| | | "lib": [ |
| | | "esnext", |
| | | "dom", |
| | | "dom.iterable", |
| | | "scripthost" |
| | | ] |
| | | } |
| | | } |
New file |
| | |
| | | { |
| | | "name": "ai-client", |
| | | "version": "0.1.0", |
| | | "private": true, |
| | | "scripts": { |
| | | "serve": "vue-cli-service serve", |
| | | "build": "vue-cli-service build", |
| | | "lint": "vue-cli-service lint" |
| | | }, |
| | | "dependencies": { |
| | | "axios": "^1.8.4", |
| | | "core-js": "^3.8.3", |
| | | "element-ui": "^2.15.14", |
| | | "vue": "^2.6.14", |
| | | "vue-router": "^3.5.1", |
| | | "vuex": "^3.6.2" |
| | | }, |
| | | "devDependencies": { |
| | | "@babel/core": "^7.12.16", |
| | | "@babel/eslint-parser": "^7.12.16", |
| | | "@vue/cli-plugin-babel": "~5.0.0", |
| | | "@vue/cli-plugin-eslint": "~5.0.0", |
| | | "@vue/cli-plugin-router": "~5.0.0", |
| | | "@vue/cli-plugin-vuex": "~5.0.0", |
| | | "@vue/cli-service": "~5.0.0", |
| | | "eslint": "^7.32.0", |
| | | "eslint-plugin-vue": "^8.0.3", |
| | | "less": "^4.0.0", |
| | | "less-loader": "^8.0.0", |
| | | "vue-template-compiler": "^2.6.14" |
| | | } |
| | | } |
New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang=""> |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| | | <meta name="viewport" content="width=device-width,initial-scale=1.0"> |
| | | <link rel="icon" href="<%= BASE_URL %>favicon.ico"> |
| | | <title><%= htmlWebpackPlugin.options.title %></title> |
| | | </head> |
| | | <body> |
| | | <noscript> |
| | | <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> |
| | | </noscript> |
| | | <div id="app"></div> |
| | | <!-- built files will be auto injected --> |
| | | </body> |
| | | </html> |
New file |
| | |
| | | <template> |
| | | <div id="app"> |
| | | <router-view/> |
| | | </div> |
| | | </template> |
| | | |
| | | <style lang="less"> |
| | | #app { |
| | | font-family: Avenir, Helvetica, Arial, sans-serif; |
| | | -webkit-font-smoothing: antialiased; |
| | | -moz-osx-font-smoothing: grayscale; |
| | | text-align: center; |
| | | color: #2c3e50; |
| | | } |
| | | |
| | | nav { |
| | | padding: 30px; |
| | | |
| | | a { |
| | | font-weight: bold; |
| | | color: #2c3e50; |
| | | |
| | | &.router-link-exact-active { |
| | | color: #42b983; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import axios from "./request"; |
| | | |
| | | // 发送知识库问题 |
| | | export const sendKbMsg = (data) => { |
| | | return axios({ |
| | | url: "chat/kb_chat", |
| | | method: "POST", |
| | | data: data |
| | | }) |
| | | } |
New file |
| | |
| | | import axios from "axios"; |
| | | import router from "../router"; |
| | | import { Message } from 'element-ui'; |
| | | |
| | | const instance = axios.create({ |
| | | baseURL: '/api/', |
| | | timeout: 50000, |
| | | // 不携带cookie |
| | | withCredentials: false, |
| | | headers: { |
| | | "Content-Type": "application/json" |
| | | } |
| | | }); |
| | | |
| | | // 添加请求拦截器 |
| | | instance.interceptors.request.use(function (config) { |
| | | // 带上token |
| | | if (sessionStorage.getItem('token') !== null) { |
| | | config.headers['Authentication'] = sessionStorage.getItem("token"); |
| | | } |
| | | return config; |
| | | }, function (error) { |
| | | Message.error("请求存在问题,请检查") |
| | | return Promise.reject(error); |
| | | }); |
| | | |
| | | // 添加响应拦截器 |
| | | instance.interceptors.response.use(function (response) { |
| | | // 处理自定义状态码 |
| | | if(response.data.code === 200) { |
| | | return response; |
| | | // 验证码错误放行,以便刷新验证码 |
| | | } else if(response.data.code === 404) { |
| | | Message.error(response.data.msg); |
| | | } else if (response.data.code === 1998) { |
| | | return response; |
| | | } else { |
| | | Message.error(response.data.msg); |
| | | return Promise.reject(response.data.msg); |
| | | } |
| | | }, function (error) { |
| | | // 处理http状态码 |
| | | if(error.response.data) { |
| | | error.message = error.response.data.msg; |
| | | } |
| | | if(error.response.status === 401) { |
| | | error.message = "登录已过期,请重新登录"; |
| | | // 删掉sessionStorage中过期token |
| | | sessionStorage.clear(); |
| | | router.push("/"); |
| | | } |
| | | if(error.response.status === 403) { |
| | | error.message = "权限不足"; |
| | | } |
| | | if(error.response.status === 404) { |
| | | error.message = "请求地址不存在"; |
| | | } |
| | | Message.error(error.message); |
| | | return Promise.reject(error); |
| | | }); |
| | | |
| | | export default instance; |
New file |
| | |
| | | <template> |
| | | <div class="ai-chat-dialog"> |
| | | <!-- 聊天消息列表 --> |
| | | <div class="chat-messages"> |
| | | <div |
| | | v-for="(message, index) in messages" |
| | | :key="index" |
| | | :class="['message', message.role]" |
| | | > |
| | | <div class="avatar"> |
| | | <img :src="getAvatar(message.role)" alt="avatar" /> |
| | | </div> |
| | | <div class="content"> |
| | | <!-- <div class="name">{{ getRoleName(message.role) }}</div>--> |
| | | <div class="text">{{ message.content }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 输入框 --> |
| | | <div class="chat-input"> |
| | | <el-input |
| | | style="width: 50%" |
| | | v-model="inputMessage" |
| | | placeholder="请输入消息" |
| | | @keyup.native.enter="sendMessage" |
| | | > |
| | | <div slot="append"> |
| | | <el-button type="primary" @click="sendMessage" icon="el-icon-thumb"></el-button> |
| | | </div> |
| | | </el-input> |
| | | <!-- <div class="send-but" @click="sendMessage">--> |
| | | <!-- <svg t="1742455190051" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1160" width="32" height="32"><path d="M417.016025 991.681197h266.763504l286.972859-930.353884z" fill="#6E6E96" opacity=".2" p-id="1161"></path><path d="M318.168022 667.620127l106.879202 307.873376L985.008068 34.974312z" fill="#E5C0A8" p-id="1162"></path><path d="M425.051265 993.681924a18.180337 18.180337 0 0 1-17.186036-12.222619l-106.87516-307.873376a18.188421 18.188421 0 0 1 4.664319-19.15847L972.498476 21.777602a18.184379 18.184379 0 0 1 28.14355 22.497056L440.67714 984.797891a18.192463 18.192463 0 0 1-15.625875 8.884033z m-85.849345-320.940746l90.210525 259.868071L902.064828 138.741273 339.20192 672.741178z" fill="#6E6E96" p-id="1163"></path><path d="M51.287304 548.991206L985.008068 34.974312 318.168022 667.620127z" fill="#FFDAC1" p-id="1164"></path><path d="M318.16398 685.808548c-2.497876 0-5.01192-0.513318-7.384499-1.568246l-266.876675-118.628922a18.188421 18.188421 0 0 1-10.783713-15.848177 18.204588 18.204588 0 0 1 9.401393-16.713137L976.237207 19.041256a18.188421 18.188421 0 1 1 21.292578 29.129766L330.689739 680.816837a18.184379 18.184379 0 0 1-12.525759 4.991711z m-226.005273-138.555347l222.302919 98.811627L869.915784 119.097779 92.158707 547.253201z" fill="#6E6E96" p-id="1165"></path><path d="M985.008068 34.974312L425.281652 711.656315l429.48924 166.755482z" fill="#FFDAC1" p-id="1166"></path><path d="M854.770892 896.60426a18.196504 18.196504 0 0 1-6.584208-1.232771l-429.48924-166.759524a18.176295 18.176295 0 0 1-7.42896-28.551779L970.998942 23.382225a18.196504 18.196504 0 0 1 31.995453 14.368853l-130.245259 843.445569a18.224798 18.224798 0 0 1-8.87595 12.978448 18.325844 18.325844 0 0 1-9.102294 2.429165z m-399.409634-192.781092l384.887191 149.44819L956.969607 97.417181 455.361258 703.823168z" fill="#6E6E96" p-id="1167"></path><path d="M425.281652 993.681924a18.192463 18.192463 0 0 1-18.188421-18.188421v-263.837188a18.188421 18.188421 0 0 1 36.376842 0v263.837188a18.188421 18.188421 0 0 1-18.188421 18.188421z" fill="#6E6E96" p-id="1168"></path><path d="M985.008068 34.974312L518.147686 728.203736l-62.786428-24.380568zM417.016025 711.656315l78.937746 109.18711 32.504729-54.593555z" fill="#6E6E96" opacity=".2" p-id="1169"></path></svg>--> |
| | | <!-- </div>--> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import {sendKbMsg} from "@/api/chat"; |
| | | |
| | | export default { |
| | | name: 'AiChat', |
| | | data() { |
| | | return { |
| | | messages: [ |
| | | { |
| | | role: 'assistant', |
| | | content: '你好!我是你的 AI 助手,有什么可以帮你的吗?', |
| | | }, |
| | | ], |
| | | inputMessage: '', |
| | | sendMsgForm: { |
| | | query: "", |
| | | mode: "local_kb", |
| | | kb_name: "samples", |
| | | top_k: 3, |
| | | score_threshold: 2, |
| | | history: [ |
| | | { |
| | | "content": "我们来玩成语接龙,我先来,生龙活虎", |
| | | "role": "user" |
| | | }, |
| | | { |
| | | "content": "虎头虎脑", |
| | | "role": "assistant" |
| | | } |
| | | ], |
| | | stream: true, |
| | | model: "qwen:7b", |
| | | temperature: 0.7, |
| | | max_tokens: 0, |
| | | prompt_name: "default", |
| | | return_direct: false |
| | | } |
| | | }; |
| | | }, |
| | | methods: { |
| | | // 获取角色头像 |
| | | getAvatar(role) { |
| | | const avatars = { |
| | | user: 'https://picsum.photos/200', |
| | | assistant: 'https://picsum.photos/200', |
| | | }; |
| | | return avatars[role]; |
| | | }, |
| | | |
| | | // 获取角色名称 |
| | | getRoleName(role) { |
| | | const roleNames = { |
| | | user: '用户', |
| | | assistant: 'AI 助手', |
| | | }; |
| | | return roleNames[role]; |
| | | }, |
| | | |
| | | // 发送消息 |
| | | async sendMessage() { |
| | | if (this.inputMessage.trim() === '') return; |
| | | |
| | | // 添加用户消息 |
| | | this.messages.push({ |
| | | role: 'user', |
| | | content: this.inputMessage, |
| | | }); |
| | | this.sendMsgForm.query = this.inputMessage |
| | | // 清空输入框 |
| | | this.inputMessage = ''; |
| | | |
| | | sendKbMsg(this.sendMsgForm).then(res => { |
| | | console.log(res, "拿到回复了!!") |
| | | this.messages.push({ |
| | | role: 'assistant', |
| | | content: '这是一条 AI 回复。', |
| | | }); |
| | | }) |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .send-but { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: 58px; |
| | | height: 54px; |
| | | background-color: #f6f6f6; |
| | | margin-left: 5px; |
| | | } |
| | | .send-but:hover { |
| | | cursor: pointer; |
| | | } |
| | | .ai-chat-dialog { |
| | | display: flex; |
| | | flex-direction: column; |
| | | border-radius: 8px; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .chat-messages { |
| | | flex: 1; |
| | | padding: 16px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .message { |
| | | display: flex; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .message.user { |
| | | flex-direction: row-reverse; |
| | | } |
| | | |
| | | .avatar img { |
| | | width: 40px; |
| | | height: 40px; |
| | | border-radius: 50%; |
| | | } |
| | | |
| | | .content { |
| | | max-width: 70%; |
| | | margin: 0 12px; |
| | | padding: 8px 12px; |
| | | border-radius: 8px; |
| | | background-color: #fff; |
| | | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
| | | } |
| | | |
| | | .message.user .content { |
| | | background-color: #409eff; |
| | | color: #fff; |
| | | } |
| | | |
| | | .name { |
| | | font-size: 12px; |
| | | color: #666; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | .message.user .name { |
| | | color: #fff; |
| | | } |
| | | |
| | | .text { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .chat-input { |
| | | padding: 16px; |
| | | background-color: #fff; |
| | | border-top: 1px solid #ddd; |
| | | display: flex; |
| | | width: 100%; |
| | | flex-direction: row; |
| | | justify-content: center; |
| | | align-items: center; |
| | | position: absolute; |
| | | bottom: 70px; |
| | | } |
| | | </style> |
New file |
| | |
| | | import Vue from 'vue' |
| | | import App from './App.vue' |
| | | import router from './router' |
| | | import store from './store' |
| | | |
| | | import ElementUI from 'element-ui'; |
| | | import 'element-ui/lib/theme-chalk/index.css'; |
| | | |
| | | Vue.config.productionTip = false |
| | | |
| | | Vue.use(ElementUI); |
| | | |
| | | new Vue({ |
| | | router, |
| | | store, |
| | | render: h => h(App) |
| | | }).$mount('#app') |
New file |
| | |
| | | import Vue from 'vue' |
| | | import VueRouter from 'vue-router' |
| | | import HomeView from '../views/HomeView.vue' |
| | | |
| | | Vue.use(VueRouter) |
| | | |
| | | const routes = [ |
| | | { |
| | | path: '/', |
| | | name: 'home', |
| | | component: HomeView |
| | | }, |
| | | // { |
| | | // path: '/about', |
| | | // name: 'about', |
| | | // // route level code-splitting |
| | | // // this generates a separate chunk (about.[hash].js) for this route |
| | | // // which is lazy-loaded when the route is visited. |
| | | // component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue') |
| | | // } |
| | | ] |
| | | |
| | | const router = new VueRouter({ |
| | | mode: 'history', |
| | | routes |
| | | }) |
| | | |
| | | export default router |
New file |
| | |
| | | import Vue from 'vue' |
| | | import Vuex from 'vuex' |
| | | |
| | | Vue.use(Vuex) |
| | | |
| | | export default new Vuex.Store({ |
| | | state: { |
| | | }, |
| | | getters: { |
| | | }, |
| | | mutations: { |
| | | }, |
| | | actions: { |
| | | }, |
| | | modules: { |
| | | } |
| | | }) |
New file |
| | |
| | | <template> |
| | | <div class="home"> |
| | | <ai-chat/> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | |
| | | import AiChat from "@/components/AiChat"; |
| | | |
| | | export default { |
| | | name: 'HomeView', |
| | | components: { |
| | | AiChat |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | const { defineConfig } = require('@vue/cli-service') |
| | | module.exports = defineConfig({ |
| | | transpileDependencies: true, |
| | | devServer: { |
| | | proxy: { |
| | | "/api": { |
| | | target: 'http://i-1.gpushare.com:10695/',//代理地址 凡是使用/api |
| | | changeOrigin: true,//允许跨域请求 |
| | | secure: false, |
| | | pathRewrite: { //重写路径 替换请求地址中的指定路径 |
| | | ['^/api']: '/' //将请求地址中的api替换为空 |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }) |