diff --git a/mateclaw-server/src/main/java/vip/mate/channel/web/ChatController.java b/mateclaw-server/src/main/java/vip/mate/channel/web/ChatController.java
index f31411f4..105fef0f 100644
--- a/mateclaw-server/src/main/java/vip/mate/channel/web/ChatController.java
+++ b/mateclaw-server/src/main/java/vip/mate/channel/web/ChatController.java
@@ -1416,6 +1416,14 @@ public class ChatController {
payload.put("status", status);
if (savedAssistant != null && savedAssistant.getId() != null) {
payload.put("assistantMessageId", savedAssistant.getId());
+ // Surface runtime model attribution so the chat bubble can show
+ // which model produced this reply without waiting for a history reload.
+ if (savedAssistant.getRuntimeModel() != null && !savedAssistant.getRuntimeModel().isBlank()) {
+ payload.put("runtimeModel", savedAssistant.getRuntimeModel());
+ }
+ if (savedAssistant.getRuntimeProvider() != null && !savedAssistant.getRuntimeProvider().isBlank()) {
+ payload.put("runtimeProvider", savedAssistant.getRuntimeProvider());
+ }
}
if (promptTokens > 0) payload.put("promptTokens", promptTokens);
if (completionTokens > 0) payload.put("completionTokens", completionTokens);
diff --git a/mateclaw-server/src/main/java/vip/mate/workspace/conversation/vo/MessageVO.java b/mateclaw-server/src/main/java/vip/mate/workspace/conversation/vo/MessageVO.java
index 37c7381e..111ce8b2 100644
--- a/mateclaw-server/src/main/java/vip/mate/workspace/conversation/vo/MessageVO.java
+++ b/mateclaw-server/src/main/java/vip/mate/workspace/conversation/vo/MessageVO.java
@@ -41,6 +41,12 @@ public class MessageVO {
/** Completion tokens 消耗 */
private Integer completionTokens;
+ /** Model name actually used to produce this message (e.g. "deepseek-chat"). */
+ private String runtimeModel;
+
+ /** Provider id of the runtime model (e.g. "deepseek", "zhipu"). */
+ private String runtimeProvider;
+
private LocalDateTime createTime;
private LocalDateTime updateTime;
@@ -59,6 +65,8 @@ public class MessageVO {
vo.setMetadata(parseMetadataToObject(entity.getMetadata()));
vo.setPromptTokens(entity.getPromptTokens());
vo.setCompletionTokens(entity.getCompletionTokens());
+ vo.setRuntimeModel(entity.getRuntimeModel());
+ vo.setRuntimeProvider(entity.getRuntimeProvider());
vo.setCreateTime(entity.getCreateTime());
vo.setUpdateTime(entity.getUpdateTime());
vo.setContentParts(contentParts);
diff --git a/mateclaw-ui/src/components/chat/MessageBubble.vue b/mateclaw-ui/src/components/chat/MessageBubble.vue
index 15684c84..17697c86 100644
--- a/mateclaw-ui/src/components/chat/MessageBubble.vue
+++ b/mateclaw-ui/src/components/chat/MessageBubble.vue
@@ -337,6 +337,12 @@
>
+
+ {{ replyModel }}
{{ formattedTime }}
@@ -710,6 +716,16 @@ const formattedTime = computed(() => {
})
})
+// Reply model attribution: shows which model produced the assistant message.
+// Empty for streaming (server only emits runtimeModel after persistence) and
+// historical messages prior to MessageVO carrying the field.
+const replyModel = computed(() => props.message.runtimeModel || '')
+const replyModelTitle = computed(() => {
+ const provider = props.message.runtimeProvider
+ const base = t('chat.replyModel', { model: replyModel.value })
+ return provider ? `${base} (${provider})` : base
+})
+
const formatFileSize = (size: number) => {
if (size < 1024) return `${size} B`
if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`
@@ -1475,6 +1491,18 @@ watch(isGenerating, (generating) => {
user-select: none;
}
+.action-model {
+ font-size: 11px;
+ color: var(--mc-text-secondary, #64748b);
+ margin-left: 4px;
+ padding: 1px 6px;
+ border-radius: 4px;
+ background: var(--mc-fill-2, rgba(100, 116, 139, 0.08));
+ font-family: var(--mc-mono-font, ui-monospace, "SF Mono", Menlo, monospace);
+ user-select: text;
+ white-space: nowrap;
+}
+
/* ==================== 主内容区域 ==================== */
.msg-content {
position: relative;
diff --git a/mateclaw-ui/src/composables/chat/useChat.ts b/mateclaw-ui/src/composables/chat/useChat.ts
index 86553d0a..e6a511c3 100644
--- a/mateclaw-ui/src/composables/chat/useChat.ts
+++ b/mateclaw-ui/src/composables/chat/useChat.ts
@@ -487,6 +487,8 @@ export function useChat(options: UseChatOptions): UseChatReturn {
const msg = messages.value[msgIndex]
if (data.promptTokens !== undefined) msg.promptTokens = data.promptTokens
if (data.completionTokens !== undefined) msg.completionTokens = data.completionTokens
+ if (data.runtimeModel) msg.runtimeModel = data.runtimeModel
+ if (data.runtimeProvider) msg.runtimeProvider = data.runtimeProvider
// Replace the local temp ID with the backend-persisted ID so reconcile can match by ID
if (data.assistantMessageId) {
msg.id = data.assistantMessageId
diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts
index 269b544f..ff02e473 100644
--- a/mateclaw-ui/src/i18n/locales/en-US.ts
+++ b/mateclaw-ui/src/i18n/locales/en-US.ts
@@ -148,6 +148,7 @@ export default {
copy: 'Copy',
copied: 'Copied',
regenerate: 'Regenerate',
+ replyModel: 'Reply model: {model}',
ttsPlay: 'Read Aloud',
ttsStop: 'Stop Reading',
conversations: 'Conversations',
diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts
index b5b90508..fe3d7f61 100644
--- a/mateclaw-ui/src/i18n/locales/zh-CN.ts
+++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts
@@ -148,6 +148,7 @@ export default {
copy: '复制',
copied: '已复制',
regenerate: '重新生成',
+ replyModel: '本条回复模型: {model}',
ttsPlay: '朗读',
ttsStop: '停止朗读',
conversations: '会话列表',
diff --git a/mateclaw-ui/src/types/index.ts b/mateclaw-ui/src/types/index.ts
index 3efd79f1..ee3d2de6 100644
--- a/mateclaw-ui/src/types/index.ts
+++ b/mateclaw-ui/src/types/index.ts
@@ -83,6 +83,9 @@ export interface Message {
// Token 统计
promptTokens?: number
completionTokens?: number
+ // Runtime model attribution (assistant messages): the model that actually produced this reply
+ runtimeModel?: string
+ runtimeProvider?: string
// 前端临时字段
streaming?: boolean // 内部动画控制,UI 渲染以 status 为准
attachments?: ChatAttachment[]
diff --git a/mateclaw-ui/src/views/ChatConsole.vue b/mateclaw-ui/src/views/ChatConsole.vue
index 51cf4b34..f3301e57 100644
--- a/mateclaw-ui/src/views/ChatConsole.vue
+++ b/mateclaw-ui/src/views/ChatConsole.vue
@@ -1774,6 +1774,8 @@ function normalizeMessage(raw: Message): Message {
// 保留后端返回的 token 字段(MessageVO 新增)
if ((raw as any).promptTokens) msg.promptTokens = (raw as any).promptTokens
if ((raw as any).completionTokens) msg.completionTokens = (raw as any).completionTokens
+ if ((raw as any).runtimeModel) msg.runtimeModel = (raw as any).runtimeModel
+ if ((raw as any).runtimeProvider) msg.runtimeProvider = (raw as any).runtimeProvider
if (msg.contentParts.length === 0 && msg.content) {
if (msg.role === 'assistant') {