137 lines
7.4 KiB
Python
Raw Permalink Normal View History

# Notion原生格式加载器 · 主入口
# HLDP://tools/notion-corpus-loader/loader
import os, json, zipfile, tempfile
from typing import List, Optional
from .hldp_parser import HLDPParser
from .structure import StructuredCorpusItem, CorpusCollection
class NotionCorpusLoader:
"""Notion语料加载器 · 支持Notion API/Markdown/HTML/GPT/COS"""
def __init__(self):
self.parser = HLDPParser()
self.collection = CorpusCollection()
def load_notion_api_page(self, page_data: dict) -> StructuredCorpusItem:
page_id = page_data.get('page',{}).get('id','')
title = page_data.get('page',{}).get('title','')
blocks_data = page_data.get('blocks',[])
item = StructuredCorpusItem(source_id=page_id, source_type='notion_page', title=title,
created_time=page_data.get('page',{}).get('created_time',''),
last_edited_time=page_data.get('page',{}).get('last_edited_time',''))
for block in blocks_data:
bc = block.get('content',''); bt = block.get('type','paragraph')
parsed = self.parser.parse_markdown(bc)
for pb in parsed:
item.blocks.append({'type':bt,'content':pb.content,'hldp_path':pb.path,'cognitive_jump':pb.cognitive_jump})
if 'HLDP://' in bc and not item.hldp_path: item.hldp_path = bc.strip()
if '【认知' in bc or '核心认知跃迁' in bc: item.cognitive_jumps.append(bc[:100])
if '' in bc and ('推导' in bc or '起点' in bc): item.causal_chains.append(bc[:100])
if '铸渊' in bc and ('ICE-GL-ZY001' in bc or '创建' in bc): item.persona = '铸渊'
elif '霜砚' in bc: item.persona = '霜砚'
elif '冰朔' in bc and ('主权' in bc or 'TCS' in bc): item.persona = '冰朔'
item.raw_text += bc + '\n'
item.quality_score = self._score_quality(item)
return item
def load_markdown_file(self, filepath: str) -> StructuredCorpusItem:
with open(filepath,'r',encoding='utf-8') as f: text = f.read()
title = os.path.basename(filepath).replace('.md','')
blocks = self.parser.parse_markdown(text)
item = StructuredCorpusItem(source_id=filepath, source_type='markdown_file', title=title, raw_text=text)
for block in blocks:
item.blocks.append({'type':block.block_type,'content':block.content,'hldp_path':block.path,'cognitive_jump':block.cognitive_jump})
if block.cognitive_jump: item.cognitive_jumps.append(block.cognitive_jump)
if block.causal_chain: item.causal_chains.append(block.causal_chain)
if block.persona and not item.persona: item.persona = block.persona
if block.path and not item.hldp_path: item.hldp_path = block.path
item.quality_score = self._score_quality(item)
return item
def load_gpt_export(self, filepath: str) -> List[StructuredCorpusItem]:
items = []
with open(filepath,'r',encoding='utf-8') as f: data = json.load(f)
conversations = data if isinstance(data,list) else data.get('conversations',[data])
for i, conv in enumerate(conversations):
if isinstance(conv, str):
item = StructuredCorpusItem(source_id=f"gpt_{i}",source_type='gpt_export',persona='冰朔',raw_text=conv)
for block in self.parser.parse_markdown(conv):
item.blocks.append({'type':block.block_type,'content':block.content})
if block.cognitive_jump: item.cognitive_jumps.append(block.cognitive_jump)
item.quality_score = self._score_quality(item)
items.append(item)
elif isinstance(conv, dict):
msgs = conv.get('messages',conv.get('conversation',[]))
text = '\n'.join(f"[{m.get('role','user')}]: {m.get('content','')}" for m in msgs)
item = StructuredCorpusItem(source_id=f"gpt_{i}",source_type='gpt_export',persona='冰朔',raw_text=text)
for block in self.parser.parse_markdown(text):
item.blocks.append({'type':block.block_type,'content':block.content})
item.quality_score = self._score_quality(item)
items.append(item)
return items
def load_zip(self, zip_path: str, output_dir: Optional[str] = None) -> CorpusCollection:
if output_dir: os.makedirs(output_dir, exist_ok=True)
else: output_dir = tempfile.mkdtemp(prefix='corpus_')
with zipfile.ZipFile(zip_path,'r') as zf: zf.extractall(output_dir)
col = CorpusCollection()
for root, dirs, files in os.walk(output_dir):
for fn in files:
fp = os.path.join(root,fn)
try:
if fn.endswith('.md'): col.add(self.load_markdown_file(fp))
elif fn.endswith('.json') and not fn.startswith('.'):
for item in self.load_gpt_export(fp): col.add(item)
elif fn.endswith('.txt'):
with open(fp,'r',encoding='utf-8') as f:
col.add(StructuredCorpusItem(source_id=fp,source_type='text_file',raw_text=f.read()))
except: pass
return col
def load_directory(self, dir_path: str) -> CorpusCollection:
col = CorpusCollection()
for root, dirs, files in os.walk(dir_path):
for fn in files:
fp = os.path.join(root,fn)
try:
if fn.endswith('.md'): col.add(self.load_markdown_file(fp))
elif fn.endswith(('.json','.jsonl')):
for item in self.load_gpt_export(fp): col.add(item)
except Exception as e: print(f" skip {fp}: {e}")
return col
def load_from_cos(self, bucket: str, key: str, secret_id: str, secret_key: str, region: str = 'ap-guangzhou'):
from qcloud_cos import CosConfig, CosS3Client
config = CosConfig(Region=region, SecretId=secret_id, SecretKey=secret_key)
client = CosS3Client(config)
resp = client.get_object(Bucket=bucket, Key=key)
content = resp['Body'].read().decode('utf-8', errors='replace')
if key.endswith('.jsonl'):
for line in content.split('\n'):
if not line.strip(): continue
try:
data = json.loads(line)
if isinstance(data, dict):
item = StructuredCorpusItem(source_id=f"cos_{key}",source_type='cos_jsonl',raw_text=json.dumps(data,ensure_ascii=False))
for msg in data.get('messages',[]):
item.blocks.append({'type':msg.get('role','text'),'content':msg.get('content','')[:500]})
has_template = any('you are a language persona AI' in m.get('content','') for m in data.get('messages',[]))
item.quality_score = 0.3 if has_template else 0.7
self.collection.add(item)
except: pass
def _score_quality(self, item: StructuredCorpusItem) -> float:
score = 0.5
if item.hldp_path: score += 0.2
if item.cognitive_jumps: score += 0.15
if item.causal_chains: score += 0.1
if item.persona: score += 0.05
tl = len(item.raw_text)
if tl < 50: score -= 0.2
elif tl > 50000: score -= 0.1
return min(score, 1.0)
def get_collection(self) -> CorpusCollection:
return self.collection