patholens 2.0.2__tar.gz → 2.0.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. patholens-2.0.4/PKG-INFO +88 -0
  2. patholens-2.0.4/README.md +67 -0
  3. {patholens-2.0.2 → patholens-2.0.4}/patholens-backend/app/main.py +32 -1
  4. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/dashboard/page.tsx +116 -1
  5. {patholens-2.0.2 → patholens-2.0.4}/patholens_cli/__init__.py +1 -1
  6. {patholens-2.0.2 → patholens-2.0.4}/patholens_cli/main.py +6 -15
  7. {patholens-2.0.2 → patholens-2.0.4}/pyproject.toml +2 -2
  8. patholens-2.0.2/PKG-INFO +0 -40
  9. patholens-2.0.2/README.md +0 -19
  10. {patholens-2.0.2 → patholens-2.0.4}/.gitignore +0 -0
  11. {patholens-2.0.2 → patholens-2.0.4}/patholens-backend/app/database.py +0 -0
  12. {patholens-2.0.2 → patholens-2.0.4}/patholens-backend/app/models.py +0 -0
  13. {patholens-2.0.2 → patholens-2.0.4}/patholens-backend/requirements.txt +0 -0
  14. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/.gitignore +0 -0
  15. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/OncoGemma-logo-no-bg_low.png +0 -0
  16. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/OncoGemma-no-bg-high_gemini.png +0 -0
  17. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/README.md +0 -0
  18. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/favicon.ico +0 -0
  19. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/globals.css +0 -0
  20. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/layout.tsx +0 -0
  21. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/lib/auth.ts +0 -0
  22. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/lib/config.ts +0 -0
  23. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/login/page.tsx +0 -0
  24. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/page.tsx +0 -0
  25. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/app/viewer/page.tsx +0 -0
  26. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/components/AIResults.tsx +0 -0
  27. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/components/ClinicalChat.tsx +0 -0
  28. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/components/OncoLLM.tsx +0 -0
  29. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/components/WSIViewer.tsx +0 -0
  30. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/eslint.config.mjs +0 -0
  31. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/next-env.d.ts +0 -0
  32. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/next.config.ts +0 -0
  33. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/package-lock.json +0 -0
  34. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/package.json +0 -0
  35. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/postcss.config.mjs +0 -0
  36. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/file.svg +0 -0
  37. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/globe.svg +0 -0
  38. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/hawkfranklin_logo.png +0 -0
  39. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/next.svg +0 -0
  40. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/onco-gemma-logo.png +0 -0
  41. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/oncogemma_full_high_crop.png +0 -0
  42. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/oncogemma_logo_only.png +0 -0
  43. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/oncogemma_text_only.png +0 -0
  44. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/pathogemma_icon.png +0 -0
  45. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/vercel.svg +0 -0
  46. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/public/window.svg +0 -0
  47. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/tsconfig.json +0 -0
  48. {patholens-2.0.2 → patholens-2.0.4}/patholens-ui/types.d.ts +0 -0
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: patholens
3
+ Version: 2.0.4
4
+ Summary: A command-line tool to manage, deploy, and launch PathoLens with Google Colab or local backends.
5
+ Requires-Python: >=3.13
6
+ Requires-Dist: click>=8.0.0
7
+ Requires-Dist: fastapi>=0.110.0
8
+ Requires-Dist: google-colab-cli>=0.1.0
9
+ Requires-Dist: httpx>=0.24.0
10
+ Requires-Dist: numpy>=1.24.0
11
+ Requires-Dist: openslide-bin>=4.0.1.1
12
+ Requires-Dist: openslide-python>=1.3.0
13
+ Requires-Dist: pillow>=10.0.0
14
+ Requires-Dist: pydantic>=2.0.0
15
+ Requires-Dist: pyjwt[crypto]>=2.8.0
16
+ Requires-Dist: python-multipart>=0.0.9
17
+ Requires-Dist: sqlalchemy>=2.0.0
18
+ Requires-Dist: uvicorn[standard]>=0.20.0
19
+ Requires-Dist: websockets>=12.0
20
+ Description-Content-Type: text/markdown
21
+
22
+ # 🔬 PathoLens Digital Pathology Workstation
23
+
24
+ PathoLens is a digital pathology workstation & AI workspace with a command-line launcher supporting **Local Machine** and **Google Colab Cloud VM** execution backends.
25
+
26
+ ---
27
+
28
+ ## 🚀 Key Features
29
+
30
+ * **Dual Deployment Target:** Run completely locally on your hardware, or spin up a free Google Colab GPU-accelerated cloud VM with one command.
31
+ * **Zero-friction Colab Deploy (Zero Code Uploads):** The CLI uses PyPI directly. Instead of slow zip uploads of the codebase, it initiates a PyPI installation (`uv pip install`) over Google's datacenter backbone (under 5 seconds) and only uploads the locally compiled static Next.js frontend (~6MB `static.zip`).
32
+ * **Dynamic Slide Directory Configuration:** Switch slide folders dynamically inside the browser UI Settings modal without having to restart the backend. Point it to local directories (e.g. `/home/prime/slides`) or Google Drive paths (`/content/drive/MyDrive/...`).
33
+ * **Edge AI Diagnostics:** Supports local execution of the `gemma-4-E2B-it-litert-lm` LiteRT model for local digital pathology clinical questions and diagnostics.
34
+
35
+ ---
36
+
37
+ ## 📦 Installation
38
+
39
+ To install the CLI globally using `uv`:
40
+
41
+ ```bash
42
+ uv tool install patholens
43
+ ```
44
+
45
+ ---
46
+
47
+ ## 🛠️ Usage
48
+
49
+ Simply run:
50
+
51
+ ```bash
52
+ patholens launch
53
+ ```
54
+
55
+ This launches the onboarding wizard in your terminal:
56
+
57
+ ```
58
+ █▀█ █▀█ ▀█▀ █▄█ █▀█ █  █▀▀ █▄ █ █▀
59
+ █▀▀ █▀█  █  █ █ █▄█ █▄▄ ██▄ █ ▀█ ▄█ cli v2.0.2
60
+ 🔬 Digital Pathology Workstation & AI Workspace
61
+
62
+ } Environment: Cloud-Scalable / Multi-Tenant Ready
63
+ } Local Fallback: SQLite + Local Slide Storage
64
+
65
+ ? Where would you like to run the PathoLens backend?
66
+ 1) Local Machine (Your computer's CPU/GPU)
67
+ 2) Google Colab VM (Free cloud CPU/GPU accelerators)
68
+ ```
69
+
70
+ ### Option 1: Local Machine
71
+ Runs a local FastAPI backend and serves the Next.js frontend statically.
72
+ * **Local AI option:** You can choose to download the `litert_lm` package and cache the `gemma-4-E2B-it-litert-lm` model for local inference.
73
+ * **Launch link:** The CLI automatically opens your default browser at `http://localhost:8000`.
74
+
75
+ ### Option 2: Google Colab VM
76
+ * **Drive Integration:** Prompts you to mount your Google Drive on the VM.
77
+ * **Fast Setup:** Installs `patholens` from PyPI, uploads and extracts the frontend `static.zip`, installs slide bindings (`libopenslide0`), and opens a Cloudflare secure tunnel.
78
+ * **Direct Browser Launch:** The CLI automatically grabs the public `*.trycloudflare.com` tunnel URL and opens it in your web browser.
79
+
80
+ ---
81
+
82
+ ## ⚙️ Slide Directory Configuration
83
+
84
+ You can configure the active slide folder inside the browser UI dashboard.
85
+ 1. Click the **Settings** gear icon in the header.
86
+ 2. Input the absolute path to your slide directory (e.g. `/content/drive/MyDrive/Slides` on Colab, or a local directory).
87
+ 3. Click **Save Settings**. The backend updates its active path on the fly and immediately lists the `.svs` files found in the folder.
88
+
@@ -0,0 +1,67 @@
1
+ # 🔬 PathoLens Digital Pathology Workstation
2
+
3
+ PathoLens is a digital pathology workstation & AI workspace with a command-line launcher supporting **Local Machine** and **Google Colab Cloud VM** execution backends.
4
+
5
+ ---
6
+
7
+ ## 🚀 Key Features
8
+
9
+ * **Dual Deployment Target:** Run completely locally on your hardware, or spin up a free Google Colab GPU-accelerated cloud VM with one command.
10
+ * **Zero-friction Colab Deploy (Zero Code Uploads):** The CLI uses PyPI directly. Instead of slow zip uploads of the codebase, it initiates a PyPI installation (`uv pip install`) over Google's datacenter backbone (under 5 seconds) and only uploads the locally compiled static Next.js frontend (~6MB `static.zip`).
11
+ * **Dynamic Slide Directory Configuration:** Switch slide folders dynamically inside the browser UI Settings modal without having to restart the backend. Point it to local directories (e.g. `/home/prime/slides`) or Google Drive paths (`/content/drive/MyDrive/...`).
12
+ * **Edge AI Diagnostics:** Supports local execution of the `gemma-4-E2B-it-litert-lm` LiteRT model for local digital pathology clinical questions and diagnostics.
13
+
14
+ ---
15
+
16
+ ## 📦 Installation
17
+
18
+ To install the CLI globally using `uv`:
19
+
20
+ ```bash
21
+ uv tool install patholens
22
+ ```
23
+
24
+ ---
25
+
26
+ ## 🛠️ Usage
27
+
28
+ Simply run:
29
+
30
+ ```bash
31
+ patholens launch
32
+ ```
33
+
34
+ This launches the onboarding wizard in your terminal:
35
+
36
+ ```
37
+ █▀█ █▀█ ▀█▀ █▄█ █▀█ █  █▀▀ █▄ █ █▀
38
+ █▀▀ █▀█  █  █ █ █▄█ █▄▄ ██▄ █ ▀█ ▄█ cli v2.0.2
39
+ 🔬 Digital Pathology Workstation & AI Workspace
40
+
41
+ } Environment: Cloud-Scalable / Multi-Tenant Ready
42
+ } Local Fallback: SQLite + Local Slide Storage
43
+
44
+ ? Where would you like to run the PathoLens backend?
45
+ 1) Local Machine (Your computer's CPU/GPU)
46
+ 2) Google Colab VM (Free cloud CPU/GPU accelerators)
47
+ ```
48
+
49
+ ### Option 1: Local Machine
50
+ Runs a local FastAPI backend and serves the Next.js frontend statically.
51
+ * **Local AI option:** You can choose to download the `litert_lm` package and cache the `gemma-4-E2B-it-litert-lm` model for local inference.
52
+ * **Launch link:** The CLI automatically opens your default browser at `http://localhost:8000`.
53
+
54
+ ### Option 2: Google Colab VM
55
+ * **Drive Integration:** Prompts you to mount your Google Drive on the VM.
56
+ * **Fast Setup:** Installs `patholens` from PyPI, uploads and extracts the frontend `static.zip`, installs slide bindings (`libopenslide0`), and opens a Cloudflare secure tunnel.
57
+ * **Direct Browser Launch:** The CLI automatically grabs the public `*.trycloudflare.com` tunnel URL and opens it in your web browser.
58
+
59
+ ---
60
+
61
+ ## ⚙️ Slide Directory Configuration
62
+
63
+ You can configure the active slide folder inside the browser UI dashboard.
64
+ 1. Click the **Settings** gear icon in the header.
65
+ 2. Input the absolute path to your slide directory (e.g. `/content/drive/MyDrive/Slides` on Colab, or a local directory).
66
+ 3. Click **Save Settings**. The backend updates its active path on the fly and immediately lists the `.svs` files found in the folder.
67
+
@@ -468,6 +468,34 @@ async def get_toxicity(current_user: dict = Depends(get_current_user)):
468
468
  }
