fix: skip hidden tool messages in exports

This commit is contained in:
shallfun
2026-04-29 03:25:02 +08:00
committed by Pionxzh
parent 3111334b3a
commit 3d491744f4
5 changed files with 48 additions and 80 deletions

View File

@@ -689,6 +689,39 @@ export interface ConversationResult {
projectId?: string
}
export function shouldSkipMessageInExport(message?: ConversationNodeMessage): boolean {
if (!message || !message.content) return true
// ChatGPT is talking to tool
if (message.recipient !== 'all') return true
// Skip "thinking" content (hidden reasoning steps from thinking models)
if (message.content.content_type === 'thoughts') return true
if (message.content.content_type === 'reasoning_recap') return true
// Skip messages marked as visually hidden (e.g., internal system prompts)
if (message.metadata?.is_visually_hidden_from_conversation) return true
// Skip tool's intermediate message.
if (message.author.role === 'tool') {
const hasExecutionImages = message.content.content_type === 'execution_output'
&& !!message.metadata?.aggregate_result?.messages?.some(msg => msg.message_type === 'image')
const hasDalleImage = message.content.content_type === 'multimodal_text'
&& message.content.parts.some((part) => {
return typeof part !== 'string'
&& part.content_type === 'image_asset_pointer'
&& !!part.metadata?.dalle
})
if (!hasExecutionImages && !hasDalleImage) {
return true
}
}
return false
}
const ModelMapping: { [key in ModelSlug]: string } & { [key: string]: string } = {
'text-davinci-002-render-sha': 'GPT-3.5',
'text-davinci-002-render-paid': 'GPT-3.5',
@@ -773,6 +806,8 @@ function extractConversationResult(conversationMapping: Record<string, Conversat
&& node.message?.content.content_type !== 'model_editable_context'
// Skip user custom instructions
&& node.message?.content.content_type !== 'user_editable_context'
// Skip hidden/tool-only messages that should not appear in exported output
&& !shouldSkipMessageInExport(node.message)
) {
result.unshift(node)
}

View File

@@ -1,5 +1,5 @@
import JSZip from 'jszip'
import { fetchConversation, getCurrentChatId, processConversation } from '../api'
import { fetchConversation, getCurrentChatId, processConversation, shouldSkipMessageInExport } from '../api'
import { KEY_TIMESTAMP_24H, KEY_TIMESTAMP_ENABLED, KEY_TIMESTAMP_HTML, baseUrl } from '../constants'
import i18n from '../i18n'
import { checkIfConversationStarted, getUserAvatar } from '../page'
@@ -80,31 +80,7 @@ function conversationToHtml(conversation: ConversationResult, avatar: string, me
const conversationHtml = conversationNodes.map(({ message }) => {
if (!message || !message.content) return null
// ChatGPT is talking to tool
if (message.recipient !== 'all') return null
// Skip "thinking" content (hidden reasoning steps from thinking models)
if (message.content.content_type === 'thoughts') return null
if (message.content.content_type === 'reasoning_recap') return null
// Skip messages marked as visually hidden (e.g., internal system prompts)
if (message.metadata?.is_visually_hidden_from_conversation) return null
// Skip tool's intermediate message.
if (message.author.role === 'tool') {
if (
// HACK: we special case the content_type 'multimodal_text' here because it is used by
// the dalle tool to return the image result, and we do want to show that.
message.content.content_type !== 'multimodal_text'
// Code execution result with image
&& !(
message.content.content_type === 'execution_output'
&& message.metadata?.aggregate_result?.messages?.some(msg => msg.message_type === 'image')
)
) {
return null
}
}
if (shouldSkipMessageInExport(message)) return null
const author = transformAuthor(message.author)
const model = message?.metadata?.model_slug === 'gpt-4' ? 'GPT-4' : 'GPT-3'

View File

@@ -1,5 +1,5 @@
import JSZip from 'jszip'
import { fetchConversation, getCurrentChatId, processConversation } from '../api'
import { fetchConversation, getCurrentChatId, processConversation, shouldSkipMessageInExport } from '../api'
import { KEY_TIMESTAMP_24H, KEY_TIMESTAMP_ENABLED, KEY_TIMESTAMP_MARKDOWN, baseUrl } from '../constants'
import i18n from '../i18n'
import { checkIfConversationStarted } from '../page'
@@ -96,31 +96,7 @@ function conversationToMarkdown(conversation: ConversationResult, metaList?: Exp
const content = conversationNodes.map(({ message }) => {
if (!message || !message.content) return null
// ChatGPT is talking to tool
if (message.recipient !== 'all') return null
// Skip "thinking" content (hidden reasoning steps from thinking models)
if (message.content.content_type === 'thoughts') return null
if (message.content.content_type === 'reasoning_recap') return null
// Skip messages marked as visually hidden (e.g., internal system prompts)
if (message.metadata?.is_visually_hidden_from_conversation) return null
// Skip tool's intermediate message.
if (message.author.role === 'tool') {
if (
// HACK: we special case the content_type 'multimodal_text' here because it is used by
// the dalle tool to return the image result, and we do want to show that.
message.content.content_type !== 'multimodal_text'
// Code execution result with image
&& !(
message.content.content_type === 'execution_output'
&& message.metadata?.aggregate_result?.messages?.some(msg => msg.message_type === 'image')
)
) {
return null
}
}
if (shouldSkipMessageInExport(message)) return null
const timestamp = message?.create_time ?? ''
const showTimestamp = enableTimestamp && timeStampMarkdown && timestamp

View File

@@ -1,4 +1,4 @@
import { fetchConversation, getCurrentChatId, processConversation } from '../api'
import { fetchConversation, getCurrentChatId, processConversation, shouldSkipMessageInExport } from '../api'
import i18n from '../i18n'
import { checkIfConversationStarted } from '../page'
import { copyToClipboard } from '../utils/clipboard'
@@ -34,31 +34,7 @@ const LatexRegex = /(\s\$\$.+\$\$\s|\s\$.+\$\s|\\\[.+\\\]|\\\(.+\\\))|(^\$$[\S\s
function transformMessage(message?: ConversationNodeMessage) {
if (!message || !message.content) return null
// ChatGPT is talking to tool
if (message.recipient !== 'all') return null
// Skip "thinking" content (hidden reasoning steps from thinking models)
if (message.content.content_type === 'thoughts') return null
if (message.content.content_type === 'reasoning_recap') return null
// Skip messages marked as visually hidden (e.g., internal system prompts)
if (message.metadata?.is_visually_hidden_from_conversation) return null
// Skip tool's intermediate message.
if (message.author.role === 'tool') {
if (
// HACK: we special case the content_type 'multimodal_text' here because it is used by
// the dalle tool to return the image result, and we do want to show that.
message.content.content_type !== 'multimodal_text'
// Code execution result with image
&& !(
message.content.content_type === 'execution_output'
&& message.metadata?.aggregate_result?.messages?.some(msg => msg.message_type === 'image')
)
) {
return null
}
}
if (shouldSkipMessageInExport(message)) return null
const author = transformAuthor(message.author)
let content = transformContent(message.content, message.metadata)

View File

@@ -1,3 +1,4 @@
import { shouldSkipMessageInExport } from '../api'
import { jsonlStringify, nonNullable } from './utils'
import type { ConversationNode, ConversationResult } from '../api'
@@ -22,7 +23,7 @@ interface OobaData {
}
function convertMessageToTavern(node: ConversationNode): TavernMessage | null {
if (!node.message || node.message.content.content_type !== 'text') {
if (!node.message || shouldSkipMessageInExport(node.message) || node.message.content.content_type !== 'text') {
return null
}
@@ -56,7 +57,11 @@ export function convertToTavern(conversation: ConversationResult): string {
export function convertToOoba(conversation: ConversationResult): string {
const pairs: [string, string][] = []
const messages = conversation.conversationNodes.filter(node => node.message?.author.role !== 'tool' && node.message?.content.content_type === 'text')
const messages = conversation.conversationNodes.filter((node) => {
return !!node.message
&& !shouldSkipMessageInExport(node.message)
&& node.message.content.content_type === 'text'
})
let idx = 0
while (idx < messages.length - 1) {