Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ def _write_context(node_variable: Dict, workflow_variable: Dict, node: INode, wo
node.context['message_tokens'] = message_tokens
node.context['answer_tokens'] = answer_tokens
node.context['answer'] = answer
node.context['history_message'] = node_variable['history_message']
node.context['question'] = node_variable['question']
node.context['run_time'] = time.time() - node.context['start_time']
node.context['reasoning_content'] = reasoning_content
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.2.8 on 2025-12-23 10:28

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('application', '0004_application_application_enable_and_more'),
]

operations = [
migrations.AddField(
model_name='chatrecord',
name='vote_other_content',
field=models.CharField(default='', max_length=1024, verbose_name='其他原因'),
),
migrations.AddField(
model_name='chatrecord',
name='vote_reason',
field=models.CharField(blank=True, choices=[('accurate', '内容准确'), ('complete', '内容完善'), ('inaccurate', '内容不准确'), ('incomplete', '内容不完善'), ('other', '其他')], max_length=50, null=True, verbose_name='投票原因'),
),
]
8 changes: 8 additions & 0 deletions apps/application/models/application_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ class VoteChoices(models.TextChoices):
STAR = "0", '赞同'
TRAMPLE = "1", '反对'

class VoteReasonChoices(models.TextChoices):
ACCURATE = 'accurate', '内容准确'
COMPLETE = 'complete', '内容完善'
INACCURATE = 'inaccurate', '内容不准确'
INCOMPLETE = 'incomplete', '内容不完善'
OTHER = 'other', '其他'

