gpuhire 0.2.0__py3-none-any.whl

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 (103) hide show
  1. gpuhire-0.2.0.dist-info/METADATA +247 -0
  2. gpuhire-0.2.0.dist-info/RECORD +103 -0
  3. gpuhire-0.2.0.dist-info/WHEEL +4 -0
  4. gpuhire-0.2.0.dist-info/entry_points.txt +5 -0
  5. gpuhire-0.2.0.dist-info/licenses/LICENSE +21 -0
  6. gpurunner/__init__.py +3 -0
  7. gpurunner/__main__.py +11 -0
  8. gpurunner/_embedded/__init__.py +0 -0
  9. gpurunner/_embedded/_common.py +71 -0
  10. gpurunner/_embedded/churro_runner.py +537 -0
  11. gpurunner/_embedded/dino_surname_verifier_runner.py +988 -0
  12. gpurunner/_embedded/htr_case_runner.py +2690 -0
  13. gpurunner/_embedded/htr_eval_runner.py +194 -0
  14. gpurunner/_embedded/htr_lines_eval_runner.py +299 -0
  15. gpurunner/_embedded/htr_page_bench_runner.py +169 -0
  16. gpurunner/_embedded/kraken_lines_runner.py +136 -0
  17. gpurunner/_embedded/kraken_train_runner.py +385 -0
  18. gpurunner/_embedded/paddle_page_bench_runner.py +98 -0
  19. gpurunner/_embedded/paddleocr_runner.py +343 -0
  20. gpurunner/_embedded/parseq_train_runner.py +1328 -0
  21. gpurunner/_embedded/rukopys_ocr_runner.py +574 -0
  22. gpurunner/_embedded/spotter_page_bench_runner.py +155 -0
  23. gpurunner/_embedded/trocr_lines_runner.py +177 -0
  24. gpurunner/_embedded/vit_classifier_modal_runner.py +412 -0
  25. gpurunner/_embedded/vit_classifier_runner.py +443 -0
  26. gpurunner/_embedded/yolo_spotter_modal_runner.py +1154 -0
  27. gpurunner/auth/__init__.py +5 -0
  28. gpurunner/auth/beam.py +171 -0
  29. gpurunner/auth/google.py +194 -0
  30. gpurunner/auth/kaggle.py +96 -0
  31. gpurunner/auth/lightning.py +227 -0
  32. gpurunner/auth/modal.py +27 -0
  33. gpurunner/auth/saturn.py +148 -0
  34. gpurunner/auth/vast.py +257 -0
  35. gpurunner/backends/__init__.py +49 -0
  36. gpurunner/backends/beam.py +1041 -0
  37. gpurunner/backends/colab.py +797 -0
  38. gpurunner/backends/kaggle.py +622 -0
  39. gpurunner/backends/lightning.py +805 -0
  40. gpurunner/backends/modal.py +652 -0
  41. gpurunner/backends/saturn.py +806 -0
  42. gpurunner/backends/vast.py +2313 -0
  43. gpurunner/cli.py +3153 -0
  44. gpurunner/config.py +74 -0
  45. gpurunner/core/__init__.py +16 -0
  46. gpurunner/core/backend.py +90 -0
  47. gpurunner/core/balances.py +359 -0
  48. gpurunner/core/boxes.py +712 -0
  49. gpurunner/core/budget.py +193 -0
  50. gpurunner/core/contract.py +81 -0
  51. gpurunner/core/gpu_equiv.py +226 -0
  52. gpurunner/core/htr_sizing.py +517 -0
  53. gpurunner/core/inputs.py +50 -0
  54. gpurunner/core/job.py +272 -0
  55. gpurunner/core/locks.py +214 -0
  56. gpurunner/core/manifest.py +380 -0
  57. gpurunner/core/models.py +109 -0
  58. gpurunner/core/offer_score.py +532 -0
  59. gpurunner/core/quota.py +583 -0
  60. gpurunner/core/usage.py +260 -0
  61. gpurunner/htr/__init__.py +11 -0
  62. gpurunner/htr/box_transport.py +204 -0
  63. gpurunner/htr/calibrate.py +219 -0
  64. gpurunner/htr/fetch_ckpt.py +254 -0
  65. gpurunner/htr/origin.py +217 -0
  66. gpurunner/htr/plan_build.py +568 -0
  67. gpurunner/htr/preflight.py +190 -0
  68. gpurunner/htr/r2.py +277 -0
  69. gpurunner/jobs/__init__.py +94 -0
  70. gpurunner/jobs/_net_probe.py +89 -0
  71. gpurunner/jobs/churro.py +94 -0
  72. gpurunner/jobs/crop_verifier.py +40 -0
  73. gpurunner/jobs/dino_surname_verifier.py +156 -0
  74. gpurunner/jobs/htr_case.py +426 -0
  75. gpurunner/jobs/htr_eval.py +115 -0
  76. gpurunner/jobs/htr_lines_eval.py +81 -0
  77. gpurunner/jobs/htr_page_bench.py +86 -0
  78. gpurunner/jobs/kraken_lines.py +70 -0
  79. gpurunner/jobs/kraken_train.py +170 -0
  80. gpurunner/jobs/paddle_page_bench.py +76 -0
  81. gpurunner/jobs/paddleocr.py +258 -0
  82. gpurunner/jobs/parseq_train.py +401 -0
  83. gpurunner/jobs/rukopys_ocr.py +78 -0
  84. gpurunner/jobs/spotter_page_bench.py +83 -0
  85. gpurunner/jobs/trocr_lines.py +77 -0
  86. gpurunner/jobs/vit_classifier.py +187 -0
  87. gpurunner/jobs/yolo_spotter.py +427 -0
  88. gpurunner/plugins/__init__.py +6 -0
  89. gpurunner/plugins/nyshporka_cloud.py +802 -0
  90. gpurunner/supervise/__init__.py +1 -0
  91. gpurunner/supervise/append.py +96 -0
  92. gpurunner/supervise/bg.py +181 -0
  93. gpurunner/supervise/decide.py +601 -0
  94. gpurunner/supervise/detach.py +312 -0
  95. gpurunner/supervise/gate.py +399 -0
  96. gpurunner/supervise/htr.py +3369 -0
  97. gpurunner/supervise/plan.py +404 -0
  98. gpurunner/supervise/state.py +437 -0
  99. gpurunner/supervise/verify.py +173 -0
  100. gpurunner/supervise/wrapup.py +65 -0
  101. gpurunner/web/__init__.py +15 -0
  102. gpurunner/web/app.py +305 -0
  103. gpurunner/web/static/index.html +809 -0
