mirror of
				https://github.com/open-webui/open-webui
				synced 2025-06-26 18:26:48 +00:00 
			
		
		
		
	enh: auto tag feedback
This commit is contained in:
		
							parent
							
								
									73ab84efe6
								
							
						
					
					
						commit
						076f9fd9c0
					
				@ -3,6 +3,7 @@
 | 
			
		||||
 | 
			
		||||
	import { createEventDispatcher, onMount, getContext } from 'svelte';
 | 
			
		||||
	import { config, models } from '$lib/stores';
 | 
			
		||||
	import Tags from '$lib/components/common/Tags.svelte';
 | 
			
		||||
 | 
			
		||||
	const i18n = getContext('i18n');
 | 
			
		||||
 | 
			
		||||
@ -14,24 +15,28 @@
 | 
			
		||||
	let LIKE_REASONS = [];
 | 
			
		||||
	let DISLIKE_REASONS = [];
 | 
			
		||||
 | 
			
		||||
	let tags = [];
 | 
			
		||||
 | 
			
		||||
	function loadReasons() {
 | 
			
		||||
		LIKE_REASONS = [
 | 
			
		||||
			$i18n.t('Accurate information'),
 | 
			
		||||
			$i18n.t('Followed instructions perfectly'),
 | 
			
		||||
			$i18n.t('Showcased creativity'),
 | 
			
		||||
			$i18n.t('Positive attitude'),
 | 
			
		||||
			$i18n.t('Attention to detail'),
 | 
			
		||||
			$i18n.t('Thorough explanation'),
 | 
			
		||||
			$i18n.t('Other')
 | 
			
		||||
			'accurate_information',
 | 
			
		||||
			'followed_instructions_perfectly',
 | 
			
		||||
			'showcased_creativity',
 | 
			
		||||
			'positive_attitude',
 | 
			
		||||
			'attention_to_detail',
 | 
			
		||||
			'thorough_explanation',
 | 
			
		||||
			'other'
 | 
			
		||||
		];
 | 
			
		||||
 | 
			
		||||
		DISLIKE_REASONS = [
 | 
			
		||||
			$i18n.t("Don't like the style"),
 | 
			
		||||
			$i18n.t('Not factually correct'),
 | 
			
		||||
			$i18n.t("Didn't fully follow instructions"),
 | 
			
		||||
			$i18n.t("Refused when it shouldn't have"),
 | 
			
		||||
			$i18n.t('Being lazy'),
 | 
			
		||||
			$i18n.t('Other')
 | 
			
		||||
			'dont_like_the_style',
 | 
			
		||||
			'too_verbose',
 | 
			
		||||
			'not_helpful',
 | 
			
		||||
			'not_factually_correct',
 | 
			
		||||
			'didnt_fully_follow_instructions',
 | 
			
		||||
			'refused_when_it_shouldnt_have',
 | 
			
		||||
			'being_lazy',
 | 
			
		||||
			'other'
 | 
			
		||||
		];
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -50,6 +55,9 @@
 | 
			
		||||
	onMount(() => {
 | 
			
		||||
		selectedReason = message?.annotation?.reason ?? '';
 | 
			
		||||
		comment = message?.annotation?.comment ?? '';
 | 
			
		||||
		tags = (message?.annotation?.tags ?? []).map((tag) => ({
 | 
			
		||||
			name: tag
 | 
			
		||||
		}));
 | 
			
		||||
 | 
			
		||||
		if (message?.arena) {
 | 
			
		||||
			selectedModel = $models.find((m) => m.id === message.selectedModelId);
 | 
			
		||||
@ -66,14 +74,15 @@
 | 
			
		||||
	const saveHandler = () => {
 | 
			
		||||
		console.log('saveHandler');
 | 
			
		||||
 | 
			
		||||
		if (!selectedReason) {
 | 
			
		||||
			toast.error($i18n.t('Please select a reason'));
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		// if (!selectedReason) {
 | 
			
		||||
		// 	toast.error($i18n.t('Please select a reason'));
 | 
			
		||||
		// 	return;
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		dispatch('save', {
 | 
			
		||||
			reason: selectedReason,
 | 
			
		||||
			comment: comment
 | 
			
		||||
			comment: comment,
 | 
			
		||||
			tags: tags
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		toast.success($i18n.t('Thanks for your feedback!'));
 | 
			
		||||
@ -82,7 +91,7 @@
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
{#if message?.arena}
 | 
			
		||||
	<div class="text-xs font-medium translate-y-1.5">
 | 
			
		||||
	<div class="text-xs font-medium pt-1.5 -mb-0.5">
 | 
			
		||||
		{$i18n.t('This response was generated by "{{model}}"', {
 | 
			
		||||
			model: selectedModel ? (selectedModel?.name ?? selectedModel.id) : message.selectedModelId
 | 
			
		||||
		})}
 | 
			
		||||
@ -115,10 +124,10 @@
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	{#if reasons.length > 0}
 | 
			
		||||
		<div class="flex flex-wrap gap-2 text-sm mt-2.5">
 | 
			
		||||
		<div class="flex flex-wrap gap-1.5 text-sm mt-2.5">
 | 
			
		||||
			{#each reasons as reason}
 | 
			
		||||
				<button
 | 
			
		||||
					class="px-3.5 py-1 border border-gray-50 dark:border-gray-850 hover:bg-gray-100 dark:hover:bg-gray-850 {selectedReason ===
 | 
			
		||||
					class="px-3 py-0.5 border border-gray-50 dark:border-gray-850 hover:bg-gray-100 dark:hover:bg-gray-850 {selectedReason ===
 | 
			
		||||
					reason
 | 
			
		||||
						? 'bg-gray-200 dark:bg-gray-800'
 | 
			
		||||
						: ''} transition rounded-lg"
 | 
			
		||||
@ -126,7 +135,37 @@
 | 
			
		||||
						selectedReason = reason;
 | 
			
		||||
					}}
 | 
			
		||||
				>
 | 
			
		||||
					{reason}
 | 
			
		||||
					{#if reason === 'accurate_information'}
 | 
			
		||||
						{$i18n.t('Accurate information')}
 | 
			
		||||
					{:else if reason === 'followed_instructions_perfectly'}
 | 
			
		||||
						{$i18n.t('Followed instructions perfectly')}
 | 
			
		||||
					{:else if reason === 'showcased_creativity'}
 | 
			
		||||
						{$i18n.t('Showcased creativity')}
 | 
			
		||||
					{:else if reason === 'positive_attitude'}
 | 
			
		||||
						{$i18n.t('Positive attitude')}
 | 
			
		||||
					{:else if reason === 'attention_to_detail'}
 | 
			
		||||
						{$i18n.t('Attention to detail')}
 | 
			
		||||
					{:else if reason === 'thorough_explanation'}
 | 
			
		||||
						{$i18n.t('Thorough explanation')}
 | 
			
		||||
					{:else if reason === 'dont_like_the_style'}
 | 
			
		||||
						{$i18n.t("Don't like the style")}
 | 
			
		||||
					{:else if reason === 'too_verbose'}
 | 
			
		||||
						{$i18n.t('Too verbose')}
 | 
			
		||||
					{:else if reason === 'not_helpful'}
 | 
			
		||||
						{$i18n.t('Not helpful')}
 | 
			
		||||
					{:else if reason === 'not_factually_correct'}
 | 
			
		||||
						{$i18n.t('Not factually correct')}
 | 
			
		||||
					{:else if reason === 'didnt_fully_follow_instructions'}
 | 
			
		||||
						{$i18n.t("Didn't fully follow instructions")}
 | 
			
		||||
					{:else if reason === 'refused_when_it_shouldnt_have'}
 | 
			
		||||
						{$i18n.t("Refused when it shouldn't have")}
 | 
			
		||||
					{:else if reason === 'being_lazy'}
 | 
			
		||||
						{$i18n.t('Being lazy')}
 | 
			
		||||
					{:else if reason === 'other'}
 | 
			
		||||
						{$i18n.t('Other')}
 | 
			
		||||
					{:else}
 | 
			
		||||
						{reason}
 | 
			
		||||
					{/if}
 | 
			
		||||
				</button>
 | 
			
		||||
			{/each}
 | 
			
		||||
		</div>
 | 
			
		||||
@ -137,22 +176,26 @@
 | 
			
		||||
			bind:value={comment}
 | 
			
		||||
			class="w-full text-sm px-1 py-2 bg-transparent outline-none resize-none rounded-xl"
 | 
			
		||||
			placeholder={$i18n.t('Feel free to add specific details')}
 | 
			
		||||
			rows="2"
 | 
			
		||||
			rows="3"
 | 
			
		||||
		/>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<div class="mt-2 gap-1.5 flex justify-end">
 | 
			
		||||
		<!-- {#if $config?.features.enable_community_sharing && selectedModel}
 | 
			
		||||
			<button
 | 
			
		||||
				class=" self-center px-3.5 py-2 rounded-xl text-sm font-medium bg-gray-50 hover:bg-gray-100 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white transition"
 | 
			
		||||
				type="button"
 | 
			
		||||
				on:click={() => {
 | 
			
		||||
					show = false;
 | 
			
		||||
	<div class="mt-2 gap-1.5 flex justify-between">
 | 
			
		||||
		<div class="flex items-end group">
 | 
			
		||||
			<Tags
 | 
			
		||||
				{tags}
 | 
			
		||||
				on:delete={(e) => {
 | 
			
		||||
					tags = tags.filter(
 | 
			
		||||
						(tag) =>
 | 
			
		||||
							tag.name.replaceAll(' ', '_').toLowerCase() !==
 | 
			
		||||
							e.detail.replaceAll(' ', '_').toLowerCase()
 | 
			
		||||
					);
 | 
			
		||||
				}}
 | 
			
		||||
			>
 | 
			
		||||
				{$i18n.t('Share to OpenWebUI Community')}
 | 
			
		||||
			</button>
 | 
			
		||||
		{/if} -->
 | 
			
		||||
				on:add={(e) => {
 | 
			
		||||
					tags = [...tags, { name: e.detail }];
 | 
			
		||||
				}}
 | 
			
		||||
			/>
 | 
			
		||||
		</div>
 | 
			
		||||
 | 
			
		||||
		<button
 | 
			
		||||
			class=" bg-emerald-700 hover:bg-emerald-800 transition text-white text-sm font-medium rounded-xl px-3.5 py-1.5"
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,8 @@
 | 
			
		||||
		extractSentencesForAudio,
 | 
			
		||||
		cleanText,
 | 
			
		||||
		getMessageContentParts,
 | 
			
		||||
		sanitizeResponseContent
 | 
			
		||||
		sanitizeResponseContent,
 | 
			
		||||
		createMessagesList
 | 
			
		||||
	} from '$lib/utils';
 | 
			
		||||
	import { WEBUI_BASE_URL } from '$lib/constants';
 | 
			
		||||
 | 
			
		||||
@ -42,6 +43,7 @@
 | 
			
		||||
	import ContentRenderer from './ContentRenderer.svelte';
 | 
			
		||||
	import { createNewFeedback, getFeedbackById, updateFeedbackById } from '$lib/apis/evaluations';
 | 
			
		||||
	import { getChatById } from '$lib/apis/chats';
 | 
			
		||||
	import { generateTags } from '$lib/apis';
 | 
			
		||||
 | 
			
		||||
	interface MessageType {
 | 
			
		||||
		id: string;
 | 
			
		||||
@ -355,6 +357,24 @@
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!annotation) {
 | 
			
		||||
			const messages = createMessagesList(history, message.id);
 | 
			
		||||
			const tags = await generateTags(
 | 
			
		||||
				localStorage.token,
 | 
			
		||||
				message?.selectedModelId ?? message.model,
 | 
			
		||||
				messages,
 | 
			
		||||
				chatId
 | 
			
		||||
			).catch((error) => {
 | 
			
		||||
				console.error(error);
 | 
			
		||||
				return [];
 | 
			
		||||
			});
 | 
			
		||||
			console.log(tags);
 | 
			
		||||
 | 
			
		||||
			if (tags) {
 | 
			
		||||
				updatedMessage.annotation.tags = tags;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		let feedbackItem = {
 | 
			
		||||
			type: 'rating',
 | 
			
		||||
			data: {
 | 
			
		||||
@ -1183,6 +1203,7 @@
 | 
			
		||||
							bind:show={showRateComment}
 | 
			
		||||
							on:save={async (e) => {
 | 
			
		||||
								await feedbackHandler(null, {
 | 
			
		||||
									tags: e.detail.tags,
 | 
			
		||||
									comment: e.detail.comment,
 | 
			
		||||
									reason: e.detail.reason
 | 
			
		||||
								});
 | 
			
		||||
 | 
			
		||||
@ -142,7 +142,7 @@
 | 
			
		||||
 | 
			
		||||
			{#if edit === true}
 | 
			
		||||
				<div class=" w-full bg-gray-50 dark:bg-gray-800 rounded-3xl px-5 py-3 mb-2">
 | 
			
		||||
					<div class="max-h-[25dvh] overflow-auto">
 | 
			
		||||
					<div class="max-h-96 overflow-auto">
 | 
			
		||||
						<textarea
 | 
			
		||||
							id="message-edit-{message.id}"
 | 
			
		||||
							bind:this={messageEditTextAreaElement}
 | 
			
		||||
 | 
			
		||||
@ -15,6 +15,14 @@
 | 
			
		||||
 | 
			
		||||
<Modal bind:show size="xs">
 | 
			
		||||
	<div class="px-4 pt-4 pb-5 w-full flex flex-col justify-center">
 | 
			
		||||
		<Tags {tags} {deleteTag} {addTag} />
 | 
			
		||||
		<Tags
 | 
			
		||||
			{tags}
 | 
			
		||||
			on:delete={(e) => {
 | 
			
		||||
				deleteTag(e.detail);
 | 
			
		||||
			}}
 | 
			
		||||
			on:add={(e) => {
 | 
			
		||||
				addTag(e.detail);
 | 
			
		||||
			}}
 | 
			
		||||
		/>
 | 
			
		||||
	</div>
 | 
			
		||||
</Modal>
 | 
			
		||||
 | 
			
		||||
@ -71,4 +71,12 @@
 | 
			
		||||
	});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<Tags {tags} {deleteTag} {addTag} />
 | 
			
		||||
<Tags
 | 
			
		||||
	{tags}
 | 
			
		||||
	on:delete={(e) => {
 | 
			
		||||
		deleteTag(e.detail);
 | 
			
		||||
	}}
 | 
			
		||||
	on:add={(e) => {
 | 
			
		||||
		addTag(e.detail);
 | 
			
		||||
	}}
 | 
			
		||||
/>
 | 
			
		||||
 | 
			
		||||
@ -1,28 +1,26 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
	import TagInput from './Tags/TagInput.svelte';
 | 
			
		||||
	import TagList from './Tags/TagList.svelte';
 | 
			
		||||
	import { getContext } from 'svelte';
 | 
			
		||||
	import { getContext, createEventDispatcher } from 'svelte';
 | 
			
		||||
	const dispatch = createEventDispatcher();
 | 
			
		||||
 | 
			
		||||
	const i18n = getContext('i18n');
 | 
			
		||||
 | 
			
		||||
	export let tags = [];
 | 
			
		||||
 | 
			
		||||
	export let deleteTag: Function;
 | 
			
		||||
	export let addTag: Function;
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<div class="flex flex-row flex-wrap gap-1 line-clamp-1">
 | 
			
		||||
	<TagList
 | 
			
		||||
		{tags}
 | 
			
		||||
		on:delete={(e) => {
 | 
			
		||||
			deleteTag(e.detail);
 | 
			
		||||
			dispatch('delete', e.detail);
 | 
			
		||||
		}}
 | 
			
		||||
	/>
 | 
			
		||||
 | 
			
		||||
	<TagInput
 | 
			
		||||
		label={tags.length == 0 ? $i18n.t('Add Tags') : ''}
 | 
			
		||||
		on:add={(e) => {
 | 
			
		||||
			addTag(e.detail);
 | 
			
		||||
			dispatch('add', e.detail);
 | 
			
		||||
		}}
 | 
			
		||||
	/>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -11,12 +11,12 @@
 | 
			
		||||
{#each tags as tag}
 | 
			
		||||
	<Tooltip content={tag.name}>
 | 
			
		||||
		<div
 | 
			
		||||
			class="relative group px-1.5 py-[0.2px] gap-0.5 flex justify-between h-fit max-h-fit w-fit items-center rounded-full bg-gray-500/20 text-gray-700 dark:text-gray-200 transition cursor-pointer"
 | 
			
		||||
			class="relative group/tags px-1.5 py-[0.2px] gap-0.5 flex justify-between h-fit max-h-fit w-fit items-center rounded-full bg-gray-500/20 text-gray-700 dark:text-gray-200 transition cursor-pointer"
 | 
			
		||||
		>
 | 
			
		||||
			<div class=" text-[0.7rem] font-medium self-center line-clamp-1 w-fit">
 | 
			
		||||
				{tag.name}
 | 
			
		||||
			</div>
 | 
			
		||||
			<div class="absolute invisible right-0.5 group-hover:visible transition">
 | 
			
		||||
			<div class="absolute invisible right-0.5 group-hover/tags:visible transition">
 | 
			
		||||
				<button
 | 
			
		||||
					class="rounded-full border bg-white dark:bg-gray-700 h-full flex self-center cursor-pointer"
 | 
			
		||||
					on:click={() => {
 | 
			
		||||
 | 
			
		||||
@ -671,11 +671,12 @@
 | 
			
		||||
			<div class="mt-2">
 | 
			
		||||
				<Tags
 | 
			
		||||
					tags={info?.meta?.tags ?? []}
 | 
			
		||||
					deleteTag={(tagName) => {
 | 
			
		||||
					on:delete={(e) => {
 | 
			
		||||
						const tagName = e.detail;
 | 
			
		||||
						info.meta.tags = info.meta.tags.filter((tag) => tag.name !== tagName);
 | 
			
		||||
					}}
 | 
			
		||||
					addTag={(tagName) => {
 | 
			
		||||
						console.log(tagName);
 | 
			
		||||
					on:add={(e) => {
 | 
			
		||||
						const tagName = e.detail;
 | 
			
		||||
						if (!(info?.meta?.tags ?? null)) {
 | 
			
		||||
							info.meta.tags = [{ name: tagName }];
 | 
			
		||||
						} else {
 | 
			
		||||
 | 
			
		||||
@ -618,11 +618,12 @@
 | 
			
		||||
				<div class="mt-2">
 | 
			
		||||
					<Tags
 | 
			
		||||
						tags={info?.meta?.tags ?? []}
 | 
			
		||||
						deleteTag={(tagName) => {
 | 
			
		||||
						on:delete={(e) => {
 | 
			
		||||
							const tagName = e.detail;
 | 
			
		||||
							info.meta.tags = info.meta.tags.filter((tag) => tag.name !== tagName);
 | 
			
		||||
						}}
 | 
			
		||||
						addTag={(tagName) => {
 | 
			
		||||
							console.log(tagName);
 | 
			
		||||
						on:add={(e) => {
 | 
			
		||||
							const tagName = e.detail;
 | 
			
		||||
							if (!(info?.meta?.tags ?? null)) {
 | 
			
		||||
								info.meta.tags = [{ name: tagName }];
 | 
			
		||||
							} else {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user