fyron 0.1.0__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.
fyron-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bits & Flames
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.
fyron-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,576 @@
1
+ Metadata-Version: 2.4
2
+ Name: fyron
3
+ Version: 0.1.0
4
+ Summary: Fyron is an open-source Python toolkit for interoperable healthcare data and AI workflows. It provides unified access to FHIR data via REST APIs and relational (SQL-backed) FHIR servers, integrates DICOM imaging sources, and enables semantic exploration of clinical narratives using modern language models.
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: FHIR,DICOM,healthcare,clinical,NLP,LLM
8
+ Author: Bits & Flames
9
+ Author-email: hello@bitsandflames.ai
10
+ Requires-Python: >=3.10,<4.0
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Dist: SimpleITK (>=2.3.1,<3.0.0)
19
+ Requires-Dist: dicomweb-client (>=0.59.1,<0.60.0)
20
+ Requires-Dist: pandas (>=2.2.0,<3.0.0)
21
+ Requires-Dist: psycopg (>=3.1.19,<4.0.0)
22
+ Requires-Dist: pydicom (>=2.4.4,<3.0.0)
23
+ Requires-Dist: pyjwt (>=2.9.0,<3.0.0)
24
+ Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
25
+ Requires-Dist: requests (>=2.32.0,<3.0.0)
26
+ Requires-Dist: tqdm (>=4.66.0,<5.0.0)
27
+ Project-URL: Homepage, https://github.com/bitsandflames/fyron
28
+ Project-URL: Repository, https://github.com/bitsandflames/fyron
29
+ Description-Content-Type: text/markdown
30
+
31
+ # Fyron
32
+ ![Fyron Banner](images/BF-Fyron.png)
33
+ <div align="center">
34
+ <img alt="Python" src="https://img.shields.io/badge/python-3.10%2B-blue" />
35
+ <img alt="License" src="https://img.shields.io/badge/license-MIT-green" />
36
+ <img alt="FHIR" src="https://img.shields.io/badge/FHIR-enabled-0b7dda" />
37
+ <img alt="DICOM" src="https://img.shields.io/badge/DICOM-supported-2e7d32" />
38
+ <img alt="Documents" src="https://img.shields.io/badge/Documents-supported-6a1b9a" />
39
+ </div>
40
+
41
+ Fyron is a pragmatic Python toolkit for interoperable healthcare data and AI workflows. It gives developers clean, testable primitives for FHIR (REST + SQL), DICOM imaging, document downloads, Teable integration, and LLM-assisted analysis without hiding the underlying protocols.
42
+
43
+ ## Quickstart
44
+ Install and run a first FHIR query in minutes.
45
+
46
+ This installs the package from PyPI.
47
+ ```bash
48
+ pip install fyron
49
+ ```
50
+
51
+ Requires Python 3.10+.
52
+
53
+ This copies the sample environment file so you can fill in your credentials.
54
+ ```bash
55
+ cp .example.env .env
56
+ # edit .env with your credentials
57
+ ```
58
+
59
+ This loads the .env explicitly (recommended in production or scripts).
60
+ ```python
61
+ from fyron import load_env
62
+
63
+ load_env()
64
+ ```
65
+ Options: `path` (explicit .env), `override` (replace existing env vars), `warn_if_missing`.
66
+
67
+ This runs a minimal REST query and returns a DataFrame.
68
+ ```python
69
+ from fyron import FHIRRestClient
70
+
71
+ client = FHIRRestClient()
72
+ patients = client.query_df(
73
+ resource_type="Patient",
74
+ params={"_count": 25, "_sort": "_id"},
75
+ max_pages=1,
76
+ )
77
+ print(patients.head())
78
+ ```
79
+
80
+ ## Who It’s For
81
+ - Data scientists and ML engineers building clinical datasets and cohorts.
82
+ - Clinical informatics and analytics teams working across FHIR, SQL, and imaging.
83
+ - Researchers who need reproducible pipelines and methods-ready documentation.
84
+ - Engineers integrating healthcare data into apps, dashboards, or LLM workflows.
85
+
86
+ ## Package Layout
87
+ ```
88
+ fyron/
89
+ fhir/ # REST + SQL clients, auth, types, utilities
90
+ dicom/ # DICOM downloader
91
+ documents/ # Document downloader
92
+ llm/ # LLM agent
93
+ core/ # IO + integrations
94
+ ```
95
+
96
+ ## Supported Tabs
97
+ Use the tabs below to see what Fyron supports in each area.
98
+
99
+ <details open>
100
+ <summary><strong>FHIR</strong></summary>
101
+ REST client with pagination, caching, multiprocessing, and FHIRPath extraction. SQL client for Postgres-backed FHIR stores.
102
+ </details>
103
+
104
+ <details>
105
+ <summary><strong>DICOM</strong></summary>
106
+ DICOMweb downloads with optional NIfTI conversion and per-series/study manifests.
107
+ </details>
108
+
109
+ <details>
110
+ <summary><strong>Documents</strong></summary>
111
+ Deterministic URL downloads with metadata, hashes, and DataFrame helpers.
112
+ </details>
113
+
114
+ <details>
115
+ <summary><strong>Teable</strong></summary>
116
+ Read/write DataFrames to Teable tables with pagination, overwrite support, and base/table creation.
117
+ </details>
118
+
119
+ <details>
120
+ <summary><strong>LLM</strong></summary>
121
+ Prompting utilities for text, documents, images, and PDFs via configurable providers.
122
+ </details>
123
+
124
+ ## Setup
125
+ Fyron reads environment variables (see `.example.env`). Minimal examples:
126
+
127
+ This shows the smallest set of environment variables for the main features.
128
+ ```bash
129
+ # FHIR REST
130
+ export FHIR_BASE_URL="https://example.org/fhir"
131
+ export FHIR_AUTH_URL="https://example.org/auth/token"
132
+ export FHIR_REFRESH_URL="https://example.org/auth/refresh"
133
+ export FHIR_USER="alice"
134
+ export FHIR_PASSWORD="secret"
135
+
136
+ # DICOMweb
137
+ export DICOM_WEB_URL="https://pacs.example.org/dicomweb"
138
+ export DICOM_USER="alice"
139
+ export DICOM_PASSWORD="secret"
140
+
141
+ # FHIR SQL
142
+ export FHIR_DB_HOST="localhost"
143
+ export FHIR_DB_PORT="5432"
144
+ export FHIR_DB_NAME="fhir"
145
+ export FHIR_DB_USER="postgres"
146
+ export FHIR_DB_PASSWORD="secret"
147
+
148
+ # LLM
149
+ export LLM_PROVIDER="openai"
150
+ export LLM_BASE_URL="https://api.openai.com"
151
+ export LLM_API_KEY="your_api_key"
152
+ export LLM_MODEL="gpt-4.1-mini"
153
+
154
+ # Teable
155
+ export TEABLE_BASE_URL="https://app.teable.ai"
156
+ export TEABLE_TOKEN="your_teable_token"
157
+ ```
158
+
159
+ Notes:
160
+ - FHIR token auth uses `FHIR_USER`/`FHIR_PASSWORD` plus `FHIR_AUTH_URL`.
161
+ - Use `Auth.token_env(...)` for the simplest token-based auth flow.
162
+ - FHIRPath extraction requires `fhirpathpy` (`pip install fhirpathpy`).
163
+ - Use `load_env()` to load a `.env` from the current working directory.
164
+ ## Core Workflows
165
+
166
+ ### FHIR REST
167
+ This queries a single FHIR resource type and returns rows as a DataFrame.
168
+ ```python
169
+ from fyron import FHIRRestClient
170
+
171
+ client = FHIRRestClient()
172
+ patients = client.query_df(
173
+ resource_type="Patient",
174
+ params={"_count": 25, "_sort": "_id"},
175
+ max_pages=1,
176
+ )
177
+ print(patients.head())
178
+ ```
179
+
180
+ `FHIRRestClient` arguments:
181
+
182
+ | Argument | Description | Values/Defaults |
183
+ | --- | --- | --- |
184
+ | `base_url` | FHIR server base URL | Defaults to `FHIR_BASE_URL` |
185
+ | `auth` | Reuse `Auth` or `requests.Session` | Optional |
186
+ | `num_processes` | Parallel worker count | Default `4` |
187
+ | `request_timeout` | Per-request timeout in seconds | Default `30` |
188
+ | `log_requests` | Log timing and summaries | `True`/`False` |
189
+ | `return_fhir_obj` | Wrap bundles as `FHIRObj` | `True`/`False` |
190
+
191
+ This uses FHIRPath expressions to extract specific fields.
192
+ ```python
193
+ from fyron import FHIRRestClient
194
+
195
+ client = FHIRRestClient()
196
+
197
+ df = client.query_df(
198
+ resource_type="Observation",
199
+ params={"_count": 25},
200
+ fhir_paths=[
201
+ ("patient_id", "subject.reference.replace('Patient/', '')"),
202
+ ("code", "code.coding.code"),
203
+ ("value", "valueQuantity.value"),
204
+ ],
205
+ max_pages=1,
206
+ )
207
+ ```
208
+
209
+ This applies a custom bundle processor with safe FHIR extraction.
210
+ `safe_get` supports dotted paths and list indexes (for example, `"code.coding[0].code"`).
211
+ ```python
212
+ from fyron import FHIRObj, FHIRRestClient, safe_get
213
+
214
+ client = FHIRRestClient(return_fhir_obj=True)
215
+
216
+
217
+ def process_bundle(bundle):
218
+ bundle = FHIRObj(**bundle) if isinstance(bundle, dict) else bundle
219
+ rows = []
220
+ for entry in bundle.entry or []:
221
+ resource = entry.resource
222
+ rows.append({
223
+ "resourceType": safe_get(resource, "resourceType"),
224
+ "id": safe_get(resource, "id"),
225
+ "subject": safe_get(resource, "subject.reference"),
226
+ "code": safe_get(resource, "code.coding[0].code"),
227
+ })
228
+ return {"Resource": rows}
229
+
230
+ custom_df = client.query_df(
231
+ resource_type="Patient",
232
+ params={"_count": 10},
233
+ mode="custom",
234
+ process_function=process_bundle,
235
+ max_pages=1,
236
+ )
237
+ ```
238
+
239
+ This runs one query per row in a DataFrame and fetches in parallel.
240
+ ```python
241
+ from fyron import Auth, FHIRRestClient
242
+
243
+ auth = Auth.token_env(
244
+ auth_url="https://example.org/auth/token",
245
+ refresh_url="https://example.org/auth/refresh",
246
+ )
247
+
248
+ client = FHIRRestClient(auth=auth, num_processes=4)
249
+
250
+ result_df = client.query_df_from(
251
+ df=patients_df,
252
+ resource_type="Observation",
253
+ column_map={"subject": "patient_id"},
254
+ params={"_count": 50},
255
+ parallel_fetch=True,
256
+ )
257
+ ```
258
+
259
+ `Auth.token_env` arguments:
260
+
261
+ | Argument | Description | Values/Defaults |
262
+ | --- | --- | --- |
263
+ | `auth_url` | Token endpoint URL | Required |
264
+ | `refresh_url` | Refresh endpoint | Optional |
265
+ | `token` | Pre-issued bearer token | Optional |
266
+
267
+ ### FHIR SQL
268
+ This runs a SQL query and returns a DataFrame.
269
+ ```python
270
+ from fyron import FHIRSQLClient
271
+
272
+ sql_client = FHIRSQLClient()
273
+
274
+ patients = sql_client.query_df(
275
+ "SELECT id, resource_type FROM fhir_resources WHERE resource_type = %s",
276
+ params=["Patient"],
277
+ )
278
+
279
+ sql = """
280
+ SELECT id, resource_type, subject_id
281
+ FROM observation
282
+ WHERE subject_id IN ({patient_ids})
283
+ """
284
+
285
+ obs = sql_client.query_df_from(
286
+ df=patients_df,
287
+ sql=sql,
288
+ column_map={"patient_ids": "patient_id"},
289
+ chunk_size=500,
290
+ parallel=True,
291
+ )
292
+ ```
293
+
294
+ `FHIRSQLClient` arguments:
295
+
296
+ | Argument | Description | Values/Defaults |
297
+ | --- | --- | --- |
298
+ | `dsn` | Full connection string | Optional |
299
+ | `host` | DB host | Optional |
300
+ | `port` | DB port | Optional |
301
+ | `dbname` | Database name | Optional |
302
+ | `user` | DB user | Optional |
303
+ | `password` | DB password | Optional |
304
+ | `connect_timeout` | Connect timeout (seconds) | Default `10` |
305
+ | `log_queries` | Log SQL queries | `True`/`False` |
306
+
307
+ ### FHIR Utilities
308
+ This uses built-in bundle processors to standardize common resources.
309
+ ```python
310
+ from fyron import (
311
+ FHIRRestClient,
312
+ process_patient_bundle,
313
+ process_encounter_bundle,
314
+ process_observation_bundle,
315
+ process_condition_bundle,
316
+ process_procedure_bundle,
317
+ process_imaging_study_bundle,
318
+ process_diagnostic_report_bundle,
319
+ )
320
+
321
+ client = FHIRRestClient(return_fhir_obj=True)
322
+
323
+ patients = client.query_df(
324
+ resource_type="Patient",
325
+ params={"_count": 25},
326
+ mode="custom",
327
+ process_function=process_patient_bundle,
328
+ )
329
+ ```
330
+
331
+ ### DICOM
332
+ This downloads a single DICOM series and optionally converts to NIfTI.
333
+ ```python
334
+ from fyron import DICOMDownloader
335
+
336
+ loader = DICOMDownloader(output_format="nifti", num_processes=2)
337
+
338
+ results = loader.download_series(
339
+ study_uid="1.2.3.4.5",
340
+ series_uid="1.2.3.4.5.6",
341
+ output_dir="dicom_out",
342
+ )
343
+ print(results)
344
+ ```
345
+
346
+ `DICOMDownloader` arguments:
347
+
348
+ | Argument | Description | Values/Defaults |
349
+ | --- | --- | --- |
350
+ | `dicom_web_url` | DICOMweb endpoint | Defaults to `DICOM_WEB_URL` |
351
+ | `output_format` | Output format | `"dicom"` or `"nifti"` |
352
+ | `num_processes` | Parallel workers for DataFrame downloads | Default `1` |
353
+ | `use_compression` | Compress NIfTI output | `True`/`False` |
354
+
355
+ This downloads studies or series listed in a DataFrame.
356
+ ```python
357
+ from fyron import DICOMDownloader
358
+
359
+ loader = DICOMDownloader(output_format="nifti", num_processes=2)
360
+
361
+ downloads = loader.download_from_df(
362
+ df=imaging_df, # must include study_instance_uid and series_instance_uid
363
+ output_dir="dicom_out",
364
+ )
365
+ print(downloads.head())
366
+ ```
367
+
368
+ ### Documents
369
+ This downloads a list of document URLs to a deterministic folder structure.
370
+ ```python
371
+ from fyron import DocumentDownloader
372
+
373
+ urls = [
374
+ "https://example.org/reports/report1.pdf",
375
+ "https://example.org/reports/report2.pdf",
376
+ ]
377
+
378
+ loader = DocumentDownloader(output_dir="docs_out")
379
+ results = loader.download_urls(urls)
380
+ print(results.head())
381
+ ```
382
+
383
+ `DocumentDownloader` arguments:
384
+
385
+ | Argument | Description | Values/Defaults |
386
+ | --- | --- | --- |
387
+ | `base_url` | Prefix for relative URLs | Defaults to `FHIR_BASE_URL` |
388
+ | `output_dir` | Download folder | Default `documents_out` |
389
+ | `timeout` | Request timeout (seconds) | Default `30` |
390
+ | `skip_existing` | Skip if file exists | `True`/`False` |
391
+ | `max_workers` | Thread pool size | Default `4` |
392
+ | `save_mode` | File mode | `"auto"`, `"txt"`, `"pdf"` |
393
+ | `force_extension` | Force file extension | Optional |
394
+
395
+ This downloads documents referenced by a DataFrame column.
396
+ ```python
397
+ from fyron import DocumentDownloader
398
+
399
+ loader = DocumentDownloader(output_dir="docs_out")
400
+ results = loader.download_from_df(df=docs_df, url_col="document_url")
401
+ print(results.head())
402
+ ```
403
+
404
+ ### LLM Agent
405
+ This sends a single prompt and returns the model response.
406
+ ```python
407
+ from fyron import LLMAgent
408
+
409
+ agent = LLMAgent(provider="openai")
410
+ response = agent.prompt("Summarize the key findings in this dataset")
411
+ print(response)
412
+ ```
413
+
414
+ `LLMAgent` arguments:
415
+
416
+ | Argument | Description | Values/Defaults |
417
+ | --- | --- | --- |
418
+ | `provider` | Provider type | `"openai"`, `"anyllm"`, `"custom"` |
419
+ | `base_url` | API base or custom endpoint | Defaults to `LLM_BASE_URL` |
420
+ | `api_key` | API key | Optional (required by some providers) |
421
+ | `model` | Model name | Defaults to `LLM_MODEL` |
422
+ | `timeout` | Request timeout (seconds) | Default `30` |
423
+ | `verify_ssl` | Verify TLS | `True`/`False` |
424
+
425
+ This runs a prompt across a list of documents or file paths.
426
+ ```python
427
+ from fyron import LLMAgent
428
+
429
+ agent = LLMAgent(provider="openai")
430
+
431
+ docs = ["doc1 text", "doc2 text", "/path/to/file.txt"]
432
+ summary_df = agent.prompt_documents(
433
+ documents=docs,
434
+ prompt="Summarize clinically relevant findings.",
435
+ output_csv="doc_summaries.csv",
436
+ )
437
+ print(summary_df.head())
438
+ ```
439
+
440
+ This runs a prompt across a DataFrame column and appends results.
441
+ ```python
442
+ from fyron import LLMAgent
443
+
444
+ agent = LLMAgent(provider="openai")
445
+
446
+ out = agent.prompt_dataframe(
447
+ df=notes_df,
448
+ text_col="note_text",
449
+ prompt="Extract key diagnoses.",
450
+ output_col="diagnoses",
451
+ output_csv="notes_with_diagnoses.csv",
452
+ )
453
+ print(out.head())
454
+ ```
455
+
456
+ This generates a Methods-style description of a Python module.
457
+ ```python
458
+ from fyron import LLMAgent
459
+
460
+ agent = LLMAgent(provider="openai")
461
+ md = agent.describe_python_file(
462
+ file_path="src/project/pipeline.py",
463
+ output_md="docs/methods_pipeline.md",
464
+ )
465
+ print(md[:400])
466
+ ```
467
+
468
+ ### Data IO
469
+ This provides shortcut CSV and Excel read/write helpers.
470
+ ```python
471
+ from fyron import read_csv, write_excel
472
+
473
+ df = read_csv("patients.csv")
474
+ write_excel(df, "patients.xlsx")
475
+ ```
476
+
477
+ ### Teable
478
+ This reads and writes Teable tables as DataFrames.
479
+ ```python
480
+ from fyron import TeableClient
481
+
482
+ teable = TeableClient(base_url="https://app.teable.ai", token="YOUR_TOKEN")
483
+
484
+ df = teable.read_table("tblXXXX")
485
+ record_ids = teable.write_dataframe("tblXXXX", df)
486
+ ```
487
+
488
+ `TeableClient` arguments:
489
+
490
+ | Argument | Description | Values/Defaults |
491
+ | --- | --- | --- |
492
+ | `base_url` | Teable API base | Defaults to `TEABLE_BASE_URL` |
493
+ | `token` | Teable API token | Required |
494
+ | `timeout` | Request timeout (seconds) | Default `30` |
495
+
496
+ This lists spaces, bases, and tables for discovery.
497
+ ```python
498
+ from fyron import TeableClient
499
+
500
+ teable = TeableClient(base_url="https://app.teable.ai", token="YOUR_TOKEN")
501
+
502
+ spaces = teable.list_spaces()
503
+ space_id = spaces[0]["id"] if spaces else None
504
+ bases = teable.list_bases(space_id=space_id) if space_id else []
505
+ tables = teable.list_tables(bases[0]["id"]) if bases else []
506
+ ```
507
+
508
+ This ensures a base and table exist (creating them if needed).
509
+ ```python
510
+ from fyron import TeableClient
511
+
512
+ teable = TeableClient(base_url="https://app.teable.ai", token="YOUR_TOKEN")
513
+
514
+ base = teable.get_or_create_base(name="Clinical", space_name="My Workspace")
515
+ base_id = base["id"]
516
+
517
+ table = teable.get_or_create_table(
518
+ base_id=base_id,
519
+ name="Observations",
520
+ )
521
+ ```
522
+
523
+ This fully replaces a table by deleting then inserting all rows.
524
+ ```python
525
+ from fyron import TeableClient
526
+
527
+ teable = TeableClient(base_url="https://app.teable.ai", token="YOUR_TOKEN")
528
+ record_ids = teable.overwrite_table("tblXXXX", df)
529
+ ```
530
+
531
+ ## Caching
532
+ This configures the in-memory cache for FHIR REST GET responses.
533
+ ```python
534
+ from fyron import FHIRRestClient
535
+
536
+ client = FHIRRestClient(
537
+ cache_ttl_seconds=300, # default 5 minutes
538
+ cache_max_entries=1000, # default 1000 responses
539
+ )
540
+
541
+ # Disable caching
542
+ client = FHIRRestClient(cache_ttl_seconds=0, cache_max_entries=0)
543
+ ```
544
+
545
+ ## Logging
546
+ This enables request logging and summary output.
547
+ ```python
548
+ import logging
549
+ from fyron import FHIRRestClient
550
+
551
+ logging.basicConfig(level=logging.INFO)
552
+ client = FHIRRestClient(log_requests=True)
553
+ ```
554
+
555
+ ## CLI
556
+ This runs a DICOM download from the command line.
557
+
558
+ ```bash
559
+ fyron-dicom --dicom-web-url https://pacs.example.org/dicomweb \
560
+ --study-uid 1.2.3.4.5 --series-uid 1.2.3.4.5.6 --output-dir dicom_out
561
+ ```
562
+
563
+ ## Example Scripts
564
+ These examples connect to public or configured services for quick validation.
565
+
566
+ ```bash
567
+ python examples/hapi_rest_example.py
568
+ python examples/sql_examples.py
569
+ ```
570
+
571
+ ## Contributing
572
+ See `CONTRIBUTING.md`.
573
+
574
+ ## License
575
+ MIT
576
+