469
469
  ]
470
470
 
471
+ class SlidePathRequest(pydantic.BaseModel):
472
+ path: str
473
+
474
+ @app.post("/api/settings/slide_path")
475
+ async def update_slide_path(request: SlidePathRequest, current_user: dict = Depends(get_current_user)):
476
+ global SLIDE_DIR
477
+ target_path = os.path.abspath(request.path)
478
+ if not os.path.exists(target_path):
479
+ raise HTTPException(status_code=400, detail=f"Directory path does not exist: {target_path}")
480
+
481
+ SLIDE_DIR = target_path
482
+ logger.info("Slide directory dynamically changed to: %s", SLIDE_DIR)
483
+
484
+ # Return updated slide list
485
+ slides = []
486
+ if os.path.exists(SLIDE_DIR):
487
+ slides = [f for f in os.listdir(SLIDE_DIR) if f.endswith('.svs')]
488
+ return {
489
+ "status": "success",
490
+ "slide_dir": SLIDE_DIR,
491
+ "slides": slides
492
+ }
493
+
494
+ @app.get("/api/settings/slide_path")
495
+ async def get_slide_path(current_user: dict = Depends(get_current_user)):
496
+ return {"slide_dir": SLIDE_DIR}
497
+
498
+
471
499
  class OncoLLMQuery(pydantic.BaseModel):
