115 lines
4.5 KiB
Python
115 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
IX Forge — Community Fine-tuning Platform
|
|
Part of the Inference-X Ecosystem
|
|
# SALKA ELMADANI | inference-x.com | BSL-1.1
|
|
Copyright (C) 2024-2026 Salka Elmadani. BSL-1.1.
|
|
https://git.inference-x.com/inference-x-community/ix-forge
|
|
|
|
Collective intelligence. Community-owned models.
|
|
Contribute training data. Improve models. Share results.
|
|
"""
|
|
from fastapi import FastAPI, Request, UploadFile, File
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from fastapi.responses import JSONResponse
|
|
import sqlite3, json, time, os, hashlib
|
|
|
|
app = FastAPI(title="IX Forge", version="1.0.0")
|
|
app.add_middleware(CORSMiddleware, allow_origins=["*"],
|
|
allow_methods=["*"], allow_headers=["*"])
|
|
|
|
DB = os.environ.get("FORGE_DB", "./forge.db")
|
|
|
|
def db():
|
|
conn = sqlite3.connect(DB); conn.row_factory = sqlite3.Row; return conn
|
|
|
|
def init_db():
|
|
with db() as c:
|
|
c.execute("""CREATE TABLE IF NOT EXISTS contributions (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
type TEXT DEFAULT 'qa',
|
|
language TEXT DEFAULT 'en',
|
|
domain TEXT DEFAULT 'general',
|
|
data TEXT,
|
|
pairs_count INTEGER DEFAULT 0,
|
|
contributor TEXT DEFAULT 'anonymous',
|
|
created_at INTEGER,
|
|
approved INTEGER DEFAULT 0
|
|
)""")
|
|
c.execute("""CREATE TABLE IF NOT EXISTS adapters (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT, base_model TEXT, description TEXT,
|
|
download_url TEXT, score REAL DEFAULT 0,
|
|
contributor TEXT DEFAULT 'anonymous',
|
|
created_at INTEGER
|
|
)""")
|
|
c.commit()
|
|
|
|
init_db()
|
|
|
|
@app.post("/contribute")
|
|
async def contribute(request: Request):
|
|
"""Submit training data (Q&A pairs, documents)."""
|
|
data = await request.json()
|
|
pairs = data.get("pairs", [])
|
|
if not pairs:
|
|
return JSONResponse({"error":"No pairs provided"}, status_code=400)
|
|
with db() as c:
|
|
c.execute("""INSERT INTO contributions
|
|
(type,language,domain,data,pairs_count,contributor,created_at)
|
|
VALUES (?,?,?,?,?,?,?)""",
|
|
(data.get("type","qa"), data.get("language","en"),
|
|
data.get("domain","general"), json.dumps(pairs), len(pairs),
|
|
data.get("contributor","anonymous"), int(time.time())))
|
|
c.commit()
|
|
return {"status":"ok","pairs_accepted":len(pairs),
|
|
"message":"Thank you for contributing to the community."}
|
|
|
|
@app.get("/datasets")
|
|
async def datasets(domain: str = None, language: str = None):
|
|
with db() as c:
|
|
q = "SELECT id,type,language,domain,pairs_count,contributor,created_at FROM contributions WHERE approved=1"
|
|
params = []
|
|
if domain: q += " AND domain=?"; params.append(domain)
|
|
if language: q += " AND language=?"; params.append(language)
|
|
rows = c.execute(q, params).fetchall()
|
|
return {"datasets":[dict(r) for r in rows],"total":len(rows)}
|
|
|
|
@app.post("/submit-adapter")
|
|
async def submit_adapter(request: Request):
|
|
"""Submit a trained LoRA/QLoRA adapter."""
|
|
data = await request.json()
|
|
with db() as c:
|
|
c.execute("""INSERT INTO adapters (name,base_model,description,download_url,contributor,created_at)
|
|
VALUES (?,?,?,?,?,?)""",
|
|
(data.get("name",""), data.get("base_model",""),
|
|
data.get("description",""), data.get("download_url",""),
|
|
data.get("contributor","anonymous"), int(time.time())))
|
|
c.commit()
|
|
return {"status":"ok","message":"Adapter submitted for community review."}
|
|
|
|
@app.get("/adapters")
|
|
async def adapters():
|
|
with db() as c:
|
|
rows = c.execute("SELECT id,name,base_model,description,score,contributor FROM adapters ORDER BY score DESC").fetchall()
|
|
return {"adapters":[dict(r) for r in rows]}
|
|
|
|
@app.get("/leaderboard")
|
|
async def leaderboard():
|
|
with db() as c:
|
|
rows = c.execute("""SELECT contributor, COUNT(*) as contributions,
|
|
SUM(pairs_count) as total_pairs
|
|
FROM contributions WHERE approved=1
|
|
GROUP BY contributor ORDER BY total_pairs DESC LIMIT 20""").fetchall()
|
|
return {"leaderboard":[dict(r) for r in rows]}
|
|
|
|
@app.get("/health")
|
|
async def health():
|
|
return {"status":"ok","service":"IX Forge","author":"Salka Elmadani"}
|
|
|
|
if __name__ == "__main__":
|
|
import uvicorn
|
|
print("IX Forge — Community Fine-tuning Platform")
|
|
print("Collective intelligence. Community-owned models.")
|
|
uvicorn.run(app, host="0.0.0.0", port=int(os.environ.get("PORT","7937")))
|