mirror of
https://github.com/pionxzh/chatgpt-exporter.git
synced 2026-07-23 09:00:51 -05:00
fix: skip hidden tool messages in exports
This commit is contained in:
35
src/api.ts
35
src/api.ts
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user