class ChatRecord(AppModelMixin):
"""
Expand All @@ -63,6 +69,8 @@ class ChatRecord(AppModelMixin):
chat = models.ForeignKey(Chat, on_delete=models.CASCADE)
vote_status = models.CharField(verbose_name='投票', max_length=10, choices=VoteChoices.choices,
default=VoteChoices.UN_VOTE)
vote_reason =models.CharField(verbose_name='投票原因', max_length=50,choices=VoteReasonChoices.choices, null=True, blank=True)
vote_other_content = models.CharField(verbose_name='其他原因', max_length=1024, default='')
problem_text = models.CharField(max_length=10240, verbose_name="问题")
answer_text = models.CharField(max_length=40960, verbose_name="答案")
answer_text_list = ArrayField(verbose_name="改进标注列表",
Expand Down
2 changes: 1 addition & 1 deletion apps/application/serializers/application_chat_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
class ChatRecordSerializerModel(serializers.ModelSerializer):
class Meta:
model = ChatRecord
fields = ['id', 'chat_id', 'vote_status', 'problem_text', 'answer_text',
fields = ['id', 'chat_id', 'vote_status','vote_reason','vote_other_content', 'problem_text', 'answer_text',
'message_tokens', 'answer_tokens', 'const', 'improve_paragraph_id_list', 'run_time', 'index',
'answer_text_list',
'create_time', 'update_time']
Expand Down
18 changes: 17 additions & 1 deletion apps/chat/serializers/chat_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from django.utils.translation import gettext_lazy as _, gettext
from rest_framework import serializers

from application.models import VoteChoices, ChatRecord, Chat, ApplicationAccessToken
from application.models import VoteChoices, ChatRecord, Chat, ApplicationAccessToken, VoteReasonChoices
from application.serializers.application_chat import ChatCountSerializer
from application.serializers.application_chat_record import ChatRecordSerializerModel, \
ApplicationChatRecordQuerySerializers
Expand All @@ -25,7 +25,9 @@
class VoteRequest(serializers.Serializer):
vote_status = serializers.ChoiceField(choices=VoteChoices.choices,
label=_("Bidding Status"))
vote_reason = serializers.ChoiceField(choices=VoteReasonChoices.choices, label=_("Vote Reason"), required=False, allow_null=True)

vote_other_content = serializers.CharField(required=False, allow_blank=True,label=_("Vote other content"))

class HistoryChatModel(serializers.ModelSerializer):
class Meta:
Expand Down Expand Up @@ -59,19 +61,33 @@ def vote(self, instance: Dict, with_valid=True):
if chat_record_details_model is None:
raise AppApiException(500, gettext("Non-existent conversation chat_record_id"))
vote_status = instance.get("vote_status")

# 未投票状态,可以进行投票
if chat_record_details_model.vote_status == VoteChoices.UN_VOTE:
# 投票时获取字段
vote_reason = instance.get("vote_reason")
vote_other_content = instance.get("vote_other_content") or ''

if vote_status == VoteChoices.STAR:
# 点赞
chat_record_details_model.vote_status = VoteChoices.STAR
chat_record_details_model.vote_reason = vote_reason
chat_record_details_model.vote_other_content = vote_other_content

if vote_status == VoteChoices.TRAMPLE:
# 点踩
chat_record_details_model.vote_status = VoteChoices.TRAMPLE
chat_record_details_model.vote_reason = vote_reason
chat_record_details_model.vote_other_content = vote_other_content

chat_record_details_model.save()
# 已投票状态
else:
if vote_status == VoteChoices.UN_VOTE:
# 取消点赞
chat_record_details_model.vote_status = VoteChoices.UN_VOTE
chat_record_details_model.vote_reason = None
chat_record_details_model.vote_other_content = ''
chat_record_details_model.save()
else:
raise AppApiException(500, gettext("Already voted, please cancel first and then vote again"))
Expand Down
14 changes: 10 additions & 4 deletions ui/src/api/chat/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,19 @@ const vote: (
chat_id: string,
chat_record_id: string,
vote_status: string,
vote_reason?: string,
vote_other_content?: string,
loading?: Ref<boolean>,
) => Promise<Result<boolean>> = (chat_id, chat_record_id, vote_status, loading) => {
) => Promise<Result<boolean>> = (chat_id, chat_record_id, vote_status, vote_reason, vote_other_content, loading) => {

const data = {
vote_status,
...(vote_reason !== undefined && { vote_reason }),
...(vote_other_content !== undefined && { vote_other_content })
}
return put(
`/vote/chat/${chat_id}/chat_record/${chat_record_id}`,
{
vote_status,
},
data,
undefined,
loading,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,44 +52,73 @@
</el-button>
</el-tooltip>
<el-divider direction="vertical" />
<el-tooltip
effect="dark"
:content="$t('chat.operation.like')"
placement="top"
v-if="buttonData?.vote_status === '-1'"
>
<el-button text @click="voteHandle('0')" :disabled="loading">
<AppIcon iconName="app-like"></AppIcon>
</el-button>
</el-tooltip>
<el-popover ref="likePopoverRef" trigger="click" placement="bottom-start" :width="400">
<template #reference>
<span>
<el-tooltip
effect="dark"
:content="$t('chat.operation.like')"
placement="top"
v-if="buttonData?.vote_status === '-1'"
>
<el-button text :disabled="loading">
<AppIcon iconName="app-like"></AppIcon>
</el-button>
</el-tooltip>
</span>
</template>
<VoteReasonContent
vote-type="0"
:chat-id="props.chatId"
:record-id="props.data.record_id"
@success="handleVoteSuccess"
@close="closePopover"
>
</VoteReasonContent>
</el-popover>

<el-tooltip
effect="dark"
:content="$t('chat.operation.cancelLike')"
placement="top"
v-if="buttonData?.vote_status === '0'"
>
<el-button text @click="voteHandle('-1')" :disabled="loading">
<el-button text @click="cancelVoteHandle('-1')" :disabled="loading">
<AppIcon iconName="app-like-color"></AppIcon>
</el-button>
</el-tooltip>
<el-divider direction="vertical" v-if="buttonData?.vote_status === '-1'" />
<el-tooltip
effect="dark"
:content="$t('chat.operation.oppose')"
placement="top"
v-if="buttonData?.vote_status === '-1'"
>
<el-button text @click="voteHandle('1')" :disabled="loading">
<AppIcon iconName="app-oppose"></AppIcon>
</el-button>
</el-tooltip>
<el-popover ref="opposePopoverRef" trigger="click" placement="bottom-start" :width="400">
<template #reference>
<span>
<el-tooltip
effect="dark"
:content="$t('chat.operation.oppose')"
placement="top"
v-if="buttonData?.vote_status === '-1'"
>
<el-button text :disabled="loading">
<AppIcon iconName="app-oppose"></AppIcon>
</el-button>
</el-tooltip>
</span>
</template>
<VoteReasonContent
vote-type="1"
:chat-id="props.chatId"
:record-id="props.data.record_id"
@success="handleVoteSuccess"
@close="closePopover"
>
</VoteReasonContent>
</el-popover>
<el-tooltip
effect="dark"
:content="$t('chat.operation.cancelOppose')"
placement="top"
v-if="buttonData?.vote_status === '1'"
>
<el-button text @click="voteHandle('-1')" :disabled="loading">
<el-button text @click="cancelVoteHandle('-1')" :disabled="loading">
<AppIcon iconName="app-oppose-color"></AppIcon>
</el-button>
</el-tooltip>
Expand All @@ -106,6 +135,7 @@ import applicationApi from '@/api/application/application'
import chatAPI from '@/api/chat/chat'
import { datetimeFormat } from '@/utils/time'
import { MsgError } from '@/utils/message'
import VoteReasonContent from '@/components/ai-chat/component/operation-button/VoteReasonContent.vue'
import bus from '@/bus'
const copy = (data: any) => {
try {
Expand All @@ -117,6 +147,12 @@ const copy = (data: any) => {
copyClick(removeFormRander(data?.answer_text.trim()))
}
}
const likePopoverRef = ref()
const opposePopoverRef = ref()
const closePopover = () => {
likePopoverRef.value.hide()
opposePopoverRef.value.hide()
}
const route = useRoute()
const {
params: { id },
Expand Down Expand Up @@ -145,15 +181,20 @@ const audioPlayer = ref<HTMLAudioElement[] | null>([])
const audioCiontainer = ref<HTMLDivElement>()
const buttonData = ref(props.data)
const loading = ref(false)

const audioList = ref<string[]>([])

function regeneration() {
emit('regeneration')
}

function voteHandle(val: string) {
chatAPI.vote(props.chatId, props.data.record_id, val, loading).then(() => {
function handleVoteSuccess(voteStatus: string) {
buttonData.value['vote_status'] = voteStatus
emit('update:data', buttonData.value)
closePopover()
}

function cancelVoteHandle(val: string) {
chatAPI.vote(props.chatId, props.data.record_id, val, undefined, '', loading).then(() => {
buttonData.value['vote_status'] = val
emit('update:data', buttonData.value)
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@
<div>
<!-- 语音播放 -->
<span v-if="tts">
<el-tooltip effect="dark" :content="$t('chat.operation.play')" placement="top" v-if="!audioPlayerStatus">
<el-tooltip
effect="dark"
:content="$t('chat.operation.play')"
placement="top"
v-if="!audioPlayerStatus"
>
<el-button text @click="playAnswerText(data?.answer_text)">
<AppIcon iconName="app-video-play"></AppIcon>
</el-button>
Expand All @@ -27,8 +32,12 @@
</el-tooltip>
<el-divider direction="vertical" />
<template v-if="permissionPrecise.chat_log_add_knowledge(id)">
<el-tooltip v-if="buttonData.improve_paragraph_id_list.length === 0" effect="dark"
:content="$t('views.chatLog.editContent')" placement="top">
<el-tooltip
v-if="buttonData.improve_paragraph_id_list.length === 0"
effect="dark"
:content="$t('views.chatLog.editContent')"
placement="top"
>
<el-button text @click="editContent(data)">
<AppIcon iconName="app-edit"></AppIcon>
</el-button>
Expand All @@ -52,9 +61,32 @@
<EditContentDialog ref="EditContentDialogRef" @refresh="refreshContent" />
<EditMarkDialog ref="EditMarkDialogRef" @refresh="refreshMark" />
<!-- 先渲染,不然不能播放 -->
<audio ref="audioPlayer" v-for="item in audioList" :key="item" controls hidden="hidden"></audio>
<audio
ref="audioPlayer"
v-for="item in audioList"
:key="item"
controls
hidden="hidden"
></audio>
</div>
</div>
<div>
<el-card
class="mt-16"
shadow="always"
v-if="buttonData?.vote_status !== '-1' && data.vote_reason"
>
<VoteReasonContent
:vote-type="buttonData?.vote_status"
:chat-id="buttonData?.chat_id"
:record-id="buttonData?.id"
readonly
:default-reason="data.vote_reason"
:default-other-content="data.vote_other_content"
>
</VoteReasonContent>
</el-card>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
Expand All @@ -67,16 +99,16 @@ import { useRoute } from 'vue-router'
import permissionMap from '@/permission'
import { MsgError } from '@/utils/message'
import { t } from '@/locales'
import VoteReasonContent from '@/components/ai-chat/component/operation-button/VoteReasonContent.vue'
const route = useRoute()
const {
params: { id },
} = route as any


const props = defineProps({
data: {
type: Object,
default: () => { },
default: () => {},
},
applicationId: {
type: String,
Expand Down
Loading
Loading