472
500
  query: str
473
501
  context: Optional[dict] = None
@@ -534,8 +562,11 @@ async def serve_frontend(full_path: str):
534
562
  if os.path.isfile(candidate):
535
563
  return FileResponse(candidate)
536
564
 
565
+ html_candidate = os.path.join(STATIC_FRONTEND_DIR, f"{full_path}.html")
566
+ if full_path and os.path.isfile(html_candidate):
567
+ return FileResponse(html_candidate)
568
+
537
569
  if STATIC_INDEX and os.path.isfile(STATIC_INDEX):
538
570
  return FileResponse(STATIC_INDEX)
539
571
 
540
572
  raise HTTPException(status_code=404, detail="Not Found")
541
-
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import React, { useEffect, useState } from 'react';
4
- import { Search, AlertCircle, RefreshCw, Clock, FileText, CheckCircle, LogOut } from 'lucide-react';
4
+ import { Search, AlertCircle, RefreshCw, Clock, FileText, CheckCircle, LogOut, Settings } from 'lucide-react';
5
5
  import Link from 'next/link';
6
6
  import { useRouter } from 'next/navigation';
7
7
  import Image from 'next/image';
@@ -14,6 +14,53 @@ export default function Dashboard() {
14
14
  const [loading, setLoading] = useState(true);
15
15
  const [error, setError] = useState<string | null>(null);
16
16
 
17
+ // Dynamic Slide Storage Configuration
18
+ const [isSettingsOpen, setIsSettingsOpen] = useState(false);
19
+ const [newSlidePath, setNewSlidePath] = useState('');
20
+ const [currentSlidePath, setCurrentSlidePath] = useState('');
21
+
22
+ const fetchSettings = async () => {
23
+ try {
24
+ const res = await fetch(`${config.apiBaseUrl}/api/settings/slide_path`, {
25
+ headers: {
26
+ 'Authorization': `Bearer ${authHelper.getToken()}`
27
+ }
28
+ });
29
+ if (res.ok) {
30
+ const data = await res.json();
31
+ setCurrentSlidePath(data.slide_dir);
32
+ setNewSlidePath(data.slide_dir);
33
+ }
34
+ } catch (err) {
35
+ console.error("Failed to fetch settings:", err);
36
+ }
37
+ };
38
+
39
+ const saveSlidePath = async () => {
40
+ try {
41
+ const res = await fetch(`${config.apiBaseUrl}/api/settings/slide_path`, {
42
+ method: 'POST',
43
+ headers: {
44
+ 'Content-Type': 'application/json',
45
+ 'Authorization': `Bearer ${authHelper.getToken()}`
46
+ },
47
+ body: JSON.stringify({ path: newSlidePath })
48
+ });
49
+ if (res.ok) {
50
+ const data = await res.json();
51
+ setCurrentSlidePath(data.slide_dir);
52
+ setIsSettingsOpen(false);
53
+ fetchSlides();
54
+ } else {
55
+ const errData = await res.json();
56
+ alert(`Error: ${errData.detail || 'Failed to update slide path'}`);
57
+ }
58
+ } catch (err) {
59
+ console.error("Failed to save slide path:", err);
60
+ alert("Connection error. Could not save settings.");
61
+ }
62
+ };
63
+
17
64
  const handleLogout = () => {
18
65
  authHelper.clearToken();
19
66
  router.push('/login');
@@ -41,6 +88,7 @@ export default function Dashboard() {
41
88
 
42
89
  useEffect(() => {
43
90
  fetchSlides();
91
+ fetchSettings();
44
92
  }, []);
45
93
 
46
94
  // Mock data for patient insights
@@ -92,6 +140,13 @@ export default function Dashboard() {
92
140
  <div className="w-10 h-10 bg-purple-600 rounded-full flex items-center justify-center text-white font-bold">
93
141
  SC
94
142
  </div>
143
+ <button
144
+ onClick={() => setIsSettingsOpen(true)}
145
+ className="p-2 text-gray-400 hover:text-blue-600 transition-colors"
146
+ title="Slide Folder Settings"
147
+ >
148
+ <Settings size={20} />
149
+ </button>
95
150
  <button
96
151
  onClick={handleLogout}
97
152
  className="p-2 text-gray-400 hover:text-red-600 transition-colors"
@@ -240,6 +295,66 @@ export default function Dashboard() {
240
295
  </div>
241
296
  )}
242
297
  </div>
298
+
299
+ {/* Slide Directory Settings Modal */}
300
+ {isSettingsOpen && (
301
+ <div className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50 flex items-center justify-center p-4">
302
+ <div className="bg-white rounded-2xl border border-gray-200 shadow-2xl w-full max-w-md overflow-hidden text-gray-900">
303
+ <div className="p-6 border-b border-gray-100 flex items-center justify-between bg-gray-50">
304
+ <h3 className="font-bold text-gray-800 flex items-center gap-2">
305
+ <Settings size={18} className="text-blue-600" />
306
+ Slide Storage Configuration
307
+ </h3>
308
+ <button
309
+ onClick={() => setIsSettingsOpen(false)}
310
+ className="text-gray-400 hover:text-gray-600 text-sm font-semibold"
311
+ >
312
+ Close
313
+ </button>
314
+ </div>
315
+ <div className="p-6 space-y-4">
316
+ <div>
317
+ <label className="block text-xs font-bold text-gray-500 uppercase tracking-wider mb-2">
318
+ Current Slides Folder
319
+ </label>
320
+ <div className="bg-gray-50 p-3 rounded-lg border border-gray-100 font-mono text-xs text-gray-700 select-all truncate" title={currentSlidePath}>
321
+ {currentSlidePath || 'Not configured'}
322
+ </div>
323
+ </div>
324
+ <div>
325
+ <label className="block text-xs font-bold text-gray-500 uppercase tracking-wider mb-2">
326
+ New Slide Directory Path
327
+ </label>
328
+ <input
329
+ type="text"
330
+ value={newSlidePath}
331
+ onChange={(e) => setNewSlidePath(e.target.value)}
332
+ placeholder="e.g. /home/prime/data/slides"
333
+ className="w-full px-3 py-2 border border-gray-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500 font-mono text-sm text-gray-900 bg-white"
334
+ />
335
+ <p className="text-[10px] text-gray-400 mt-1.5 leading-relaxed">
336
+ Enter the absolute directory path on the server/VM where SVS slides are located.
337
+ For Colab sessions, this is typically under <code className="bg-gray-100 px-1 py-0.5 rounded text-blue-600 font-mono">/content/drive/MyDrive/...</code>
338
+ </p>
339
+ </div>
340
+ </div>
341
+ <div className="p-4 border-t border-gray-100 bg-gray-50 flex justify-end gap-2">
342
+ <button
343
+ onClick={() => setIsSettingsOpen(false)}
344
+ className="px-4 py-2 border border-gray-200 rounded-lg text-sm text-gray-600 hover:bg-gray-100 bg-white"
345
+ >
346
+ Cancel
347
+ </button>
348
+ <button
349
+ onClick={saveSlidePath}
350
+ className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg text-sm font-semibold shadow-sm"
351
+ >
352
+ Save Settings
353
+ </button>
354
+ </div>
355
+ </div>
356
+ </div>
357
+ )}
243
358
  </div>
244
359
  );
245
360
  }
@@ -1,2 +1,2 @@
1
1
  # PathoLens CLI module
2
- __version__ = "2.0.2"
2
+ __version__ = "2.0.4"
@@ -18,7 +18,7 @@ CONFIG_PATH = os.path.join(CONFIG_DIR, "config.json")
18
18
 
19
19
  BANNER = """\033[95m
20
20
  █▀█ █▀█ ▀█▀ █▄█ █▀█ █  █▀▀ █▄ █ █▀
21
- █▀▀ █▀█  █  █ █ █▄█ █▄▄ ██▄ █ ▀█ ▄█\033[0m \033[93mcli v2.0.2\033[0m
21
+ █▀▀ █▀█  █  █ █ █▄█ █▄▄ ██▄ █ ▀█ ▄█\033[0m \033[93mcli v2.0.4\033[0m
22
22
  \033[90m🔬 Digital Pathology Workstation & AI Workspace\033[0m
23
23
  """
24
24
 
@@ -272,7 +272,8 @@ def launch(colab, gpu, session, local, litert_model, litert_backend):
272
272
  port=8000,
273
273
  static_dir=os.path.join(project_root, "patholens-ui", "out"),
274
274
  litert_model=litert_model,
275
- litert_backend=litert_backend or "cpu"
275
+ litert_backend=litert_backend or "cpu",
276
+ sample_data_path=None
276
277
  )