@@ -0,0 +1,247 @@
1
+ Metadata-Version: 2.5
2
+ Name: gpuhire
3
+ Version: 0.2.0
4
+ Summary: Один CLI для GPU-задач на Kaggle, Modal, Colab, Vast, Lightning, Beam і Saturn: подати, стежити, забрати, не переплатити
5
+ Project-URL: Homepage, https://github.com/SERGIUSH-UA/gpurunner
6
+ Project-URL: Source, https://github.com/SERGIUSH-UA/gpurunner
7
+ Project-URL: Issues, https://github.com/SERGIUSH-UA/gpurunner/issues
8
+ Project-URL: Changelog, https://github.com/SERGIUSH-UA/gpurunner/blob/main/CHANGELOG.md
9
+ Author: Serhii Dalishchynskyi
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: batch,colab,gpu,htr,kaggle,modal,ocr,rental,vast
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Intended Audience :: Science/Research
17
+ Classifier: Natural Language :: Ukrainian
18
+ Classifier: Operating System :: MacOS
19
+ Classifier: Operating System :: Microsoft :: Windows
20
+ Classifier: Operating System :: POSIX :: Linux
21
+ Classifier: Programming Language :: Python :: 3.11
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
25
+ Classifier: Topic :: System :: Distributed Computing
26
+ Requires-Python: >=3.11
27
+ Requires-Dist: httpx>=0.27
28
+ Requires-Dist: kaggle>=1.6
29
+ Requires-Dist: nbformat>=5.10
30
+ Requires-Dist: platformdirs>=4.2
31
+ Requires-Dist: psutil>=5.9
32
+ Requires-Dist: pydantic>=2.6
33
+ Requires-Dist: rich>=13.7
34
+ Requires-Dist: typer>=0.12
35
+ Provides-Extra: all
36
+ Requires-Dist: boto3>=1.34; extra == 'all'
37
+ Requires-Dist: fastapi>=0.115; extra == 'all'
38
+ Requires-Dist: google-api-python-client>=2.130; extra == 'all'
39
+ Requires-Dist: google-auth-oauthlib>=1.2; extra == 'all'
40
+ Requires-Dist: modal>=0.64; extra == 'all'
41
+ Requires-Dist: paramiko>=3.4; extra == 'all'
42
+ Requires-Dist: pillow>=10.0; extra == 'all'
43
+ Requires-Dist: uvicorn>=0.30; extra == 'all'
44
+ Provides-Extra: beam
45
+ Requires-Dist: beam-client>=0.2.200; extra == 'beam'
46
+ Provides-Extra: colab
47
+ Requires-Dist: google-api-python-client>=2.130; extra == 'colab'
48
+ Requires-Dist: google-auth-oauthlib>=1.2; extra == 'colab'
49
+ Provides-Extra: lightning
50
+ Requires-Dist: lightning-sdk>=2026.7; extra == 'lightning'
51
+ Provides-Extra: modal
52
+ Requires-Dist: modal>=0.64; extra == 'modal'
53
+ Provides-Extra: r2
54
+ Requires-Dist: boto3>=1.34; extra == 'r2'
55
+ Requires-Dist: pillow>=10.0; extra == 'r2'
56
+ Provides-Extra: saturn
57
+ Requires-Dist: saturn-client>=2026.2; extra == 'saturn'
58
+ Requires-Dist: saturnfs>=2024.3; extra == 'saturn'
59
+ Provides-Extra: vast
60
+ Requires-Dist: paramiko>=3.4; extra == 'vast'
61
+ Provides-Extra: web
62
+ Requires-Dist: fastapi>=0.115; extra == 'web'
63
+ Requires-Dist: uvicorn>=0.30; extra == 'web'
64
+ Description-Content-Type: text/markdown
65
+
66
+ # gpurunner
67
+
68
+ Один CLI для GPU-задач на чужому залізі: подати, стежити, забрати результат,
69
+ не переплатити. Сім бекендів за одним інтерфейсом — Kaggle, Modal, Google Colab,
70
+ Vast.ai, Lightning, Beam, Saturn Cloud.
71
+
72
+ > **In English.** `gpurunner` is a command-line tool that submits GPU jobs (OCR,
73
+ > handwriting recognition, model training) to Kaggle, Modal, Colab, Vast.ai,
74
+ > Lightning, Beam or Saturn Cloud through one interface, tracks them in a local
75
+ > manifest, fetches outputs and keeps an eye on spending. Documentation and CLI
76
+ > messages are in Ukrainian. It works on its own and also serves as the GPU
77
+ > rental module of [nyshporka](https://github.com/SERGIUSH-UA/nyshporka).
78
+
79
+ Статус: альфа (0.2). Команди й машинний вивід, на які спираються інші програми,
80
+ описано в [`docs/contract.md`](docs/contract.md) і в межах мінорної версії не
81
+ міняються; решта ще рухається.
82
+
83
+ ## 1. Навіщо
84
+
85
+ - Той самий job іде на безкоштовну квоту Kaggle, на Modal із посекундним
86
+ білінгом або на орендований бокс Vast — міняється лише `-b`.
87
+ - Кожен прогін лишає запис у локальному маніфесті: що подано, куди, з якими
88
+ параметрами, чим закінчилось і чому впало.
89
+ - Гроші видно до старту: прорахунок вартості, стеля витрат, залишки по бекендах
90
+ (`gpurunner balance`), і що зараз тарифікується (`gpurunner burn`).
91
+ - Для довгих заходів на Vast є наглядач (`gpurunner htr supervise`): орендує
92
+ бокс, стежить за темпом, доганяє пропуски, звіряє повноту й гасить оренду сам.
93
+ - Бакет S3 для цього не обов'язковий: `htr plan --transport box` везе кадри й
94
+ ваги на саму машину по `scp` і піднімає на ній крихітний склад, з якого їх
95
+ бере робота. Чекпоінти наглядач забирає додому сам — машина може вмерти.
96
+
97
+ ## 2. Установлення
98
+
99
+ Потрібен Python 3.11+.
100
+
101
+ ⚠ На PyPI пакет зветься **`gpuhire`**, а команда в терміналі — `gpurunner`.
102
+ Ім'я `gpurunner` на PyPI зайняте схожим проєктом (`gpu-runner`), і майданчик
103
+ не дав його взяти; перейменовувати команду заради цього не стали.
104
+
105
+ ```
106
+ pip install gpuhire # ядро + Kaggle
107
+ pip install "gpuhire[modal]" # + Modal
108
+ pip install "gpuhire[vast,r2]" # + Vast.ai і доставка даних через R2
109
+ pip install "gpuhire[all]" # modal, colab, vast, web, r2
110
+ ```
111
+
112
+ `lightning`, `beam`, `saturn` ставляться поіменно: їхні SDK важкі й потрібні
113
+ одиницям. Як модуль Нишпорки: `pip install "gpuhire[vast,r2]"` поруч із
114
+ нею — Нишпорка знайде плагін сама.
115
+
116
+ Поруч із Нишпоркою gpurunner стає її **бекендом оренди**: оголошує entry point
117
+ `nyshporka.cloud`, і `nysh cloud` сам орендує бокс на Vast.ai, читає справу по
118
+ SSH, забирає результат і гасить оренду. Від людини — лише ключ API
119
+ (`gpurunner auth vast --key <КЛЮЧ>`) і баланс; SSH-ключ генерується сам, R2 не
120
+ потрібен. Докладно — [`docs/nyshporka.md`](docs/nyshporka.md).
121
+
122
+ З вихідного коду:
123
+
124
+ ```
125
+ git clone https://github.com/SERGIUSH-UA/gpurunner && cd gpurunner
126
+ uv sync --extra all
127
+ uv run gpurunner --version
128
+ ```
129
+
130
+ ## 3. Перший прогін
131
+
132
+ ```
133
+ gpurunner auth kaggle --verify # обліковка на місці?
134
+ gpurunner ls # які є job'и й бекенди
135
+ gpurunner run net-probe -b kaggle # найдешевша перевірка: чи є в кернелі мережа
136
+ gpurunner watch <handle> # чекати до кінця
137
+ gpurunner fetch <handle> -o out/ # забрати вихід
138
+ ```
139
+
140
+ `--dry-run` перевіряє параметри й показує код, який поїде на бекенд, нічого не
141
+ подаючи. Параметри job'а — `-p ключ=значення` (значення розбирається як JSON,
142
+ якщо виходить).
143
+
144
+ 🔴 **Vast тарифікує погодинно й не спиняється після завершення job'а.** Після
145
+ `fetch` обов'язково `gpurunner cancel <handle>`; що зараз горить, показує
146
+ `gpurunner burn`.
147
+
148
+ ## 4. Бекенди
149
+
150
+ | бекенд | extra | обліковка | особливість |
151
+ |---|---|---|---|
152
+ | `kaggle` | — | `~/.kaggle/kaggle.json` або `KAGGLE_USERNAME` + `KAGGLE_KEY` | безкоштовна тижнева квота; вхід — Kaggle Dataset |
153
+ | `modal` | `modal` | `~/.modal.toml` (`modal token new`) | посекундний білінг; дані — у томі Modal; локальний Python має бути 3.12 — як в образі |
154
+ | `colab` | `colab` | OAuth-клієнт Google у теці конфігурації | нотбук треба запустити руками: Runtime → Run all |
155
+ | `vast` | `vast` | `VAST_API_KEY` або файл `vast_api_key` | оренда боксу по SSH; тарифікація до `cancel` |
156
+ | `lightning` | `lightning` | `LIGHTNING_USER_ID`, `LIGHTNING_API_KEY` | Studio + teamspace |
157
+ | `beam` | `beam` | `BEAM_TOKEN` або `~/.beam/config.ini` | стеля витрат на прогін і на місяць |
158
+ | `saturn` | `saturn` | `SATURN_TOKEN`, `SATURN_BASE_URL` | типи інстансів — `gpurunner saturn sizes` |
159
+
160
+ `gpurunner auth <бекенд>` показує, де шукається обліковка й чого бракує;
161
+ `--verify` робить пробний запит. Тека конфігурації — `%LOCALAPPDATA%\gpurunner`
162
+ (Windows) або `~/.config/gpurunner`; перекривається `GPURUNNER_CONFIG_DIR`.
163
+ Приклади файлів — у [`docs/config/`](docs/config/).
164
+
165
+ ## 5. Job'и
166
+
167
+ Перелік з описами й бекендами — `gpurunner ls`; готові набори параметрів із
168
+ прорахунком вартості — `gpurunner recipes <job>`.
169
+
170
+ Загальні:
171
+
172
+ | job | що робить |
173
+ |---|---|
174
+ | `net-probe` | чи є у віддаленому кернелі мережа |
175
+ | `paddleocr` | PaddleOCR PP-OCRv5 по переліку PDF за посиланнями → txt і json по сторінках |
176
+ | `vit_classifier`, `crop_verifier`, `dino_surname_verifier` | дотренування бінарного класифікатора зображень (ViT / DINOv2) і скоринг |
177
+ | `yolo_spotter` | дотренування YOLO на пошук одного рукописного слова |
178
+ | `churro`, `rukopys_ocr` | сторінкове читання історичних документів VLM-моделями |
179
+ | `kraken_lines`, `trocr_lines` | сегментація сторінок на рядки й читання рядків |
180
+ | `kraken_train`, `parseq_train` | трен моделей читання рядків (kraken, PARSeq) |
181
+ | `htr_eval`, `htr_lines_eval`, `htr_page_bench`, `paddle_page_bench`, `spotter_page_bench` | бенчмарки читання рукопису |
182
+
183
+ Потребує пакета `nyshporka`:
184
+
185
+ | job | що робить |
186
+ |---|---|
187
+ | `htr_case` | прогін архівної справи раннером Нишпорки на орендованій карті; раннер і моделі приїжджають архівом ассетів, gpurunner доставляє їх на бокс і стежить |
188
+
189
+ Команди `gpurunner htr …` (план, передполіт, наглядач, забір чекпоінтів,
190
+ калібрування) обслуговують саме `htr_case`.
191
+
192
+ ## 6. Команди
193
+
194
+ ```
195
+ run · status · watch · logs · fetch · cancel · sweep життєвий цикл прогону
196
+ ls · recipes · balance · burn · reconcile · dash огляд, гроші, звірка
197
+ auth <бекенд> обліковки
198
+ dataset push|files Kaggle-датасети
199
+ drive push|files вхідні дані Colab у Google Drive
200
+ vast offers|instances · saturn sizes ринок і типи машин
201
+ boxes ls|explain|ban|star|prune|forget реєстр орендних боксів
202
+ bg start|status|stop довгі задачі у фоні
203
+ htr plan|preflight|supervise|state|stop|quiesce|fetch-ckpt|calibrate|append
204
+ ```
205
+
206
+ `gpurunner <команда> --help` — повний перелік прапорців.
207
+
208
+ ## 7. Реєстр боксів
209
+
210
+ Кожна оренда на Vast лишає рядок «обіцяне поруч із виміряним»: яке залізо
211
+ заявив хост і який темп воно дало. З цього складається вердикт машини, і
212
+ наступний пошук оферти погані машини оминає, а добрі шукає адресно.
213
+
214
+ Реєстр — дані користувача, а не пакета: у ньому ідентифікатори машин, ціни й
215
+ назви власних прогонів. Лежить у `GPURUNNER_REPO_DATA_DIR`, без змінної — у
216
+ `<тека даних>/registry`. Змінну варто спрямувати в теку власного
217
+ git-репозиторію: тоді в дифі видно, коли й чому бокс став поганим.
218
+
219
+ ## 8. Для програм
220
+
221
+ Інші програми кличуть gpurunner окремим процесом і читають `--json`: останній
222
+ рядок stdout, що починається з `{`, — один об'єкт. Форма відповідей, перелік
223
+ стабільних команд, схема `plan.json` і змінні оточення —
224
+ [`docs/contract.md`](docs/contract.md).
225
+
226
+ Другий шлях — плагін у тому самому інтерпретаторі: клас
227
+ `gpurunner.plugins.nyshporka_cloud:VastRent` (entry point `nyshporka.cloud`)
228
+ орендує й гасить бокс для конвеєра Нишпорки. Методи, форма `Box.meta` і
229
+ необов'язкові `status / login / estimate` — [§6 контракту](docs/contract.md#6-плагін-nyshporkacloud).
230
+
231
+ ## 9. Розробка
232
+
233
+ ```
234
+ uv sync --extra all
235
+ uv run pytest
236
+ uv run ruff check .
237
+ uv run mypy src
238
+ python tools/scan_private.py # ворота проти приватних даних
239
+ ```
240
+
241
+ Тести не ходять у мережу й не потребують обліковок. Порядок внесення змін і
242
+ чекліст релізу — [`CONTRIBUTING.md`](CONTRIBUTING.md); про вразливості —
243
+ [`SECURITY.md`](SECURITY.md).
244
+
245
+ ## 10. Ліцензія
246
+
247
+ MIT — див. [`LICENSE`](LICENSE).
@@ -0,0 +1,103 @@
1
+ gpurunner/__init__.py,sha256=QDbH_ktfuajY2_X5yXRyKpR-fsbFQXCq1rEBcc0S0aA,105
2
+ gpurunner/__main__.py,sha256=S-Ex1h2wwgrpw3R3jHJ__Nrgbn4Nrwf_yYGkxA0gxBI,514
3
+ gpurunner/cli.py,sha256=K35TY2AWEN2X4D8BCQeFJZGyJ-1_2NY7UOpaP4vvzLU,154558
4
+ gpurunner/config.py,sha256=iNgkZ5DqgRcbmBDq0dcBiYCT3Ci5jFxLaoPzLagwO5s,3382
5
+ gpurunner/_embedded/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ gpurunner/_embedded/_common.py,sha256=ltgW5rL8w3WuMe82HieOXunWVh8GtOgCTMPGH0ofUUc,4568
7
+ gpurunner/_embedded/churro_runner.py,sha256=66tF9iCJBj6-cKmpP1WLMi0tEIWSfMfvE2EdVc1qibM,29425
8
+ gpurunner/_embedded/dino_surname_verifier_runner.py,sha256=JinTHZjpFqsw-GYfWfvhoCPiP1hJIz4YWk0yhWJ63R0,38705
9
+ gpurunner/_embedded/htr_case_runner.py,sha256=NA4O7e8D3xsVd48se5Zho6ehMvZSDaAyjr3dKPH0LWA,152704
10
+ gpurunner/_embedded/htr_eval_runner.py,sha256=o-zunWFKiiOXr9-qHLFGIhwnV_i2sh7g5VP7upRxrZ8,8850
11
+ gpurunner/_embedded/htr_lines_eval_runner.py,sha256=Qgf6y9XRhAMcwVN1e8vT-PW5TgApwArTpbWnGQ6DNYo,12507
12
+ gpurunner/_embedded/htr_page_bench_runner.py,sha256=ejP-cnsnNhLKA39VQyD1NXvnfLOP-tss3FVmnuFnd84,7541
13
+ gpurunner/_embedded/kraken_lines_runner.py,sha256=CqLMJt2PzM4zQLVUeztz3igk4AZvv7mXdtUierJPQhc,5773
14
+ gpurunner/_embedded/kraken_train_runner.py,sha256=00eH0vCpV7MOZkWiFbNqfcCQ7H8QkIwuxBcvjxdUtNc,22981
15
+ gpurunner/_embedded/paddle_page_bench_runner.py,sha256=zFoiv3EQJ4-lXQ8fqYpvaoiV6NsfnRc3CkmxPhtKajI,4212
16
+ gpurunner/_embedded/paddleocr_runner.py,sha256=r4SatfLrFqsy43zp3IuCjVck44RoVY_crGKjk3vEtsw,12821
17
+ gpurunner/_embedded/parseq_train_runner.py,sha256=TAiVXheTcC5nv2_Om6Qe9jc_rrrUS6RVPSOjxAPD-9c,72502
18
+ gpurunner/_embedded/rukopys_ocr_runner.py,sha256=RPyQ2SlZv6A_OPTP8OO49Pe62HtAVklTvANOmliHeGQ,30501
19
+ gpurunner/_embedded/spotter_page_bench_runner.py,sha256=Ca6SYyLRS_5HQnTwcXbQEoPg68ExNJsqKFoWCftgb_M,7343
20
+ gpurunner/_embedded/trocr_lines_runner.py,sha256=WgaBr8-VaYt0mu9GA6toUiIP-jXxIXX63jupW6ws2Ng,7607
21
+ gpurunner/_embedded/vit_classifier_modal_runner.py,sha256=DjRqLd5l63C0j2igseAPQLIvWVQqjCIg8Mzc6ODo-sY,15143
22
+ gpurunner/_embedded/vit_classifier_runner.py,sha256=599fhKNpYiC1x8UzMorricCvpkDl-oJbXMXnP_eIs3I,15591
23
+ gpurunner/_embedded/yolo_spotter_modal_runner.py,sha256=3JhTmN-lOirbIWcCn0x7SJsoSNX1bVxH4dU1qnnOETU,47690
24
+ gpurunner/auth/__init__.py,sha256=DqDOcl2x1Vs-ItpKWDib6mVnool9dG8PS_4DHnbFVC4,167
25
+ gpurunner/auth/beam.py,sha256=YUtJmkLL-tnNMCLUcMxzgqtsIiQ9Kjb8allW90Cpp5Q,6090
26
+ gpurunner/auth/google.py,sha256=HH-J8alyBrF2p-XJDhsWrW9gLnl0m1LBTyAQJeHfFPU,7464
27
+ gpurunner/auth/kaggle.py,sha256=MOjK0KmkOL6GJS2o7Mmq75sfZmkqRRbpDPPw0RD01Ec,3270
28
+ gpurunner/auth/lightning.py,sha256=UTF8P14zPxTn9Drnw-dAaF7_xwLmZnUwkRW-lK-h1aQ,8417
29
+ gpurunner/auth/modal.py,sha256=dXISJgGpgwKE1OeUyFk5LJH-Y9JpomRlekLQ8rBTYgo,836
30
+ gpurunner/auth/saturn.py,sha256=Jc2NjSxNN0Go-x3iWhwzOmk2eacMVkHlPKh5a6NP2xw,5190
31
+ gpurunner/auth/vast.py,sha256=5ZomWE9YapcbbW6fDtMc4jeYfHVI-q1F_XIGOf_zUVk,11458
32
+ gpurunner/backends/__init__.py,sha256=abwuCtUCyc1l9IH6LinwdJIzJD7eoVWizSwdZzjSF3k,1353
33
+ gpurunner/backends/beam.py,sha256=42Iz6wjpilA7OpPZ-Ewfb7RB-CEj6iH9AQRf7iNYQBs,45260
34
+ gpurunner/backends/colab.py,sha256=FspAUYdEoD1lTrOc6Mf2_lR6IRWD-iMtdomhwyh9T1I,30505
35
+ gpurunner/backends/kaggle.py,sha256=nK9DPVqtyxHOKxZUcdlp4v_DeuZWoLMLkD2OGJUDJRU,29353
36
+ gpurunner/backends/lightning.py,sha256=5lAZJiUxcOMomDfwk9zspsHFVzziHkmtJCBko3IcbrQ,32847
37
+ gpurunner/backends/modal.py,sha256=Dz27b0Ktf70G0JhSUkEzviCuiKQHitLwlvPRngS-mmg,27311
38
+ gpurunner/backends/saturn.py,sha256=f5ImitChMKWrX9w_HpCGHbkvSUkQl7P_X6v1B6Gbm4M,31984
39
+ gpurunner/backends/vast.py,sha256=FqEdy0Pf4oNE1w3m-vaRRyq8R2tZ_rYbwkfVE8R9Rg4,131063
40
+ gpurunner/core/__init__.py,sha256=Gpd4z1tPhSAAC_d6b8eEm4Av1l2-urgSjaeng0fBIbM,401
41
+ gpurunner/core/backend.py,sha256=n724a1mmvkZHeDDxAM8R_GEkW7lmyAn8nMV_ImF2_s0,3714
42
+ gpurunner/core/balances.py,sha256=_6kVpdzzNRLu24czuISijWNTSJBdSlF9k5KZzIbZ3HY,17346
43
+ gpurunner/core/boxes.py,sha256=_2HdUjPrwZLvLFdEEJ_v0_MnTShzqAmlF2hr3Qo4Mv0,38053
44
+ gpurunner/core/budget.py,sha256=3lQk5EqNaNA3XM1OX_bAYHtVdzQqexo-xTYRrJJEiuk,6804
45
+ gpurunner/core/contract.py,sha256=x3hVnvK0gZxvhlKVJ6HKNZvcWf1OP5xB5x1Yi9JuC-A,4089
46
+ gpurunner/core/gpu_equiv.py,sha256=VjwVBdwnaWguN9VIashOrqkmUrDATIX1TDXMWVT6Iik,12885
47
+ gpurunner/core/htr_sizing.py,sha256=TSB5QG1P2wlfBnZPXYH9YIKqDGKd09TprNBh64L5sFU,36682
48
+ gpurunner/core/inputs.py,sha256=3dRGm50t3v-u02xlrZ8AUn833sKcP7P4gCxweZ6om2o,1882
49
+ gpurunner/core/job.py,sha256=qBChgvkkIyG1B7dQsTV1TA5Gwl_uZ7Mwo-rIryjqvfg,10779
50
+ gpurunner/core/locks.py,sha256=fCM5EJ7Msvr8_ljcc-rxYiwz2al2GER3oVuKYx8KNL8,9745
51
+ gpurunner/core/manifest.py,sha256=EAMvE1ixx8lchu6u1HDNSs2vw-9WMpVFFlSkEhtuqTc,15085
52
+ gpurunner/core/models.py,sha256=ZT0L93cxozpH35VsGXsgy6kfpGbB6wQobjxuuziJbSQ,4371
53
+ gpurunner/core/offer_score.py,sha256=wdjxyZOB0qFcmZExleq0yE3rmIwq0bw-U2MA9h0qyUU,29278
54
+ gpurunner/core/quota.py,sha256=F4d3Nfh9iCPPD0NtezE3b8QQxiLToCbjFsKjukuwPWQ,28360
55
+ gpurunner/core/usage.py,sha256=iNxsxwIM96w75jNm40EQ8DO61NbAgSyZH1qmLlpWHYg,14098
56
+ gpurunner/htr/__init__.py,sha256=S5IJh1Nbl1iQ9KKlDWE9-1MYmKpWj4gg8jfwF97Jwlg,1079
57
+ gpurunner/htr/box_transport.py,sha256=6d8MK-W1wqZnC-NNQ1cvjppn9OLo__my4MDn43zMixI,10839
58
+ gpurunner/htr/calibrate.py,sha256=2UH0s2NkJTOkRKW_9Qoj9fwuRHGoj8FwLspWrubuSO0,11064
59
+ gpurunner/htr/fetch_ckpt.py,sha256=pIH6LYabzt1fLYz0yH5WLN1iXf9c62Tbt_SlOcONAr8,13054
60
+ gpurunner/htr/origin.py,sha256=mkgPf1YADmyHnNmfSxau1gyd7fYRGKP_OdvM5GBegQc,10497
61
+ gpurunner/htr/plan_build.py,sha256=-eExZaRUhKEDZ71OEGbtKJDj1TF1z2OLOxws95sB_0c,33737
62
+ gpurunner/htr/preflight.py,sha256=k6iYuljeI_NE6RMNTTzVdNeyfQajB05yti4WiYG3SkY,11133
63
+ gpurunner/htr/r2.py,sha256=Mcb-4Z8E9YSDfetIWo0J-1l5zrfwzjVVpYUI9_z19GU,12644
64
+ gpurunner/jobs/__init__.py,sha256=q4DxOrtdOfQC1yTHYIwOGPNah1rmffF0bt89jjsgI3g,2989
65
+ gpurunner/jobs/_net_probe.py,sha256=v6zgHRk2oKX9Gg1jVDZpza-FsePSPyGBk8BASw77ulk,2555
66
+ gpurunner/jobs/churro.py,sha256=2Hirb-4B05VBhmSCep0QML7L_I3SIZ6m4jx2ea7n_tY,4547
67
+ gpurunner/jobs/crop_verifier.py,sha256=XNjOYxTHdcD1iub2ZrIn0Z5yT5LxoeMHKOCqpu-K2_8,1407
68
+ gpurunner/jobs/dino_surname_verifier.py,sha256=o0j3nr-aQWIO4rjFiiqsnkWbK5xXCNc2SAGBateVncU,6385
69
+ gpurunner/jobs/htr_case.py,sha256=FdpoC0FsKskk5xLn57CWLtjtX_rMpUvRnx8jueBLq6o,29969
70
+ gpurunner/jobs/htr_eval.py,sha256=PpB7tXVu8kQ98x6ASDA2GDMeceDDbizphySWihNQ1Hg,4666
71
+ gpurunner/jobs/htr_lines_eval.py,sha256=ntRJRtI-o72LugdWxxepjHNI5ShqgI7CGQfSO6U0WSs,3341
72
+ gpurunner/jobs/htr_page_bench.py,sha256=2bq35GC_26nt8y5sOF9_eZ9A1aj9rLUBATbUvrZ5jLw,3847
73
+ gpurunner/jobs/kraken_lines.py,sha256=zqRNUXXPTUyT02gKr0kIc3_KZPmxTUUdU-q4v8_Zszs,3049
74
+ gpurunner/jobs/kraken_train.py,sha256=8Luakris5cW2ecvQa1shFqU7VekzaVYG3bd83Ux5YXk,11233
75
+ gpurunner/jobs/paddle_page_bench.py,sha256=2CJIhpUgzzp3LV3sNwegTJriXmqpBujEp5T4uYWHJUs,3224
76
+ gpurunner/jobs/paddleocr.py,sha256=FzO7i9LL_OWn91Tdf_SjMvqxO8DXn3OVQiRpjIjhIys,10513
77
+ gpurunner/jobs/parseq_train.py,sha256=B2gwhJsMgVJT7jSmROdKPtBg-k35HIpLsvkCHEWRokw,27559
78
+ gpurunner/jobs/rukopys_ocr.py,sha256=1Q-xTr-sB86-yTGsQ0OvswVbWTDCOZwHZS0AJLH_j60,4078
79
+ gpurunner/jobs/spotter_page_bench.py,sha256=8FE6rhRX2ja4UzQblSdrL6Bu73HN9L4HgSA8oGORvws,3815
80
+ gpurunner/jobs/trocr_lines.py,sha256=ylyiB4wOwHfevcwvR9b4q6pBi_5HTjrrNbXYEsb3hjc,3339
81
+ gpurunner/jobs/vit_classifier.py,sha256=5OjzvOZrx_iLa1zs84b4TLWPXSPNjVynEbkQR8WmfJ8,7556
82
+ gpurunner/jobs/yolo_spotter.py,sha256=7wkEmpulBfNKzzUk4npmbyqdFZIv9SvnU8b1hqmxQT8,21827
83
+ gpurunner/plugins/__init__.py,sha256=Oyt6m2wwYaCzVcTmfosXTP5kpbiGt0KMfGMQHffeSBU,407
84
+ gpurunner/plugins/nyshporka_cloud.py,sha256=AcE705muEwKYV7qLJ-yB6RQymoEBOBzLfZExvCjfKG4,41365
85
+ gpurunner/supervise/__init__.py,sha256=yZ1pE32Upl-F5Gy9xos3k_s1FDzjl-b2vCjeU7dW-yo,126
86
+ gpurunner/supervise/append.py,sha256=xDVGMYdy64sTfahfERAv9znw8YBPWPCg4S6SStCHETY,5056
87
+ gpurunner/supervise/bg.py,sha256=ODKfGtUK4v1Zse3ePol5BhsKsjM44HeFngmGVyvfBVo,7775
88
+ gpurunner/supervise/decide.py,sha256=xOzlhgUGB0j6BZZ0uaM57SSp1z2GAQXCd_H2gksoj6s,44380
89
+ gpurunner/supervise/detach.py,sha256=rF5X__iefl3fypr3BG5i3XhSp-h411gijRJdyYh_ZpU,17314
90
+ gpurunner/supervise/gate.py,sha256=1Wh5X-wT4lb_naAnAWeeFt3Q_yyZTRA7dHvQnpcEpAU,25133
91
+ gpurunner/supervise/htr.py,sha256=sx8XnxrEireAFGt-_Qshpve-8_uU5tmXvrkajRmT2ZY,212986
92
+ gpurunner/supervise/plan.py,sha256=ah3-zTsrnIfMWifI29_p91Sh4s6Vr6Ris7hZaQZuEfQ,27996
93
+ gpurunner/supervise/state.py,sha256=I8Qaukq-pWZ_SKyxPAD44L4RyPbXdNI_Aps5N-5ntTo,23266
94
+ gpurunner/supervise/verify.py,sha256=-OlFL6aZvtyTAQdYj_z9xpFXNeSUeMhRQcsGgc869Cw,9707
95
+ gpurunner/supervise/wrapup.py,sha256=ecmtbF_gNSZVKmcUNPfJCC1KjLskgyBLNL3iUwlXvdU,3819
96
+ gpurunner/web/__init__.py,sha256=L_zFK82CxcXXxpEJdIsCOa0Hq0r7_A2vUcMMAX3lDSY,546
97
+ gpurunner/web/app.py,sha256=sGcfhmyQfPtBEIG8k3cCyov9EcIso7sXSwVZtSHF2BM,14834
98
+ gpurunner/web/static/index.html,sha256=JlArNRv5O7eG6-m9BgH9aOGC-F498-lPFuiEYnG4NOQ,41736
99
+ gpuhire-0.2.0.dist-info/METADATA,sha256=_CN8jIiXahpb04XDgIDzs8xV-tfHF9zWi1kte8RAcQU,14448
100
+ gpuhire-0.2.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
101
+ gpuhire-0.2.0.dist-info/entry_points.txt,sha256=I6QBdnQbrgzOXKI34zm-Fro9XhJptRkcw2z5cxxyp7s,117
102
+ gpuhire-0.2.0.dist-info/licenses/LICENSE,sha256=sPEhlEAJ1y1dh5hgVz46C0wnuR3AwVtWUptJrf5TGQc,1078
103
+ gpuhire-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ gpurunner = gpurunner.cli:app
3
+
4
+ [nyshporka.cloud]
5
+ vast = gpurunner.plugins.nyshporka_cloud:VastRent
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Serhii Dalishchynskyi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
gpurunner/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """gpurunner — submit GPU jobs to Kaggle/Modal/etc with a unified interface."""
2
+
3
+ __version__ = "0.2.0"
gpurunner/__main__.py ADDED
@@ -0,0 +1,11 @@
1
+ """`python -m gpurunner` — останній запасний шлях відчеплених задач.
2
+
3
+ Задача планувальника не має PATH, і коли ні `gpurunner` у PATH, ні
4
+ `gpurunner.exe` поруч не знайдено, `detach.self_argv()` кличе саме так. Доти
5
+ цього модуля не було, і запасний шлях падав на «No module named gpurunner.__main__».
6
+ """
7
+
8
+ from gpurunner.cli import app
9
+
10
+ if __name__ == "__main__":
11
+ app()
File without changes
@@ -0,0 +1,71 @@
1
+ """Спільний пролог для embedded-ранерів; вклеюється рендером ПЕРЕД раннером.
2
+
3
+ Це не імпортований модуль (як і самі ранери) — ``Job.render_kaggle_code``
4
+ конкатенує цей файл із раннером в один текст, який їде на кернел. Звідси
5
+ обмеження ті самі: лише stdlib на верхньому рівні, жодних імпортів gpurunner,
6
+ без ``from __future__ import annotations``.
7
+
8
+ 🔴 Порядок склеювання (спершу _common, потім раннер) навмисний: якщо раннер
9
+ визначає функцію з тим самим іменем, перемагає ЙОГО версія. Тому додавання
10
+ цього файлу нічого не ламає само по собі, а прибирати локальні копії можна
11
+ поступово, по одному раннеру.
12
+
13
+ Що сюди варто виносити: дрібні чисті функції, які вже скопійовані дослівно в
14
+ кілька ранерів і де розходження копій — це баг. Що НЕ варто: усе, що ранери
15
+ свідомо роблять по-різному (parseq розпаковує tgz системним ``tar``, решта —
16
+ ``tarfile``), і все, що потрібне раннерам, які їздять ще й на Modal
17
+ (``paddleocr``, ``vit_classifier``, ``yolo_spotter``): Modal бере лише сам
18
+ модуль раннера, без цього прологу, тож їхні копії мусять лишатись при них.
19
+ """
20
+ from datetime import datetime, timezone
21
+ from pathlib import Path
22
+
23
+ KAGGLE_INPUT = Path("/kaggle/input")
24
+ KAGGLE_WORKING = Path("/kaggle/working")
25
+
26
+
27
+ def _utc_iso() -> str:
28
+ return datetime.now(tz=timezone.utc).isoformat(timespec="seconds")
29
+
30
+
31
+ def _dataset_root(slug: str, gt_file: str) -> Path:
32
+ """Тека змонтованого датасету, знайдена за характерним файлом.
33
+
34
+ 🔴 Точка монтування Kaggle не одна: буває ``/kaggle/input/<slug>``, буває
35
+ ``/kaggle/input/<owner>-<slug>``, а з якогось моменту — ще й
36
+ ``/kaggle/input/datasets/<owner>/<slug>/``. Через це job падав із «не
37
+ знайдено», хоча дані лежали поруч. Тому: спершу очевидні кандидати, далі
38
+ рекурсивний пошук, і аж тоді помилка — але з деревом того, що реально
39
+ змонтовано (найчастіша справжня причина — датасет ще не розпакувався).
40
+ """
41
+ slug_short = slug.split("/")[-1]
42
+ candidates = [KAGGLE_INPUT / slug_short, KAGGLE_INPUT / slug.replace("/", "-")]
43
+ candidates += [d for d in KAGGLE_INPUT.iterdir() if d.is_dir()] if KAGGLE_INPUT.exists() else []
44
+ for c in candidates:
45
+ if (c / gt_file).exists():
46
+ return c
47
+ hits = sorted(KAGGLE_INPUT.glob(f"**/{gt_file}")) if KAGGLE_INPUT.exists() else []
48
+ if hits:
49
+ print(f"[diag] dataset root via rglob: {hits[0].parent}", flush=True)
50
+ return hits[0].parent
51
+ tree = []
52
+ if KAGGLE_INPUT.exists():
53
+ for d in sorted(KAGGLE_INPUT.iterdir()):
54
+ tree.append(str(d))
55
+ if d.is_dir():
56
+ tree += [f" {x.name}" for x in sorted(d.iterdir())[:8]]
57
+ raise RuntimeError(
58
+ f"dataset root with {gt_file} not found under {KAGGLE_INPUT}; mounted:\n"
59
+ + ("\n".join(tree) or "<порожньо>"))
60
+
61
+
62
+ def _norm(s: str) -> str:
63
+ """Нормалізація для нечіткого порівняння дореформеної кирилиці.
64
+
65
+ ѣ→е, і→и, ъ викидається — інакше та сама лексема в тексті XIX ст. і в
66
+ сучасному GT не збігається. Правило мусить бути одне на всі бенчмарки:
67
+ коли копій три, вони розходяться, і числа з різних job-ів перестають бути
68
+ порівнюваними — а вони існують саме заради порівняння.
69
+ """
70
+ s = s.lower().replace("ё", "е").replace("ъ", "").replace("ѣ", "е").replace("і", "и")
71
+ return "".join(ch for ch in s if ch.isalpha() or ch == " ").strip()