Feature Extraction
Transformers
Safetensors
lemondecider_decision
decision-engine
typed-decision
choice
classification
Merge
qwen3.5
custom_code
Instructions to use inclusionAI/LemonDecider with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use inclusionAI/LemonDecider with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="inclusionAI/LemonDecider", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("inclusionAI/LemonDecider", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Upload local_images.py with huggingface_hub
Browse files- local_images.py +97 -0
local_images.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Versioned, bounded local image admission. Never opens a URL or unregistered path."""
|
| 2 |
+
import hashlib
|
| 3 |
+
import io
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import re
|
| 6 |
+
import stat
|
| 7 |
+
import warnings
|
| 8 |
+
from PIL import Image
|
| 9 |
+
|
| 10 |
+
SCHEMA = 'lemonjev-local-images-v1'
|
| 11 |
+
POLICY = 'registered-local-v1'
|
| 12 |
+
MAX_BYTES = 32 * 1024 * 1024
|
| 13 |
+
MAX_PIXELS = 16_000_000
|
| 14 |
+
MAX_IMAGES = 8
|
| 15 |
+
IMAGE_SIZE = {'shortest_edge': 65536, 'longest_edge': 262144}
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def asset_record(record):
|
| 19 |
+
if not isinstance(record, dict) or set(record) != {'path', 'sha256', 'bytes'}:
|
| 20 |
+
raise ValueError('Image assets require exactly path, sha256, bytes')
|
| 21 |
+
path = record['path']
|
| 22 |
+
if not isinstance(path, str) or not Path(path).is_absolute() or str(Path(path)) != path or '..' in Path(path).parts:
|
| 23 |
+
raise ValueError('Image path must be a canonical absolute local path')
|
| 24 |
+
if not isinstance(record['sha256'], str) or not re.fullmatch('[0-9a-f]{64}', record['sha256']):
|
| 25 |
+
raise ValueError('Invalid image SHA256')
|
| 26 |
+
if type(record['bytes']) is not int or not 0 < record['bytes'] <= MAX_BYTES:
|
| 27 |
+
raise ValueError('Image byte bound exceeded')
|
| 28 |
+
return dict(record)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class LocalImageRegistry:
|
| 32 |
+
def __init__(self, manifest):
|
| 33 |
+
if not isinstance(manifest, dict) or set(manifest) != {'schema', 'images'} or manifest['schema'] != SCHEMA:
|
| 34 |
+
raise ValueError('Missing versioned local_images manifest')
|
| 35 |
+
if not isinstance(manifest['images'], list):
|
| 36 |
+
raise ValueError('Image registry must be a list')
|
| 37 |
+
self.assets = {}
|
| 38 |
+
for value in manifest['images']:
|
| 39 |
+
record = asset_record(value)
|
| 40 |
+
if record['path'] in self.assets:
|
| 41 |
+
raise ValueError('Duplicate registered image path')
|
| 42 |
+
self.assets[record['path']] = record
|
| 43 |
+
|
| 44 |
+
def records(self, row):
|
| 45 |
+
paths = row.get('images', [])
|
| 46 |
+
records = row.get('source', {}).get('image_assets', [])
|
| 47 |
+
if not isinstance(paths, list) or len(paths) > MAX_IMAGES or not isinstance(records, list) or len(paths) != len(records):
|
| 48 |
+
raise ValueError('Images/image_assets must correspond one-to-one within image limit')
|
| 49 |
+
result = []
|
| 50 |
+
for path, value in zip(paths, records, strict=True):
|
| 51 |
+
record = asset_record(value)
|
| 52 |
+
if not isinstance(path, str) or path != record['path'] or self.assets.get(path) != record:
|
| 53 |
+
raise ValueError('Unregistered or mismatched row image asset')
|
| 54 |
+
result.append(record)
|
| 55 |
+
return result
|
| 56 |
+
|
| 57 |
+
def decode(self, record):
|
| 58 |
+
record = asset_record(record)
|
| 59 |
+
if self.assets.get(record['path']) != record:
|
| 60 |
+
raise ValueError('Unregistered image')
|
| 61 |
+
path = Path(record['path'])
|
| 62 |
+
# No symlinks, special devices or unbounded streams. Decode these verified
|
| 63 |
+
# bytes, not a second path open, so a replacement cannot evade the hash.
|
| 64 |
+
if path.is_symlink() or any(p.is_symlink() for p in path.parents):
|
| 65 |
+
raise ValueError('Image symlinks are not allowed')
|
| 66 |
+
import os
|
| 67 |
+
fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
|
| 68 |
+
with os.fdopen(fd, 'rb') as stream:
|
| 69 |
+
info = os.fstat(stream.fileno())
|
| 70 |
+
if not stat.S_ISREG(info.st_mode) or info.st_size != record['bytes']:
|
| 71 |
+
raise ValueError('Image size/type mismatch')
|
| 72 |
+
data = stream.read(MAX_BYTES + 1)
|
| 73 |
+
if len(data) != record['bytes'] or hashlib.sha256(data).hexdigest() != record['sha256']:
|
| 74 |
+
raise ValueError('Image size/hash mismatch')
|
| 75 |
+
try:
|
| 76 |
+
with warnings.catch_warnings():
|
| 77 |
+
warnings.simplefilter('error', Image.DecompressionBombWarning)
|
| 78 |
+
with Image.open(io.BytesIO(data)) as image:
|
| 79 |
+
if image.format not in {'PNG', 'JPEG', 'WEBP'} or image.width * image.height > MAX_PIXELS or getattr(image, 'n_frames', 1) != 1:
|
| 80 |
+
raise ValueError('Unsupported or oversized image')
|
| 81 |
+
image.verify()
|
| 82 |
+
with Image.open(io.BytesIO(data)) as image:
|
| 83 |
+
image.load()
|
| 84 |
+
return image.convert('RGB')
|
| 85 |
+
except (OSError, SyntaxError, Image.DecompressionBombWarning, Image.DecompressionBombError) as exc:
|
| 86 |
+
raise ValueError('Corrupt or oversized registered image') from exc
|
| 87 |
+
|
| 88 |
+
def decode_row(self, row):
|
| 89 |
+
return [self.decode(record) for record in self.records(row)]
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def configure_local_images(processor, registry):
|
| 93 |
+
if not hasattr(processor, 'image_processor'):
|
| 94 |
+
raise ValueError('Registered local images require a multimodal processor')
|
| 95 |
+
processor.image_processor.size = dict(IMAGE_SIZE)
|
| 96 |
+
processor.local_image_registry = registry
|
| 97 |
+
return processor
|