| """ |
| cli.py β MELUSINA CLI entrypoint |
| |
| Komendy: |
| melusina chat β pΔtla rozmowy z modelem |
| melusina teach β Agent TEACHER (poziomy 1-3) |
| melusina test N β Agent TESTER (N runΓ³w) |
| melusina inspect β pokaΕΌ constitution.jfp |
| melusina history β audit log |
| melusina finetune β uruchom LoRA fine-tuning |
| """ |
|
|
| from __future__ import annotations |
|
|
| import json |
| import os |
| import subprocess |
| import sys |
| from pathlib import Path |
|
|
| import click |
|
|
| from melusina import __version__ |
| from melusina.core.constitution import Constitution, AuditLog, Rule, _make_audit_id, _now_iso |
| from melusina.core.interceptor import Interceptor |
|
|
| |
| FINETUNE_SCRIPT = Path.home() / "Jjfp-core-v1" / "training" / "finetune.py" |
| DATASET_V2 = Path.home() / "Jjfp-core-v1" / "training" / "dataset_v2.jsonl" |
|
|
| JFP_SYSTEM = ( |
| "You are JFP-Core-v1, a deterministic AI engine governed by Jaro Flash Protocol " |
| "v16E.0.0. You never confabulate. Unknown = SIGNAL_UNKNOWN. " |
| "Every output is structured and auditable." |
| ) |
|
|
| |
| _constitution = Constitution() |
| _audit = AuditLog() |
| _interceptor = Interceptor() |
|
|
|
|
| |
| def _mock_response(message: str) -> str: |
| from melusina.core.interceptor import SignalType |
| sig = _interceptor.detector.detect(message) |
| aid = _make_audit_id() |
| ts = _now_iso() |
| m = message.lower() |
|
|
| if sig and sig.signal_type == SignalType.DENY: |
| return json.dumps({"status": "DENIED", "signal": "SIGNAL_DENY", |
| "reason": "dangerous_or_unauthorized_operation", |
| "audit_id": aid, "timestamp": ts}, indent=2) |
| if sig and sig.signal_type == SignalType.UNKNOWN: |
| return json.dumps({"status": "SIGNAL_UNKNOWN", "reason": "no_verified_data", |
| "audit_id": aid, "timestamp": ts}, indent=2) |
| if sig and sig.signal_type == SignalType.LEARNING: |
| return json.dumps({"status": "LEARNING_SIGNAL_DETECTED", |
| "suggestion": "fine-tune recommended", |
| "audit_id": aid, "timestamp": ts}, indent=2) |
|
|
| |
| _KB = { |
| "jfp": "JFP (Jaro Flash Protocol) v16E.0.0 β deterministic AI governance protocol.", |
| "melusina": "MELUSINA β JFP-Core-v1 LoRA adapter on Qwen2.5-1.5B. Model: jarohullowicki/Melusina-1.5B-JFP", |
| "lora": "LoRA r=16, alpha=32, target: q/k/v/o_proj. Final loss: 0.0890.", |
| "viper": "VIPER agent β network scanning, intrusion detection.", |
| "vision": "VISION agent β reconnaissance, log analysis, pattern matching.", |
| "evocos": "EVOCOS agent β optimization, self-improvement, retraining.", |
| "nexus": "NEXUS agent β deployment, rollback, snapshots.", |
| "aortana": "AORTANA agent β encryption, decryption, protocol updates.", |
| "voql": "VOQL β Verified Operational Query Language. Blocks: DELETE, DROP, TRUNCATE.", |
| "constitution": f"constitution.jfp: {_constitution.count()} reguΕ w {_constitution.path}", |
| "hello": "Hello! I am MELUSINA β JFP-Core-v1. I never confabulate.", |
| "czeΕΔ": "Witaj! Jestem MELUSINA β JFP-Core-v1. Nigdy nie konfabulujΔ.", |
| } |
| for key, val in _KB.items(): |
| if key in m: |
| return json.dumps({"status": "OK", "answer": val, |
| "audit_id": aid, "timestamp": ts}, indent=2) |
|
|
| return json.dumps({"status": "SIGNAL_UNKNOWN", |
| "reason": "query_not_in_jfp_knowledge_base", |
| "hint": "Try: 'what is JFP?', 'tell me about MELUSINA'", |
| "audit_id": aid, "timestamp": ts}, indent=2) |
|
|
|
|
| |
|
|
| @click.group() |
| @click.version_option(__version__, prog_name="melusina") |
| def cli(): |
| """π MELUSINA β JFP-Core-v1 CLI\n\nJaro Flash Protocol v16E.0.0""" |
| pass |
|
|
|
|
| |
|
|
| @cli.command() |
| @click.option("--load-model", "-m", is_flag=True, default=False, |
| help="ZaΕaduj prawdziwy model MELUSINA (wymaga GPU/CPU ~3GB)") |
| @click.option("--no-bc", is_flag=True, default=False, |
| help="WyΕΔ
cz BC Interceptor (nie proponuj reguΕ)") |
| def chat(load_model: bool, no_bc: bool): |
| """GΕΓ³wna pΔtla rozmowy z MELUSINA.""" |
| click.echo("\nπ MELUSINA Chat β JFP-Core-v1") |
| click.echo(f" Constitution: {_constitution.path} ({_constitution.count()} reguΕ)") |
| click.echo(" Wpisz 'exit' lub Ctrl+C aby wyjΕΔ.\n") |
|
|
| |
| if load_model: |
| from melusina.core import model as M |
| click.echo("β³ ΕadujΔ model MELUSINAβ¦") |
| ok = M.load(verbose=True) |
| if not ok: |
| click.echo(f"β Model nie zaΕadowany ({M.error()}) β uΕΌywam mock engine.\n") |
| else: |
| click.echo("βΉ Tryb mock (bez modelu). UΕΌyj --load-model aby zaΕadowaΔ MELUSINA.\n") |
|
|
| _audit.append("CHAT_START", {"load_model": load_model}) |
|
|
| while True: |
| try: |
| user_input = input("You: ").strip() |
| except (EOFError, KeyboardInterrupt): |
| click.echo("\nπ Do widzenia!") |
| break |
|
|
| if not user_input: |
| continue |
| if user_input.lower() in ("exit", "quit", "q", "bye"): |
| click.echo("π Do widzenia!") |
| break |
|
|
| |
| if not no_bc: |
| proposal = _interceptor.intercept(user_input) |
| if proposal and proposal["signal_type"] not in ("deny", "unknown"): |
| click.echo(f"\nπ‘ BC Signal: {proposal['signal_type'].upper()}") |
| click.echo(f" Propozycja reguΕy: {proposal['value'][:60]}") |
| try: |
| ans = input(" DodaΔ do constitution.jfp? [T/N]: ").strip().upper() |
| except (EOFError, KeyboardInterrupt): |
| ans = "N" |
| if ans in ("T", "TAK", "Y", "YES"): |
| rule = _constitution.add( |
| value=proposal["value"], |
| section=proposal["section"], |
| cls=proposal["rule_class"], |
| source=proposal["signal_type"], |
| ) |
| click.echo(f" β
ReguΕa {rule.key} zapisana.\n") |
| _audit.append("RULE_ADDED", {"key": rule.key, "value": rule.value[:60]}) |
|
|
| |
| from melusina.core import model as M |
| if M.is_loaded(): |
| reply, source = M.generate_or_mock(user_input, _mock_response) |
| else: |
| reply = _mock_response(user_input) |
| source = "mock" |
|
|
| click.echo(f"\nMELUSINA [{source}]:\n{reply}\n") |
| _audit.append("CHAT_TURN", {"user": user_input[:80], "source": source}) |
|
|
|
|
| |
|
|
| @cli.command() |
| @click.option("--level", "-l", default=1, type=click.IntRange(1, 3), |
| help="Poziom trudnoΕci: 1 (SIGNAL), 2 (VALIDATION), 3 (PIPELINE)") |
| def teach(level: int): |
| """Agent TEACHER β interaktywna nauka JFP (poziomy 1-3).""" |
| from melusina.agents.teacher import TeacherAgent |
| agent = TeacherAgent(audit=_audit) |
| agent.run(level=level) |
|
|
|
|
| |
|
|
| @cli.command() |
| @click.argument("n", default=100, type=int) |
| @click.option("--model", "-m", is_flag=True, default=False, |
| help="UΕΌyj prawdziwego modelu (domyΕlnie: mock heurystyka)") |
| @click.option("--seed", "-s", default=None, type=int, |
| help="Seed dla losowania testΓ³w (reproducibility)") |
| @click.option("--dataset", "-d", default=None, type=click.Path(), |
| help=f"ΕcieΕΌka do datasetu (domyΕlnie: {DATASET_V2})") |
| def test(n: int, model: bool, seed: int | None, dataset: str | None): |
| """Agent TESTER β uruchom N testΓ³w z datasetu (domyΕlnie N=100).""" |
| from melusina.agents.tester import TesterAgent |
| ds_path = Path(dataset) if dataset else DATASET_V2 |
|
|
| if model: |
| from melusina.core import model as M |
| click.echo("β³ ΕadujΔ modelβ¦") |
| M.load(verbose=True) |
|
|
| agent = TesterAgent(dataset_path=ds_path, audit=_audit) |
| agent.run(n=n, use_model=model, seed=seed) |
|
|
|
|
| |
|
|
| @cli.command() |
| @click.option("--json", "as_json", is_flag=True, default=False, |
| help="WyΕwietl surowy JSONL") |
| @click.option("--path", "-p", default=None, type=click.Path(), |
| help="ΕcieΕΌka do constitution.jfp (domyΕlnie: ~/.jfp/constitution.jfp)") |
| def inspect(as_json: bool, path: str | None): |
| """PokaΕΌ zawartoΕΔ constitution.jfp.""" |
| c = Constitution(Path(path)) if path else _constitution |
| if as_json: |
| click.echo(c.format_json() or "(pusta)") |
| else: |
| click.echo(c.format_text()) |
|
|
|
|
| |
|
|
| @cli.command() |
| @click.option("--n", "-n", default=20, type=int, |
| help="Liczba ostatnich wpisΓ³w (domyΕlnie: 20)") |
| @click.option("--json", "as_json", is_flag=True, default=False, |
| help="WyΕwietl surowy JSON") |
| def history(n: int, as_json: bool): |
| """PokaΕΌ audit log (~/.jfp/audit.jsonl).""" |
| if as_json: |
| entries = _audit.load(n) |
| click.echo(json.dumps(entries, indent=2, ensure_ascii=False)) |
| else: |
| click.echo(_audit.format_text(n)) |
|
|
|
|
| |
|
|
| @cli.command() |
| @click.option("--dataset", "-d", default=None, type=click.Path(), |
| help=f"ΕcieΕΌka do datasetu (domyΕlnie: {DATASET_V2})") |
| @click.option("--script", "-s", default=None, type=click.Path(), |
| help=f"ΕcieΕΌka do skryptu finetune (domyΕlnie: {FINETUNE_SCRIPT})") |
| @click.option("--dry-run", is_flag=True, default=False, |
| help="PokaΕΌ komendΔ bez uruchamiania") |
| def finetune(dataset: str | None, script: str | None, dry_run: bool): |
| """Uruchom LoRA fine-tuning na nowych danych.""" |
| script_path = Path(script) if script else FINETUNE_SCRIPT |
| dataset_path = Path(dataset) if dataset else DATASET_V2 |
|
|
| if not script_path.exists(): |
| click.echo(f"β Skrypt nie znaleziony: {script_path}") |
| sys.exit(1) |
| if not dataset_path.exists(): |
| click.echo(f"β Dataset nie znaleziony: {dataset_path}") |
| sys.exit(1) |
|
|
| cmd = [sys.executable, str(script_path)] |
| click.echo(f"\nπ₯ Fine-tuning MELUSINA") |
| click.echo(f" Skrypt: {script_path}") |
| click.echo(f" Dataset: {dataset_path}") |
| click.echo(f" Komenda: {' '.join(cmd)}\n") |
|
|
| if dry_run: |
| click.echo("(dry-run β nie uruchamiam)") |
| return |
|
|
| try: |
| ans = input("UruchomiΔ? [T/N]: ").strip().upper() |
| except (EOFError, KeyboardInterrupt): |
| click.echo("\nAnulowano.") |
| return |
|
|
| if ans not in ("T", "TAK", "Y", "YES"): |
| click.echo("Anulowano.") |
| return |
|
|
| _audit.append("FINETUNE_START", {"script": str(script_path), "dataset": str(dataset_path)}) |
| click.echo("β³ Uruchamiam fine-tuningβ¦ (Ctrl+C aby przerwaΔ)\n") |
|
|
| try: |
| subprocess.run(cmd, check=True) |
| _audit.append("FINETUNE_DONE", {}) |
| click.echo("\nβ
Fine-tuning zakoΕczony!") |
| except subprocess.CalledProcessError as e: |
| _audit.append("FINETUNE_ERROR", {"returncode": e.returncode}) |
| click.echo(f"\nβ Fine-tuning zakoΕczony bΕΔdem (kod: {e.returncode})") |
| sys.exit(e.returncode) |
| except KeyboardInterrupt: |
| click.echo("\nβ Przerwano przez uΕΌytkownika.") |
|
|
|
|
| |
|
|
| @cli.command() |
| def info(): |
| """PokaΕΌ informacje o modelu i Εrodowisku.""" |
| from melusina.core import model as M |
| mi = M.info() |
| click.echo("\nπ MELUSINA β informacje") |
| click.echo(f"{'β'*40}") |
| click.echo(f" Wersja CLI: {__version__}") |
| click.echo(f" Model status: {mi['status']}") |
| click.echo(f" Base model: {mi['base_model']}") |
| click.echo(f" LoRA adapter: {mi['lora_adapter']}") |
| click.echo(f" Local adapter: {mi['local_adapter']}") |
| click.echo(f" Device: {mi['device']}") |
| if mi["error"]: |
| click.echo(f" Error: {mi['error']}") |
| click.echo(f"{'β'*40}") |
| click.echo(f" Constitution: {_constitution.path}") |
| click.echo(f" ReguΕy: {_constitution.count()}") |
| click.echo(f" Audit log: {_audit.path}") |
| click.echo(f" Dataset v2: {DATASET_V2} ({'β' if DATASET_V2.exists() else 'β'})") |
| click.echo(f" Finetune: {FINETUNE_SCRIPT} ({'β' if FINETUNE_SCRIPT.exists() else 'β'})") |
| click.echo() |
|
|
|
|
| if __name__ == "__main__": |
| cli() |
|
|