import Foundation struct ChatGPTRawConversationsParser: ImportParser { func parse(sourceURL: URL, dedupIndex: DedupIndex) throws -> ImportParseResult { let files = try ImportFiles.immediateFiles( at: sourceURL, expected: "eine ChatGPT-conversations-.json-Datei oder einen passenden Exportordner", directFileMatches: { $0.pathExtension.lowercased() == "json" }, directoryFileMatches: { $0.pathExtension.lowercased() == "json" && $0.lastPathComponent.hasPrefix("conversations") } ) var accumulator = ImportAccumulator( minimumLength: 20, maximumLength: 10_000, dedupIndex: dedupIndex ) for fileURL in files { let root: Any do { root = try ImportJSON.object(from: fileURL) } catch { accumulator.skipMalformed() continue } guard let conversations = root as? [Any] else { accumulator.skipMalformed() continue } for conversationValue in conversations { guard let conversation = conversationValue as? [String: Any] else { accumulator.skipMalformed() continue } let model = conversation["model_slug"] as? String guard let mapping = conversation["mapping"] as? [String: Any] else { accumulator.skipMalformed() continue } for nodeValue in mapping.values { guard let node = nodeValue as? [String: Any], let message = node["message"] as? [String: Any] else { continue } let author = message["author"] as? [String: Any] guard author?["role"] as? String == "user" else { continue } guard let content = message["content"] as? [String: Any], let parts = content["parts"] as? [Any], let text = parts.first as? String else { accumulator.skipMalformed() continue } accumulator.accept( text, sourcePrefix: "chatgpt-raw", tags: "chatgpt", model: model, promptedAt: ImportTimestamp.normalize(message["create_time"]) ) } } } return accumulator.result() } }