add submodule: kvstore

This commit is contained in:
2026-04-14 04:48:40 +00:00
parent 8d8130bbf0
commit a1254d5723
15 changed files with 68 additions and 227 deletions

View File

@@ -50,6 +50,8 @@ export default {
showRawText: 'Show as raw text',
sourceSemantic: 'Semantic Match',
sourceLlm: 'LLM Output',
inputTokensShort: 'Input',
outputTokensShort: 'Output',
inputTokens: 'Input {count}',
outputTokens: 'Output {count}',
sessionTokens: 'Session {count} tokens',

View File

@@ -50,6 +50,8 @@ export default {
showRawText: '显示原文',
sourceSemantic: '语义匹配',
sourceLlm: '大模型输出',
inputTokensShort: '输入',
outputTokensShort: '输出',
inputTokens: 'Input {count}',
outputTokens: 'Output {count}',
sessionTokens: '本轮消耗 {count} tokens',

View File

@@ -50,6 +50,8 @@ export default {
showRawText: '顯示原文',
sourceSemantic: '語義匹配',
sourceLlm: '大模型輸出',
inputTokensShort: '輸入',
outputTokensShort: '輸出',
inputTokens: 'Input {count}',
outputTokens: 'Output {count}',
sessionTokens: '本輪消耗 {count} tokens',

View File

@@ -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>