Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion inference/core/version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "1.6.2"
__version__ = "1.6.2-post1"


if __name__ == "__main__":
Expand Down
2 changes: 1 addition & 1 deletion requirements/requirements.workflows.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# keep in sync with workflows/pyproject.toml version
roboflow-workflows[enterprise]==0.1.2
roboflow-workflows[enterprise]==0.1.2.post1
8 changes: 8 additions & 0 deletions workflows/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## Unreleased

### Added

- SpaceXAI block (`spacexai@v3`): `grok-4.7` model option with `low`/`medium`/`high`/`xhigh` reasoning effort. Detection reuses the Grok 4.5/4.6 prompt.

### Fixed

- SpaceXAI object detection encodes PNG at compression level 9. OpenCV's default compression put large frames over xAI's 25MB upload limit.

---

## `0.1.2`
Expand Down
2 changes: 1 addition & 1 deletion workflows/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "roboflow-workflows"
version = "0.1.2"
version = "0.1.2-post1"
description = "Roboflow Workflows execution engine and block library"
readme = "README.md"
license = {file = "LICENSE"}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,13 @@
XAI_BASE_URL = "https://api.x.ai/v1"

# grok-4.5 `xhigh` excluded: xAI silently downgrades it to `high`.
# grok-4.7 was not re-benchmarked; it reuses the 4.5/4.6 detection prompt.
GROK_MODELS = [
{
"id": "grok-4.7",
"name": "Grok 4.7",
"reasoning_levels": ["low", "medium", "high", "xhigh"],
},
{
"id": "grok-4.6",
"name": "Grok 4.6",
Expand Down Expand Up @@ -149,6 +155,7 @@

Images for object detection are sent at original resolution as lossless PNG
with `detail: "high"`, matching the vlm-exam benchmark setup for Grok 4.5/4.6.
Grok 4.7 uses the same prompt and image handling.

## Version Differences

Expand Down Expand Up @@ -278,7 +285,7 @@ class BlockManifest(WorkflowBlockManifest):
] = Field(
default="grok-4.6",
description="Model to be used",
examples=["grok-4.6", "grok-4.5", "$inputs.grok_model"],
examples=["grok-4.7", "grok-4.6", "grok-4.5", "$inputs.grok_model"],
json_schema_extra={
"values_metadata": MODEL_VERSION_METADATA,
},
Expand All @@ -293,9 +300,10 @@ class BlockManifest(WorkflowBlockManifest):
description=(
"Optional reasoning effort passed to xAI as "
'`reasoning: {"effort": ...}`. Grok models default to "high" and '
'cannot disable reasoning. "xhigh" is only supported by grok-4.6. '
"For requests with a direct xAI key, the request is retried "
"without reasoning when the model rejects the parameter."
'cannot disable reasoning. "xhigh" is not supported by grok-4.5. '
"With a direct xAI key, if the model rejects the `reasoning` "
"parameter the request is retried without it, so the model falls "
"back to its default effort."
),
examples=["low", "high"],
)
Expand Down Expand Up @@ -563,7 +571,15 @@ def encode_image_for_task(


def _encode_image_to_png_bytes(image: np.ndarray) -> bytes:
_, encoded_image = cv2.imencode(".png", image)
# OpenCV's default PNG compression (level 1) puts the large drone frames
# in the detection benchmark over xAI's 25MB upload limit, so those
# requests 400. Level 9 is still lossless and stays under the limit,
# matching the benchmark's PIL encoder.
_, encoded_image = cv2.imencode(
".png",
image,
[cv2.IMWRITE_PNG_COMPRESSION, 9],
)
return encoded_image.tobytes()


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from roboflow_workflows.core_steps.models.foundation.openai import v6 as openai_v6
from roboflow_workflows.core_steps.models.foundation.qwen_vlm import v3 as qwen_vlm_v3
from roboflow_workflows.core_steps.models.foundation.spacexai import v2 as spacexai_v2
from roboflow_workflows.core_steps.models.foundation.spacexai import v3 as spacexai_v3
from roboflow_workflows.core_steps.models.foundation.zai_vlm import v1 as zai_vlm_v1

_LEVELS = {"model-a": ["low", "high"], "model-b": []}
Expand Down Expand Up @@ -71,6 +72,12 @@ def test_validate_reasoning_level_rejects_model_without_levels():
spacexai_v2.MODEL_VERSION_METADATA,
None,
),
"spacexai@v3": (
spacexai_v3.MODEL_REASONING_LEVELS,
spacexai_v3.REASONING_EFFORT_VALUES,
spacexai_v3.MODEL_VERSION_METADATA,
None,
),
"meta_vlm@v2": (
meta_vlm_v2.MODEL_REASONING_LEVELS,
meta_vlm_v2.REASONING_EFFORT_OPTIONS,
Expand Down Expand Up @@ -141,6 +148,7 @@ def test_block_default_level_is_valid_for_every_model(block):
"open_ai@v6": openai_v6.BlockManifest,
"google_gemini@v5": gemini_v5.BlockManifest,
"spacexai@v2": spacexai_v2.BlockManifest,
"spacexai@v3": spacexai_v3.BlockManifest,
"meta_vlm@v2": meta_vlm_v2.BlockManifest,
"qwen_vlm@v3": qwen_vlm_v3.BlockManifest,
}
Expand All @@ -154,7 +162,7 @@ def _manifest(block_type: str, **overrides):
"task_type": "unconstrained",
"prompt": "describe",
}
if block_type == "spacexai@v2":
if block_type in {"spacexai@v2", "spacexai@v3"}:
spec["api_key"] = "$inputs.xai_api_key"
spec.update(overrides)
return _MANIFESTS[block_type].model_validate(spec)
Expand All @@ -166,6 +174,7 @@ def _manifest(block_type: str, **overrides):
("open_ai@v6", {"model_version": "$inputs.model", "reasoning_effort": "xhigh"}),
("open_ai@v6", {"model_version": "gpt-4o"}),
("qwen_vlm@v3", {"backend": "native"}),
("spacexai@v3", {"model_version": "grok-4.7", "reasoning_effort": "xhigh"}),
],
)
def test_manifest_accepts_edges_rejects_cannot_cover(block_type, overrides):
Expand Down Expand Up @@ -194,6 +203,7 @@ def test_manifest_accepts_edges_rejects_cannot_cover(block_type, overrides):
{"model_version": "gemini-2.5-pro", "thinking_level": "low"},
),
("spacexai@v2", {"model_version": "grok-4.5", "reasoning_effort": "xhigh"}),
("spacexai@v3", {"model_version": "grok-4.5", "reasoning_effort": "xhigh"}),
(
"meta_vlm@v2",
{"model_version": "Muse Glimmer", "reasoning_effort": "minimal"},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@

from unittest.mock import MagicMock, patch

import cv2
import numpy as np
import pytest
from roboflow_workflows.core_steps.models.foundation.spacexai.v3 import (
BlockManifest,
SpaceXAIBlockV3,
_encode_image_to_png_bytes,
)
from roboflow_workflows.execution_engine.entities.base import WorkflowImageData

Expand Down Expand Up @@ -174,3 +176,12 @@ def test_run_reports_error_status_for_undecodable_detection_output(mock_promptin

assert result[0]["predictions"] is None
assert result[0]["error_status"] is True


def test_detection_png_uses_compression_that_fits_xai_upload_limit():
image = np.random.default_rng(0).integers(0, 256, (128, 128, 3), dtype=np.uint8)

encoded = _encode_image_to_png_bytes(image)
_, compressed = cv2.imencode(".png", image, [cv2.IMWRITE_PNG_COMPRESSION, 9])

assert encoded == compressed.tobytes()
12 changes: 11 additions & 1 deletion workflows/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading