Image-Text-to-Text
Transformers
Safetensors
qwen3_5
clef
cloudflare
systemone
decision-model
qwen3.8
post-train
image-text-to-typed-output
multimodal
structured-output
classification
custom-code
conversational
Eval Results
Instructions to use Cloudflare/clef with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Cloudflare/clef with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-text-to-text", model="Cloudflare/clef") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] pipe(text=messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoProcessor, AutoModelForMultimodalLM processor = AutoProcessor.from_pretrained("Cloudflare/clef") model = AutoModelForMultimodalLM.from_pretrained("Cloudflare/clef", device_map="auto") messages = [ { "role": "user", "content": [ {"type": "image", "url": "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/p-blog/candy.JPG"}, {"type": "text", "text": "What animal is on the candy?"} ] }, ] inputs = processor.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_dict=True, return_tensors="pt", ).to(model.device) outputs = model.generate(**inputs, max_new_tokens=256) print(processor.decode(outputs[0][inputs["input_ids"].shape[-1]:])) - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use Cloudflare/clef with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "Cloudflare/clef" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Cloudflare/clef", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker
docker model run hf.co/Cloudflare/clef
- SGLang
How to use Cloudflare/clef with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "Cloudflare/clef" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Cloudflare/clef", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "Cloudflare/clef" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "Cloudflare/clef", "messages": [ { "role": "user", "content": [ { "type": "text", "text": "Describe this image in one sentence." }, { "type": "image_url", "image_url": { "url": "https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" } } ] } ] }' - Docker Model Runner
How to use Cloudflare/clef with Docker Model Runner:
docker model run hf.co/Cloudflare/clef
Add systemone: Jev/SystemOne-compatible request and response API
Browse files- README.md +28 -1
- joint_schema_model.py +62 -1
README.md
CHANGED
|
@@ -43,7 +43,7 @@ smaller, faster variant.
|
|
| 43 |
|---|---|
|
| 44 |
| `model-*.safetensors`, `model.safetensors.index.json`, `config.json`, `generation_config.json` | Backbone, including the vision encoder |
|
| 45 |
| `joint_head.safetensors`, `joint_head_config.json` | Joint schema head |
|
| 46 |
-
| `joint_schema_model.py` | Record encoding, batching, the model, and `
|
| 47 |
| `tokenizer.json`, `tokenizer_config.json`, `chat_template.jinja`, `processor_config.json` | Tokenizer and image/video processor |
|
| 48 |
| `LICENSE` | Apache-2.0 license |
|
| 49 |
|
|
@@ -86,6 +86,33 @@ for question, question_logits in zip(encoded.questions, logits):
|
|
| 86 |
print(question.question_id, dict(zip(question.option_ids, probabilities)))
|
| 87 |
```
|
| 88 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
### Images and video
|
| 90 |
|
| 91 |
Add `images` (PIL images) or `videos` (frame arrays) to the record and pass the processor to
|
|
|
|
| 43 |
|---|---|
|
| 44 |
| `model-*.safetensors`, `model.safetensors.index.json`, `config.json`, `generation_config.json` | Backbone, including the vision encoder |
|
| 45 |
| `joint_head.safetensors`, `joint_head_config.json` | Joint schema head |
|
| 46 |
+
| `joint_schema_model.py` | Record encoding, batching, the model, `load_release_model`, and `systemone` |
|
| 47 |
| `tokenizer.json`, `tokenizer_config.json`, `chat_template.jinja`, `processor_config.json` | Tokenizer and image/video processor |
|
| 48 |
| `LICENSE` | Apache-2.0 license |
|
| 49 |
|
|
|
|
| 86 |
print(question.question_id, dict(zip(question.option_ids, probabilities)))
|
| 87 |
```
|
| 88 |
|
| 89 |
+
### Jev / SystemOne API
|
| 90 |
+
|
| 91 |
+
`systemone` takes a Jev/SystemOne `POST /v1/systemone` request body and returns the same response
|
| 92 |
+
body: `model`, `answers` keyed by question ID, and `usage`. A `choice` answer has `choice`,
|
| 93 |
+
`confidence`, and `probabilities`; a `score` answer has the expected `score`, `confidence`, `legend`,
|
| 94 |
+
and `probabilities`; a `noul` answer has the probability of true. `instructions` is optional, and
|
| 95 |
+
`images` and `videos` may be added to the request.
|
| 96 |
+
|
| 97 |
+
```python
|
| 98 |
+
from joint_schema_model import systemone
|
| 99 |
+
|
| 100 |
+
response = systemone(model, processor, {
|
| 101 |
+
"model": "clef",
|
| 102 |
+
"state": "Our checkout started returning errors and orders are blocked.",
|
| 103 |
+
"questions": {
|
| 104 |
+
"department": {
|
| 105 |
+
"type": "choice",
|
| 106 |
+
"instructions": "Which team should handle the message?",
|
| 107 |
+
"criteria": {"billing": "Payments or invoices", "technical": "Bugs or outages"},
|
| 108 |
+
},
|
| 109 |
+
"urgency": {"type": "score", "criteria": ["Can wait", "This week", "Today"]},
|
| 110 |
+
"outage": {"type": "noul", "instructions": "Is a service down?"},
|
| 111 |
+
},
|
| 112 |
+
})
|
| 113 |
+
print(response["answers"])
|
| 114 |
+
```
|
| 115 |
+
|
| 116 |
### Images and video
|
| 117 |
|
| 118 |
Add `images` (PIL images) or `videos` (frame arrays) to the record and pass the processor to
|
joint_schema_model.py
CHANGED
|
@@ -4,6 +4,8 @@ A record provides a ``state`` (any JSON value), optional ``images`` and ``videos
|
|
| 4 |
and ``questions``. Each question has a ``type`` (``noul``, ``choice``, or ``score``),
|
| 5 |
``instructions``, and, for ``choice`` and ``score``, ``criteria`` describing the
|
| 6 |
allowed options. The model returns one logit per allowed option for every question.
|
|
|
|
|
|
|
| 7 |
"""
|
| 8 |
|
| 9 |
from __future__ import annotations
|
|
@@ -115,7 +117,10 @@ def encode_record(
|
|
| 115 |
)
|
| 116 |
)
|
| 117 |
question_start = len(schema_ids)
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
| 119 |
question_end = len(schema_ids)
|
| 120 |
schema_ids.extend(_tokens(tokenizer, "\nALLOWED OPTIONS:\n"))
|
| 121 |
|
|
@@ -513,3 +518,59 @@ def load_release_model(
|
|
| 513 |
head = head.to(device=device, dtype=dtype)
|
| 514 |
processor = AutoProcessor.from_pretrained(path)
|
| 515 |
return ClefModel(backbone, head).eval(), processor
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
and ``questions``. Each question has a ``type`` (``noul``, ``choice``, or ``score``),
|
| 5 |
``instructions``, and, for ``choice`` and ``score``, ``criteria`` describing the
|
| 6 |
allowed options. The model returns one logit per allowed option for every question.
|
| 7 |
+
``systemone`` answers a Jev/SystemOne ``/v1/systemone`` request body with the same
|
| 8 |
+
response body.
|
| 9 |
"""
|
| 10 |
|
| 11 |
from __future__ import annotations
|
|
|
|
| 117 |
)
|
| 118 |
)
|
| 119 |
question_start = len(schema_ids)
|
| 120 |
+
instructions = question.get("instructions")
|
| 121 |
+
if instructions is None or instructions == "":
|
| 122 |
+
instructions = str(question_id)
|
| 123 |
+
schema_ids.extend(_tokens(tokenizer, render(instructions)))
|
| 124 |
question_end = len(schema_ids)
|
| 125 |
schema_ids.extend(_tokens(tokenizer, "\nALLOWED OPTIONS:\n"))
|
| 126 |
|
|
|
|
| 518 |
head = head.to(device=device, dtype=dtype)
|
| 519 |
processor = AutoProcessor.from_pretrained(path)
|
| 520 |
return ClefModel(backbone, head).eval(), processor
|
| 521 |
+
|
| 522 |
+
|
| 523 |
+
def systemone_answer(question: dict[str, Any], probabilities: dict[str, float]) -> dict[str, Any]:
|
| 524 |
+
"""Convert per-option probabilities for one question into a SystemOne answer."""
|
| 525 |
+
if question["type"] == "noul":
|
| 526 |
+
return {"type": "noul", "noul": round(probabilities["true"], 4)}
|
| 527 |
+
if question["type"] == "choice":
|
| 528 |
+
options = [str(option) for option in question["criteria"]]
|
| 529 |
+
choice = max(options, key=probabilities.__getitem__)
|
| 530 |
+
return {
|
| 531 |
+
"type": "choice",
|
| 532 |
+
"choice": choice,
|
| 533 |
+
"confidence": round(probabilities[choice], 4),
|
| 534 |
+
"probabilities": {option: round(probabilities[option], 4) for option in options},
|
| 535 |
+
}
|
| 536 |
+
levels = [str(index) for index in range(len(question["criteria"]))]
|
| 537 |
+
return {
|
| 538 |
+
"type": "score",
|
| 539 |
+
"score": round(sum(index * probabilities[level] for index, level in enumerate(levels)), 4),
|
| 540 |
+
"confidence": round(max(probabilities[level] for level in levels), 4),
|
| 541 |
+
"legend": dict(zip(levels, question["criteria"])),
|
| 542 |
+
"probabilities": {level: round(probabilities[level], 4) for level in levels},
|
| 543 |
+
}
|
| 544 |
+
|
| 545 |
+
|
| 546 |
+
@torch.inference_mode()
|
| 547 |
+
def systemone(model: ClefModel, processor: Any, request: dict[str, Any], max_length: int = 16384) -> dict[str, Any]:
|
| 548 |
+
"""Answer a Jev/SystemOne ``/v1/systemone`` request body with a SystemOne response body.
|
| 549 |
+
|
| 550 |
+
The request has ``model``, ``state``, and ``questions``, plus optional ``images`` and ``videos``.
|
| 551 |
+
"""
|
| 552 |
+
questions = request.get("questions")
|
| 553 |
+
if not isinstance(request.get("model"), str) or "state" not in request:
|
| 554 |
+
raise ValueError("model and state are required")
|
| 555 |
+
if not isinstance(questions, dict) or not questions:
|
| 556 |
+
raise ValueError("at least one question is required")
|
| 557 |
+
for question_id, question in questions.items():
|
| 558 |
+
if question.get("type") not in QUESTION_TYPES:
|
| 559 |
+
raise ValueError(f"{question_id}: type must be noul, choice, or score")
|
| 560 |
+
if question["type"] != "noul" and not question.get("criteria"):
|
| 561 |
+
raise ValueError(f"{question_id}: criteria must not be empty")
|
| 562 |
+
encoded = encode_record(processor.tokenizer, request, max_length=max_length, processor=processor)
|
| 563 |
+
device = next(model.parameters()).device
|
| 564 |
+
logits = model(collate_records([encoded], processor.tokenizer.pad_token_id, device))[0]
|
| 565 |
+
answers = {
|
| 566 |
+
question.question_id: systemone_answer(
|
| 567 |
+
questions[question.question_id],
|
| 568 |
+
dict(zip(question.option_ids, question_logits.float().softmax(-1).tolist())),
|
| 569 |
+
)
|
| 570 |
+
for question, question_logits in zip(encoded.questions, logits)
|
| 571 |
+
}
|
| 572 |
+
return {
|
| 573 |
+
"model": request["model"],
|
| 574 |
+
"answers": answers,
|
| 575 |
+
"usage": {"input_tokens": len(encoded.input_ids), "output_tokens": 0},
|
| 576 |
+
}
|