277
278
  except KeyboardInterrupt:
278
279
  click.echo("\n[patholens] Local server stopped.")
@@ -336,12 +337,8 @@ def launch(colab, gpu, session, local, litert_model, litert_backend):
336
337
  click.echo("[patholens] Aborting launch: Frontend static export files missing.")
337
338
  return
338
339
 
339
- # Create temporary zip archive of whole project and static frontend
340
+ # Create temporary zip archive of static frontend
340
341
  with tempfile.TemporaryDirectory() as temp_dir:
341
- click.echo("[patholens] Packaging codebase for Colab installation (excluding local slide directories)...")
342
- project_zip = os.path.join(temp_dir, "patholens.zip")
343
- zip_project(project_root, project_zip)
344
-
345
342
  click.echo("[patholens] Packaging static UI files...")
346
343
  ui_zip = os.path.join(temp_dir, "static.zip")
347
344
  zip_dir(os.path.join(project_root, "patholens-ui", "out"), ui_zip)
@@ -362,9 +359,6 @@ def launch(colab, gpu, session, local, litert_model, litert_backend):
362
359
  time.sleep(10)
363
360
 
364
361
  # Upload files
365
- click.echo("[patholens] Uploading project codebase...")
366
- subprocess.run(["colab", "upload", "-s", session, project_zip, "content/patholens.zip"], check=True)
367
-
368
362
  click.echo("[patholens] Uploading static UI bundle...")
