mirror of
https://github.com/open-webui/open-webui
synced 2025-01-19 01:06:45 +00:00
Implement admin UI for user feedback
This commit is contained in:
parent
5e548fa7b8
commit
7541575600
@ -0,0 +1,30 @@
|
|||||||
|
"""add_chat_id_to_feedback
|
||||||
|
|
||||||
|
Revision ID: 790ecce592df
|
||||||
|
Revises: 3781e22d8b01
|
||||||
|
Create Date: 2025-01-08 15:13:16.063379
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import open_webui.internal.db
|
||||||
|
|
||||||
|
revision: str = '790ecce592df'
|
||||||
|
down_revision: Union[str, None] = '3781e22d8b01'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
|
||||||
|
# Add chat_id column to feedback
|
||||||
|
op.add_column(
|
||||||
|
"feedback",
|
||||||
|
sa.Column("chat_id", sa.Text(), nullable=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_column("feedback", "chat_id")
|
@ -0,0 +1,50 @@
|
|||||||
|
"""migrate_chat_id_in_feedback
|
||||||
|
|
||||||
|
Revision ID: 96dcb0b3212f
|
||||||
|
Revises: 790ecce592df
|
||||||
|
Create Date: 2025-01-13 08:08:22.582335
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import open_webui.internal.db
|
||||||
|
|
||||||
|
revision: str = '96dcb0b3212f'
|
||||||
|
down_revision: Union[str, None] = '790ecce592df'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
|
||||||
|
# Migrate chat_id data from meta to new chat_id column
|
||||||
|
conn = op.get_bind()
|
||||||
|
feedback = sa.table(
|
||||||
|
'feedback',
|
||||||
|
sa.column('id', sa.String),
|
||||||
|
sa.column('chat_id', sa.Text),
|
||||||
|
sa.column('meta', sa.JSON)
|
||||||
|
)
|
||||||
|
|
||||||
|
result = conn.execute(
|
||||||
|
sa.select(feedback.c.id, feedback.c.meta)
|
||||||
|
.where(sa.and_(
|
||||||
|
feedback.c.chat_id.is_(None),
|
||||||
|
feedback.c.meta.isnot(None)
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
for row in result:
|
||||||
|
if row.meta and 'chat_id' in row.meta:
|
||||||
|
chat_id = row.meta['chat_id']
|
||||||
|
conn.execute(
|
||||||
|
feedback.update()
|
||||||
|
.where(feedback.c.id == row.id)
|
||||||
|
.values(chat_id=chat_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
pass
|
@ -0,0 +1,42 @@
|
|||||||
|
"""make_chat_id_in_feedback_required
|
||||||
|
|
||||||
|
Revision ID: e56c262570d7
|
||||||
|
Revises: 96dcb0b3212f
|
||||||
|
Create Date: 2025-01-13 08:18:51.348376
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import open_webui.internal.db
|
||||||
|
|
||||||
|
revision: str = 'e56c262570d7'
|
||||||
|
down_revision: Union[str, None] = '96dcb0b3212f'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
|
||||||
|
# Check if there are feedbacks with NULL chat_ids, if not, make chat_id required
|
||||||
|
conn = op.get_bind()
|
||||||
|
feedback = sa.table('feedback', sa.column('chat_id', sa.Text))
|
||||||
|
result = conn.execute(sa.select(feedback).where(feedback.c.chat_id.is_(None)))
|
||||||
|
|
||||||
|
if result.first() is not None:
|
||||||
|
raise Exception(
|
||||||
|
"There are feedbacks with NULL chat_ids. Previous migration failed."
|
||||||
|
)
|
||||||
|
|
||||||
|
with op.batch_alter_table('feedback') as batch_op:
|
||||||
|
batch_op.alter_column('chat_id',
|
||||||
|
existing_type=sa.Text(),
|
||||||
|
nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
with op.batch_alter_table('feedback') as batch_op:
|
||||||
|
batch_op.alter_column('chat_id',
|
||||||
|
existing_type=sa.Text(),
|
||||||
|
nullable=True)
|
@ -30,6 +30,7 @@ class Feedback(Base):
|
|||||||
snapshot = Column(JSON, nullable=True)
|
snapshot = Column(JSON, nullable=True)
|
||||||
created_at = Column(BigInteger)
|
created_at = Column(BigInteger)
|
||||||
updated_at = Column(BigInteger)
|
updated_at = Column(BigInteger)
|
||||||
|
chat_id = Column(Text)
|
||||||
|
|
||||||
|
|
||||||
class FeedbackModel(BaseModel):
|
class FeedbackModel(BaseModel):
|
||||||
@ -42,7 +43,7 @@ class FeedbackModel(BaseModel):
|
|||||||
snapshot: Optional[dict] = None
|
snapshot: Optional[dict] = None
|
||||||
created_at: int
|
created_at: int
|
||||||
updated_at: int
|
updated_at: int
|
||||||
|
chat_id: str
|
||||||
model_config = ConfigDict(from_attributes=True)
|
model_config = ConfigDict(from_attributes=True)
|
||||||
|
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ class FeedbackResponse(BaseModel):
|
|||||||
type: str
|
type: str
|
||||||
data: Optional[dict] = None
|
data: Optional[dict] = None
|
||||||
meta: Optional[dict] = None
|
meta: Optional[dict] = None
|
||||||
|
snapshot: Optional[dict] = None
|
||||||
created_at: int
|
created_at: int
|
||||||
updated_at: int
|
updated_at: int
|
||||||
|
|
||||||
@ -98,10 +100,12 @@ class FeedbackTable:
|
|||||||
) -> Optional[FeedbackModel]:
|
) -> Optional[FeedbackModel]:
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
id = str(uuid.uuid4())
|
id = str(uuid.uuid4())
|
||||||
|
chat_id = form_data.meta.get('chat_id') if form_data.meta else None
|
||||||
feedback = FeedbackModel(
|
feedback = FeedbackModel(
|
||||||
**{
|
**{
|
||||||
"id": id,
|
"id": id,
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
|
"chat_id": chat_id,
|
||||||
"version": 0,
|
"version": 0,
|
||||||
**form_data.model_dump(),
|
**form_data.model_dump(),
|
||||||
"created_at": int(time.time()),
|
"created_at": int(time.time()),
|
||||||
@ -220,6 +224,16 @@ class FeedbackTable:
|
|||||||
db.delete(feedback)
|
db.delete(feedback)
|
||||||
db.commit()
|
db.commit()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_feedbacks_by_chat_id(self, chat_id: str) -> list[FeedbackModel]:
|
||||||
|
with get_db() as db:
|
||||||
|
return [
|
||||||
|
FeedbackModel.model_validate(feedback)
|
||||||
|
for feedback in db.query(Feedback)
|
||||||
|
.filter_by(chat_id=chat_id)
|
||||||
|
.order_by(Feedback.updated_at.desc())
|
||||||
|
.all()
|
||||||
|
]
|
||||||
|
|
||||||
def delete_feedback_by_id_and_user_id(self, id: str, user_id: str) -> bool:
|
def delete_feedback_by_id_and_user_id(self, id: str, user_id: str) -> bool:
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
|
@ -157,3 +157,13 @@ async def delete_feedback_by_id(id: str, user=Depends(get_verified_user)):
|
|||||||
)
|
)
|
||||||
|
|
||||||
return success
|
return success
|
||||||
|
|
||||||
|
@router.get("/feedbacks/chat/{id}", response_model=list[FeedbackUserResponse])
|
||||||
|
async def get_feedbacks_by_chat_id(id: str, user=Depends(get_admin_user)):
|
||||||
|
feedbacks = Feedbacks.get_feedbacks_by_chat_id(chat_id=id)
|
||||||
|
return [
|
||||||
|
FeedbackUserResponse(
|
||||||
|
**feedback.model_dump(), user=Users.get_user_by_id(feedback.user_id)
|
||||||
|
)
|
||||||
|
for feedback in feedbacks
|
||||||
|
]
|
@ -186,6 +186,37 @@ export const getFeedbackById = async (token: string, feedbackId: string) => {
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getFeedbacksByChatId = async (token: string = '', chatId: string) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_API_BASE_URL}/evaluations/feedbacks/chat/${chatId}`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) throw await res.json();
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((json) => {
|
||||||
|
return json;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
error = err.detail;
|
||||||
|
console.log(err);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
export const updateFeedbackById = async (token: string, feedbackId: string, feedback: object) => {
|
export const updateFeedbackById = async (token: string, feedbackId: string, feedback: object) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
import FeedbackMenu from './FeedbackMenu.svelte';
|
import FeedbackMenu from './FeedbackMenu.svelte';
|
||||||
import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
|
import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
|
||||||
|
|
||||||
export let feedbacks = [];
|
export let feedbacks: Feedback[] = [];
|
||||||
|
|
||||||
let page = 1;
|
let page = 1;
|
||||||
$: paginatedFeedbacks = feedbacks.slice((page - 1) * 10, page * 10);
|
$: paginatedFeedbacks = feedbacks.slice((page - 1) * 10, page * 10);
|
||||||
@ -35,10 +35,19 @@
|
|||||||
comment: string;
|
comment: string;
|
||||||
tags: string[];
|
tags: string[];
|
||||||
};
|
};
|
||||||
|
meta: {
|
||||||
|
chat_id: string;
|
||||||
|
message_id?: string;
|
||||||
|
};
|
||||||
user: {
|
user: {
|
||||||
name: string;
|
name: string;
|
||||||
profile_image_url: string;
|
profile_image_url: string;
|
||||||
};
|
};
|
||||||
|
snapshot: {
|
||||||
|
chat: {
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
};
|
||||||
updated_at: number;
|
updated_at: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -152,6 +161,10 @@
|
|||||||
{$i18n.t('Models')}
|
{$i18n.t('Models')}
|
||||||
</th>
|
</th>
|
||||||
|
|
||||||
|
<th scope="col" class="px-3 pr-1.5 cursor-pointer select-none">
|
||||||
|
{$i18n.t('Chat')}
|
||||||
|
</th>
|
||||||
|
|
||||||
<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-fit">
|
<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-fit">
|
||||||
{$i18n.t('Result')}
|
{$i18n.t('Result')}
|
||||||
</th>
|
</th>
|
||||||
@ -211,6 +224,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
|
<td class="px-3 py-1 font-medium text-sm text-gray-900 dark:text-white">
|
||||||
|
<a href="/s/{feedback.meta.chat_id}?showFeedback=true" target="_blank">
|
||||||
|
<div class="underline line-clamp-1 max-w-96">
|
||||||
|
{feedback.snapshot?.chat?.title}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
<td class="px-3 py-1 text-right font-medium text-gray-900 dark:text-white w-max">
|
<td class="px-3 py-1 text-right font-medium text-gray-900 dark:text-white w-max">
|
||||||
<div class=" flex justify-end">
|
<div class=" flex justify-end">
|
||||||
{#if feedback.data.rating.toString() === '1'}
|
{#if feedback.data.rating.toString() === '1'}
|
||||||
|
@ -12,6 +12,8 @@
|
|||||||
import Loader from '../common/Loader.svelte';
|
import Loader from '../common/Loader.svelte';
|
||||||
import Spinner from '../common/Spinner.svelte';
|
import Spinner from '../common/Spinner.svelte';
|
||||||
|
|
||||||
|
import Feedback from '$lib/components/admin/Evaluations/Feedbacks.svelte';
|
||||||
|
|
||||||
import ChatPlaceholder from './ChatPlaceholder.svelte';
|
import ChatPlaceholder from './ChatPlaceholder.svelte';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
@ -333,6 +335,8 @@
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export let feedbacks: Feedback[] = [];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={className}>
|
<div class={className}>
|
||||||
@ -410,6 +414,7 @@
|
|||||||
{addMessages}
|
{addMessages}
|
||||||
{triggerScroll}
|
{triggerScroll}
|
||||||
{readOnly}
|
{readOnly}
|
||||||
|
feedback={feedbacks.find(f => f.meta.message_id === message.id)}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
import MultiResponseMessages from './MultiResponseMessages.svelte';
|
import MultiResponseMessages from './MultiResponseMessages.svelte';
|
||||||
import ResponseMessage from './ResponseMessage.svelte';
|
import ResponseMessage from './ResponseMessage.svelte';
|
||||||
import UserMessage from './UserMessage.svelte';
|
import UserMessage from './UserMessage.svelte';
|
||||||
|
import Badge from '$lib/components/common/Badge.svelte';
|
||||||
|
|
||||||
|
import Feedback from '$lib/components/admin/Evaluations/Feedbacks.svelte';
|
||||||
|
|
||||||
export let chatId;
|
export let chatId;
|
||||||
export let idx = 0;
|
export let idx = 0;
|
||||||
@ -38,6 +41,8 @@
|
|||||||
export let addMessages;
|
export let addMessages;
|
||||||
export let triggerScroll;
|
export let triggerScroll;
|
||||||
export let readOnly = false;
|
export let readOnly = false;
|
||||||
|
|
||||||
|
export let feedback: Feedback | undefined = undefined;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -46,62 +51,120 @@
|
|||||||
: 'max-w-5xl'} mx-auto rounded-lg group"
|
: 'max-w-5xl'} mx-auto rounded-lg group"
|
||||||
>
|
>
|
||||||
{#if history.messages[messageId]}
|
{#if history.messages[messageId]}
|
||||||
{#if history.messages[messageId].role === 'user'}
|
<div style="border: {feedback ? feedback.data.rating === 1 ? '2px solid green' : '2px solid red' : 'none'}; border-radius: 10px; padding: 10px;">
|
||||||
<UserMessage
|
{#if history.messages[messageId].role === 'user'}
|
||||||
{user}
|
<UserMessage
|
||||||
{history}
|
{user}
|
||||||
{messageId}
|
{history}
|
||||||
isFirstMessage={idx === 0}
|
{messageId}
|
||||||
siblings={history.messages[messageId].parentId !== null
|
isFirstMessage={idx === 0}
|
||||||
? (history.messages[history.messages[messageId].parentId]?.childrenIds ?? [])
|
siblings={history.messages[messageId].parentId !== null
|
||||||
: (Object.values(history.messages)
|
? (history.messages[history.messages[messageId].parentId]?.childrenIds ?? [])
|
||||||
.filter((message) => message.parentId === null)
|
: (Object.values(history.messages)
|
||||||
.map((message) => message.id) ?? [])}
|
.filter((message) => message.parentId === null)
|
||||||
{showPreviousMessage}
|
.map((message) => message.id) ?? [])}
|
||||||
{showNextMessage}
|
{showPreviousMessage}
|
||||||
{editMessage}
|
{showNextMessage}
|
||||||
{deleteMessage}
|
{editMessage}
|
||||||
{readOnly}
|
{deleteMessage}
|
||||||
/>
|
{readOnly}
|
||||||
{:else if (history.messages[history.messages[messageId].parentId]?.models?.length ?? 1) === 1}
|
/>
|
||||||
<ResponseMessage
|
{:else if (history.messages[history.messages[messageId].parentId]?.models?.length ?? 1) === 1}
|
||||||
{chatId}
|
<ResponseMessage
|
||||||
{history}
|
{chatId}
|
||||||
{messageId}
|
{history}
|
||||||
isLastMessage={messageId === history.currentId}
|
{messageId}
|
||||||
siblings={history.messages[history.messages[messageId].parentId]?.childrenIds ?? []}
|
isLastMessage={messageId === history.currentId}
|
||||||
{showPreviousMessage}
|
siblings={history.messages[history.messages[messageId].parentId]?.childrenIds ?? []}
|
||||||
{showNextMessage}
|
{showPreviousMessage}
|
||||||
{updateChat}
|
{showNextMessage}
|
||||||
{editMessage}
|
{updateChat}
|
||||||
{saveMessage}
|
{editMessage}
|
||||||
{rateMessage}
|
{saveMessage}
|
||||||
{actionMessage}
|
{rateMessage}
|
||||||
{submitMessage}
|
{actionMessage}
|
||||||
{continueResponse}
|
{submitMessage}
|
||||||
{regenerateResponse}
|
{continueResponse}
|
||||||
{addMessages}
|
{regenerateResponse}
|
||||||
{readOnly}
|
{addMessages}
|
||||||
/>
|
{readOnly}
|
||||||
{:else}
|
/>
|
||||||
<MultiResponseMessages
|
{:else}
|
||||||
bind:history
|
<MultiResponseMessages
|
||||||
{chatId}
|
bind:history
|
||||||
{messageId}
|
{chatId}
|
||||||
isLastMessage={messageId === history?.currentId}
|
{messageId}
|
||||||
{updateChat}
|
isLastMessage={messageId === history?.currentId}
|
||||||
{editMessage}
|
{updateChat}
|
||||||
{saveMessage}
|
{editMessage}
|
||||||
{rateMessage}
|
{saveMessage}
|
||||||
{actionMessage}
|
{rateMessage}
|
||||||
{submitMessage}
|
{actionMessage}
|
||||||
{continueResponse}
|
{submitMessage}
|
||||||
{regenerateResponse}
|
{continueResponse}
|
||||||
{mergeResponses}
|
{regenerateResponse}
|
||||||
{triggerScroll}
|
{mergeResponses}
|
||||||
{addMessages}
|
{triggerScroll}
|
||||||
{readOnly}
|
{addMessages}
|
||||||
/>
|
{readOnly}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if feedback}
|
||||||
|
<div class="flex gap-2 mt-2">
|
||||||
|
<div class="flex items-start mt-1">
|
||||||
|
{#if feedback.data?.rating === 1}
|
||||||
|
<Badge type="success" content={$i18n.t('Feedback')} />
|
||||||
|
{:else if feedback.data?.rating === 0}
|
||||||
|
<Badge type="info" content={$i18n.t('Feedback')} />
|
||||||
|
{:else if feedback.data?.rating === -1}
|
||||||
|
<Badge type="error" content={$i18n.t('Feedback')} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<div>
|
||||||
|
{$i18n.t('Comment')}: {feedback.data?.comment}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{$i18n.t('Reason')}:
|
||||||
|
{#if feedback.data?.reason === 'accurate_information'}
|
||||||
|
{$i18n.t('Accurate information')}
|
||||||
|
{:else if feedback.data?.reason === 'followed_instructions_perfectly'}
|
||||||
|
{$i18n.t('Followed instructions perfectly')}
|
||||||
|
{:else if feedback.data?.reason === 'showcased_creativity'}
|
||||||
|
{$i18n.t('Showcased creativity')}
|
||||||
|
{:else if feedback.data?.reason === 'positive_attitude'}
|
||||||
|
{$i18n.t('Positive attitude')}
|
||||||
|
{:else if feedback.data?.reason === 'attention_to_detail'}
|
||||||
|
{$i18n.t('Attention to detail')}
|
||||||
|
{:else if feedback.data?.reason === 'thorough_explanation'}
|
||||||
|
{$i18n.t('Thorough explanation')}
|
||||||
|
{:else if feedback.data?.reason === 'dont_like_the_style'}
|
||||||
|
{$i18n.t("Don't like the style")}
|
||||||
|
{:else if feedback.data?.reason === 'too_verbose'}
|
||||||
|
{$i18n.t('Too verbose')}
|
||||||
|
{:else if feedback.data?.reason === 'not_helpful'}
|
||||||
|
{$i18n.t('Not helpful')}
|
||||||
|
{:else if feedback.data?.reason === 'not_factually_correct'}
|
||||||
|
{$i18n.t('Not factually correct')}
|
||||||
|
{:else if feedback.data?.reason === 'didnt_fully_follow_instructions'}
|
||||||
|
{$i18n.t("Didn't fully follow instructions")}
|
||||||
|
{:else if feedback.data?.reason === 'refused_when_it_shouldnt_have'}
|
||||||
|
{$i18n.t("Refused when it shouldn't have")}
|
||||||
|
{:else if feedback.data?.reason === 'being_lazy'}
|
||||||
|
{$i18n.t('Being lazy')}
|
||||||
|
{:else if feedback.data?.reason === 'other'}
|
||||||
|
{$i18n.t('Other')}
|
||||||
|
{:else}
|
||||||
|
{feedback.data?.reason}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{$i18n.t('Rating')}: {feedback.data?.details?.rating}/10
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
import { settings, chatId, WEBUI_NAME, models } from '$lib/stores';
|
import { settings, chatId, WEBUI_NAME, models, user as _user } from '$lib/stores';
|
||||||
import { convertMessagesToHistory, createMessagesList } from '$lib/utils';
|
import { convertMessagesToHistory, createMessagesList } from '$lib/utils';
|
||||||
|
|
||||||
import { getChatByShareId, cloneSharedChatById } from '$lib/apis/chats';
|
import { getChatByShareId, cloneSharedChatById } from '$lib/apis/chats';
|
||||||
@ -16,6 +16,7 @@
|
|||||||
import { getUserById } from '$lib/apis/users';
|
import { getUserById } from '$lib/apis/users';
|
||||||
import { getModels } from '$lib/apis';
|
import { getModels } from '$lib/apis';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
|
import { getFeedbacksByChatId } from '$lib/apis/evaluations';
|
||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
@ -24,6 +25,9 @@
|
|||||||
let autoScroll = true;
|
let autoScroll = true;
|
||||||
let processing = '';
|
let processing = '';
|
||||||
let messagesContainerElement: HTMLDivElement;
|
let messagesContainerElement: HTMLDivElement;
|
||||||
|
|
||||||
|
let showFeedback = false;
|
||||||
|
let feedbacks: Feedback[] = [];
|
||||||
|
|
||||||
// let chatId = $page.params.id;
|
// let chatId = $page.params.id;
|
||||||
let showModelSelector = false;
|
let showModelSelector = false;
|
||||||
@ -41,6 +45,10 @@
|
|||||||
currentId: null
|
currentId: null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getChatFeedbacks = async () => {
|
||||||
|
feedbacks = await getFeedbacksByChatId(localStorage.token, $chatId);
|
||||||
|
};
|
||||||
|
|
||||||
$: messages = createMessagesList(history, history.currentId);
|
$: messages = createMessagesList(history, history.currentId);
|
||||||
|
|
||||||
$: if ($page.params.id) {
|
$: if ($page.params.id) {
|
||||||
@ -48,6 +56,11 @@
|
|||||||
if (await loadSharedChat()) {
|
if (await loadSharedChat()) {
|
||||||
await tick();
|
await tick();
|
||||||
loaded = true;
|
loaded = true;
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
showFeedback = urlParams.get('showFeedback') === 'true';
|
||||||
|
if(showFeedback && $_user?.role === 'admin') {
|
||||||
|
await getChatFeedbacks();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await goto('/');
|
await goto('/');
|
||||||
}
|
}
|
||||||
@ -153,6 +166,7 @@
|
|||||||
readOnly={true}
|
readOnly={true}
|
||||||
{selectedModels}
|
{selectedModels}
|
||||||
{processing}
|
{processing}
|
||||||
|
{feedbacks}
|
||||||
bind:history
|
bind:history
|
||||||
bind:messages
|
bind:messages
|
||||||
bind:autoScroll
|
bind:autoScroll
|
||||||
|
Loading…
Reference in New Issue
Block a user