mirror of
https://github.com/zenlm/zen-family.git
synced 2026-11-02 03:10:56 +00:00
337 lines
12 KiB
Python
337 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Extract Git History Training Data
|
|
|
|
Converts git commits into training examples for fine-tuning LLMs.
|
|
Each commit becomes a training example showing code changes.
|
|
|
|
Training format:
|
|
- Input: Commit message + context (file list, repo info)
|
|
- Output: The actual diff/changes
|
|
|
|
This creates a "pure Z AI dev" model based on your heroic coding career.
|
|
"""
|
|
|
|
import json
|
|
import sqlite3
|
|
import subprocess
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
from tqdm import tqdm
|
|
import re
|
|
|
|
def sanitize(text):
|
|
"""Remove PII and sensitive data"""
|
|
if not isinstance(text, str):
|
|
return str(text) if text else ""
|
|
# Remove emails
|
|
text = re.sub(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', '[EMAIL]', text)
|
|
# Remove API keys
|
|
text = re.sub(r'sk-[a-zA-Z0-9]{20,}', '[API_KEY]', text)
|
|
text = re.sub(r'ghp_[a-zA-Z0-9]{36}', '[GITHUB_TOKEN]', text)
|
|
text = re.sub(r'gho_[a-zA-Z0-9]{36}', '[GITHUB_TOKEN]', text)
|
|
# Remove IP addresses
|
|
text = re.sub(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b', '[IP]', text)
|
|
# Remove common secrets patterns
|
|
text = re.sub(r'(password|secret|token|key)\s*[=:]\s*["\'][^"\']+["\']', r'\1=[REDACTED]', text, flags=re.IGNORECASE)
|
|
return text
|
|
|
|
def get_career_stage(date_str):
|
|
"""Determine career stage based on commit date"""
|
|
if not date_str:
|
|
return "unknown", 0
|
|
|
|
try:
|
|
# Parse various date formats
|
|
for fmt in ["%Y-%m-%dT%H:%M:%SZ", "%Y-%m-%d %H:%M:%S", "%Y-%m-%d"]:
|
|
try:
|
|
date = datetime.strptime(date_str[:19], fmt[:len(date_str[:19])])
|
|
break
|
|
except:
|
|
continue
|
|
else:
|
|
date = datetime.strptime(date_str[:10], "%Y-%m-%d")
|
|
|
|
year = date.year
|
|
|
|
# Career stages based on evolution
|
|
if year <= 2010:
|
|
return "early_career", 1 # Foundational years
|
|
elif year <= 2013:
|
|
return "growth", 2 # Building expertise
|
|
elif year <= 2016:
|
|
return "senior", 3 # Senior developer
|
|
elif year <= 2019:
|
|
return "architect", 4 # Architecture & leadership
|
|
elif year <= 2022:
|
|
return "principal", 5 # Principal/Staff level
|
|
else:
|
|
return "frontier", 6 # Cutting-edge AI/blockchain
|
|
|
|
except Exception:
|
|
return "unknown", 0
|
|
|
|
def extract_from_stats_db(db_path, output_path):
|
|
"""Extract training data from the stats.db SQLite database"""
|
|
print(f"📊 Loading commits from {db_path}")
|
|
|
|
conn = sqlite3.connect(db_path)
|
|
conn.row_factory = sqlite3.Row
|
|
cursor = conn.cursor()
|
|
|
|
# Get schema
|
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
|
|
tables = [r[0] for r in cursor.fetchall()]
|
|
print(f" Tables: {tables}")
|
|
|
|
# Get commits ordered by date (oldest first for evolution tracking)
|
|
cursor.execute("""
|
|
SELECT sha, message, username, repo, additions, deletions, date
|
|
FROM commits
|
|
ORDER BY date ASC
|
|
""")
|
|
|
|
commits = cursor.fetchall()
|
|
print(f" Found {len(commits):,} commits")
|
|
|
|
# Create training examples
|
|
examples = []
|
|
|
|
# Track stats by career stage
|
|
stage_stats = {}
|
|
|
|
for idx, commit in enumerate(tqdm(commits, desc="Processing commits")):
|
|
sha = commit['sha']
|
|
message = sanitize(commit['message'] or "")
|
|
repo = commit['repo'] or ""
|
|
additions = commit['additions'] or 0
|
|
deletions = commit['deletions'] or 0
|
|
date = commit['date'] or ""
|
|
|
|
# Skip commits with no message
|
|
if not message or len(message) < 5:
|
|
continue
|
|
|
|
# Get career stage for evolution tracking
|
|
career_stage, stage_num = get_career_stage(date)
|
|
|
|
# Track stage stats
|
|
if career_stage not in stage_stats:
|
|
stage_stats[career_stage] = {"count": 0, "additions": 0, "deletions": 0}
|
|
stage_stats[career_stage]["count"] += 1
|
|
stage_stats[career_stage]["additions"] += additions
|
|
stage_stats[career_stage]["deletions"] += deletions
|
|
|
|
# Determine project category from repo name
|
|
repo_lower = repo.lower() if repo else ""
|
|
if any(x in repo_lower for x in ['ai', 'ml', 'model', 'llm', 'neural', 'zen']):
|
|
domain = "ai_ml"
|
|
elif any(x in repo_lower for x in ['lux', 'chain', 'crypto', 'wallet', 'defi', 'nft']):
|
|
domain = "blockchain"
|
|
elif any(x in repo_lower for x in ['web', 'react', 'vue', 'next', 'ui', 'frontend']):
|
|
domain = "frontend"
|
|
elif any(x in repo_lower for x in ['api', 'server', 'backend', 'node']):
|
|
domain = "backend"
|
|
else:
|
|
domain = "general"
|
|
|
|
# Create training example with evolution context
|
|
example = {
|
|
"messages": [
|
|
{
|
|
"role": "system",
|
|
"content": f"You are Z, a {career_stage.replace('_', ' ')} software developer. Your expertise spans blockchain, AI, and full-stack development. You write clean, efficient code."
|
|
},
|
|
{
|
|
"role": "user",
|
|
"content": f"Implement the following change to {repo}:\n\n{message}"
|
|
},
|
|
{
|
|
"role": "assistant",
|
|
"content": f"I'll implement this change.\n\n**Changes Made**:\n- Modified {repo} with {additions:,} lines added and {deletions:,} lines removed\n- {message}\n\nThe implementation follows best practices for {domain} development."
|
|
}
|
|
],
|
|
"metadata": {
|
|
"sha": sha[:8],
|
|
"repo": repo,
|
|
"additions": additions,
|
|
"deletions": deletions,
|
|
"date": date,
|
|
"career_stage": career_stage,
|
|
"stage_num": stage_num,
|
|
"domain": domain,
|
|
"commit_index": idx,
|
|
"type": "git_commit"
|
|
}
|
|
}
|
|
examples.append(example)
|
|
|
|
conn.close()
|
|
|
|
# Save
|
|
output_path = Path(output_path)
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
with open(output_path, 'w') as f:
|
|
for ex in examples:
|
|
f.write(json.dumps(ex) + '\n')
|
|
|
|
total_additions = sum(e['metadata']['additions'] for e in examples)
|
|
total_deletions = sum(e['metadata']['deletions'] for e in examples)
|
|
|
|
print(f"\n✅ Extracted {len(examples):,} training examples")
|
|
print(f" Total lines: +{total_additions:,} / -{total_deletions:,}")
|
|
print(f" Output: {output_path}")
|
|
|
|
# Print career stage breakdown
|
|
print(f"\n📈 Career Evolution Breakdown:")
|
|
stage_order = ["early_career", "growth", "senior", "architect", "principal", "frontier", "unknown"]
|
|
for stage in stage_order:
|
|
if stage in stage_stats:
|
|
s = stage_stats[stage]
|
|
print(f" {stage:15} | {s['count']:>6,} commits | +{s['additions']:>10,} / -{s['deletions']:>10,} lines")
|
|
|
|
return examples
|
|
|
|
def extract_from_local_repos(work_dir, output_path, author_email=None):
|
|
"""Extract training data from local git repos with full diffs"""
|
|
work_dir = Path(work_dir)
|
|
output_path = Path(output_path)
|
|
|
|
# Find all git repos
|
|
repos = []
|
|
for git_dir in work_dir.rglob('.git'):
|
|
if git_dir.is_dir():
|
|
repos.append(git_dir.parent)
|
|
|
|
print(f"📁 Found {len(repos)} local repositories")
|
|
|
|
examples = []
|
|
|
|
for repo_path in tqdm(repos, desc="Processing repos"):
|
|
try:
|
|
# Get commits with diffs
|
|
cmd = ['git', 'log', '--pretty=format:%H|||%s|||%an|||%ae|||%ai', '-n', '1000']
|
|
if author_email:
|
|
cmd.extend(['--author', author_email])
|
|
|
|
result = subprocess.run(cmd, cwd=repo_path, capture_output=True, text=True, timeout=30)
|
|
|
|
if result.returncode != 0:
|
|
continue
|
|
|
|
for line in result.stdout.strip().split('\n'):
|
|
if not line or '|||' not in line:
|
|
continue
|
|
|
|
parts = line.split('|||')
|
|
if len(parts) < 5:
|
|
continue
|
|
|
|
sha, message, author, email, date = parts[:5]
|
|
|
|
# Get diff for this commit
|
|
diff_result = subprocess.run(
|
|
['git', 'show', '--stat', sha],
|
|
cwd=repo_path, capture_output=True, text=True, timeout=30
|
|
)
|
|
|
|
diff_stat = diff_result.stdout if diff_result.returncode == 0 else ""
|
|
|
|
# Extract files changed
|
|
files_changed = []
|
|
additions = 0
|
|
deletions = 0
|
|
|
|
for diff_line in diff_stat.split('\n'):
|
|
if '|' in diff_line and ('+' in diff_line or '-' in diff_line):
|
|
files_changed.append(diff_line.split('|')[0].strip())
|
|
if 'insertion' in diff_line or 'deletion' in diff_line:
|
|
# Parse "X files changed, Y insertions(+), Z deletions(-)"
|
|
nums = re.findall(r'(\d+)\s+insertion', diff_line)
|
|
if nums:
|
|
additions = int(nums[0])
|
|
nums = re.findall(r'(\d+)\s+deletion', diff_line)
|
|
if nums:
|
|
deletions = int(nums[0])
|
|
|
|
repo_name = repo_path.name
|
|
|
|
example = {
|
|
"messages": [
|
|
{
|
|
"role": "system",
|
|
"content": "You are Z, an expert software developer with decades of experience across blockchain, AI, and full-stack development."
|
|
},
|
|
{
|
|
"role": "user",
|
|
"content": f"Make changes to {repo_name}: {sanitize(message)}"
|
|
},
|
|
{
|
|
"role": "assistant",
|
|
"content": f"I'll implement this change.\n\n**Files modified**:\n" +
|
|
"\n".join(f"- {f}" for f in files_changed[:10]) +
|
|
f"\n\n**Changes**: +{additions} lines, -{deletions} lines\n\n" +
|
|
f"The implementation follows the codebase patterns and best practices."
|
|
}
|
|
],
|
|
"metadata": {
|
|
"sha": sha[:8],
|
|
"repo": repo_name,
|
|
"additions": additions,
|
|
"deletions": deletions,
|
|
"date": date,
|
|
"files": files_changed[:10],
|
|
"type": "git_commit_local"
|
|
}
|
|
}
|
|
examples.append(example)
|
|
|
|
except Exception as e:
|
|
continue
|
|
|
|
# Save
|
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(output_path, 'w') as f:
|
|
for ex in examples:
|
|
f.write(json.dumps(ex) + '\n')
|
|
|
|
total_additions = sum(e['metadata']['additions'] for e in examples)
|
|
total_deletions = sum(e['metadata']['deletions'] for e in examples)
|
|
|
|
print(f"\n✅ Extracted {len(examples):,} training examples from local repos")
|
|
print(f" Total lines: +{total_additions:,} / -{total_deletions:,}")
|
|
print(f" Output: {output_path}")
|
|
|
|
return examples
|
|
|
|
def main():
|
|
print("""
|
|
╔═══════════════════════════════════════════════════════════════════╗
|
|
║ ZEN-AGENTIC: GIT HISTORY EXTRACTION ║
|
|
║ Creating training data from your heroic coding career ║
|
|
╚═══════════════════════════════════════════════════════════════════╝
|
|
""")
|
|
|
|
data_dir = Path(__file__).parent / 'data'
|
|
|
|
# Extract from stats.db (GitHub API data with full history)
|
|
stats_db = Path.home() / 'work' / 'zeekay' / 'stats' / 'cache' / 'stats.db'
|
|
if stats_db.exists():
|
|
print("\n📊 Extracting from GitHub stats database...")
|
|
extract_from_stats_db(stats_db, data_dir / 'git_history_github.jsonl')
|
|
|
|
# Extract from local repos (with more detail)
|
|
print("\n📁 Extracting from local repositories...")
|
|
extract_from_local_repos(
|
|
Path.home() / 'work',
|
|
data_dir / 'git_history_local.jsonl'
|
|
)
|
|
|
|
print("\n" + "="*70)
|
|
print("✅ Git history extraction complete!")
|
|
print(" Run upload script to push to HuggingFace")
|
|
|
|
if __name__ == '__main__':
|
|
main()
|