Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add partial success status to the app log #11869

Merged
merged 3 commits into from
Dec 20, 2024
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
3 changes: 2 additions & 1 deletion api/fields/conversation_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def format(self, value):
}

feedback_stat_fields = {"like": fields.Integer, "dislike": fields.Integer}

status_count_fields = {"success": fields.Integer, "failed": fields.Integer, "partial_success": fields.Integer}
model_config_fields = {
"opening_statement": fields.String,
"suggested_questions": fields.Raw,
Expand Down Expand Up @@ -166,6 +166,7 @@ def format(self, value):
"message_count": fields.Integer,
"user_feedback_stats": fields.Nested(feedback_stat_fields),
"admin_feedback_stats": fields.Nested(feedback_stat_fields),
"status_count": fields.Nested(status_count_fields),
}

conversation_with_summary_pagination_fields = {
Expand Down
24 changes: 24 additions & 0 deletions api/models/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from extensions.ext_database import db
from libs.helper import generate_string
from models.enums import CreatedByRole
from models.workflow import WorkflowRunStatus

from .account import Account, Tenant
from .types import StringUUID
Expand Down Expand Up @@ -679,6 +680,29 @@ def admin_feedback_stats(self):

return {"like": like, "dislike": dislike}

@property
def status_count(self):
messages = db.session.query(Message).filter(Message.conversation_id == self.id).all()
status_counts = {
WorkflowRunStatus.SUCCEEDED: 0,
WorkflowRunStatus.FAILED: 0,
WorkflowRunStatus.PARTIAL_SUCCESSED: 0,
}

for message in messages:
if message.workflow_run:
status_counts[message.workflow_run.status] += 1

return (
{
"success": status_counts[WorkflowRunStatus.SUCCEEDED],
"failed": status_counts[WorkflowRunStatus.FAILED],
"partial_success": status_counts[WorkflowRunStatus.PARTIAL_SUCCESSED],
}
if messages
else None
)

@property
def first_message(self):
return db.session.query(Message).filter(Message.conversation_id == self.id).first()
Expand Down
51 changes: 45 additions & 6 deletions web/app/components/app/log/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { createContext, useContext } from 'use-context-selector'
import { useShallow } from 'zustand/react/shallow'
import { useTranslation } from 'react-i18next'
import type { ChatItemInTree } from '../../base/chat/types'
import Indicator from '../../header/indicator'
import VarPanel from './var-panel'
import type { FeedbackFunc, FeedbackType, IChatItem, SubmitAnnotationFunc } from '@/app/components/base/chat/chat/type'
import type { Annotation, ChatConversationGeneralDetail, ChatConversationsResponse, ChatMessage, ChatMessagesRequest, CompletionConversationGeneralDetail, CompletionConversationsResponse, LogAnnotation } from '@/models/log'
Expand Down Expand Up @@ -57,6 +58,12 @@ type IDrawerContext = {
appDetail?: App
}

type StatusCount = {
success: number
failed: number
partial_success: number
}

const DrawerContext = createContext<IDrawerContext>({} as IDrawerContext)

/**
Expand All @@ -71,6 +78,33 @@ const HandThumbIconWithCount: FC<{ count: number; iconType: 'up' | 'down' }> = (
</div>
}

const statusTdRender = (statusCount: StatusCount) => {
if (statusCount.partial_success + statusCount.failed === 0) {
return (
<div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
<Indicator color={'green'} />
<span className='text-util-colors-green-green-600'>Success</span>
</div>
)
}
else if (statusCount.failed === 0) {
return (
<div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
<Indicator color={'green'} />
<span className='text-util-colors-green-green-600'>Partial Success</span>
</div>
)
}
else {
return (
<div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
<Indicator color={'red'} />
<span className='text-util-colors-red-red-600'>{statusCount.failed} {`${statusCount.failed > 1 ? 'Failures' : 'Failure'}`}</span>
</div>
)
}
}

const getFormattedChatList = (messages: ChatMessage[], conversationId: string, timezone: string, format: string) => {
const newChatList: IChatItem[] = []
messages.forEach((item: ChatMessage) => {
Expand Down Expand Up @@ -496,8 +530,8 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
}

/**
* Text App Conversation Detail Component
*/
* Text App Conversation Detail Component
*/
const CompletionConversationDetailComp: FC<{ appId?: string; conversationId?: string }> = ({ appId, conversationId }) => {
// Text Generator App Session Details Including Message List
const detailParams = ({ url: `/apps/${appId}/completion-conversations/${conversationId}` })
Expand Down Expand Up @@ -542,8 +576,8 @@ const CompletionConversationDetailComp: FC<{ appId?: string; conversationId?: st
}

/**
* Chat App Conversation Detail Component
*/
* Chat App Conversation Detail Component
*/
const ChatConversationDetailComp: FC<{ appId?: string; conversationId?: string }> = ({ appId, conversationId }) => {
const detailParams = { url: `/apps/${appId}/chat-conversations/${conversationId}` }
const { data: conversationDetail } = useSWR(() => (appId && conversationId) ? detailParams : null, fetchChatConversationDetail)
Expand Down Expand Up @@ -585,8 +619,8 @@ const ChatConversationDetailComp: FC<{ appId?: string; conversationId?: string }
}

/**
* Conversation list component including basic information
*/
* Conversation list component including basic information
*/
const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh }) => {
const { t } = useTranslation()
const { formatTime } = useTimestamp()
Expand All @@ -597,6 +631,7 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
const [showDrawer, setShowDrawer] = useState<boolean>(false) // Whether to display the chat details drawer
const [currentConversation, setCurrentConversation] = useState<ChatConversationGeneralDetail | CompletionConversationGeneralDetail | undefined>() // Currently selected conversation
const isChatMode = appDetail.mode !== 'completion' // Whether the app is a chat app
const isChatflow = appDetail.mode === 'advanced-chat' // Whether the app is a chatflow app
const { setShowPromptLogModal, setShowAgentLogModal } = useAppStore(useShallow(state => ({
setShowPromptLogModal: state.setShowPromptLogModal,
setShowAgentLogModal: state.setShowAgentLogModal,
Expand Down Expand Up @@ -639,6 +674,7 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
<td className='pl-2 pr-1 w-5 rounded-l-lg bg-background-section-burn whitespace-nowrap'></td>
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{isChatMode ? t('appLog.table.header.summary') : t('appLog.table.header.input')}</td>
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.endUser')}</td>
{isChatflow && <td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.status')}</td>}
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{isChatMode ? t('appLog.table.header.messageCount') : t('appLog.table.header.output')}</td>
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.userRate')}</td>
<td className='pl-3 py-1.5 bg-background-section-burn whitespace-nowrap'>{t('appLog.table.header.adminRate')}</td>
Expand Down Expand Up @@ -669,6 +705,9 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
{renderTdValue(leftValue || t('appLog.table.empty.noChat'), !leftValue, isChatMode && log.annotated)}
</td>
<td className='p-3 pr-2'>{renderTdValue(endUser || defaultValue, !endUser)}</td>
{isChatflow && <td className='p-3 pr-2 w-[160px]' style={{ maxWidth: isChatMode ? 300 : 200 }}>
{statusTdRender(log.status_count)}
</td>}
<td className='p-3 pr-2' style={{ maxWidth: isChatMode ? 100 : 200 }}>
{renderTdValue(rightValue === 0 ? 0 : (rightValue || t('appLog.table.empty.noOutput')), !rightValue, !isChatMode && !!log.annotation?.content, log.annotation)}
</td>
Expand Down
8 changes: 8 additions & 0 deletions web/app/components/app/workflow-log/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ const WorkflowAppLogList: FC<ILogs> = ({ logs, appDetail, onRefresh }) => {
</div>
)
}
if (status === 'partial-succeeded') {
return (
<div className='inline-flex items-center gap-1 system-xs-semibold-uppercase'>
<Indicator color={'green'} />
<span className='text-util-colors-green-green-600'>Partial Success</span>
</div>
)
}
}

const onCloseDrawer = () => {
Expand Down
Loading