369
363
  subprocess.run(["colab", "upload", "-s", session, ui_zip, "content/static.zip"], check=True)
370
364
 
@@ -383,9 +377,6 @@ import subprocess
383
377
  import time
384
378
 
385
379
  print("[colab-setup] Extracting files...")
386
- with zipfile.ZipFile('/content/patholens.zip', 'r') as zip_ref:
387
- zip_ref.extractall('/content')
388
-
389
380
  with zipfile.ZipFile('/content/static.zip', 'r') as zip_ref:
390
381
  zip_ref.extractall('/content/static')
391
382
 
@@ -395,8 +386,8 @@ os.system("apt-get update && apt-get install -y libopenslide0")
395
386
  print("[colab-setup] Installing uv package manager...")
396
387
  os.system("pip install uv")
397
388
 
398
- print("[colab-setup] Installing patholens package via uv...")
399
- os.system("uv pip install --system /content/patholens")
389
+ print("[colab-setup] Installing patholens package via uv from PyPI...")
390
+ os.system("uv pip install --system patholens")
400
391
 
401
392
  if "{litert_model or ''}":
402
393
  print("[colab-setup] Installing litert-lm package...")
@@ -4,10 +4,10 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "patholens"
7
- version = "2.0.2"
7
+ version = "2.0.4"
8
8
  description = "A command-line tool to manage, deploy, and launch PathoLens with Google Colab or local backends."
