|
|
|
|
@@ -54,28 +54,43 @@ const sourceLabel = computed(() => {
|
|
|
|
|
const sourceClass = computed(() => {
|
|
|
|
|
switch (props.messageMeta?.source) {
|
|
|
|
|
case 'semantic_match':
|
|
|
|
|
return 'border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-900/60 dark:bg-sky-950/40 dark:text-sky-300'
|
|
|
|
|
case 'llm':
|
|
|
|
|
return 'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/60 dark:bg-amber-950/40 dark:text-amber-300'
|
|
|
|
|
return 'border-[#d8e2eb] bg-[#f7fafc] text-[#52606d] dark:border-neutral-700 dark:bg-[#1a1d22] dark:text-neutral-200'
|
|
|
|
|
default:
|
|
|
|
|
return 'border-[#d0d7de] bg-white text-[#57606a] dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-300'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const inputUsageLabel = computed(() => {
|
|
|
|
|
const usage = props.messageMeta?.usage
|
|
|
|
|
if (!usage || props.inversion)
|
|
|
|
|
return ''
|
|
|
|
|
|
|
|
|
|
return t('chat.inputTokens', { count: usage.prompt_tokens })
|
|
|
|
|
const sourceIconClass = computed(() => {
|
|
|
|
|
switch (props.messageMeta?.source) {
|
|
|
|
|
case 'semantic_match':
|
|
|
|
|
return 'bg-emerald-50 text-emerald-600 shadow-[inset_0_0_0_1px_rgba(52,211,153,0.18)] dark:bg-emerald-950/35 dark:text-emerald-300'
|
|
|
|
|
case 'llm':
|
|
|
|
|
return 'bg-slate-100 text-slate-500 shadow-[inset_0_0_0_1px_rgba(148,163,184,0.18)] dark:bg-slate-800/70 dark:text-slate-300'
|
|
|
|
|
default:
|
|
|
|
|
return 'bg-white/90 text-[#52606d] shadow-[inset_0_0_0_1px_rgba(130,140,150,0.14)] dark:bg-neutral-800 dark:text-neutral-300'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const outputUsageLabel = computed(() => {
|
|
|
|
|
const tokenStats = computed(() => {
|
|
|
|
|
const usage = props.messageMeta?.usage
|
|
|
|
|
if (!usage || props.inversion)
|
|
|
|
|
return ''
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
return t('chat.outputTokens', { count: usage.completion_tokens })
|
|
|
|
|
return [
|
|
|
|
|
{
|
|
|
|
|
key: 'input',
|
|
|
|
|
label: t('chat.inputTokensShort'),
|
|
|
|
|
count: usage.prompt_tokens,
|
|
|
|
|
icon: 'ri:arrow-right-up-line',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'output',
|
|
|
|
|
label: t('chat.outputTokensShort'),
|
|
|
|
|
count: usage.completion_tokens,
|
|
|
|
|
icon: 'ri:sparkling-line',
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const options = computed(() => {
|
|
|
|
|
@@ -138,30 +153,29 @@ function handleRegenerate() {
|
|
|
|
|
<div class="flex flex-col gap-2" :class="[inversion ? 'items-end' : 'items-start']">
|
|
|
|
|
<span class="text-xs font-medium text-[#9aa4af] dark:text-neutral-500">{{ dateTime }}</span>
|
|
|
|
|
<div
|
|
|
|
|
v-if="sourceLabel || inputUsageLabel || outputUsageLabel"
|
|
|
|
|
class="flex flex-wrap items-center gap-2 rounded-2xl border border-[#e6edf3] bg-white/85 px-2.5 py-2 shadow-[0_10px_25px_rgba(15,23,42,0.06)] backdrop-blur-sm dark:border-neutral-800 dark:bg-[#14161a]/90"
|
|
|
|
|
v-if="sourceLabel || tokenStats.length"
|
|
|
|
|
class="flex flex-wrap items-center gap-2"
|
|
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
v-if="sourceLabel"
|
|
|
|
|
class="inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-[11px] font-semibold leading-none"
|
|
|
|
|
class="inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-[11px] font-semibold leading-none shadow-sm"
|
|
|
|
|
:class="sourceClass"
|
|
|
|
|
>
|
|
|
|
|
<SvgIcon icon="ri:radar-line" />
|
|
|
|
|
<span class="inline-flex h-4 w-4 items-center justify-center rounded-full" :class="sourceIconClass">
|
|
|
|
|
<SvgIcon icon="ri:radar-line" />
|
|
|
|
|
</span>
|
|
|
|
|
<span>{{ sourceLabel }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
v-if="inputUsageLabel"
|
|
|
|
|
class="inline-flex items-center gap-1.5 rounded-full border border-violet-200 bg-violet-50 px-2.5 py-1 text-[11px] font-semibold leading-none text-violet-700 dark:border-violet-900/60 dark:bg-violet-950/40 dark:text-violet-300"
|
|
|
|
|
v-for="item in tokenStats"
|
|
|
|
|
:key="item.key"
|
|
|
|
|
class="inline-flex items-center gap-1.5 rounded-full border border-[#dfe8e3] bg-[#f7fbf8] px-2.5 py-1 text-[11px] font-medium leading-none text-[#5d7063] dark:border-[#2d3831] dark:bg-[#141916] dark:text-[#aebdb2]"
|
|
|
|
|
>
|
|
|
|
|
<SvgIcon icon="ri:login-circle-line" />
|
|
|
|
|
<span>{{ inputUsageLabel }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div
|
|
|
|
|
v-if="outputUsageLabel"
|
|
|
|
|
class="inline-flex items-center gap-1.5 rounded-full border border-fuchsia-200 bg-fuchsia-50 px-2.5 py-1 text-[11px] font-semibold leading-none text-fuchsia-700 dark:border-fuchsia-900/60 dark:bg-fuchsia-950/40 dark:text-fuchsia-300"
|
|
|
|
|
>
|
|
|
|
|
<SvgIcon icon="ri:logout-circle-r-line" />
|
|
|
|
|
<span>{{ outputUsageLabel }}</span>
|
|
|
|
|
<span class="inline-flex h-4 w-4 items-center justify-center rounded-full bg-white/90 text-[#4b9e5f] shadow-[inset_0_0_0_1px_rgba(75,158,95,0.12)] dark:bg-[#202622] dark:text-[#8ec79a]">
|
|
|
|
|
<SvgIcon :icon="item.icon" />
|
|
|
|
|
</span>
|
|
|
|
|
<span class="tracking-[0.02em]">{{ item.label }}</span>
|
|
|
|
|
<span class="font-semibold tabular-nums text-[#355340] dark:text-[#dbe7de]">{{ item.count }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|