Files
archy/aiui/packages/app/src/utils/conversation-import.ts
T

99 lines
3.2 KiB
TypeScript

import type { Conversation, Message } from '@aiui/core/types/message'
function generateId(): string {
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
return crypto.randomUUID()
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16)
})
}
interface ImportResult {
conversations: Conversation[]
format: 'aiui' | 'claude' | 'unknown'
error?: string
}
/** Try to parse as AIUI JSON export (single conversation) */
function parseAIUIFormat(data: unknown): Conversation | null {
if (!data || typeof data !== 'object') return null
const obj = data as Record<string, unknown>
if (typeof obj.id === 'string' && typeof obj.title === 'string' && Array.isArray(obj.messages)) {
return obj as unknown as Conversation
}
return null
}
/** Try to parse Claude.ai export format */
function parseClaudeFormat(data: unknown): Conversation[] {
if (!Array.isArray(data)) return []
const conversations: Conversation[] = []
for (const item of data) {
if (!item || typeof item !== 'object') continue
const obj = item as Record<string, unknown>
// Claude.ai exports have { uuid, name, chat_messages: [...] }
if (typeof obj.uuid === 'string' && typeof obj.name === 'string' && Array.isArray(obj.chat_messages)) {
const messages: Message[] = []
for (const cm of obj.chat_messages as Record<string, unknown>[]) {
if (!cm || typeof cm !== 'object') continue
const role = cm.sender === 'human' ? 'user' as const : 'assistant' as const
const content = typeof cm.text === 'string' ? cm.text : ''
messages.push({
id: generateId(),
role,
content,
timestamp: typeof cm.created_at === 'string' ? new Date(cm.created_at as string).getTime() : Date.now(),
})
}
conversations.push({
id: generateId(),
title: obj.name as string,
messages,
createdAt: typeof obj.created_at === 'string' ? new Date(obj.created_at as string).getTime() : Date.now(),
updatedAt: typeof obj.updated_at === 'string' ? new Date(obj.updated_at as string).getTime() : Date.now(),
})
}
}
return conversations
}
export function parseImportFile(jsonString: string): ImportResult {
try {
const data = JSON.parse(jsonString)
// Try AIUI single conversation
const aiui = parseAIUIFormat(data)
if (aiui) {
return { conversations: [aiui], format: 'aiui' }
}
// Try Claude.ai export (array of conversations)
const claude = parseClaudeFormat(data)
if (claude.length > 0) {
return { conversations: claude, format: 'claude' }
}
// Try AIUI array format
if (Array.isArray(data)) {
const aiuiConvs: Conversation[] = []
for (const item of data) {
const c = parseAIUIFormat(item)
if (c) aiuiConvs.push(c)
}
if (aiuiConvs.length > 0) {
return { conversations: aiuiConvs, format: 'aiui' }
}
}
return { conversations: [], format: 'unknown', error: 'Unrecognized format' }
} catch {
return { conversations: [], format: 'unknown', error: 'Invalid JSON' }
}
}