Commit 701e61fb authored by Administrator's avatar Administrator

agent-chat2

parent a5e4ad9b
VITE_APP_BASE_URL='http://wm-tools-backend-test.frp.wmdigit.com:8888/'
\ No newline at end of file
......@@ -18,6 +18,7 @@ declare module 'vue' {
ElOption: typeof import('element-plus/es')['ElOption']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
ElStep: typeof import('element-plus/es')['ElStep']
ElSteps: typeof import('element-plus/es')['ElSteps']
ElText: typeof import('element-plus/es')['ElText']
......
......@@ -19,7 +19,7 @@
"crypto-js": "^4.2.0",
"element-plus": "^2.10.7",
"file-saver": "^2.0.5",
"marked": "^15.0.8",
"marked": "^15.0.12",
"pinia": "^2.1.7",
"sass": "^1.69.5",
"vue": "^3.3.4",
......
/**
* 相关接口
*/
import request from '@/api/request'
export default {
// 通用接口
commonApi(api_name: string, api: string, post_data: any): Promise<any> {
if (!api_name || !api || !post_data) {
return Promise.reject('所有参数不能为空')
}
// console.log(post_data);
return request
.post(`/api/v1/chat_bot/chat/${api}`, post_data)
.then((res: any) => {
// console.log(res);
if (res && res.code === 0) {
if (res.data.result) {
return res.data.result
} else {
return Promise.reject(`${api_name}接口未返回结果`)
}
} else {
const errorMessage = res ? res.message : `${api_name}接口返回错误`
return Promise.reject(errorMessage)
}
})
.catch((err: any) => {
console.log(`err = ${JSON.stringify(err)}`)
try {
return Promise.reject(err)
} catch (e) {
return Promise.reject(`与后端${api_name}接口通讯失败`)
}
})
},
}
<script setup lang="ts">
import utils from '@/utils/utils'
import { onUpdated, onMounted, reactive, ref } from 'vue'
import {
Check,
Delete,
Edit,
Message,
Search,
Star,
} from '@element-plus/icons-vue'
import aiAgentService from '@/api/service/aiAgentService';
import { ElMessage } from 'element-plus';
// 引入marked库
import { marked } from 'marked';
const title = ref('元芒数字')
// 对话数据
const chatSessionId = ref('')
const chatMessages = ref<Array<{role: string, content: string}>>([])
const inputMessage = ref('')
const loading = ref(false)
// 设置marked选项
marked.setOptions({
breaks: true, // 将换行符转换为<br>标签
gfm: true, // 启用GitHub Flavored Markdown
})
onMounted(async () => {
// 设置页面标题
document.title = title.value;
})
const onInitSubmit = async () => {
try {
chatSessionId.value = ''
chatMessages.value = []
let param: any = {}
await aiAgentService.commonApi('初始化session', 'Cooking Eating shopping buddy/init', param)
.then((response) => {
console.log(response)
// 初始化成功后清空聊天记录
chatSessionId.value = response.session_id || ''
chatMessages.value.push({
role: response.messages[0]?.role || 'assistant',
content: response.messages[0]?.content.markdown || '抱歉,我没有理解您的问题。'
})
ElMessage({
message: '会话初始化成功',
type: 'success'
})
})
.catch((error) => {
ElMessage({
message: String(error),
type: 'error'
})
})
} catch (error) {
ElMessage({
message: String(error),
type: 'error'
})
}
}
// 发送消息
const sendMessage = async () => {
if (!chatSessionId.value.trim()) {
ElMessage({
message: '请先初始化会话',
type: 'warning'
})
return
}
if (!inputMessage.value.trim() || loading.value) return
const userMessage = inputMessage.value.trim()
// 添加用户消息到聊天记录
chatMessages.value.push({
role: 'user',
content: userMessage
})
const currentMessage = inputMessage.value
inputMessage.value = ''
loading.value = true
try {
// 调用AI接口
const response = await aiAgentService.commonApi(
'发送消息',
'Cooking Eating shopping buddy/send',
{
channel: "WEB",
device_id: "a1c8bbd0-bd07-40f0-bd6b-a5efb9cf9347",
message: {
text: currentMessage
},
session_id: chatSessionId.value,
user_id: ""
}
)
// 添加AI回复到聊天记录
chatMessages.value.push({
role: response.messages[0]?.role || 'assistant',
content: response.messages[0]?.content.markdown || '抱歉,我没有理解您的问题。'
})
} catch (error) {
ElMessage({
message: String(error),
type: 'error'
})
// 出错时也添加错误信息到聊天记录
chatMessages.value.push({
role: 'assistant',
content: '抱歉,服务器出现错误,请稍后再试。'
})
} finally {
loading.value = false
}
}
// 清除会话
const clearSession = () => {
chatSessionId.value = ''
chatMessages.value = []
ElMessage({
message: '会话已清除',
type: 'success'
})
}
// 处理回车发送消息
const handleKeyPress = (e: KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
sendMessage()
}
}
// 将Markdown转换为HTML
const renderMarkdown = (content: string) => {
if (!content) return '';
return marked.parse(content);
}
</script>
<template>
<main class="home-container">
<!-- 标题 -->
<div class="title"><el-text>Agent Chat</el-text></div>
<div class="subtitle"><el-text>Agent Chat</el-text></div>
<div class="button">
<el-button type="success" @click="onInitSubmit">start session</el-button>
<el-button type="warning" @click="clearSession">clear session</el-button>
</div>
<text>SessionId = {{ chatSessionId }}</text>
<!-- 对话显示区域 -->
<div class="chat-container">
<div
v-for="(message, index) in chatMessages"
:key="index"
:class="['message', message.role]"
>
<div class="message-content">
<div class="avatar">
<el-avatar :icon="message.role === 'user' ? 'User' : 'Star'" size="small" />
</div>
<div class="content" v-html="renderMarkdown(message.content)">
</div>
</div>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="message assistant">
<div class="message-content">
<div class="avatar">
<el-avatar icon="Star" size="small" />
</div>
<div class="content">
<el-skeleton :rows="1" animated />
</div>
</div>
</div>
</div>
<!-- 输入区域 -->
<div class="input-container">
<el-input
v-model="inputMessage"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
placeholder="请输入您的问题..."
@keydown="handleKeyPress"
:disabled="loading"
/>
<el-button
type="primary"
@click="sendMessage"
:loading="loading"
:disabled="!inputMessage.trim() || loading"
class="send-button"
>
发送
</el-button>
</div>
</main>
</template>
<!-- 样式 只在当前页面生效,优先级比组件样式低 -->
<style lang="scss" scoped>
.home-container {
width: 80%;
display: flex;
flex-direction: column;
height: 100%;
padding: 20px;
box-sizing: border-box;
}
.title {
:is(span) {
font-size: 25px;
font-weight: bold;
color: #181818;
}
text-align: center;
margin: 20px 0 0 0;
}
.subtitle {
:is(span) {
font-size: 13px;
color: #181818;
}
text-align: center;
margin: 0 0 20px 0;
}
.button {
text-align: center;
margin: 10px 0 20px 0;
}
.chat-container {
flex: 1;
overflow-y: auto;
border: 1px solid #e4e7ed;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
background-color: #f5f7fa;
.message {
margin-bottom: 15px;
&.user {
.message-content {
flex-direction: row-reverse;
.content {
background-color: #409eff;
color: white;
border-radius: 15px 5px 15px 15px;
}
}
}
&.assistant {
.content {
background-color: white;
border-radius: 5px 15px 15px 15px;
// 添加表格样式
:deep(table) {
border-collapse: collapse;
width: 100%;
margin: 10px 0;
}
:deep(th), :deep(td) {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
:deep(th) {
background-color: #f2f2f2;
font-weight: bold;
}
:deep(tr:nth-child(even)) {
background-color: #f9f9f9;
}
}
}
.message-content {
display: flex;
gap: 10px;
.avatar {
flex-shrink: 0;
}
.content {
padding: 10px 15px;
line-height: 1.5;
word-wrap: break-word;
max-width: 80%;
// 处理Markdown渲染后的元素样式
:deep(p) {
margin: 0 0 10px 0;
}
:deep(ul), :deep(ol) {
padding-left: 20px;
}
:deep(li) {
margin-bottom: 5px;
}
:deep(pre) {
background-color: #f5f5f5;
padding: 10px;
border-radius: 5px;
overflow-x: auto;
}
:deep(code) {
background-color: #f5f5f5;
padding: 2px 4px;
border-radius: 3px;
}
}
}
}
}
.input-container {
display: flex;
flex-direction: column;
gap: 10px;
.el-textarea {
flex: 1;
}
.send-button {
align-self: flex-end;
width: 80px;
}
}
</style>
\ No newline at end of file
<script setup lang="ts">
import utils from '@/utils/utils'
import { onUpdated, onMounted, reactive, ref } from 'vue'
import {
Check,
Delete,
Edit,
Message,
Search,
Star,
} from '@element-plus/icons-vue'
import aiAgentService from '@/api/service/aiAgentService';
import { ElMessage } from 'element-plus';
const title = ref('元芒数字')
// 对话数据
const chatSessionId = ref('')
const chatMessages = ref<Array<{role: string, content: string}>>([])
const inputMessage = ref('')
const loading = ref(false)
onMounted(async () => {
// 设置页面标题
document.title = title.value;
})
const onInitSubmit = async () => {
try {
chatSessionId.value = ''
chatMessages.value = []
let param: any = {}
await aiAgentService.commonApi('初始化session', 'Cooking Eating shopping buddy/init', param)
.then((response) => {
console.log(response)
// 初始化成功后清空聊天记录
chatSessionId.value = response.session_id || ''
chatMessages.value.push({
role: response.messages[0]?.role || 'assistant',
content: response.messages[0]?.content.markdown || '抱歉,我没有理解您的问题。'
})
ElMessage({
message: '会话初始化成功',
type: 'success'
})
})
.catch((error) => {
ElMessage({
message: String(error),
type: 'error'
})
})
} catch (error) {
ElMessage({
message: String(error),
type: 'error'
})
}
}
// 发送消息
const sendMessage = async () => {
if (!chatSessionId.value.trim()) {
ElMessage({
message: '请先初始化会话',
type: 'warning'
})
return
}
if (!inputMessage.value.trim() || loading.value) return
const userMessage = inputMessage.value.trim()
// 添加用户消息到聊天记录
chatMessages.value.push({
role: 'user',
content: userMessage
})
const currentMessage = inputMessage.value
inputMessage.value = ''
loading.value = true
try {
// 调用AI接口
const response = await aiAgentService.commonApi(
'发送消息',
'Cooking Eating shopping buddy/send',
{
channel: "WEB",
device_id: "a1c8bbd0-bd07-40f0-bd6b-a5efb9cf9347",
message: {
text: currentMessage
},
session_id: chatSessionId.value,
user_id: ""
}
)
// 添加AI回复到聊天记录
chatMessages.value.push({
role: response.messages[0]?.role || 'assistant',
content: response.messages[0]?.content.markdown || '抱歉,我没有理解您的问题。'
})
} catch (error) {
ElMessage({
message: String(error),
type: 'error'
})
// 出错时也添加错误信息到聊天记录
chatMessages.value.push({
role: 'assistant',
content: '抱歉,服务器出现错误,请稍后再试。'
})
} finally {
loading.value = false
}
}
// 清除会话
const clearSession = () => {
chatSessionId.value = ''
chatMessages.value = []
ElMessage({
message: '会话已清除',
type: 'success'
})
}
// 处理回车发送消息
const handleKeyPress = (e: KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
sendMessage()
}
}
</script>
<template>
<main class="home-container">
<!-- 标题 -->
<div class="title"><el-text>Agent Chat</el-text></div>
<div class="subtitle"><el-text>Agent Chat</el-text></div>
<div class="button">
<el-button type="success" @click="onInitSubmit">start session</el-button>
<el-button type="warning" @click="clearSession">clear session</el-button>
</div>
<text>SessionId = {{ chatSessionId }}</text>
<!-- 对话显示区域 -->
<div class="chat-container">
<div
v-for="(message, index) in chatMessages"
:key="index"
:class="['message', message.role]"
>
<div class="message-content">
<div class="avatar">
<el-avatar :icon="message.role === 'user' ? 'User' : 'Star'" size="small" />
</div>
<div class="content">
{{ message.content }}
</div>
</div>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="message assistant">
<div class="message-content">
<div class="avatar">
<el-avatar icon="Star" size="small" />
</div>
<div class="content">
<el-skeleton :rows="1" animated />
</div>
</div>
</div>
</div>
<!-- 输入区域 -->
<div class="input-container">
<el-input
v-model="inputMessage"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
placeholder="请输入您的问题..."
@keydown="handleKeyPress"
:disabled="loading"
/>
<el-button
type="primary"
@click="sendMessage"
:loading="loading"
:disabled="!inputMessage.trim() || loading"
class="send-button"
>
发送
</el-button>
</div>
</main>
</template>
<!-- 样式 只在当前页面生效,优先级比组件样式低 -->
<style lang="scss" scoped>
.home-container {
width: 80%;
display: flex;
flex-direction: column;
height: 100%;
padding: 20px;
box-sizing: border-box;
}
.title {
:is(span) {
font-size: 25px;
font-weight: bold;
color: #181818;
}
text-align: center;
margin: 20px 0 0 0;
}
.subtitle {
:is(span) {
font-size: 13px;
color: #181818;
}
text-align: center;
margin: 0 0 20px 0;
}
.button {
text-align: center;
margin: 10px 0 20px 0;
}
.chat-container {
flex: 1;
overflow-y: auto;
border: 1px solid #e4e7ed;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
background-color: #f5f7fa;
.message {
margin-bottom: 15px;
&.user {
.message-content {
flex-direction: row-reverse;
.content {
background-color: #409eff;
color: white;
border-radius: 15px 5px 15px 15px;
}
}
}
&.assistant {
.content {
background-color: white;
border-radius: 5px 15px 15px 15px;
}
}
.message-content {
display: flex;
gap: 10px;
.avatar {
flex-shrink: 0;
}
.content {
padding: 10px 15px;
line-height: 1.5;
word-wrap: break-word;
max-width: 80%;
}
}
}
}
.input-container {
display: flex;
flex-direction: column;
gap: 10px;
.el-textarea {
flex: 1;
}
.send-button {
align-self: flex-end;
width: 80px;
}
}
</style>
\ No newline at end of file
.home-container {
width: 100%;
.title {
:is(span) {
font-size: 25px;
font-weight: bold;
color: #181818;
}
text-align: center;
margin: 20px 0 0 0;
}
.subtitle {
:is(span) {
font-size: 13px;
color: #181818;
}
text-align: center;
margin: 0 0 20px 0;
}
.button {
text-align: center;
margin: 10px 0 20px 0;
}
}
<script setup lang="ts">
import utils from '@/utils/utils'
import { onUpdated, onMounted, reactive, ref } from 'vue'
import { onUpdated, onMounted, reactive, ref, nextTick } from 'vue' // 引入 nextTick
import {
Check,
Delete,
......@@ -9,20 +9,68 @@ import {
Search,
Star,
} from '@element-plus/icons-vue'
import aitoolsService from '@/api/service/aitoolsService';
import aiAgentService from '@/api/service/aiAgentService';
import { ElMessage } from 'element-plus';
// 引入marked库
import { marked } from 'marked';
const title = ref('元芒数字')
// 对话数据
const chatSessionId = ref('')
const chatMessages = ref<Array<{role: string, content: string}>>([])
const inputMessage = ref('')
const loading = ref(false)
// 聊天容器的引用
const chatContainerRef = ref<HTMLElement | null>(null) // 1. 添加一个ref来引用聊天容器
// 设置marked选项
marked.setOptions({
breaks: true, // 将换行符转换为<br>标签
gfm: true, // 启用GitHub Flavored Markdown
})
onMounted(async () => {
// 设置页面标题
document.title = title.value;
})
// 滚动到底部的函数
const scrollToBottom = () => {
nextTick(() => { // 确保在DOM更新后执行
if (chatContainerRef.value) {
chatContainerRef.value.scrollTop = chatContainerRef.value.scrollHeight
}
})
}
const onInitSubmit = async () => {
try {
chatSessionId.value = ''
chatMessages.value = []
let param: any = {}
await aitoolsService.commonApi('初始化session', 'take_customer_info', param);
await aiAgentService.commonApi('初始化session', 'Cooking Eating shopping buddy/init', param)
.then((response) => {
console.log(response)
// 初始化成功后清空聊天记录
chatSessionId.value = response.session_id || ''
chatMessages.value.push({
role: response.messages[0]?.role || 'assistant',
content: response.messages[0]?.content.markdown || '抱歉,我没有理解您的问题。'
})
ElMessage({
message: '会话初始化成功',
type: 'success'
})
scrollToBottom() // 2. 初始化成功后滚动到底部
})
.catch((error) => {
ElMessage({
message: String(error),
type: 'error'
})
})
} catch (error) {
ElMessage({
message: String(error),
......@@ -31,9 +79,91 @@ const onInitSubmit = async () => {
}
}
// 发送消息
const sendMessage = async () => {
if (!chatSessionId.value.trim()) {
ElMessage({
message: '请先初始化会话',
type: 'warning'
})
return
}
if (!inputMessage.value.trim() || loading.value) return
const userMessage = inputMessage.value.trim()
// 添加用户消息到聊天记录
chatMessages.value.push({
role: 'user',
content: userMessage
})
scrollToBottom() // 3. 添加用户消息后先滚动一次 (可选,为了更好体验)
const currentMessage = inputMessage.value
inputMessage.value = ''
loading.value = true
</script>
try {
// 调用AI接口
const response = await aiAgentService.commonApi(
'发送消息',
'Cooking Eating shopping buddy/send',
{
channel: "WEB",
device_id: "a1c8bbd0-bd07-40f0-bd6b-a5efb9cf9347",
message: {
text: currentMessage
},
session_id: chatSessionId.value,
user_id: ""
}
)
// 添加AI回复到聊天记录
chatMessages.value.push({
role: response.messages[0]?.role || 'assistant',
content: response.messages[0]?.content.markdown || '抱歉,我没有理解您的问题。'
})
scrollToBottom() // 4. 添加AI回复后滚动到底部
} catch (error) {
ElMessage({
message: String(error),
type: 'error'
})
// 出错时也添加错误信息到聊天记录
chatMessages.value.push({
role: 'assistant',
content: '抱歉,服务器出现错误,请稍后再试。'
})
scrollToBottom() // 5. 出错添加消息后滚动到底部
} finally {
loading.value = false
}
}
// 清除会话
const clearSession = () => {
chatSessionId.value = ''
chatMessages.value = []
ElMessage({
message: '会话已清除',
type: 'success'
})
}
// 处理回车发送消息
const handleKeyPress = (e: KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
sendMessage()
}
}
// 将Markdown转换为HTML
const renderMarkdown = (content: string) => {
if (!content) return '';
return marked.parse(content);
}
</script>
<template>
<main class="home-container">
......@@ -42,25 +172,206 @@ const onInitSubmit = async () => {
<div class="subtitle"><el-text>Agent Chat</el-text></div>
<div class="button">
<el-button type="success">start session</el-button>
<el-button type="success">clear session</el-button>
<el-button type="success" @click="onInitSubmit">start session</el-button>
<el-button type="warning" @click="clearSession">clear session</el-button>
</div>
<div class="chat-container">
<div class="chat-item">
<div class="chat-item-avatar">
<el-avatar src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"></el-avatar>
<text>SessionId = {{ chatSessionId }}</text>
<!-- 对话显示区域 -->
<!-- 添加ref引用以便操作DOM -->
<div class="chat-container" ref="chatContainerRef">
<div
v-for="(message, index) in chatMessages"
:key="index"
:class="['message', message.role]"
>
<div class="message-content">
<div class="avatar">
<el-avatar :icon="message.role === 'user' ? 'User' : 'Star'" size="small" />
</div>
<div class="content" v-html="renderMarkdown(message.content)">
</div>
</div>
<div class="chat-item-content">
<div class="chat-item-content-text">
<el-text>你好,我是元小芒</el-text>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="message assistant">
<div class="message-content">
<div class="avatar">
<el-avatar icon="Star" size="small" />
</div>
<div class="content">
<el-skeleton :rows="1" animated />
</div>
</div>
</div>
</div>
<!-- 输入区域 -->
<div class="input-container">
<el-input
v-model="inputMessage"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
placeholder="请输入您的问题..."
@keydown="handleKeyPress"
:disabled="loading"
/>
<el-button
type="primary"
@click="sendMessage"
:loading="loading"
:disabled="!inputMessage.trim() || loading"
class="send-button"
>
发送
</el-button>
</div>
</main>
</template>
<!-- 样式 只在当前页面生效,优先级比组件样式低 -->
<style lang="scss" scoped src="./index.css"></style>
<style lang="scss" scoped>
.home-container {
width: 80%;
display: flex;
flex-direction: column;
height: 100%;
padding: 20px;
box-sizing: border-box;
}
.title {
:is(span) {
font-size: 25px;
font-weight: bold;
color: #181818;
}
text-align: center;
margin: 20px 0 0 0;
}
.subtitle {
:is(span) {
font-size: 13px;
color: #181818;
}
text-align: center;
margin: 0 0 20px 0;
}
.button {
text-align: center;
margin: 10px 0 20px 0;
}
.chat-container {
flex: 1;
overflow-y: auto;
border: 1px solid #e4e7ed;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
background-color: #f5f7fa;
.message {
margin-bottom: 15px;
&.user {
.message-content {
flex-direction: row-reverse;
.content {
background-color: #409eff;
color: white;
border-radius: 15px 5px 15px 15px;
}
}
}
&.assistant {
.content {
background-color: white;
border-radius: 5px 15px 15px 15px;
// 添加表格样式
:deep(table) {
border-collapse: collapse;
width: 100%;
margin: 10px 0;
}
:deep(th), :deep(td) {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
:deep(th) {
background-color: #f2f2f2;
font-weight: bold;
}
:deep(tr:nth-child(even)) {
background-color: #f9f9f9;
}
}
}
.message-content {
display: flex;
gap: 10px;
.avatar {
flex-shrink: 0;
}
.content {
padding: 10px 15px;
line-height: 1.5;
word-wrap: break-word;
max-width: 80%;
// 处理Markdown渲染后的元素样式
:deep(p) {
margin: 0 0 10px 0;
}
:deep(ul), :deep(ol) {
padding-left: 20px;
}
:deep(li) {
margin-bottom: 5px;
}
:deep(pre) {
background-color: #f5f5f5;
padding: 10px;
border-radius: 5px;
overflow-x: auto;
}
:deep(code) {
background-color: #f5f5f5;
padding: 2px 4px;
border-radius: 3px;
}
}
}
}
}
.input-container {
display: flex;
flex-direction: column;
gap: 10px;
.el-textarea {
flex: 1;
}
.send-button {
align-self: flex-end;
width: 80px;
}
}
</style>
\ No newline at end of file
......@@ -27,9 +27,9 @@ export default defineConfig({
server: {
proxy: {
'/api/': {
// target: 'http://127.0.0.1:5001/', // 本机后端服务
target: 'http://127.0.0.1:5000/', // 本机后端服务
// target: 'http://wm-tools-backend.frp.wmdigit.com:8888/', // new3090后端服务
target: 'http://wm-tools-backend-test.frp.wmdigit.com:8888/', // 测试后端服务
// target: 'http://wm-tools-backend-test.frp.wmdigit.com:8888/', // 测试后端服务
changeOrigin: true,
rewrite: (path: any) => path.replace(/^\/api/, '')
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment