refactor(workflows): decontaminate inference imports - #2949
refactor(workflows): decontaminate inference imports#2949grzegorz-roboflow wants to merge 78 commits into
Conversation
Remove 196 of 571 imports from inference/core/workflows in place, behind ports bound through the existing init_parameters path: - AST import lint with a shrink-only baseline (571 -> 375) - module-local stdlib loggers + structlog ProcessorFormatter adapter (parity test) - ModelsProvider, WorkflowsCache, BackgroundTaskScheduler protocols; vendored LRUCache - pure helpers vendored into workflows.utils with local exception/warning classes - enterprise blocks loaded as a workflows_core-sourced plugin (prepended) - Roboflow step error handlers moved to the server and passed explicitly at all four composition roots; workflows default is mapping-free legacy - dead model_manager parameters removed from 30 leaf blocks - isolation probe + strict-xfail wrapper (diagnostic; fails until phases 5-12) Direct ExecutionEngine.init callers must now pass the server handler to keep the Roboflow error mappings; server error handlers also catch the workflows environment-value error. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
👋 Thanks for the pull request! Here is how automated Claude review works here, so you spend credits (and reviewer time) wisely. 🚦 This PR is marked Ready for review, so automated Claude review will run — and every pass spends real credits. Warning 💸 The Claude reviewer bills in credits, not vibesAutomated review spins up a real agent that reads real code and spends real credits on every pass. It is glad to help — but it is not a rubber duck, a linter you poke in a loop, or a substitute for reading the contributing guide. Treat it like an expensive senior reviewer whose time you booked, and show up prepared. Draft when unsure, Ready when you mean it:
However you get there, arrive prepared:
Reviews are not free. A draft costs nothing to review; a Ready PR is a promise that it is worth reviewing.
|
|
🤖 Claude review started at commit New commits are not auto-reviewed. Add the |
|
Remaining 300+ imports are handled now, follow-up commit is coming |
|
Skills: review-workflows-execution-engine, review-workflows-blocks, review-http-api-server, review-core-infra, review-topic-test-hygiene, review-topic-backward-compat-and-versioning No dedicated surface skill covers Review scope. Large decontamination refactor (228 files). I traced the non-mechanical plumbing personally and ran two focused verification passes over the breadth-heavy areas:
Minor doubts (non-blocking): Reviewed at HEAD: 6090d3f |
|
😎 PR passes the vibe-check and trust-me-bro verification. |
…plugin The dataset-upload, custom-metadata, model-monitoring, vision-events, asset-library and visual-search blocks move to inference/roboflow_workflows_plugin/, loaded through WORKFLOWS_PLUGINS with BLOCKS_SOURCE = "workflows_core" so workflows_core.* init parameters keep resolving. The plugin applies WORKFLOW_DISABLED_BLOCK_TYPES/PATTERNS itself, including patterns written against the blocks' old module path. Removes 38 baseline rows. Release notes: - fully_qualified_block_class_name for these 9 blocks changes to inference.roboflow_workflows_plugin.*. - describe_available_blocks ordering: they now follow the core list. Block type identifiers and the block-disable policy are unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
Codemod now buffers every rewrite in memory and validates the aggregate count and post-state before writing any file to disk, instead of writing each file as it goes and only failing the aggregate check afterward. env.py's WORKFLOWS_PLUGINS expansion for the Roboflow plugin now normalises (removes any existing occurrence, then prepends once) instead of only prepending when absent, so an operator who already listed the plugin anywhere in WORKFLOWS_PLUGINS still gets the required roboflow -> enterprise -> user-plugin order. CR-1 completed: the acceptance test's two probes and the plugin-blocks regression test's new-path probe now PRINT the effective tensor mode and the exact module leaf per relocated identifier as JSON, and the parent test functions assert those exact values - not just an internal child assert. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…nstant All 37 uses inside Workflows were CORE_MODEL. Blocks pass the plain string and the two server functions that read endpoint_type coerce it back into the enum. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
Move the endpoint_type coercion before the @ttl_cache decorator in _check_if_api_key_has_access_to_model so that string and enum forms share a single cache entry. Fixes Finding 1 (cache efficiency). Update docstring in models_provider.py (Finding 3), remove unused ModelEndpointType import in test_dependent_resources.py (Finding 2), and add cache-aware tests for the coercion behavior. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
… ports Ports, a standalone offline default, a server adapter forwarding to roboflow_api/url_utils without reimplementing wrap_url, the header policy or the api-key redaction, and setdefault bindings at all four composition roots observed at ExecutionEngine.init. No baseline rows yet. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…form port platform_client is threaded alongside roboflow_api_key through all 49 helper signatures and 49 call sites, the three Qwen subclasses forward it to the OpenRouter base, post_to_roboflow_api is gone from workflows, and the 50 affected test calls and 46 block constructions carry the recording double. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…nt codemods Skip the abstract OpenRouterWorkflowBlockBase construction case instead of failing it; fix CRLF round-tripping in both phase9 codemods (read_text() silently normalized \r\n, and the inserted blocks hardcoded \n); resolve aliased chain-helper calls and untouched-module same-named classes correctly in the test-side codemod; stop silently swallowing unparsable test files in the AST reference checker. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…M/seg blocks build_roboflow_api_headers, get_extra_weights_provider_headers and the secure-gateway wrap_url now arrive through the platform port; wrap_url is injected, never reimplemented. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
The Step 6 whole-file black run also reformatted a pre-existing frame_idx assertion unrelated to the platform_client migration; revert that hunk to its pre-task text. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…olvers block_scaffolding asks a WorkspaceResolver, mirrored into the dynamic_workflows_blocks namespace beside api_key, and compilation reads the same effective value the generated block will use. Resolver-dependent compilations bypass COMPILATION_CACHE, so an injected resolver cannot be skipped on a warm cache. The authenticated /workflows/blocks/describe handler binds the server resolver explicitly. Release note: Modal validation of a dynamic block now resolves the workspace with the api key the block executes with (dynamic_workflows_blocks.api_key); callers that never override that key are unaffected. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
compile_workflow_graph called collect_dynamic_blocks_definitions_from_workflow_definition up to three times (cacheable check, cache-hit path, post-normalisation) - the collector warns on duplicate block types, so it warned repeatedly for the same compile. Collect once into a local and reuse it for the cacheable check and the cache-hit path's ensure_dynamic_blocks_allowed; the post-normalisation collection stays, since inlined inner workflows can contribute additional dynamic blocks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
… workflows RoboflowAPIRequestError, RoboflowAPIUnsuccessfulRequestError, RoboflowAPIForbiddenError and FeatureDeprecatedError move to workflows/prototypes/platform_errors.py and inference/core/exceptions.py imports them back. One class object per name, so class names, constructors, MROs, isinstance checks, handler arms and serialized error_type payloads are all unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…s.py The four relocated exception classes (Task 9.7) took their only Optional[...] usage with them into platform_errors.py, leaving the import dead in exceptions.py. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…ts the shared one core_steps/loader.py defaults to a process-local InMemoryWorkflowsCache whose falsy-expire semantics match MemoryCache exactly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…el loader get_workflow_schema returns a dict and the server wraps it (route name and OpenAPI operation id unchanged); merge_window_segments is copied in and pinned by a behavioural differential; load_action_recognition_model moves behind the ModelsProvider port. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…eline Parses the probe's blocked_import_attempts JSON for both tensor modes and compares each name against the baseline at ancestor granularity, failing when extraction finds nothing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…ds; single duplicate-block warning The endpoint-type, test-side and exception-import codemods now compute every rewrite in memory and write only after the aggregate counts validate, matching the move and platform-client codemods; regression tests prove a count mismatch writes nothing. The pre-resolution dynamic-block collection no longer repeats the duplicate-definition warning, and the standalone inner-workflow resolver no longer recommends a "local" workspace it refuses. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…rting _is_response_dc_to_dict Add public to_dict() method to InstanceSegmentationInferenceResponseDC and update instance_segmentation/v3.py to use duck-typing instead of importing private symbols. Removes 1 baseline row (68 -> 67). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…only entity imports load_core_model now takes model_manager, core_model, version_id and api_key directly instead of a request object, dropping the five inference.core.entities.requests imports it needed only for that Union. The 12 call sites pass version_id/api_key read off the same request attributes as before (pure refactor, no observable change). Five SAM blocks rewrite an annotation-only entities.responses import to List[Any] (with a comment naming the real type) since only the fields already accessed are read. Baseline 67 -> 57. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…re-exports); response-level DTOs as local dataclasses Move Point, InstanceSegmentationBasePrediction, InstanceSegmentationPrediction, InstanceSegmentationRLEPrediction (from responses/inference.py) and Sam2SegmentationPrediction (from responses/sam2.py) into core_steps/common/segmentation_entities.py verbatim; both server modules re-export them (one class object per name), so pydantic validation/coercion built from remote responses is unchanged. InferenceResponseImage and InstanceSegmentationInferenceResponse are only ever built from locals, so they become workflows-local dataclasses (inference_response_dc.py) whose to_dict() is bit-equivalent to model_dump(by_alias=True, exclude_none=True). Repoints the six SAM/seg-preview blocks and fixes the three sv.Detections.from_inference call sites in segment_anything3/v3.py to pass to_dict() (from_inference subscripts its argument unless it exposes .dict()/.json()). Decontamination baseline 57 -> 51. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…sponse Task 11.3 review nit: the two 3D SAM blocks lost their annotation-only Sam3_3D_Objects_Response import without a comment naming the real type, unlike the other three SAM blocks. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…ethods Task 11.1 repointed instance_segmentation/v3.py from model_manager[...] to the four stream-pipeline methods on the ModelsProvider port; the fake manager in the InferencePipeline workflow tests still only exposed __getitem__, so four tests failed with AttributeError. The fake now mirrors ModelManager's bodies over its single fake model. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…ts it Move ActionRecognitionPrediction from inference.core.entities.responses.action_recognition into inference.core.workflows.core_steps.models.roboflow.action_recognition.entities. The server module now re-exports the same class object so isinstance checks in the serializers and the HTTP response model keep working. Decontamination baseline: 51 -> 50. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…test The process-global usage collector flushes its own POSTs through the same requests_mock transport, so requests_mock.call_count is order-dependent (same class as the hop-cap fix in test_workflows_image_codec.py). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…sition roots Adds ModelManagerModelsProvider (inference/core/interfaces/workflows_models_provider.py), the server-side implementation of the Workflows ModelsProvider port, forwarding every port member to a wrapped ModelManager. Binds it at the four composition roots (http_api.py x2, inference_pipeline.py, local_image_adapter.py) via scripts/phase11_bind_models_provider.py, an AST-located script that substitutes only the value bound to "workflows_core.model_manager". Repoints the port's structural test and gives the integration test fixtures a raw_model_manager/model_manager pair so tests can still inspect the underlying manager while workflows receive the adapter. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…rity guard The two codec-binding tests asserted requests_mock.call_count, which the process-global usage collector's POSTs make order-dependent (unmasked once the stream-pipeline tests run to completion); assert the list of GET urls instead. Adds an AST guard that action_recognition/entities.py imports nothing from `inference.` (Task 11.5 review minor). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…run through the plain-argument port Adds run_object_detection, run_classification, run_keypoints_detection and run_semantic_segmentation to the ModelsProvider port and its ModelManager adapter, and switches the 14 object-detection / classification / keypoint / semantic-segmentation blocks to call them instead of building inference.core.entities.requests objects inline. Introduces the UNSET sentinel (and the adapter's _passed helper) so an argument a block never sets is omitted from the request while an explicit None is still forwarded and still raises ValidationError where it does today. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…yword unchanged Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…ument port Adds `run_instance_segmentation` to the workflows `ModelsProvider` port and the `ModelManagerModelsProvider` adapter (Task 11.9), including the `InferenceResultsDC` result type for the rfdetr cold-model async stream handoff (`return_raw_responses=True`). Switches the four instance_segmentation blocks (v1, v2, v3, v4) to call it instead of building `InstanceSegmentationInferenceRequest` inline and calling `infer_from_request_sync` directly. v3's registration-before-depth-check ordering is preserved unchanged so a cold model still queues its first stream frame correctly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…adapter Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…efault The explicit-None adapter case used class_filter, whose request default is already None, so it could not catch `_passed` dropping None. Use enforce_dense_masks_in_inference_models instead (Task 11.9 re-review minor). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
… plain-argument port Adds run_lmm, run_depth_estimation and run_moondream2 to the workflows ModelsProvider port and its ModelManager-backed adapter, then swaps the 11 uniform LMMInferenceRequest call sites (cosmos3, florence2, glm_ocr, qwen, qwen3_5vl v1/v2, qwen3vl, qwen_vlm v1/v2/v3, smolvlm) plus depth_estimation and moondream2 onto the new plain-argument calls, so inference.core.workflows no longer builds these pydantic request objects directly. Decontamination baseline 32 -> 19. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…out-token-budget shape Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
| new_source, | ||
| ) | ||
| ast.parse(new_source) # refuse to write anything that does not parse | ||
| path.write_text(new_source, encoding="utf-8") |
…he plain-argument port Adds run_clip_text_embedding, run_clip_image_embedding, run_clip_comparison, run_perception_encoder_text_embedding and run_perception_encoder_image_embedding to the ModelsProvider port and its ModelManagerModelsProvider adapter, and switches clip/v1.py, clip_comparison/v1.py, clip_comparison/v2.py and perception_encoder/v1.py to call them instead of building inference.core.entities request objects inline. load_core_model stays in clip/v1.py and perception_encoder/v1.py; run_clip_comparison registers the model itself (its core model id is only known from the validated request) in the same build -> register -> infer position the blocks used. Decontamination baseline 19 -> 15. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
The GET-only filter still caught the usage collector's GET of /usage/plan when it happened to flush mid-test; match on the image URL. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…ject/image-prompt comparison Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…argument port Adds run_doctr_ocr, run_easy_ocr, run_pp_ocr and run_yolo_world to the ModelsProvider port and its ModelManagerModelsProvider adapter, then switches ocr/v1.py, easy_ocr/v1.py, pp_ocr/v1.py, pp_ocr/v1_tensor.py and yolo_world/v1.py to call them instead of building pydantic request objects inline. pp_ocr registers the validator-derived model id from the adapter (build -> register -> infer), matching run_clip_comparison; the other three keep block-side load_core_model. Decontamination baseline 15 -> 10. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…s them Move Box, Point, Sam2Prompt, Sam2PromptSet (from requests/sam2.py) and Sam3Prompt (from requests/sam3.py) verbatim into core_steps/models/foundation/segment_anything_common/prompts.py; both server modules now re-export the same class objects. Migrate the three tensor block files (segment_anything3/v2_tensor.py, v3_tensor.py, segment_anything3_interactive/v1_tensor.py) to import from the new workflows-owned module (one import line each, no other changes). Decontamination baseline 10 -> 7. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…iptions, full purity guard Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
Port SAM2, SAM3 (v1/v2/v3), SAM3 interactive and SAM3-3D (v1/v1_tensor) onto ModelManagerModelsProvider.run_sam2_segmentation / run_sam3_segmentation / run_sam3_3d_objects, following the UNSET/_passed forwarding rule Tasks 11.8-11.12 established for the other model families. Registration (add_model / load_core_model) stays exactly where each block already had it. segment_anything2/v1.py keeps decoding the image before registering (CR-1), matching its pre-port order. Decontamination baseline: 7 -> 0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…ider port Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
Phases 5-11 removed every inference.* import from workflows outside the bound ports; the probe now passes, so the strict xfail placed in Phase 9 would fail as XPASS. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
…nsor parity case Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
… adapter Whole-phase review minor: the docstring still named the raw ModelManager, which no longer satisfies the completed port. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J7uVwZpZKevHkxCr1ZWSvo
What does this PR do?
Remove 196 of 571 imports from inference/core/workflows in place, behind ports bound through the existing init_parameters path:
Direct ExecutionEngine.init callers must now pass the server handler to keep the Roboflow error mappings; server error handlers also catch the workflows environment-value error.
Type of Change
Testing
All CI must be passing
Checklist
Additional Context
N/A