kofdai commited on
Commit
cd705ac
·
verified ·
1 Parent(s): a5ac882

Upload iath_decoder.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. iath_decoder.py +188 -0
iath_decoder.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import struct
2
+ import zstandard as zstd
3
+ import json
4
+ from typing import Dict # これを追加
5
+ # これを追加
6
+ from datetime import datetime
7
+
8
+ class IathDecoder:
9
+ """
10
+ .iath互換の圧縮バイナリデータをKnowledge Tileオブジェクトにデコードします。
11
+ """
12
+
13
+ def _decode_string_from_buffer(self, buffer, offset):
14
+ """バッファからNULL終端文字列をデコードします。"""
15
+ end_offset = buffer.find(b'\0', offset)
16
+ if end_offset == -1:
17
+ raise ValueError("Invalid string format in buffer")
18
+ s = buffer[offset:end_offset].decode('utf-8')
19
+ return s, end_offset + 1
20
+
21
+ def _decode_metadata(self, buffer: bytes) -> dict:
22
+ """メタデータセクションをデコードします。"""
23
+ offset = 0
24
+ kid, offset = self._decode_string_from_buffer(buffer, offset)
25
+ topic, offset = self._decode_string_from_buffer(buffer, offset)
26
+ created_at = buffer[offset:offset+27].decode('ascii').rstrip('\0')
27
+
28
+ return {"knowledge_id": kid, "topic": topic, "created_at": created_at}
29
+
30
+ def _decode_coordinates(self, buffer: bytes) -> dict:
31
+ """座標セクションをデコードします。"""
32
+ coords = struct.unpack("<ffffff", buffer)
33
+ return {
34
+ "medical_space": (coords[0], coords[1], coords[2]),
35
+ "meta_space": (coords[3], coords[4], coords[5])
36
+ }
37
+
38
+ def _decode_content(self, buffer: bytes) -> dict:
39
+ """コンテンツセクションをデコードします。"""
40
+ offset = 0
41
+
42
+ # thinking_process
43
+ think_len = struct.unpack("<I", buffer[offset:offset+4])[0]
44
+ offset += 4
45
+ thinking = buffer[offset:offset+think_len].decode('utf-8')
46
+ offset += think_len
47
+
48
+ # final_response
49
+ resp_len = struct.unpack("<I", buffer[offset:offset+4])[0]
50
+ offset += 4
51
+ response = buffer[offset:offset+resp_len].decode('utf-8')
52
+
53
+ return {"thinking_process": thinking, "final_response": response}
54
+
55
+ def _decode_verification(self, buffer: bytes) -> dict:
56
+ """検証履歴セクションをデコードします。"""
57
+ status_map = {
58
+ 0: "pending_review", 1: "partial_verified",
59
+ 2: "verified", 3: "expert_confirmed"
60
+ }
61
+
62
+ status_code, initial_certainty, reviewer_count = struct.unpack("<BBI", buffer[:6])
63
+ status = status_map.get(status_code, "unknown")
64
+
65
+ # NOTE: レビュアーIDのデコードはエンコーダーに合わせて省略
66
+
67
+ return {
68
+ "status": status,
69
+ "initial_certainty": initial_certainty,
70
+ "reviewers": [] # ダミー
71
+ }
72
+
73
+ def decode_tile(self, compressed_binary: bytes) -> dict:
74
+ """
75
+ 単一の圧縮タイルデータをデコードしてKnowledge Tileオブジェクトを復元します。
76
+
77
+ Args:
78
+ compressed_binary (bytes): 圧縮されたバイナリデータ。
79
+
80
+ Returns:
81
+ dict: 復元されたKnowledge Tileオブジェクト。
82
+ """
83
+ try:
84
+ dctx = zstd.ZstdDecompressor()
85
+ uncompressed = dctx.decompress(compressed_binary)
86
+ except zstd.ZstdError as e:
87
+ raise ValueError(f"Zstandard decompression failed: {e}")
88
+
89
+ offset = 0
90
+ decoded_sections = {}
91
+
92
+ try:
93
+ # Metadata
94
+ md_len = struct.unpack("<I", uncompressed[offset:offset+4])[0]
95
+ offset += 4
96
+ decoded_sections["metadata"] = self._decode_metadata(uncompressed[offset:offset+md_len])
97
+ offset += md_len
98
+
99
+ # Coordinates
100
+ coord_len = struct.unpack("<I", uncompressed[offset:offset+4])[0]
101
+ offset += 4
102
+ decoded_sections["coordinates"] = self._decode_coordinates(uncompressed[offset:offset+coord_len])
103
+ offset += coord_len
104
+
105
+ # Content
106
+ content_len = struct.unpack("<I", uncompressed[offset:offset+4])[0]
107
+ offset += 4
108
+ decoded_sections["content"] = self._decode_content(uncompressed[offset:offset+content_len])
109
+ offset += content_len
110
+
111
+ # Verification
112
+ verif_len = struct.unpack("<I", uncompressed[offset:offset+4])[0]
113
+ offset += 4
114
+ decoded_sections["verification"] = self._decode_verification(uncompressed[offset:offset+verif_len])
115
+ offset += verif_len
116
+
117
+ except (struct.error, IndexError, UnicodeDecodeError) as e:
118
+ raise ValueError(f"Failed to parse tile structure at offset {offset}: {e}")
119
+
120
+ # スキーマに準拠するよう、デコードしたセクションを再構成
121
+ restored_tile = {
122
+ "metadata": decoded_sections.get("metadata"),
123
+ "content": decoded_sections.get("content"),
124
+ "coordinates": decoded_sections.get("coordinates", {}),
125
+ "verification": decoded_sections.get("verification"),
126
+ # 以下はエンコードしていないためデフォルト値
127
+ "source": {},
128
+ "history": []
129
+ }
130
+ # 不足しているキーを補完
131
+ if "coordinates" in restored_tile:
132
+ restored_tile["coordinates"].setdefault("reasoning_path", [])
133
+
134
+ return restored_tile
135
+
136
+ def decode_batch(self, full_db_content: bytes) -> Dict[str, Dict]:
137
+ """
138
+ ヘッダー、インデックス、データセクションを含む完全な.iath DBファイルをデコードします。
139
+
140
+ Args:
141
+ full_db_content (bytes): .iathファイル全体のバイナリコンテンツ。
142
+
143
+ Returns:
144
+ Dict[str, Dict]: tile_idをキーとする、デコードされた知識タイルの辞書。
145
+ """
146
+ print("--- .iathデータベースのバッチデコード開始 ---")
147
+ # 1. ヘッダーをパース
148
+ if len(full_db_content) < 64:
149
+ raise ValueError("Invalid .iath file: Header is too short.")
150
+
151
+ magic, version, domain_code, compression_type, checksum, index_offset, data_offset = \
152
+ struct.unpack("<4sIBB32sQQ6x", full_db_content[:64])
153
+
154
+ if magic != b'ILMA':
155
+ raise ValueError("Invalid .iath file: Magic number is incorrect.")
156
+
157
+ print(f" - Header OK: Version={version}, Domain={domain_code}, Index Offset={index_offset}, Data Offset={data_offset}")
158
+
159
+ # 2. インデックスセクションを読み込み
160
+ # データオフセットの開始位置までがインデックスセクション
161
+ index_data_binary = full_db_content[index_offset:data_offset]
162
+ index = json.loads(index_data_binary.decode('utf-8'))
163
+ print(f" - インデックス読み込み完了: {len(index)}件")
164
+
165
+ # 3. データセクションから各タイルをデコード
166
+ all_tiles = {}
167
+ for item in index:
168
+ tile_id, offset, length = item['id'], item['offset'], item['length']
169
+
170
+ # データセクション内でのタイルの範囲を特定
171
+ start = data_offset + offset
172
+ end = start + length
173
+ tile_compressed_data = full_db_content[start:end]
174
+
175
+ # 個別のタイルをデコード
176
+ try:
177
+ decoded_tile = self.decode_tile(tile_compressed_data)
178
+ # デコード結果にIDを付与(JSONにはIDがないため)
179
+ if "metadata" in decoded_tile and "knowledge_id" not in decoded_tile["metadata"]:
180
+ decoded_tile["metadata"]["knowledge_id"] = tile_id
181
+ all_tiles[tile_id] = decoded_tile
182
+ except Exception as e:
183
+ print(f"警告: タイルID {tile_id} のデコードに失敗しました。スキップします。エラー: {e}")
184
+
185
+ print(f" - 全タイルのデコード完了: {len(all_tiles)}件")
186
+ print("--- バッチデコード完了 ---")
187
+ return all_tiles
188
+