File size: 7,987 Bytes
e93bfbd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
"""
Update script — pulls the newest models, UI files, and dependencies.

Usage:
  python update.py              # interactive menu
  python update.py --all        # update everything
  python update.py --models     # download latest ONNX models
  python update.py --code       # git pull latest source
  python update.py --deps       # upgrade pip packages

The default model source is a Hugging Face repo.
Configure with: python update.py --set-url <HF_REPO_ID>
"""
import argparse
import json
import os
import subprocess
import sys
from pathlib import Path

CONFIG_FILE = "update_config.json"
DEFAULT_HF_REPO = "your-org/medicalai-models"
DEFAULT_DIR = Path("models") / "default"
ONNX_FULL_DIR = Path("checkpoints") / "onnx_full"


def load_config():
    if os.path.exists(CONFIG_FILE):
        with open(CONFIG_FILE) as f:
            return json.load(f)
    return {"hf_repo": DEFAULT_HF_REPO, "auto_update": True}


def save_config(cfg):
    with open(CONFIG_FILE, "w") as f:
        json.dump(cfg, f, indent=2)


def _ensure_dir(path):
    os.makedirs(path, exist_ok=True)


def _file_size(path):
    return os.path.getsize(path) / 1024 / 1024


def update_models_from_hf():
    """Download latest ONNX models from Hugging Face."""
    cfg = load_config()
    repo = cfg["hf_repo"]

    from rich.console import Console
    console = Console()

    if repo == DEFAULT_HF_REPO and "your-org" in repo:
        console.print("[yellow]HF_REPO not set. Skipping model download.[/yellow]")
        console.print("[yellow]Set your model repo: python update.py --set-url your-org/your-repo[/yellow]")
        console.print("[yellow]Or train locally: python training.py --mode prepare-data && python training.py --mode train[/yellow]")
        return

    try:
        import requests
    except ImportError:
        console.print("[red]'requests' required. Install: pip install requests[/red]")
        return

    _ensure_dir(ONNX_FULL_DIR)
    _ensure_dir(DEFAULT_DIR)

    files_to_download = [
        ("fusion_full.onnx", ONNX_FULL_DIR / "fusion_full.onnx"),
        ("labels.json", ONNX_FULL_DIR / "labels.json"),
        ("fusion_classifier.onnx", DEFAULT_DIR / "fusion_classifier.onnx"),
    ]

    base_url = f"https://huggingface.co/{repo}/resolve/main"

    for fname, dest in files_to_download:
        url = f"{base_url}/{fname}"
        console.print(f"[cyan]Downloading {fname}...[/cyan]")
        try:
            resp = requests.get(url, stream=True, timeout=30)
            resp.raise_for_status()
            with open(dest, "wb") as f:
                for chunk in resp.iter_content(8192):
                    f.write(chunk)
            console.print(f"  [green]Saved {dest} ({_file_size(dest):.1f} MB)[/green]")
        except Exception as e:
            console.print(f"  [red]Failed: {e}[/red]")

    console.print("[green]Model update complete.[/green]")


def update_code():
    """Pull latest source code from git."""
    from rich.console import Console
    console = Console()

    if not os.path.exists(".git"):
        console.print("[yellow]Not a git repository. Skipping code update.[/yellow]")
        return

    try:
        result = subprocess.run(
            ["git", "pull", "--ff-only"],
            capture_output=True, text=True, timeout=60,
        )
        if result.returncode == 0:
            console.print(f"[green]{result.stdout}[/green]")
        else:
            console.print(f"[yellow]{result.stderr}[/yellow]")
    except Exception as e:
        console.print(f"[red]Git pull failed: {e}[/red]")


def update_deps():
    """Upgrade all pip packages to latest compatible versions."""
    from rich.console import Console
    console = Console()

    req = "requirements.txt"
    if not os.path.exists(req):
        console.print("[yellow]No requirements.txt found.[/yellow]")
        return

    console.print("[cyan]Upgrading dependencies...[/cyan]")
    try:
        subprocess.check_call(
            [sys.executable, "-m", "pip", "install", "--upgrade", "-r", req],
            stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
        )
        console.print("[green]Dependencies upgraded.[/green]")
    except Exception as e:
        console.print(f"[red]Upgrade failed: {e}[/red]")


def update_all():
    from rich.console import Console
    console = Console()

    console.print("[bold cyan]Full Update[/bold cyan]")
    console.print()

    console.print("[cyan]Step 1: Updating code...[/cyan]")
    update_code()

    console.print("[cyan]Step 2: Upgrading dependencies...[/cyan]")
    update_deps()

    console.print("[cyan]Step 3: Downloading latest models...[/cyan]")
    update_models_from_hf()

    console.print()
    console.print("[green]Update complete![/green]")
    console.print("  Run [bold]python quantization.py --mode status[/bold] to verify.")


def set_repo_url(url):
    cfg = load_config()
    cfg["hf_repo"] = url
    save_config(cfg)
    print(f"Hugging Face repo set to: {url}")


def show_status():
    cfg = load_config()
    print(f"Update config: {CONFIG_FILE}")
    print(f"  HF repo:     {cfg['hf_repo']}")
    print(f"  Auto update: {cfg['auto_update']}")
    print()
    print("Default models:")
    for f in ["fusion_classifier.onnx", "labels.json"]:
        p = DEFAULT_DIR / f
        exists = os.path.exists(p)
        size = f"({_file_size(p):.1f} MB)" if exists else ""
        print(f"  {f}: {'yes' if exists else 'no'} {size}")
    print()
    print("Full ONNX pipeline:")
    for f in ["fusion_full.onnx", "labels.json"]:
        p = ONNX_FULL_DIR / f
        exists = os.path.exists(p)
        size = f"({_file_size(p):.1f} MB)" if exists else ""
        print(f"  {f}: {'yes' if exists else 'no'} {size}")
    print()


def main():
    parser = argparse.ArgumentParser(description="Update MedicalAI models, code, and deps")
    parser.add_argument("--models", action="store_true", help="Download latest ONNX models")
    parser.add_argument("--code", action="store_true", help="Git pull latest source")
    parser.add_argument("--deps", action="store_true", help="Upgrade pip packages")
    parser.add_argument("--all", action="store_true", help="Update everything")
    parser.add_argument("--set-url", metavar="HF_REPO", help="Set Hugging Face model repo")
    parser.add_argument("--status", action="store_true", help="Show update status")
    args = parser.parse_args()

    if args.set_url:
        set_repo_url(args.set_url)
        return

    if args.status:
        show_status()
        return

    if args.all:
        update_all()
        return

    if args.models:
        update_models_from_hf()
        return

    if args.code:
        update_code()
        return

    if args.deps:
        update_deps()
        return

    # Interactive mode
    from rich.console import Console
    import questionary

    console = Console()
    console.print("[bold cyan]MedicalAI - Update Manager[/bold cyan]")
    console.print()

    choice = questionary.select(
        "What would you like to update?",
        choices=[
            "Everything (code + deps + models)",
            "Models only (download latest ONNX)",
            "Code only (git pull)",
            "Dependencies only (pip upgrade)",
            "Show update status",
            "Set Hugging Face model repo",
            "Cancel",
        ],
    ).ask()

    if choice == "Everything (code + deps + models)":
        update_all()
    elif choice == "Models only (download latest ONNX)":
        update_models_from_hf()
    elif choice == "Code only (git pull)":
        update_code()
    elif choice == "Dependencies only (pip upgrade)":
        update_deps()
    elif choice == "Show update status":
        show_status()
    elif "Set Hugging Face" in choice:
        repo = questionary.text("Enter Hugging Face repo (user/repo):").ask()
        if repo:
            set_repo_url(repo)
    else:
        console.print("[yellow]Cancelled.[/yellow]")


if __name__ == "__main__":
    main()