import struct import zstandard as zstd import json from typing import Dict # これを追加 # これを追加 from datetime import datetime class IathDecoder: """ .iath互換の圧縮バイナリデータをKnowledge Tileオブジェクトにデコードします。 """ def _decode_string_from_buffer(self, buffer, offset): """バッファからNULL終端文字列をデコードします。""" end_offset = buffer.find(b'\0', offset) if end_offset == -1: raise ValueError("Invalid string format in buffer") s = buffer[offset:end_offset].decode('utf-8') return s, end_offset + 1 def _decode_metadata(self, buffer: bytes) -> dict: """メタデータセクションをデコードします。""" offset = 0 kid, offset = self._decode_string_from_buffer(buffer, offset) topic, offset = self._decode_string_from_buffer(buffer, offset) created_at = buffer[offset:offset+27].decode('ascii').rstrip('\0') return {"knowledge_id": kid, "topic": topic, "created_at": created_at} def _decode_coordinates(self, buffer: bytes) -> dict: """座標セクションをデコードします。""" coords = struct.unpack(" dict: """コンテンツセクションをデコードします。""" offset = 0 # thinking_process think_len = struct.unpack(" dict: """検証履歴セクションをデコードします。""" status_map = { 0: "pending_review", 1: "partial_verified", 2: "verified", 3: "expert_confirmed" } status_code, initial_certainty, reviewer_count = struct.unpack(" dict: """ 単一の圧縮タイルデータをデコードしてKnowledge Tileオブジェクトを復元します。 Args: compressed_binary (bytes): 圧縮されたバイナリデータ。 Returns: dict: 復元されたKnowledge Tileオブジェクト。 """ try: dctx = zstd.ZstdDecompressor() uncompressed = dctx.decompress(compressed_binary) except zstd.ZstdError as e: raise ValueError(f"Zstandard decompression failed: {e}") offset = 0 decoded_sections = {} try: # Metadata md_len = struct.unpack(" Dict[str, Dict]: """ ヘッダー、インデックス、データセクションを含む完全な.iath DBファイルをデコードします。 Args: full_db_content (bytes): .iathファイル全体のバイナリコンテンツ。 Returns: Dict[str, Dict]: tile_idをキーとする、デコードされた知識タイルの辞書。 """ print("--- .iathデータベースのバッチデコード開始 ---") # 1. ヘッダーをパース if len(full_db_content) < 64: raise ValueError("Invalid .iath file: Header is too short.") magic, version, domain_code, compression_type, checksum, index_offset, data_offset = \ struct.unpack("<4sIBB32sQQ6x", full_db_content[:64]) if magic != b'ILMA': raise ValueError("Invalid .iath file: Magic number is incorrect.") print(f" - Header OK: Version={version}, Domain={domain_code}, Index Offset={index_offset}, Data Offset={data_offset}") # 2. インデックスセクションを読み込み # データオフセットの開始位置までがインデックスセクション index_data_binary = full_db_content[index_offset:data_offset] index = json.loads(index_data_binary.decode('utf-8')) print(f" - インデックス読み込み完了: {len(index)}件") # 3. データセクションから各タイルをデコード all_tiles = {} for item in index: tile_id, offset, length = item['id'], item['offset'], item['length'] # データセクション内でのタイルの範囲を特定 start = data_offset + offset end = start + length tile_compressed_data = full_db_content[start:end] # 個別のタイルをデコード try: decoded_tile = self.decode_tile(tile_compressed_data) # デコード結果にIDを付与(JSONにはIDがないため) if "metadata" in decoded_tile and "knowledge_id" not in decoded_tile["metadata"]: decoded_tile["metadata"]["knowledge_id"] = tile_id all_tiles[tile_id] = decoded_tile except Exception as e: print(f"警告: タイルID {tile_id} のデコードに失敗しました。スキップします。エラー: {e}") print(f" - 全タイルのデコード完了: {len(all_tiles)}件") print("--- バッチデコード完了 ---") return all_tiles