terbium-parse 0.2.0__tar.gz → 0.3.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.
- {terbium_parse-0.2.0/src/terbium_parse.egg-info → terbium_parse-0.3.0}/PKG-INFO +11 -1
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/README.md +10 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/pyproject.toml +1 -1
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/__init__.py +5 -3
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/cli.py +15 -0
- terbium_parse-0.3.0/src/terbium/extract.py +156 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0/src/terbium_parse.egg-info}/PKG-INFO +11 -1
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium_parse.egg-info/SOURCES.txt +1 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/tests/test_smoke.py +13 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/LICENSE +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/setup.cfg +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/api.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/documents/__init__.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/documents/base.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/documents/csv_adapter.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/documents/pdf.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/documents/pptx_adapter.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/documents/xlsx_adapter.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/__init__.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/ai.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/arrange.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/escalation.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/providers/__init__.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/providers/anthropic_provider.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/providers/base.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/providers/gemini_provider.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/router.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/vision.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/layout/__init__.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/layout/columns.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/layout/confidence.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/layout/dehead.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/layout/grid.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/layout/images.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/layout/labels.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/layout/lines.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/layout/signals.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/model/__init__.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/model/document.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/model/elements.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/model/record.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/model/table.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/py.typed +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/schema/__init__.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/schema/base.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/schema/furniture.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/schema/generic.py +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium_parse.egg-info/dependency_links.txt +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium_parse.egg-info/entry_points.txt +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium_parse.egg-info/requires.txt +0 -0
- {terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium_parse.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: terbium-parse
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A god-level algorithmic multi-file parser (PDF/PPTX/XLSX/CSV) that scores its own confidence and only reaches for AI when it is genuinely stuck.
|
|
5
5
|
Author: anishfyi
|
|
6
6
|
License: MIT
|
|
@@ -101,11 +101,21 @@ doc = terbium.parse("Furniture Catalogue.pdf",
|
|
|
101
101
|
ai=terbium.AI(anthropic_key="sk-..."))
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
+
Pull out the product images, each named after the product it sits under:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
manifest = terbium.export_images("lookbook.pdf", "out/")
|
|
108
|
+
# out/Kyoto_Bedside_Table.jpeg, out/Meadow_Bedside_Table.jpeg, ...
|
|
109
|
+
# manifest rows carry: product, collection, page, pixel size, colorspace,
|
|
110
|
+
# format, dpi, dominant_color, bbox
|
|
111
|
+
```
|
|
112
|
+
|
|
104
113
|
Run it from the shell:
|
|
105
114
|
|
|
106
115
|
```bash
|
|
107
116
|
terbium "Furniture Catalogue.pdf" --schema furniture
|
|
108
117
|
terbium report.xlsx --json out.json
|
|
118
|
+
terbium lookbook.pdf --images out/ # extract product photos + manifest.csv
|
|
109
119
|
```
|
|
110
120
|
|
|
111
121
|
## What it parses
|
|
@@ -70,11 +70,21 @@ doc = terbium.parse("Furniture Catalogue.pdf",
|
|
|
70
70
|
ai=terbium.AI(anthropic_key="sk-..."))
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
Pull out the product images, each named after the product it sits under:
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
manifest = terbium.export_images("lookbook.pdf", "out/")
|
|
77
|
+
# out/Kyoto_Bedside_Table.jpeg, out/Meadow_Bedside_Table.jpeg, ...
|
|
78
|
+
# manifest rows carry: product, collection, page, pixel size, colorspace,
|
|
79
|
+
# format, dpi, dominant_color, bbox
|
|
80
|
+
```
|
|
81
|
+
|
|
73
82
|
Run it from the shell:
|
|
74
83
|
|
|
75
84
|
```bash
|
|
76
85
|
terbium "Furniture Catalogue.pdf" --schema furniture
|
|
77
86
|
terbium report.xlsx --json out.json
|
|
87
|
+
terbium lookbook.pdf --images out/ # extract product photos + manifest.csv
|
|
78
88
|
```
|
|
79
89
|
|
|
80
90
|
## What it parses
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "terbium-parse"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "A god-level algorithmic multi-file parser (PDF/PPTX/XLSX/CSV) that scores its own confidence and only reaches for AI when it is genuinely stuck."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -10,18 +10,20 @@ confidence and only reaches for AI when it is genuinely stuck.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
from .api import parse, supported_extensions, DEFAULT_THRESHOLD
|
|
13
|
+
from .extract import export_images
|
|
13
14
|
from .harness import AI
|
|
14
|
-
from .harness.vision import read_page as
|
|
15
|
+
from .harness.vision import read_page as read_vision
|
|
15
16
|
from .model.document import ParsedDocument, Stats
|
|
16
17
|
from .model.record import Record
|
|
17
18
|
from .model.table import ExtractedTable
|
|
18
19
|
|
|
19
|
-
__version__ = "0.
|
|
20
|
+
__version__ = "0.3.0"
|
|
20
21
|
|
|
21
22
|
__all__ = [
|
|
22
23
|
"parse",
|
|
24
|
+
"export_images",
|
|
23
25
|
"AI",
|
|
24
|
-
"
|
|
26
|
+
"read_vision",
|
|
25
27
|
"supported_extensions",
|
|
26
28
|
"ParsedDocument",
|
|
27
29
|
"Record",
|
|
@@ -17,12 +17,27 @@ def main(argv=None) -> int:
|
|
|
17
17
|
ap.add_argument("file", help="path to a " + "/".join(supported_extensions()) + " file")
|
|
18
18
|
ap.add_argument("--schema", default="generic", help="generic (default) or furniture")
|
|
19
19
|
ap.add_argument("--json", metavar="OUT", help="write records as JSON to this path (or - for stdout)")
|
|
20
|
+
ap.add_argument("--images", metavar="DIR", help="extract product images into DIR (+ a manifest.csv)")
|
|
20
21
|
ap.add_argument("--ai", action="store_true", help="enable the AI lane using env keys")
|
|
21
22
|
ap.add_argument("--tier", choices=["haiku", "sonnet", "opus"], help="pin the AI model tier")
|
|
22
23
|
ap.add_argument("--limit", type=int, default=12, help="how many records to preview")
|
|
23
24
|
ap.add_argument("--version", action="version", version=f"terbium {__version__}")
|
|
24
25
|
args = ap.parse_args(argv)
|
|
25
26
|
|
|
27
|
+
if args.images:
|
|
28
|
+
from .extract import export_images
|
|
29
|
+
import csv as _csv
|
|
30
|
+
|
|
31
|
+
manifest = export_images(args.file, args.images)
|
|
32
|
+
cols = ["page", "product", "collection", "file", "format", "width_px",
|
|
33
|
+
"height_px", "colorspace", "bytes", "dpi", "dominant_color", "bbox"]
|
|
34
|
+
with open(f"{args.images}/manifest.csv", "w", newline="") as f:
|
|
35
|
+
w = _csv.DictWriter(f, fieldnames=cols)
|
|
36
|
+
w.writeheader()
|
|
37
|
+
w.writerows(manifest)
|
|
38
|
+
print(f"extracted {len(manifest)} images -> {args.images}/ (+ manifest.csv)")
|
|
39
|
+
return 0
|
|
40
|
+
|
|
26
41
|
ai = AI(force_tier=args.tier) if args.ai else None
|
|
27
42
|
doc = parse(args.file, schema=args.schema, ai=ai)
|
|
28
43
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Export the actual product images, not just the text.
|
|
2
|
+
|
|
3
|
+
This pulls every embedded photo out of a PDF, and - for lookbooks - names each
|
|
4
|
+
file after the product it sits under. It also reports the qualities that matter
|
|
5
|
+
downstream: pixel size, colorspace, format, position, effective DPI, dominant
|
|
6
|
+
colour, and the associated product name and collection.
|
|
7
|
+
|
|
8
|
+
Bytes come from PyMuPDF (already a terbium dependency), which hands back a
|
|
9
|
+
ready-to-save JPEG/PNG. Repeated images (a logo stamped on every page) are
|
|
10
|
+
detected and skipped so you do not export the same watermark 80 times.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import io
|
|
15
|
+
import os
|
|
16
|
+
import re
|
|
17
|
+
from collections import defaultdict
|
|
18
|
+
from typing import List, Optional
|
|
19
|
+
|
|
20
|
+
import fitz
|
|
21
|
+
|
|
22
|
+
from .documents.pdf import PdfAdapter
|
|
23
|
+
from .layout import labels as _labels
|
|
24
|
+
from .layout.images import classify
|
|
25
|
+
from .layout.lines import cluster_lines
|
|
26
|
+
from .model.elements import ImageRef, Page
|
|
27
|
+
|
|
28
|
+
_CS = {1: "Gray", 3: "RGB", 4: "CMYK"}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _safe(name: str) -> str:
|
|
32
|
+
return re.sub(r"[^A-Za-z0-9._-]+", "_", name).strip("_")[:80] or "image"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _label_positions(page: Page) -> List[dict]:
|
|
36
|
+
"""Merged product labels on a page, each with a centre and baseline."""
|
|
37
|
+
cands = []
|
|
38
|
+
for ln in cluster_lines(page.words):
|
|
39
|
+
for seg in _labels._segment_row(ln):
|
|
40
|
+
if _labels._is_label(seg):
|
|
41
|
+
cands.append(seg)
|
|
42
|
+
return _labels._merge_wrapped(cands) if cands else []
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _associate(bbox, items: List[dict]) -> Optional[str]:
|
|
46
|
+
"""The label sitting just below an image, horizontally aligned to it."""
|
|
47
|
+
if not bbox or not items:
|
|
48
|
+
return None
|
|
49
|
+
ix0, iy0, ix1, iy1 = bbox
|
|
50
|
+
best, best_dy = None, 1e9
|
|
51
|
+
for it in items:
|
|
52
|
+
if ix0 - 25 <= it["cx"] <= ix1 + 25 and it["y"] > iy1 - 8:
|
|
53
|
+
dy = it["y"] - iy1
|
|
54
|
+
if dy < best_dy and dy < 140:
|
|
55
|
+
best, best_dy = it, dy
|
|
56
|
+
return best["name"] if best else None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _dominant_color(data: bytes) -> Optional[str]:
|
|
60
|
+
try:
|
|
61
|
+
from PIL import Image
|
|
62
|
+
|
|
63
|
+
with Image.open(io.BytesIO(data)) as im:
|
|
64
|
+
im = im.convert("RGB").resize((16, 16))
|
|
65
|
+
px = list(im.getdata())
|
|
66
|
+
r = sum(p[0] for p in px) // len(px)
|
|
67
|
+
g = sum(p[1] for p in px) // len(px)
|
|
68
|
+
b = sum(p[2] for p in px) // len(px)
|
|
69
|
+
return f"#{r:02x}{g:02x}{b:02x}"
|
|
70
|
+
except Exception:
|
|
71
|
+
return None
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _dpi(width_px: int, bbox) -> Optional[int]:
|
|
75
|
+
if not bbox:
|
|
76
|
+
return None
|
|
77
|
+
width_pt = bbox[2] - bbox[0]
|
|
78
|
+
if width_pt <= 0:
|
|
79
|
+
return None
|
|
80
|
+
return round(width_px / (width_pt / 72.0))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def export_images(
|
|
84
|
+
path: str,
|
|
85
|
+
out_dir: str,
|
|
86
|
+
only_photos: bool = True,
|
|
87
|
+
associate: bool = True,
|
|
88
|
+
dedupe_repeats: bool = True,
|
|
89
|
+
min_side: int = 180,
|
|
90
|
+
max_aspect: float = 4.0,
|
|
91
|
+
) -> List[dict]:
|
|
92
|
+
"""Extract images from ``path`` into ``out_dir``; return a manifest.
|
|
93
|
+
|
|
94
|
+
``only_photos`` skips icons, swatches, thin banners, and slivers, keeping real
|
|
95
|
+
product photos (shorter side >= ``min_side`` px, aspect ratio <= ``max_aspect``).
|
|
96
|
+
``associate`` names lookbook photos after the product beneath them.
|
|
97
|
+
``dedupe_repeats`` drops logos/watermarks that recur across many pages.
|
|
98
|
+
"""
|
|
99
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
100
|
+
pages = PdfAdapter().parse(path)
|
|
101
|
+
n = max(1, len(pages))
|
|
102
|
+
|
|
103
|
+
xref_pages = defaultdict(set)
|
|
104
|
+
for p in pages:
|
|
105
|
+
for im in p.images:
|
|
106
|
+
if im.xref:
|
|
107
|
+
xref_pages[im.xref].add(p.index)
|
|
108
|
+
repeated = {x for x, pgs in xref_pages.items() if len(pgs) >= max(3, 0.2 * n)}
|
|
109
|
+
|
|
110
|
+
manifest: List[dict] = []
|
|
111
|
+
used_names: dict = {}
|
|
112
|
+
doc = fitz.open(path)
|
|
113
|
+
try:
|
|
114
|
+
for p in pages:
|
|
115
|
+
items = _label_positions(p) if associate else []
|
|
116
|
+
collection = _labels._pick_collection(items, p) if items else None
|
|
117
|
+
for im in p.images:
|
|
118
|
+
if only_photos:
|
|
119
|
+
if im.kind != "photo":
|
|
120
|
+
continue
|
|
121
|
+
lo, hi = min(im.width, im.height), max(im.width, im.height)
|
|
122
|
+
if lo < min_side or (lo and hi / lo > max_aspect):
|
|
123
|
+
continue # a banner strip or sliver, not a product
|
|
124
|
+
if dedupe_repeats and im.xref in repeated:
|
|
125
|
+
continue
|
|
126
|
+
try:
|
|
127
|
+
d = doc.extract_image(im.xref)
|
|
128
|
+
except Exception:
|
|
129
|
+
continue
|
|
130
|
+
data = d["image"]
|
|
131
|
+
product = _associate(im.bbox, items) if associate else None
|
|
132
|
+
|
|
133
|
+
stem = _safe(product) if product else f"page{p.index + 1:03d}"
|
|
134
|
+
count = used_names.get(stem, 0) + 1
|
|
135
|
+
used_names[stem] = count
|
|
136
|
+
fname = f"{stem}.{d['ext']}" if count == 1 else f"{stem}_{count}.{d['ext']}"
|
|
137
|
+
with open(os.path.join(out_dir, fname), "wb") as f:
|
|
138
|
+
f.write(data)
|
|
139
|
+
|
|
140
|
+
manifest.append({
|
|
141
|
+
"page": p.index + 1,
|
|
142
|
+
"product": product,
|
|
143
|
+
"collection": collection if product else None,
|
|
144
|
+
"file": fname,
|
|
145
|
+
"format": d["ext"],
|
|
146
|
+
"width_px": d["width"],
|
|
147
|
+
"height_px": d["height"],
|
|
148
|
+
"colorspace": _CS.get(d.get("colorspace"), d.get("colorspace")),
|
|
149
|
+
"bytes": len(data),
|
|
150
|
+
"dpi": _dpi(d["width"], im.bbox),
|
|
151
|
+
"dominant_color": _dominant_color(data),
|
|
152
|
+
"bbox": tuple(round(v, 1) for v in im.bbox) if im.bbox else None,
|
|
153
|
+
})
|
|
154
|
+
finally:
|
|
155
|
+
doc.close()
|
|
156
|
+
return manifest
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: terbium-parse
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A god-level algorithmic multi-file parser (PDF/PPTX/XLSX/CSV) that scores its own confidence and only reaches for AI when it is genuinely stuck.
|
|
5
5
|
Author: anishfyi
|
|
6
6
|
License: MIT
|
|
@@ -101,11 +101,21 @@ doc = terbium.parse("Furniture Catalogue.pdf",
|
|
|
101
101
|
ai=terbium.AI(anthropic_key="sk-..."))
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
+
Pull out the product images, each named after the product it sits under:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
manifest = terbium.export_images("lookbook.pdf", "out/")
|
|
108
|
+
# out/Kyoto_Bedside_Table.jpeg, out/Meadow_Bedside_Table.jpeg, ...
|
|
109
|
+
# manifest rows carry: product, collection, page, pixel size, colorspace,
|
|
110
|
+
# format, dpi, dominant_color, bbox
|
|
111
|
+
```
|
|
112
|
+
|
|
104
113
|
Run it from the shell:
|
|
105
114
|
|
|
106
115
|
```bash
|
|
107
116
|
terbium "Furniture Catalogue.pdf" --schema furniture
|
|
108
117
|
terbium report.xlsx --json out.json
|
|
118
|
+
terbium lookbook.pdf --images out/ # extract product photos + manifest.csv
|
|
109
119
|
```
|
|
110
120
|
|
|
111
121
|
## What it parses
|
|
@@ -112,6 +112,19 @@ def test_labels_need_a_grid_not_prose():
|
|
|
112
112
|
assert extract_labels(cluster_lines(words), page) is None
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
def test_image_label_association():
|
|
116
|
+
# a photo should bind to the label sitting just below it, not a distant one
|
|
117
|
+
from terbium.extract import _associate
|
|
118
|
+
|
|
119
|
+
items = [
|
|
120
|
+
{"name": "Kyoto Bedside Table", "cx": 200, "y": 280, "size": 12},
|
|
121
|
+
{"name": "Far Away", "cx": 900, "y": 280, "size": 12},
|
|
122
|
+
{"name": "Above It", "cx": 200, "y": 20, "size": 12},
|
|
123
|
+
]
|
|
124
|
+
assert _associate((100, 50, 300, 250), items) == "Kyoto Bedside Table"
|
|
125
|
+
assert _associate((100, 50, 300, 250), []) is None
|
|
126
|
+
|
|
127
|
+
|
|
115
128
|
def test_csv_roundtrip(tmp_path):
|
|
116
129
|
import terbium
|
|
117
130
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/providers/anthropic_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
{terbium_parse-0.2.0 → terbium_parse-0.3.0}/src/terbium/harness/providers/gemini_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|