9
9
  readme = "README.md"
10
- requires-python = ">=3.10"
10
+ requires-python = ">=3.13"
11
11
  dependencies = [
12
12
  "click>=8.0.0",
13
13
  "fastapi>=0.110.0",
patholens-2.0.2/PKG-INFO DELETED
@@ -1,40 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: patholens
3
- Version: 2.0.2
4
- Summary: A command-line tool to manage, deploy, and launch PathoLens with Google Colab or local backends.
5
- Requires-Python: >=3.10
6
- Requires-Dist: click>=8.0.0
7
- Requires-Dist: fastapi>=0.110.0
8
- Requires-Dist: google-colab-cli>=0.1.0
9
- Requires-Dist: httpx>=0.24.0
10
- Requires-Dist: numpy>=1.24.0
11
- Requires-Dist: openslide-bin>=4.0.1.1
12
- Requires-Dist: openslide-python>=1.3.0
13
- Requires-Dist: pillow>=10.0.0
14
- Requires-Dist: pydantic>=2.0.0
15
- Requires-Dist: pyjwt[crypto]>=2.8.0
16
- Requires-Dist: python-multipart>=0.0.9
17
- Requires-Dist: sqlalchemy>=2.0.0
18
- Requires-Dist: uvicorn[standard]>=0.20.0
19
- Requires-Dist: websockets>=12.0
20
- Description-Content-Type: text/markdown
21
-
22
- # PathoLens
23
-
24
- PathoLens is a digital pathology workstation with a CLI launcher for local or Google Colab-backed execution.
25
-
26
- ## Install
27
-
28
- ```bash
29
- uv tool install patholens
30
- ```
31
-
32
- ## Usage
33
-
34
- ```bash
35
- patholens launch
36
- ```
37
-
38
- The launcher can run the backend locally or package and deploy the application to a Google Colab VM. Local launch builds the bundled Next.js UI if a static export is not already present.
39
-
40
- For whole-slide image viewing, the backend uses OpenSlide.
patholens-2.0.2/README.md DELETED
@@ -1,19 +0,0 @@
1
- # PathoLens
2
-
3
- PathoLens is a digital pathology workstation with a CLI launcher for local or Google Colab-backed execution.
4
-
5
- ## Install
6
-
7
- ```bash
8
- uv tool install patholens
9
- ```
10
-
11
- ## Usage
12
-
13
- ```bash
14
- patholens launch
15
- ```
16
-
17
- The launcher can run the backend locally or package and deploy the application to a Google Colab VM. Local launch builds the bundled Next.js UI if a static export is not already present.
18
-
19
- For whole-slide image viewing, the backend uses OpenSlide.
File without changes