This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Artificial Intelligence

Learning guide to PhotoAIKit and RawCull’s AI integration.

Artificial Intelligence in RawCull

This section explains how RawCull uses reusable AI components without allowing model-runtime details to spread through the application. It is written as a learning path: first understand the boundary between RawCull and PhotoAIKit, then study the package itself, and finally follow CLIP from application startup to a persisted similarity artifact.

The source for this section comes from the sibling PhotoAIKit and RawCull projects. Paths beginning with Sources/ refer to PhotoAIKit. Paths beginning with Model/, Main/, Views/, or Actors/ refer to the RawCull app target.

What This Section Documents

DocumentMain questionStart here when
This overviewWhere does AI belong in the system?You need the vocabulary and responsibility split
How RawCull Enables and Uses CLIPHow does the CLIP preference become a running backend?You are tracing model discovery, activation, RAW decoding, inference, fallback, distance calculation, or persistence
How RawCull Download ModelsHow does RawCull download the models

PhotoAIKit currently contains two main AI families:

  • CLIP image embeddings for visual similarity.
  • SAM 3 subject segmentation for subject masks.

It also contains an Apple Vision feature-print backend. In RawCull, Vision is both the always-available similarity implementation and the whole-batch fallback when CLIP indexing fails.

The detailed CLIP document follows code that is connected to RawCull’s similarity and burst-analysis features. The package architecture document also covers the SAM 3 contracts, workflows, and storage so that the complete package design is understandable. Not every reusable package capability is necessarily exposed as a finished RawCull user workflow.

The Central Design Idea

RawCull and PhotoAIKit answer different kinds of questions.

PhotoAIKit asks:

  • What does an image source look like at a package boundary?
  • How is a model bundle validated and identified?
  • How does a backend produce and compare a similarity artifact?
  • How is bounded indexing or segmentation orchestrated?
  • How can reusable artifacts and masks be encoded or cached?

RawCull asks:

  • Where are models installed for this application?
  • How is a Sony or Nikon RAW file decoded for AI input?
  • Which backend did the user request?
  • When should a catalog be indexed or reindexed?
  • How do similarity distances affect burst grouping and culling?
  • What state and wording should SwiftUI present?

This is dependency inversion in practical form. PhotoAIKit defines small protocols such as ImageDecoding, ImageSimilarityArtifactProviding, and ImageSimilarityArtifactComparing. RawCull injects app-specific implementations and keeps its file model, UI, sandbox policy, and culling decisions outside the package.

flowchart LR
    UI["RawCull SwiftUI and settings"] --> Integration["RawCullAIIntegration composition root"]
    Integration --> AppAdapter["RawCull adapters: paths, RAW decoding, policy"]
    AppAdapter --> Contracts["PhotoAIContracts"]
    Integration --> CLIP["CoreAICLIPBackend"]
    Integration --> SAM3["CoreAISAM3Backend"]
    Integration --> Vision["VisionFeaturePrintBackend"]
    AppAdapter --> Workflows["PhotoAIWorkflows"]
    Workflows --> Contracts
    Storage["PhotoAIStorage"] --> Contracts
    CLIP --> Contracts
    SAM3 --> Contracts
    Vision --> Contracts

The arrow direction matters: PhotoAIKit does not import RawCull. A reusable package should not need to know what FileItem, RawCullViewModel, an app-specific model folder, or a burst winner means.

Responsibility Boundary

ConcernOwnerReason
Typed model, image, artifact, and segmentation contractsPhotoAIKitBackends and hosts need one stable language
Core AI CLIP and SAM 3 inferencePhotoAIKit backend productsFramework-specific tensor and inference code is reusable
Vision feature-print generation and native distancePhotoAIKit backend productThe opaque Vision payload stays behind its backend boundary
Bounded indexing, fallback, segmentation, and mask selectionPhotoAIKit workflowsThese algorithms do not depend on RawCull UI or culling policy
Optional embedding codecs and mask storesPhotoAIKit storagePersistence mechanics are reusable, but locations are not
Model installation directories and candidate orderRawCullPaths and sandbox policy belong to the host application
RAW decodingRawCullPhotoAIKit should not depend on RawParserKit or camera formats
Settings and capability wordingRawCullUser-facing state and localization belong to the app
Similarity ranking adjustments and burst groupingRawCullThese are photo-culling product decisions, not CLIP behavior
Burst-analysis cache location and lifecycleRawCullThe host owns when and where catalog results persist

Current Runtime Shape

RawCull deliberately has a safe startup path:

  1. RawCullApp creates one RawCullAIIntegration composition root.
  2. The main view model initially receives the Vision similarity service.
  3. RawCullAISettingsModel.refresh() asks the integration to validate the CLIP and SAM 3 model candidates.
  4. PhotoAIKit validates the selected model bundle and fingerprints its asset.
  5. If CLIP validation and provider construction succeed, the saved CLIP preference can select RawCullCLIPSimilarityService.
  6. If CLIP is disabled, missing, invalid, or cannot be constructed, RawCull continues with Vision.
  7. If CLIP starts a batch but any item fails, the complete requested batch is retried with Vision.
stateDiagram-v2
    [*] --> VisionStartup
    VisionStartup --> CheckingModels: refresh capabilities
    CheckingModels --> VisionSelected: CLIP disabled, missing, or invalid
    CheckingModels --> CLIPSelected: preference enabled and provider ready
    CLIPSelected --> CLIPArtifacts: all requested items succeed
    CLIPSelected --> VisionFallbackArtifacts: any requested item fails
    VisionSelected --> VisionArtifacts: index catalog

The whole-batch fallback is an integrity rule, not only an error-recovery convenience. CLIP vectors and Vision feature prints have different representations and distance semantics. Recomputing the entire batch prevents a catalog from containing artifacts that cannot be compared with one another.

Vocabulary

TermMeaning in this codebase
ProviderA backend object that performs inference or creates an artifact
Backend descriptorIdentity of the backend, model, representation, preprocessing, normalization, and configuration
Similarity artifactA descriptor plus a backend-owned payload; CLIP stores an encoded vector, while Vision stores an opaque archived observation
Source fingerprintStandardized file path, size, and modification date used to detect changed source images
Model fingerprintIdentity derived from the selected .aimodel or .aimodelc, cryptographically verified when the manifest provides a checksum
Composition rootThe one place where concrete providers, stores, paths, and app adapters are assembled
Whole-batch fallbackDiscard the primary pass after any failure and run every requested source through one fallback backend
HostThe application integrating PhotoAIKit; here, RawCull

Suggested Learning Order

Read How PhotoAIKit Is Constructed first if dependency boundaries, protocols, or package products are unfamiliar. It builds the mental model from the bottom up.

Then read How RawCull Enables and Uses CLIP. That document follows the runtime path and shows where the clean package abstractions meet app-specific concerns such as model locations, RAW files, settings, burst grouping, and cache validation.

Finally, keep AI Runtime Step by Step beside the source code. It expands the runtime path into exact function hops and branches, including what does and does not invoke CLIP when a developer indexes similarity, analyzes bursts, opens a burst, or enters the detailed comparison view.

1 - How RawCull Loads and Uses CLIP

A beginner-friendly source walk-through of RawCull’s OpenAI and DataComp CLIP models, model loading, PhotoAIKit packages, image similarity, semantic search, recovery, and persistence.

How RawCull Loads and Uses CLIP

RawCull supports two CLIP models:

  • OpenAI CLIP ViT-B/32, which processes a 224 × 224 image;
  • OpenCLIP DataComp ViT-B-32-256, which processes a 256 × 256 image.

The user selects one model in Settings > AI. That one selection defines the vector space used for both image-to-image similarity and text-to-image semantic search. RawCull never mixes vectors from the two models.

This page starts with the basic idea behind CLIP and then follows the current source code from application startup, through model validation and Core AI inference, to search results, burst groups, and persisted artifacts.

1. CLIP In Plain Language

CLIP stands for Contrastive Language-Image Pre-training. It was trained to place matching images and text descriptions near each other in a shared mathematical space.

For example, CLIP can turn these two inputs into vectors:

image: a photograph of a red fox at dusk  ──► [0.012, -0.084, ...]
text:  "red fox at dusk"                  ──► [0.018, -0.079, ...]

A vector is just an ordered list of numbers. RawCull does not display those numbers to the user. It compares their directions:

  • two image vectors pointing in similar directions represent visually or semantically related images;
  • an image vector pointing in a similar direction to a text vector is a possible match for that text;
  • vectors produced by different CLIP models are not comparable, even when they contain the same number of values.

CLIP does not write a caption, locate the exact pixels of an object, rate a photo, or decide which burst frame is sharpest. It provides similarity evidence. RawCull decides how to use that evidence.

The original OpenAI CLIP introduction and CLIP paper explain the contrastive training method in more depth.

2. Why There Are Two CLIP Models

Both choices use the same general CLIP idea and the same size of output vector, but they use different learned weights and different image resolutions.

PropertyOpenAI modelDataComp model
RawCull nameOpenAIDataComp
Bundle folderCLIP-OpenAICLIP-DataComp
Export sourceopenai/clip-vit-base-patch32mlfoundations/open_clip
ArchitectureViT-B-32ViT-B-32-256
Pretrained checkpointOriginal OpenAI weightsdatacomp_s34b_b86k
Model input224 × 224 RGB256 × 256 RGB
Image patch size32 × 32 pixels32 × 32 pixels
Embedding size512 floating-point values512 floating-point values
Text context77 tokens77 tokens
Token paddingEnd-of-text token 49407Zero
Text attention maskSuppliedNot required by the exported text encoder

The model details do not come from hard-coded branches inside CoreAICLIPProvider. The exporter writes them into each bundle’s metadata.json, and the provider builds a CLIPRuntimeConfiguration from that metadata.

2.1 OpenAI CLIP ViT-B/32

The OpenAI choice is the original clip-vit-base-patch32 checkpoint used by PhotoAIKit’s exporter.

ViT-B/32 can be read as:

  • ViT: the image encoder is a Vision Transformer;
  • B: the base-size transformer family;
  • 32: the image is divided into 32 × 32 pixel patches before transformer processing.

The exported image encoder receives a 224 × 224 image and returns a 512-dimensional, L2-normalized image vector. The exported text encoder receives up to 77 CLIP BPE tokens plus an attention mask and returns a normalized 512-dimensional text vector.

The original CLIP family was trained on 400 million image-text pairs collected from the internet. The aim was not to learn a fixed list of RawCull categories. It learned a broad relationship between visual content and natural-language descriptions.

2.2 OpenCLIP DataComp ViT-B-32-256

The DataComp choice uses the open-source OpenCLIP implementation and the datacomp_s34b_b86k checkpoint. Its architecture is ViT-B-32-256: a base Vision Transformer with 32 × 32 patches and a 256 × 256 image input.

This checkpoint was trained on the DataComp-1B dataset and its published name records that the training run saw approximately 34 billion samples. The DataComp model card describes the released checkpoint.

The DataComp model also produces 512-dimensional normalized vectors and uses a 77-token CLIP BPE context. Its exported text graph follows OpenCLIP’s input contract: token IDs are padded with zero and no separate attention-mask input is required.

2.3 What The Model Choice Means In RawCull

The two models can rank the same catalog differently because their weights, training data, and input resolution differ. RawCull does not declare one model universally better. It treats them as two valid alternatives.

The important rule is:

An OpenAI image vector can be compared only with OpenAI image or text vectors from the same model fingerprint and processing configuration. The equivalent rule applies to DataComp.

Both output 512 values, but matching dimensions alone do not make two vector spaces compatible.

3. Packages And Frameworks In The CLIP Path

RawCull is the host application. Most reusable CLIP mechanics live in PhotoAIKit, while RAW decoding and culling policy remain in RawCull.

3.1 Swift Packages Used At Runtime

Package or productHow CLIP uses it
PhotoAIContractsDefines model identities, bundle validation, backend descriptors, image and text embeddings, similarity artifacts, source fingerprints, and the small provider/decoder protocols shared by RawCull and the backends.
CoreAICLIPBackendSupplies CoreAICLIPProvider, the actor that reads CLIP metadata, tokenizes text, preprocesses images, lazily runs Core AI, creates artifacts, and compares compatible image and text vectors.
PhotoAIWorkflowsSupplies SimilarityArtifactIndexer, which performs bounded asynchronous decode-and-inference work and reports per-source progress and failures.
PhotoAIStorageSupplies the descriptor-complete artifact codec used by RawCull’s per-file similarity store.
VisionFeaturePrintBackendSupplies the always-available Apple Vision similarity backend used when CLIP is disabled, missing, invalid, or cannot create a provider. It is not a per-image fallback during a selected CLIP indexing pass.
RawParserKitExtracts a bounded thumbnail CGImage from supported camera RAW files before PhotoAIKit sees the image.
RawCullCoreGroups adjacent photos using the distances RawCull computed from CLIP artifacts and owns shared burst-domain types.

RawCull also links CoreAISAM3Backend, but SAM 3 is a separate segmentation model. Its resource check happens alongside the two CLIP checks; it is not used to calculate CLIP vectors.

PhotoAIKit has one external Swift package dependency: apple/coreai-models, pinned to an exact revision. Its CoreAISegmentation product makes the Core AI runtime APIs available to the CLIP and SAM 3 backend targets.

3.2 Apple Frameworks Used Around The Packages

FrameworkRole
Core AILoads the .aimodel or .aimodelc, specializes it for the Mac, creates NDArrays, and runs the named image and text functions.
Core GraphicsConverts a decoded CGImage into the model’s sRGB pixel input.
ImageIOProvides RawCull’s secondary thumbnail-decoding path when RawParserKit cannot return an image.
VisionProduces feature prints when RawCull selects its non-CLIP similarity service.
FoundationProvides URLs, JSON coding, file metadata, UserDefaults, tasks, and application-support paths.
CryptoKitSupports stable cache keys and model/artifact fingerprinting code.
SwiftUI and ObservationPresent the model picker, CLIP toggle, capability state, indexing progress, and semantic-search controls.
OSLogRecords backend selection, model fingerprints, failures, and diagnostics.

3.3 Python Packages Used Only To Export Models

The model bundle is created before RawCull runs. PhotoAIKit’s Tools/export_clip.py declares these build-time tools:

Python packageExport-time job
transformersLoads the OpenAI CLIP checkpoint and saves its tokenizer.
open_clip_torchLoads the OpenCLIP DataComp architecture and checkpoint.
torch and torchvisionHold the source model and tensors used during export.
coreai-torchConverts the PyTorch image and text encoders into Core AI functions.
coreai-coreSaves and optimizes the portable Core AI model asset.

These Python packages are not shipped as part of RawCull’s Swift runtime.

4. The Complete Runtime Shape

The main path is:

flowchart TD
    Settings["Settings: choose OpenAI or DataComp"] --> Integration["RawCullAIIntegration"]
    Integration --> Managers["Two CLIP resource managers"]
    Managers --> Validate["PhotoAIKit validates both bundles"]
    Validate --> Providers["One provider per valid bundle"]
    Providers --> Selected{"Which model is selected?"}
    Selected -->|OpenAI| OpenAI["OpenAI CoreAICLIPProvider"]
    Selected -->|DataComp| DataComp["DataComp CoreAICLIPProvider"]
    OpenAI --> Similarity["Image similarity and burst grouping"]
    OpenAI --> Search["Text-to-image semantic search"]
    DataComp --> Similarity
    DataComp --> Search

There may be two validated providers in memory, but RawCull exposes only the selected provider to the active similarity and semantic-search features.

5. Startup Uses Vision First

Main/RawCullApp.swift creates the object graph synchronously:

let integration = RawCullAIIntegration()
let viewModel = RawCullViewModel(
    similarityService: integration.visionSimilarityService,
    semanticSearchCapability: integration.capabilities()
        .semanticSearchStatus(for: .defaultSelection),
    deepAIReviewFeature: integration.deepAIReviewFeature
)

Vision is installed first because it needs no downloaded model bundle. The app can open even when both CLIP folders are absent.

RawCullAISettingsModel then receives two callbacks:

  • one installs a new RawCullSimilarityServicing value in the main view model;
  • one installs the selected model’s semantic-search capability and service.

The asynchronous .task attached to the main window calls aiSettingsModel.refresh(). Model directory inspection, fingerprinting, and provider construction therefore do not block RawCullApp.init().

6. Preferences And The Selected Model

Model/ViewModels/RawCullAISettingsModel.swift stores two values:

RawCullAI.useCLIPForSimilarity
RawCullAI.selectedCLIPModel

When no values have been saved:

  • CLIP similarity defaults to enabled;
  • the OpenAI model is the default selection.

The model picker is mutually exclusive. Choosing DataComp replaces OpenAI as the selected model; it does not combine their results.

The CLIP toggle is a request, not proof that a model is usable:

CLIP toggleSelected bundle validSimilarity backend
OffNo or yesVision feature print
OnNoVision feature print
OnYesSelected CLIP model

Semantic-search readiness is tracked separately because Vision can compare two images but cannot compare an image with text. A valid selected CLIP provider is required for semantic search.

RawCull can read already-cached compatible CLIP artifacts for semantic search. To create missing semantic-search artifacts through the normal indexing button, the selected CLIP model must also be active as the similarity backend.

Changing the selected model immediately reapplies both services. It is not necessary to restart RawCull.

7. Model Locations

RawCullAIPaths.live() creates two separate installed-model locations:

~/Library/Application Support/RawCull/Models/
├── CLIP-DataComp/
└── CLIP-OpenAI/

A sandboxed build resolves the same relative folders inside its container:

~/Library/Containers/no.blogspot.RawCull/Data/Library/Application Support/
└── RawCull/Models/
    ├── CLIP-DataComp/
    └── CLIP-OpenAI/

Settings > AI shows the exact paths for the running build.

RawCullAIModelCandidates.urls(...) puts the installed directory first. Debug builds may add app-bundle resource candidates such as Models/CLIP-OpenAI; release builds do not use bundled fallback unless it is explicitly enabled during construction.

PhotoAIKit does not search these paths. RawCull owns path policy and passes an ordered URL list into the package.

8. What A CLIP Bundle Contains

Each model has its own self-contained directory:

CLIP-OpenAI/ or CLIP-DataComp/
├── metadata.json
├── tokenizer/
│   └── tokenizer.json
└── selected-model.aimodel

The selected model may instead be a compiled .aimodelc directory.

A current metadata version 0.4 export describes:

  • model family, source, revision, architecture, and pretrained checkpoint;
  • 512 embedding dimensions;
  • input width and height;
  • shortest-side resize, center crop, and interpolation policy;
  • RGB mean and standard deviation;
  • tokenizer type, version, context length, and padding token;
  • the image_encoder and text_encoder function names;
  • output normalization and configuration versions;
  • assets.main, which names the selected Core AI asset;
  • asset_fingerprints.main, which records the expected asset fingerprint.

This matters because a model is more than its weight file. The tokenizer and preprocessing rules must match the weights used during training.

9. Validation And Provider Construction

RawCullAIIntegration owns one resource manager for each model:

clipDataCompModelResourceManager
clipOpenAIModelResourceManager

During refreshCapabilities(), RawCull loads SAM 3, DataComp CLIP, and OpenAI CLIP concurrently:

async let sam3Load = sam3ModelResourceManager.load()
async let clipDataCompLoad = clipDataCompModelResourceManager.load()
async let clipOpenAILoad = clipOpenAIModelResourceManager.load()

Each RawCullAIModelResourceManager is an actor. It first builds a lightweight snapshot from paths, file kinds, sizes, modification dates, and resolved symbolic-link paths. If nothing changed, it can reuse the previous capability and provider instead of hashing a large model again.

When the snapshot changes, PhotoAIKit remains the authority:

flowchart TD
    Candidate["RawCull candidate URL"] --> Exists{"Directory exists?"}
    Exists -->|No| Next["Try the next candidate"]
    Exists -->|Yes| Metadata["Decode metadata.json"]
    Metadata --> Asset["Resolve assets.main"]
    Asset --> Resources["Check tokenizer and selected asset"]
    Resources --> Fingerprint["Fingerprint asset and verify manifest checksum"]
    Fingerprint --> Valid{"Valid?"}
    Valid -->|No| Invalid["Report invalid and stop"]
    Valid -->|Yes| Provider["Construct CoreAICLIPProvider"]

A missing candidate is skipped. An invalid higher-priority candidate stops resolution instead of being hidden by a lower-priority debug bundle.

The provider constructor validates the bundle again, stores its fingerprinted ModelIdentity, decodes metadata.json, and validates the model-specific CLIPRuntimeConfiguration. It does not load the large Core AI model yet.

Capability and provider-construction failure are recorded separately. Settings can therefore distinguish a missing or damaged bundle from a bundle that validated but could not initialize its runtime configuration.

10. Lazy Core AI Loading

The first image or text embedding request calls CoreAICLIPProvider.loadModel().

The provider then:

  1. reads assets.main and creates an AIModel;
  2. asks Core AI to prefer GPU specialization;
  3. creates CLIPTokenizer from the bundle’s tokenizer folder;
  4. finds and loads the metadata-selected image and text functions;
  5. requires pixel_values and image_embeds on the image side;
  6. requires input_ids and text_embeds on the text side;
  7. accepts attention_mask when the text function declares it;
  8. validates tensor ranks, dimensions, scalar types, resolution, and token context length;
  9. caches the functions, descriptors, tokenizer, and compatibility inputs inside the provider actor.

New bundles use two named functions in one asset:

image_encoder(pixel_values)                 -> image_embeds
text_encoder(input_ids[, attention_mask])  -> text_embeds

This avoids running the text tower while indexing an image and avoids running the image tower for every search query.

PhotoAIKit still supports older single-function CLIP exports. If an older image function also requires text inputs, the provider supplies tokens for "a photo". If an older text function requires an image, it supplies a zero image. These are compatibility inputs, not RawCull’s semantic-search query.

The loaded model is cached in the actor, so later requests reuse it. The first request can take longer because macOS may specialize a portable model for the current Mac.

11. From A RAW File To Model Pixels

PhotoAIKit deliberately does not know how to decode camera RAW formats. RawCull’s RawCullSimilarityImageDecoder supplies that application-specific step.

For each AIImageSource, the decoder:

  1. checks task cancellation;
  2. asks RawParserKitImageLoader for a thumbnail no larger than 512 pixels;
  3. if that fails, asks ImageIO for an existing embedded thumbnail;
  4. reports imageDecodeFailed if neither path returns a CGImage.

The 512-pixel value belongs to RawCull’s embedding pipeline. It gives the model provider a bounded image instead of decoding an unnecessarily large RAW preview.

The selected provider then applies the exact preprocessing declared by that model:

  1. convert to an 8-bit sRGB RGBA drawing buffer;
  2. resize the shortest side while preserving aspect ratio;
  3. take a centered 224 × 224 OpenAI crop or 256 × 256 DataComp crop;
  4. convert red, green, and blue from bytes to values in 0 ... 1;
  5. subtract CLIP means 0.48145466, 0.4578275, and 0.40821073;
  6. divide by CLIP standard deviations 0.26862954, 0.26130258, and 0.27577711;
  7. change interleaved RGB into channel-first [1, 3, height, width] order;
  8. write a Float16 or Float32 Core AI NDArray, as required by the model.

The center crop can remove pixels near the long edges of a photograph. That is intentional: preprocessing must match the model’s training and export contract.

12. Creating A CLIP Image Artifact

After preprocessing, the provider runs image_encoder and reads image_embeds.

Both current graphs export an L2-normalized vector. PhotoAIKit wraps the result in ImageEmbedding, which also applies safe L2 normalization:

magnitude = sqrt(sum(value²))
normalizedValue = value / magnitude

The provider then creates a SimilarityArtifact:

SimilarityArtifact
├── descriptor
│   ├── backend = "clip"
│   ├── model fingerprint
│   ├── dimensions = 512
│   ├── representation version
│   ├── preprocessing version
│   ├── normalization version
│   ├── configuration version
│   ├── source path, size, and modification date
│   └── artifact schema version
└── payload
    └── JSON-encoded ImageEmbedding vector

The descriptor prevents RawCull from treating a vector as reusable merely because it can be decoded.

13. Indexing, Concurrency, And Failure Recovery

RawCullCLIPSimilarityService creates a PhotoAIKit SimilarityArtifactIndexer with:

  • the selected CLIP provider;
  • RawCull’s diagnosing RAW decoder;
  • no per-image or whole-batch fallback provider;
  • a current concurrency limit of 1.

The current CLIP behavior is:

flowchart TD
    Source["One catalog image"] --> Decode["Decode bounded CGImage"]
    Decode --> Inference["Run selected CLIP image encoder"]
    Inference --> Finite{"Vector finite and non-empty?"}
    Finite -->|Yes| Keep["Validate, persist, and keep artifact"]
    Finite -->|No| Retry["Retry once with the loaded provider"]
    Retry --> RetryOK{"Valid now?"}
    RetryOK -->|Yes| Keep
    RetryOK -->|No| Replace["Construct a fresh provider and retry"]
    Replace --> ReplaceOK{"Valid now?"}
    ReplaceOK -->|Yes| Keep
    ReplaceOK -->|No| Exclude["Record failure; exclude this image"]

Only non-finite embedding output receives the two recovery attempts. A decode error or another model error is recorded for that source without discarding valid CLIP artifacts already created for other images.

This is different from the earlier whole-batch fallback design:

  • RawCull no longer throws away a successful CLIP pass because one image failed;
  • it does not mix Vision artifacts into a selected CLIP indexing pass;
  • a failed image has no current artifact and is excluded from similarity comparisons and burst grouping until it can be indexed successfully.

When CLIP is disabled or its selected bundle is unavailable at service selection time, RawCull uses RawCullVisionSimilarityService instead. Vision currently indexes with a concurrency limit of 4.

14. How RawCull Uses Image-To-Image Distance

For two compatible CLIP artifacts, PhotoAIKit computes cosine distance:

cosineDistance(a, b) =
    1 - dot(a, b) / (magnitude(a) × magnitude(b))

For normalized vectors:

  • 0 means the directions are effectively identical;
  • 1 means the directions are orthogonal;
  • 2 means the directions are opposite.

Before computing the value, the provider verifies that both artifacts have the same model fingerprint, dimensions, representation, preprocessing, normalization, configuration, and schema. It also checks that each JSON payload matches its descriptor.

RawCull uses this primitive distance in three ways:

  1. Find similar images. It compares a selected anchor image with every compatible catalog artifact and sorts smaller distances first.
  2. Apply a small product-policy adjustment. If available saliency labels identify different subjects, SimilarityScoringModel adds 0.10 to the distance.
  3. Group bursts. It compares adjacent images in capture order and passes the distances to RawCullCore.BurstGroupingEngine. The current default visual threshold is 0.25.

CLIP supplies the distance. RawCull owns the saliency adjustment, threshold, burst rules, sharpness scoring, recommendation, and rating behavior.

If an image has no valid artifact, burst grouping splits the ordered catalog into eligible runs around that gap. It does not invent a distance across the missing image.

15. How Semantic Search Works

Semantic search uses the text half of the same selected CLIP provider and the already-persisted image artifacts.

For a query such as red fox at dusk, RawCull:

  1. trims surrounding whitespace but otherwise sends the literal query;
  2. tokenizes it with the bundle’s CLIP BPE tokenizer;
  3. truncates or pads it to the model’s 77-token context;
  4. runs text_encoder;
  5. validates that the returned 512-value vector is finite, non-empty, and L2-normalized;
  6. compares it only with cached image artifacts that match the selected model and every backend descriptor field;
  7. sorts larger cosine-similarity scores first.

Image-to-text uses cosine similarity, not distance:

cosineSimilarity(image, text) = dot(imageVector, textVector)

The provider clamps the result to -1 ... 1. It is a relative ranking score, not a calibrated probability or confidence percentage.

The text embedding is query-scoped and is not written to disk. The expensive image encoder does not run during a search. Only the text encoder runs once, followed by ordinary vector dot products over the compatible cached images.

RawCull’s deterministic tie order is:

  1. higher score;
  2. earlier catalog order;
  3. localized filename order;
  4. UUID string.

The UI initially presents the highest-ranked 20 images. The user can expand the selection without recomputing embeddings or scores. Rating and filename filters are applied before semantic ranking, and the selected semantic results become the active catalog working set until the search is cleared.

16. Persisting And Reusing Image Artifacts

RawCull persists successful similarity artifacts per source file under:

Application Support/RawCull/
└── AnalysisArtifacts/
    └── Similarity/

PerFileAnalysisArtifactStore writes one atomic JSON record per source/backend/pipeline identity. This lets an artifact survive application restart and be reused by both image similarity and semantic search.

The store validates:

  • standardized source path, file size, and modification date;
  • selected model fingerprint;
  • artifact schema and vector representation;
  • preprocessing, normalization, and configuration versions;
  • RawCull’s 512-pixel input limit;
  • RawCull embedding pipeline version 3.

Moving or renaming a source file changes its standardized path and is therefore a cache miss. The default pruning policy removes records unused for 90 days and caps the store at 50,000 entries.

RawCull also saves catalog-wide burst analysis under:

Application Support/RawCull/BurstAnalysis/

The current burst cache schema is 9. Its similarity signature includes grouping configuration, the selected backend descriptor, accepted artifact descriptors, artifact schema, 512-pixel input limit, and embedding pipeline version. It also verifies the catalog, file identities, and a digest of the artifact set.

These identities create independent invalidation paths:

model asset changed       -> model fingerprint mismatch
selected model changed    -> backend descriptor mismatch
preprocessing changed     -> preprocessing or pipeline mismatch
source file changed       -> source fingerprint mismatch
artifact format changed   -> schema mismatch
grouping policy changed   -> burst signature mismatch

17. Switching Between OpenAI And DataComp

When the selected model changes, RawCullAISettingsModel asks the composition root for new similarity and semantic-search services.

RawCullViewModel.setSimilarityService(_:) then:

  1. cancels and resets active burst analysis;
  2. resets image indexing, distances, grouping, ranking, progress, and diagnostics;
  3. installs the new backend;
  4. tries to hydrate only artifacts compatible with the new descriptor.

The semantic-search service performs a similar reset and compatible-artifact hydration.

This reset is required even though both models return 512 values. Keeping an OpenAI vector while labeling the active service DataComp would make every subsequent comparison mathematically meaningless.

18. Cancellation And Stale-Result Protection

The code uses two complementary mechanisms:

  • Task cancellation asks ongoing decode, inference, persistence, or ranking work to stop.
  • Generation counters prevent a result from being published after the user has started a newer operation or switched models.

The decoder checks cancellation before and after image loading. The CLIP recovery path never converts CancellationError into a model failure. Indexing, image ranking, semantic search, semantic artifact hydration, and burst grouping each keep their own generation.

Cancellation answers “should this work continue?” A generation check answers “even if it finished, does it still belong to the current user action?”

19. Troubleshooting

Use this order because it follows the runtime path:

  1. Check the selected model. OpenAI and DataComp use different folders.
  2. Check the CLIP toggle. It must be on to index missing CLIP artifacts through the normal similarity workflow.
  3. Check Settings capability text. A saved preference does not mean that the selected provider exists.
  4. Check the displayed folder. Use the exact path shown by the running sandboxed or non-sandboxed build.
  5. Check the bundle structure. Confirm metadata.json, tokenizer/tokenizer.json, and the .aimodel or .aimodelc selected by assets.main.
  6. Check the fingerprint. A declared checksum must match the selected asset.
  7. Check metadata compatibility. Resolution, crop policy, token context, function names, tensor names, shapes, and scalar types must match the Core AI asset.
  8. Remove or repair an invalid higher-priority install. Resolution stops at an invalid installed directory instead of silently using a debug-bundled copy.
  9. Expect the first inference to be slower. Core AI may specialize the model for the Mac.
  10. Inspect similarity diagnostics. A partial CLIP result identifies decode or inference failures by image. It does not mean that the complete catalog switched to Vision.
  11. Reindex missing images. Images that still produced invalid output after recovery remain excluded until a later successful indexing pass.
  12. Check semantic-search coverage. Search ranks compatible cached CLIP artifacts only; it never decodes and indexes missing images as a side effect of submitting a query.

20. Source Map

ConcernCurrent source
App construction and callbacksRawCull/Main/RawCullApp.swift
CLIP choices and application-support pathsRawCull/Model/AIIntegration/RawCullAIModels.swift
Candidate URLs and cached resource checksRawCull/Model/AIIntegration/RawCullAIModelResourceManager.swift
Provider dictionaries, selection, and refreshRawCull/Model/AIIntegration/RawCullAIIntegration.swift
Saved preference and selected modelRawCull/Model/ViewModels/RawCullAISettingsModel.swift
Settings model picker and capability statusRawCull/Views/Settings/AISettingsTab.swift
RAW decoding, CLIP retry, Vision service, and distance routingRawCull/Model/AIIntegration/RawCullVisionSimilarityService.swift
Image indexing, ranking, grouping, and semantic-search stateRawCull/Model/ViewModels/SimilarityScoringModel.swift
Literal query ranking serviceRawCull/Model/AIIntegration/RawCullSemanticSearchService.swift
Semantic-search controls and coverageRawCull/Views/SimilarityGridView/SemanticSearchViews.swift
Per-file artifact persistenceRawCull/Actors/PerFileAnalysisArtifactStore.swift
Catalog-wide burst persistenceRawCull/Actors/BurstAnalysisCache.swift
PhotoAIKit package productsPhotoAIKit/Package.swift
Metadata-driven runtime configurationPhotoAIKit/Sources/CoreAICLIPBackend/CLIPRuntimeConfiguration.swift
Image and text inferencePhotoAIKit/Sources/CoreAICLIPBackend/CoreAICLIPProvider.swift
Bundle resolution and fingerprint identityPhotoAIKit/Sources/PhotoAIContracts/ModelBundleResolver.swift, ModelIdentity.swift, and ModelAssetFingerprint.swift
Model exporter and exact model specificationsPhotoAIKit/Tools/export_clip.py

For the package layering behind these types, continue with How PhotoAIKit Is Constructed.

2 - AI Model Download Service

Build, validate, package, and download the three optional RawCull AI models.

AI model download service

RawCull supports exactly three optional model bundles:

ModelPhotoAIKit bundleRuntime assetUse
DataComp CLIPCLIP-DataCompViT-B-32-256-datacomp_s34b_b86k_float16_static.aimodelSimilarity and semantic search
OpenAI CLIPCLIP-OpenAIclip-vit-base-patch32_float16_static.aimodelSimilarity and semantic search
Meta SAM 3SAM3sam3_float16.aimodelPromptable segmentation

SigLIP2 and EfficientSAM are not part of the RawCull download catalogue or release manifest. Do not include their bundles, notices, archives, or asset-pack IDs in a RawCull model release.

RawCull uses Managed Background Assets. The app asks AssetPackManager for a known asset-pack ID, resolves the catalogue-owned model path, and gives that directory to PhotoAIKit for validation. RawCull does not unpack an AAR itself.

The current production manifest is pinned to:

https://github.com/rsyncOSX/RawCull-AI-Models/releases/download/v1/manifest.json

The current RawCull source enables DataComp only. OpenAI CLIP and SAM 3 remain hidden and release-blocked until their redistribution reviews and new archives are complete. The three-model release procedure is documented in Publishing new RawCull AI models.

Release gates

A working local conversion is not automatically publishable. Every downloadable model must pass all of these gates:

  1. Pin and verify the exact upstream checkpoint.
  2. Record source file checksums, PhotoAIKit revision, conversion command, runtime fingerprint, and archive checksum.
  3. Confirm that the exact trained weights may be converted, used, and redistributed through the chosen hosting channel.
  4. Include the applicable complete licence and notice files in the pack.
  5. Validate the generated bundle with PhotoAIKit and RawCull.
  6. Package only runtime files; never publish conversion intermediates.

DataComp is currently the only .ready production descriptor. OpenAI CLIP is blocked pending weight-level redistribution clearance. SAM 3 is gated upstream and remains blocked until ungated redistribution of the converted derivative is confirmed. SAM 3 also requires explicit in-app licence acceptance.

Use the reviewed PhotoAIKit revision

The procedures below are verified against PhotoAIKit commit:

6e3216027b267c27ccaf99d334807b18ea1aaec9

That revision exports CLIP metadata version 0.4, SAM 3 metadata version 0.3, separate CLIP image_encoder and text_encoder functions, normalized embeddings, and the corrected Pillow-compatible bicubic CLIP preprocessing.

Use a clean detached checkout for release evidence:

PHOTOAIKIT_REVISION='6e3216027b267c27ccaf99d334807b18ea1aaec9'
PHOTOAIKIT_DIR='/Users/thomas/ModelAssets/ReleaseEvidence/PhotoAIKit'

git clone https://github.com/rsyncOSX/PhotoAIKit.git "$PHOTOAIKIT_DIR"
git -C "$PHOTOAIKIT_DIR" switch --detach "$PHOTOAIKIT_REVISION"
test "$(git -C "$PHOTOAIKIT_DIR" rev-parse HEAD)" = "$PHOTOAIKIT_REVISION"
test -z "$(git -C "$PHOTOAIKIT_DIR" status --porcelain)"

If a later PhotoAIKit revision is used, review changes to Tools/export_clip.py, Tools/export_sam3.py, preprocessing, metadata, and runtime validation. Record the exact revision actually executed; do not silently reuse the value above.

Create the DataComp CLIP bundle

The release model is OpenCLIP ViT-B-32-256 with the registered datacomp_s34b_b86k pretrained tag.

FieldRequired value
Checkpoint repositorylaion/CLIP-ViT-B-32-256x256-DataComp-s34B-b86K
Revision4afec35ffe57a943d569ff7ee888061830164da8
Weight fileopen_clip_model.safetensors
Weight bytes605189364
Weight SHA-25692c26d60d3200ed5ed040dff31a8d19f8140648da8007216c25744c478deef27

Create an evidence-specific Hugging Face cache and verify that its main resolution is the pinned revision required by OpenCLIP:

DATACOMP_REPOSITORY='laion/CLIP-ViT-B-32-256x256-DataComp-s34B-b86K'
DATACOMP_REVISION='4afec35ffe57a943d569ff7ee888061830164da8'
DATACOMP_SHA256='92c26d60d3200ed5ed040dff31a8d19f8140648da8007216c25744c478deef27'
DATACOMP_BYTES='605189364'
DATACOMP_ROOT="/Users/thomas/ModelAssets/ReleaseEvidence/CLIP-DataComp/$DATACOMP_REVISION"
DATACOMP_HF_HOME="$DATACOMP_ROOT/huggingface"
DATACOMP_EXPORT_DIR="$DATACOMP_ROOT/export"

mkdir -p "$DATACOMP_HF_HOME" "$DATACOMP_EXPORT_DIR"
export HF_HOME="$DATACOMP_HF_HOME"

DATACOMP_SNAPSHOT="$(hf download "$DATACOMP_REPOSITORY" \
  open_clip_model.safetensors open_clip_config.json README.md \
  --revision "$DATACOMP_REVISION" --quiet)"
DATACOMP_MAIN_SNAPSHOT="$(hf download "$DATACOMP_REPOSITORY" \
  open_clip_model.safetensors open_clip_config.json README.md \
  --revision main --quiet)"

test "$(basename "$DATACOMP_SNAPSHOT")" = "$DATACOMP_REVISION"
test "$DATACOMP_MAIN_SNAPSHOT" = "$DATACOMP_SNAPSHOT"
test "$(shasum -a 256 "$DATACOMP_SNAPSHOT/open_clip_model.safetensors" | cut -d ' ' -f 1)" = "$DATACOMP_SHA256"
test "$(stat -f '%z' "$DATACOMP_SNAPSHOT/open_clip_model.safetensors")" = "$DATACOMP_BYTES"

Export with PhotoAIKit’s registered preset:

HF_HUB_OFFLINE=1 \
TRANSFORMERS_OFFLINE=1 \
uv run --script "$PHOTOAIKIT_DIR/Tools/export_clip.py" \
  --model openclip-datacomp \
  --architecture ViT-B-32-256 \
  --pretrained datacomp_s34b_b86k \
  --output-dir "$DATACOMP_EXPORT_DIR" \
  --bundle-name CLIP-DataComp \
  --dtype float16

Do not pass open_clip_model.safetensors as --pretrained. That creates the historical custom-named bundle and bypasses the registered preset identity. The tested, non-collapsing bundle uses datacomp_s34b_b86k and must contain:

CLIP-DataComp/
├── metadata.json
├── tokenizer/
├── ViT-B-32-256-datacomp_s34b_b86k_float16_static.aimodel/
└── ViT-B-32-256-datacomp_s34b_b86k_float16_static_source.aimodel/

The _source.aimodel directory is private conversion evidence. Exclude it from the downloadable pack. metadata.json must select the optimized directory in assets.main and contain its asset_fingerprints.main value.

Create the OpenAI CLIP bundle

FieldRequired value
Checkpoint repositoryopenai/clip-vit-base-patch32
Revision3d74acf9a28c67741b2f4f2ea7635f0aaf6f0268
Weight filepytorch_model.bin
Weight bytes605247071
Weight SHA-256a63082132ba4f97a80bea76823f544493bffa8082296d62d71581a4feff1576f

The exporter loads the repository ID for both the model and tokenizer. Use an evidence-specific Hugging Face cache, require its main reference to resolve to the pinned revision, and then run offline. Loading through that verified cache also lets Transformers record the immutable source revision in the generated metadata:

OPENAI_REVISION='3d74acf9a28c67741b2f4f2ea7635f0aaf6f0268'
OPENAI_SHA256='a63082132ba4f97a80bea76823f544493bffa8082296d62d71581a4feff1576f'
OPENAI_BYTES='605247071'
OPENAI_ROOT="/Users/thomas/ModelAssets/ReleaseEvidence/CLIP-OpenAI/$OPENAI_REVISION"
OPENAI_HF_HOME="$OPENAI_ROOT/huggingface"
OPENAI_EXPORT_DIR="$OPENAI_ROOT/export"

mkdir -p "$OPENAI_HF_HOME" "$OPENAI_EXPORT_DIR"
export HF_HOME="$OPENAI_HF_HOME"

OPENAI_SNAPSHOT="$(hf download openai/clip-vit-base-patch32 \
  config.json pytorch_model.bin merges.txt preprocessor_config.json \
  special_tokens_map.json tokenizer.json tokenizer_config.json vocab.json README.md \
  --revision "$OPENAI_REVISION" --quiet)"
OPENAI_MAIN_SNAPSHOT="$(hf download openai/clip-vit-base-patch32 \
  config.json pytorch_model.bin merges.txt preprocessor_config.json \
  special_tokens_map.json tokenizer.json tokenizer_config.json vocab.json README.md \
  --revision main --quiet)"

test "$(basename "$OPENAI_SNAPSHOT")" = "$OPENAI_REVISION"
test "$OPENAI_MAIN_SNAPSHOT" = "$OPENAI_SNAPSHOT"
test "$(shasum -a 256 "$OPENAI_SNAPSHOT/pytorch_model.bin" | cut -d ' ' -f 1)" = "$OPENAI_SHA256"
test "$(stat -f '%z' "$OPENAI_SNAPSHOT/pytorch_model.bin")" = "$OPENAI_BYTES"

HF_HUB_OFFLINE=1 \
TRANSFORMERS_OFFLINE=1 \
uv run --script "$PHOTOAIKIT_DIR/Tools/export_clip.py" \
  --model openai \
  --output-dir "$OPENAI_EXPORT_DIR" \
  --bundle-name CLIP-OpenAI \
  --dtype float16

The result must contain:

CLIP-OpenAI/
├── metadata.json
├── tokenizer/
├── clip-vit-base-patch32_float16_static.aimodel/
└── clip-vit-base-patch32_float16_static_source.aimodel/

metadata.json must report source revision 3d74acf9a28c67741b2f4f2ea7635f0aaf6f0268. Exclude the _source.aimodel directory from the downloadable pack.

Create the Meta SAM 3 bundle

FieldRequired value
Checkpoint repositoryfacebook/sam3
Revision3c879f39826c281e95690f02c7821c4de09afae7
Weight filemodel.safetensors
Weight bytes3439938512
Weight SHA-2566d06f0a5f84e435071fe6603e61d0b4cc7b40e0d39d487cfd4d67d8cc11cc14a
SAM License SHA-256b08db9d32c687054e99cbd41eb1dad19c76936dfb9e2b58e186a01204d8be9ab

SAM 3 is gated. Complete Meta’s Hugging Face access flow and authenticate with hf auth login. Never put the token in a command transcript, provenance file, or archive.

The exporter loads the literal path facebook/sam3 three times. As with OpenAI CLIP, stage that relative local path and force offline conversion:

SAM3_REVISION='3c879f39826c281e95690f02c7821c4de09afae7'
SAM3_SHA256='6d06f0a5f84e435071fe6603e61d0b4cc7b40e0d39d487cfd4d67d8cc11cc14a'
SAM3_BYTES='3439938512'
SAM3_LICENSE_SHA256='b08db9d32c687054e99cbd41eb1dad19c76936dfb9e2b58e186a01204d8be9ab'
SAM3_ROOT="/Users/thomas/ModelAssets/ReleaseEvidence/SAM3/$SAM3_REVISION"
SAM3_SOURCE_ROOT="$SAM3_ROOT/source"
SAM3_SOURCE_DIR="$SAM3_SOURCE_ROOT/facebook/sam3"
SAM3_EXPORT_DIR="$SAM3_ROOT/export"

mkdir -p "$SAM3_SOURCE_DIR" "$SAM3_EXPORT_DIR"
hf download facebook/sam3 \
  model.safetensors config.json processor_config.json tokenizer.json \
  tokenizer_config.json special_tokens_map.json merges.txt vocab.json LICENSE README.md \
  --revision "$SAM3_REVISION" --local-dir "$SAM3_SOURCE_DIR"

test "$(shasum -a 256 "$SAM3_SOURCE_DIR/model.safetensors" | cut -d ' ' -f 1)" = "$SAM3_SHA256"
test "$(stat -f '%z' "$SAM3_SOURCE_DIR/model.safetensors")" = "$SAM3_BYTES"
test "$(shasum -a 256 "$SAM3_SOURCE_DIR/LICENSE" | cut -d ' ' -f 1)" = "$SAM3_LICENSE_SHA256"

cd "$SAM3_SOURCE_ROOT"
HF_HUB_OFFLINE=1 \
TRANSFORMERS_OFFLINE=1 \
uv run --script "$PHOTOAIKIT_DIR/Tools/export_sam3.py" \
  --model facebook/sam3 \
  --output-dir "$SAM3_EXPORT_DIR" \
  --bundle-name SAM3 \
  --dtype float16

The result must contain:

SAM3/
├── metadata.json
├── tokenizer/
├── sam3_float16.aimodel/
└── sam3_float16_source.aimodel/

The current SAM 3 exporter records the model ID but not the upstream commit. Keep the verified source manifest and PhotoAIKit revision in PROVENANCE.json; do not claim that metadata.json alone binds the revision. Exclude sam3_float16_source.aimodel from the downloadable pack.

Validate every generated bundle

Do not use --dynamic or --overwrite for a release conversion. Preserve the terminal log and record uv --version, sw_vers, xcodebuild -version, the PhotoAIKit commit, source checksums, generated metadata, and runtime fingerprint.

For each runtime asset, compare the generated fingerprint with asset_fingerprints.main:

python3 "$PHOTOAIKIT_DIR/Tools/model_fingerprint.py" \
  /path/to/bundle/runtime.aimodel

Run PhotoAIKit’s package tests:

swift test --package-path "$PHOTOAIKIT_DIR"

For each CLIP bundle, generate PyTorch reference embeddings from the same model identity and compare them with the Core AI bundle using clipbench. The fixture set must contain representative portraits, animals, vehicles, landscapes, interiors, night scenes, macro images, motion blur, readable signs, and flowers.

cd "$PHOTOAIKIT_DIR"
swift build -c release --product clipbench

uv run --script Tools/generate_clip_reference.py \
  --model openclip-datacomp \
  --architecture ViT-B-32-256 \
  --pretrained datacomp_s34b_b86k \
  --image /path/to/01-dog-outdoors.jpg \
  --text 'a woman with a dog outdoors' \
  --output /private/tmp/datacomp-clip-reference.json

.build/release/clipbench parity \
  --reference /private/tmp/datacomp-clip-reference.json \
  --model /path/to/CLIP-DataComp \
  --minimum-cosine 0.998

Repeat with --model openai and the OpenAI bundle. Add every fixture image and matching prompt with repeated --image and --text arguments. Investigate any result below the chosen threshold; do not lower the threshold merely to publish the pack.

Finally, install each complete candidate in RawCullFB, rebuild its model-specific index, and run the same semantictest.txt for both CLIP models. Compare:

  • query-by-query top results and scores;
  • image-to-image nearest-neighbour results;
  • duplicate or near-duplicate retrieval;
  • score spread and repeated ranking patterns;
  • missing results, errors, and elapsed time.

A new model fingerprint requires a new index. Never compare one model against an index created by another model or by an older conversion.

For SAM 3, install the candidate in RawCull and verify text-prompt segmentation, mask dimensions, mask placement, licence acceptance, relaunch, and removal.

Prepare the release staging tree

Only the optimized runtime assets belong in the pack:

ModelAssets/Release/
├── Models/
│   ├── CLIP-DataComp/
│   │   ├── metadata.json
│   │   ├── tokenizer/
│   │   └── ViT-B-32-256-datacomp_s34b_b86k_float16_static.aimodel/
│   ├── CLIP-OpenAI/
│   │   ├── metadata.json
│   │   ├── tokenizer/
│   │   └── clip-vit-base-patch32_float16_static.aimodel/
│   └── SAM3/
│       ├── metadata.json
│       ├── tokenizer/
│       └── sam3_float16.aimodel/
├── Notices/
│   ├── CLIP-DataComp/
│   ├── CLIP-OpenAI/
│   └── SAM3/
├── Packaging/
│   ├── clip-datacomp.json
│   ├── clip-openai.json
│   └── sam3.json
└── Output/

Each packaging manifest must select exactly metadata.json, tokenizer, the optimized runtime directory, and the matching notice directory. Example:

{
  "assetPackID": "no.blogspot.RawCull.models.clip-datacomp",
  "downloadPolicy": { "onDemand": {} },
  "fileSelectors": [
    { "file": "Models/CLIP-DataComp/metadata.json" },
    { "directory": "Models/CLIP-DataComp/tokenizer" },
    { "directory": "Models/CLIP-DataComp/ViT-B-32-256-datacomp_s34b_b86k_float16_static.aimodel" },
    { "directory": "Notices/CLIP-DataComp" }
  ],
  "platforms": ["macOS"]
}

The three stable asset-pack IDs and installed paths are:

ModelAsset-pack IDInstalled model path
DataComp CLIPno.blogspot.RawCull.models.clip-datacompModels/CLIP-DataComp
OpenAI CLIPno.blogspot.RawCull.models.clip-openaiModels/CLIP-OpenAI
SAM 3no.blogspot.RawCull.models.sam3Models/SAM3

Generate one .aar per manifest from the release staging root:

cd /Users/thomas/ModelAssets/Release
xcrun ba-package package Packaging/clip-datacomp.json --output-path Output/clip-datacomp.aar
xcrun ba-package package Packaging/clip-openai.json --output-path Output/clip-openai.aar
xcrun ba-package package Packaging/sam3.json --output-path Output/sam3.aar

shasum -a 256 Output/*.aar
stat -f '%N %z bytes' Output/*.aar

Treat published archives as immutable. Record each AAR checksum and byte count in provenance and the matching RawCull catalogue descriptor. Upload archives before uploading the generated download manifest.json.

Runtime download and activation

Self-hosting is used for the Developer ID distribution. The app and downloader extension share group.no.blogspot.RawCull.model-assets. For an App Store build, the downloader can instead use Apple hosting; do not enable both hosting modes in one product.

When the user selects Download, RawCull:

  1. checks inclusion and release readiness;
  2. verifies any required licence acceptance;
  3. asks Managed Background Assets for the exact asset-pack ID;
  4. reports download progress and supports cancellation;
  5. resolves the catalogue-owned installed model path;
  6. validates metadata, tokenizer, runtime asset, fingerprint, and configuration;
  7. constructs the matching CLIP or SAM 3 provider only after validation passes.

Removing a model asks Managed Background Assets to remove its pack and then refreshes RawCull capabilities. Manually installed models and managed packs must not be merged into the same candidate directory.

Signing and provisioning

The application, downloader extension, and App Group identifiers are:

PurposeIdentifier
RawCull applicationno.blogspot.RawCull
Model downloader extensionno.blogspot.RawCull.ModelDownloader
Shared App Groupgroup.no.blogspot.RawCull.model-assets

Both App IDs must have the App Group capability. The app and extension use the same development team and provisioning setup. Verify the final archive contains and signs the nested downloader extension before notarizing the Developer ID release.

3 - AI Model Licence

AI Model Licence

AI model licence and provenance clearance procedure

Status: publication hold
Last reviewed: 2026-08-03

Purpose and current decision

This document defines how RawCull clears the DataComp CLIP, OpenAI CLIP, and Meta SAM 3 model packs for public download. It covers technical provenance, licence evidence, upstream contacts, questions to ask, acceptable answers, and the final release gate.

No .aar model archive has been uploaded. Do not create a public GitHub Release, publish a download manifest, or change a production descriptor to ready until every model is either:

  1. cleared under this procedure; or
  2. deliberately excluded from the release and manifest.

The current AARs are unpublished development artifacts. The safest remedy for the recorded provenance gaps is to create new release candidates from pinned, hashed source files after the applicable licence has been cleared.

This is an engineering and evidence-preservation procedure, not legal advice. For an unresolved interpretation, obtain advice from a qualified lawyer who works with software copyright, open-source licensing, AI model weights, and commercial distribution in Norway and the EEA.

What “resolved” means

There are two independent gates.

Provenance gate

RawCull must be able to demonstrate this complete chain:

upstream owner and repository
immutable revision and exact source-weight filename
SHA-256 of the downloaded source file
pinned conversion code, command, dependencies, and toolchain
SHA-256 of the converted runtime model
SHA-256 and byte size of the packaged AAR

A filename, a local cache timestamp, or a likely upstream snapshot is not a cryptographic binding. Re-exporting from a deliberately selected and hashed source is preferable to trying to infer the origin of an old conversion.

Licence and distribution gate

RawCull must have a defensible basis for all of the following:

  • the licence applies to the exact trained weight file, not only source code;
  • conversion into an Apple Core AI model is allowed;
  • the converted derivative can be redistributed to third parties;
  • the intended distribution can be public and commercial, if RawCull is commercially distributed;
  • all notices, agreement copies, acceptance steps, use restrictions, and attribution requirements have been implemented; and
  • a gated source checkpoint may be redistributed through RawCull’s proposed delivery mechanism, if applicable.

Silence, an unanswered ticket, a community member’s assumption, widespread third-party mirroring, or the technical ability to download a file does not resolve this gate. Prefer a written response from the model owner or an authorized representative. If that is unavailable, obtain a written opinion from qualified counsel or omit the model.

Current recorded evidence

The canonical records are:

  • ModelAssets/Notices/CLIP-DataComp/PROVENANCE.json
  • ModelAssets/Notices/CLIP-OpenAI/PROVENANCE.json
  • ModelAssets/Notices/SAM3/PROVENANCE.json
  • the licence and notice files beside each provenance record

The current relevant identifiers are:

PackUpstream revision presently recordedSource-weight evidenceConverted runtime SHA-256Open issue
DataComp CLIP4afec35ffe57a943d569ff7ee888061830164da8 is a reference revision, not exporter-recorded proofExact selected file and SHA-256 are missing70b6a8b3502626dbfd4e228dff1e060e06606c93e7a19c8f260dba95b9a7d01eRebuild from one explicitly selected upstream weight file and confirm its licence scope
OpenAI CLIPLocal cache snapshot 3d74acf9a28c67741b2f4f2ea7635f0aaf6f0268pytorch_model.bin, SHA-256 a63082132ba4f97a80bea76823f544493bffa8082296d62d71581a4feff1576f; not exporter-bound34b1c3f2eccfac50e5c47eeb33029b8c488fb7f3712d50d6d46965625a6a3798Confirm the exact weights’ licence and rebuild from the pinned source
SAM 3Local cache snapshot 3c879f39826c281e95690f02c7821c4de09afae7model.safetensors, SHA-256 6d06f0a5f84e435071fe6603e61d0b4cc7b40e0d39d487cfd4d67d8cc11cc14a; not exporter-bound43a9b88e40d193f5a6608a7fee536a78f4ba4ec5d95f1eb24db03031630f0a31Confirm whether an ungated public derivative download is compatible with Meta’s gated access flow, then rebuild

These hashes identify current evidence; they do not themselves approve a release.

Common provenance remediation

Perform these steps separately for every model that passes its licence gate.

  1. Choose one exact upstream repository, immutable commit, and source-weight file. Never use main, latest, an unpinned model alias, or an automatically changing download URL as the release input.
  2. Download into a new, dated evidence directory. Preserve the upstream URL, immutable revision, filename, byte size, and SHA-256 before conversion.
  3. Save the model card, licence or agreement, repository metadata, and any access terms as they appeared on the download date. Record their URLs and retrieval dates.
  4. Record the converter repository and commit, Apple coreai-models commit, coreai-core version, Python environment or package lock, conversion command, macOS version, Xcode version, and conversion timestamp.
  5. Run the conversion from that evidence directory. Do not allow the exporter to resolve or download a floating model identifier internally.
  6. Hash the complete converted model directory with the established directory-tree-sha256-v1 method and hash its runtime main.mlirb file.
  7. Validate the converted model with the same PhotoAIKit checks used by RawCull.
  8. Update the corresponding PROVENANCE.json with the actual source revision, source filename, source SHA-256, conversion command or record, tool versions, output fingerprints, and supporting evidence references.
  9. Rebuild the AAR using the explicit selectors. Verify that the chosen runtime model, tokenizer, metadata.json, and complete notice catalog are present, and that _source.aimodel and conversion intermediates are absent.
  10. Record the new AAR byte size and SHA-256. The previous unpublished AAR hash must not be reused for the rebuilt archive.

An example pinned Hugging Face acquisition has this form; select the correct source filename before running it:

hf download OWNER/MODEL SOURCE_WEIGHT_FILE \
  --revision IMMUTABLE_COMMIT \
  --local-dir /path/to/private/release-evidence/MODEL/source

shasum -a 256 \
  /path/to/private/release-evidence/MODEL/source/SOURCE_WEIGHT_FILE

Keep raw correspondence, access tokens, account data, and legal advice out of the public repository. Store them in private, access-controlled records. A public provenance summary may record the response date, organization, scope, and internal evidence-record identifier without publishing personal data or privileged legal advice.

DataComp CLIP clearance

Current position

The pinned DataComp repository page identifies the checkpoint as MIT licensed, which is positive evidence. The remaining technical problem is that the existing exporter record does not identify and hash the exact source-weight file it converted.

Official references:

Who to contact

  1. Email LAION at contact@laion.ai. LAION publishes this address on its official legal contact page.
  2. Open a discussion on the exact Hugging Face model repository so the question and any maintainer response are tied to that checkpoint.
  3. If necessary, open an issue with the OpenCLIP maintainers to identify which upstream file the datacomp_s34b_b86k configuration resolves. OpenCLIP can help with technical identity; the model owner or counsel should resolve licence scope.

Recorded LAION email request

On 2026-08-02 at 17:20 CEST, the RawCull maintainer emailed contact@laion.ai with the subject “Written clarification requested for DataComp CLIP weight licensing and redistribution.” The request identified:

  • repository: laion/CLIP-ViT-B-32-256x256-DataComp-s34B-b86K;
  • immutable revision: 4afec35ffe57a943d569ff7ee888061830164da8;
  • proposed source-weight file: open_clip_model.safetensors;
  • model configuration: OpenCLIP ViT-B-32-256 with datacomp_s34b_b86k weights;
  • transformation: a float16 Apple Core AI runtime representation for local photo similarity and text-to-image semantic search; and
  • delivery: an optional model archive downloaded by RawCull from a public GitHub Release, including possible use with a commercially distributed version of RawCull.

The email stated that RawCull has not publicly distributed the model or its converted derivative and will not redistribute the DataComp training dataset. It proposed preserving the source byte size and SHA-256 and including the applicable MIT licence, copyright and attribution notices, model information, provenance, and checksums with the archive.

LAION was asked to confirm whether the displayed MIT licence covers the exact weight file, conversion into the proposed runtime representation, public and commercial redistribution of the derivative, and whether any additional DataComp, OpenCLIP, attribution, acceptable-use, or other conditions apply. The request also asked whether the model card’s “out of scope” deployment language is safety guidance or an additional legal restriction.

Status: awaiting a substantive response from LAION or another person authorized to clarify the rights applicable to the weights. Sending the email does not clear the pack. Preserve the original message and any response in the private evidence register; do not add the maintainer’s personal email address or complete mail headers to this public repository.

Questions to ask

Identify the exact repository, revision, and proposed source file, then ask:

  1. Is that exact trained weight file offered under the MIT licence shown on the model repository?
  2. Does that permission cover conversion into another runtime representation and redistribution of the converted weights with a desktop application?
  3. Is public and commercial redistribution permitted, provided the MIT notice is included?
  4. Are any DataComp dataset terms, OpenCLIP terms, attribution requirements, or use restrictions additional to the displayed MIT licence applicable to the weight file?

Required technical work

  1. Select exactly one of the upstream weight files rather than leaving the exporter to resolve a model alias.
  2. Download it at revision 4afec35ffe57a943d569ff7ee888061830164da8 and record its SHA-256 and byte size.
  3. Re-export DataComp CLIP from that local file under the common procedure.
  4. Replace the null source revision and source checksum in ModelAssets/Notices/CLIP-DataComp/PROVENANCE.json.

Sufficient resolution

The pack may pass this gate when both conditions hold:

  • the new conversion has a complete pinned-and-hashed provenance chain; and
  • LAION or another demonstrably authorized model owner confirms the licence scope in writing, or qualified counsel concludes in writing that the repository’s MIT designation and accompanying materials are sufficient for the intended distribution.

If the answer is negative or remains materially ambiguous, replace the model with a checkpoint having explicit weight-level redistribution terms or omit the DataComp pack.

DataComp CLIP - no answer

If LAION does not provide a substantive answer after a documented follow-up, silence neither grants additional permission nor withdraws the permission already stated in the published materials. A release under the displayed MIT licence would rely on the public licence evidence rather than individualized clearance from LAION. Record it as a maintainer risk-acceptance decision, not as an upstream-approved or upstream-cleared release.

The evidence supporting that decision is:

  • the exact pinned model repository identifies the model as License: mit and contains the weight files;
  • Hugging Face’s licence documentation describes model-card licence metadata as communicating the permissions attributed to repository content; and
  • the MIT licence permits use, modification, publication, redistribution, sublicensing, and sale when its copyright and permission notice accompanies copies or substantial portions.

The residual ambiguity is that the model repository has MIT metadata but no standalone LICENSE file identifying the trained weights and their copyright holder. The OpenCLIP MIT notice clearly covers the OpenCLIP software, but an unanswered inquiry leaves no individualized confirmation that it is also the intended notice for the trained weights and converted derivative. The model card’s “out of scope” deployment language appears as safety and intended-use guidance rather than licence text, but this interpretation has not been confirmed by LAION. Qualified Norwegian counsel remains the recommended way to resolve that ambiguity before a public or commercial release.

If the maintainer nevertheless decides to release without an answer, complete all of the following before publication:

  1. Send and preserve one documented follow-up to LAION. Record the original request, follow-up date, response deadline, and absence of a substantive answer in the private evidence register.
  2. Select open_clip_model.safetensors from revision 4afec35ffe57a943d569ff7ee888061830164da8 as the only conversion input. Its pinned Hugging Face metadata reports a byte size of 605189364 and SHA-256 92c26d60d3200ed5ed040dff31a8d19f8140648da8007216c25744c478deef27. Download the file independently and verify both values before conversion.
  3. Re-export the Apple Core AI model from that pinned local file. Do not merely add the upstream hash to the provenance record for the existing conversion; the released derivative must be cryptographically tied to the verified source file.
  4. Preserve dated copies of the pinned repository tree, model card, repository API metadata, MIT licence, OpenCLIP notice, conversion command, dependency versions, and all input and output checksums.
  5. Package the complete applicable MIT copyright and permission notice, OpenCLIP and tokenizer notices, model card, safety limitations, provenance, and conversion information with every redistributed model archive. A link alone is not a substitute for including the required notice.
  6. Keep DataComp CLIP identified as a separate third-party model asset. RawCull’s own MIT licence does not relicense the model, and RawCull must not claim ownership of or permission to redistribute the DataComp training dataset.
  7. Complete the common provenance procedure, PhotoAIKit validation, AAR inspection, archive hashing, manifest verification, and download tests. Change PROVENANCE.json and the production catalogue to ready only after those technical controls describe the new release candidate accurately.
  8. Add a signed and dated release decision stating the evidence relied upon, the unresolved licence ambiguity, whether RawCull is free or commercial, the intended distribution countries and channels, and the responsible maintainer’s acceptance of the residual risk.
  9. Keep the model pack independently removable from the manifest and release so distribution can be suspended promptly if a credible rights claim or contrary clarification is received.

Releasing after these steps may provide a defensible MIT-compliance position, but it does not eliminate the residual legal risk created by the absence of a weight-specific licence file or an authorized response. This procedure records the evidence and decision; it is not a legal opinion.

OpenAI CLIP clearance

Current position

The OpenAI CLIP source repository contains an MIT licence covering the software and associated documentation. The Hugging Face checkpoint currently lacks a clear weight-level licence designation. A community discussion contains only an assumption that the repository licence covers the weights; that assumption is not authoritative evidence.

The model card also characterizes deployed uses as out of scope. Clarify whether this is safety guidance or an enforceable distribution/use condition, and independently assess RawCull’s use, testing, limitations, and disclosures.

Official references:

Who to contact

  1. Contact OpenAI Support using the chat control at help.openai.com. Ask that the request be routed to the team responsible for CLIP/open-source model licensing. Retain the ticket or conversation identifier.
  2. Submit the same narrowly framed question through the feedback form linked by the official CLIP model card.
  3. Open a model-specific discussion on the Hugging Face Community page using its New discussion action. This requires a Hugging Face login. Use a discussion rather than a pull request so the licensing question remains attached to the exact model distribution.
  4. A response from an OpenAI employee or repository maintainer authorized to address the model’s licence is preferred. An unsupported answer from another community member is not sufficient.

GitHub issue creation for openai/CLIP is currently restricted, so it should not be the only planned contact route.

Recorded support outcome and Hugging Face escalation

OpenAI Support declined to confirm or provide an authoritative interpretation that the MIT licence in openai/CLIP applies to the pretrained weight files in openai/clip-vit-base-patch32. Support also declined to confirm that the licence permits RawCull’s intended commercial redistribution. The response said that the applicable terms must be determined from the licence and notices shipped with the exact code and weights being used.

This response does not clear the pack. Record the current conclusion as:

CLIP source code: MIT licensed.
openai/clip-vit-base-patch32 pretrained weights: no explicit weight-specific
licence identified in the downloaded distribution, and OpenAI Support did not
confirm that the source-repository MIT licence applies.
Commercial redistribution: unresolved and blocked pending sufficient evidence.

The OpenAI Report Content form is intended for reports of potentially illegal or policy-violating content. It is not evidence of permission and should not be treated as the route for prospective licensing clearance. Support’s recommended next step was to ask the maintainers on the distribution source. For Hugging Face, use the model-specific Community page linked above rather than the general Hugging Face forum.

On 2026-08-02, the RawCull maintainer opened Hugging Face discussion #72, “License applicable to pretrained CLIP ViT-B/32 weights,” asking the OpenAI maintainers to identify the licence covering the hosted weights and to confirm whether it permits commercial use and redistribution. The request also asks the maintainers to add the applicable licence identifier or licence file to the model repository. Opening the discussion records the escalation but does not clear the pack; retain the response and assess the responder’s authority and the scope of any answer before changing the release decision.

Suggested discussion title:

Licence applicable to pretrained CLIP ViT-B/32 weights

Suggested discussion body:

Could the OpenAI maintainers clarify the licence applicable specifically to
the pretrained weight files in openai/clip-vit-base-patch32?

In particular, does OpenAI intend the MIT License from the official
openai/CLIP repository to cover the original ViT-B/32 checkpoint and the
converted weight files hosted here, including commercial use, conversion to
another runtime representation, and redistribution subject to the MIT
conditions?

If so, could you add the applicable licence identifier and/or LICENSE file to
this model repository so downstream users can establish a reliable licensing
record?

A maintainer comment may clarify intent, but the strongest resolution is a licence file, model-card licence declaration, or written statement from the rights holder that expressly covers the exact weights and proposed use. Until then, do not approve commercial redistribution of this pack.

Questions to ask

Provide this exact identity:

  • repository: openai/clip-vit-base-patch32;
  • revision: 3d74acf9a28c67741b2f4f2ea7635f0aaf6f0268;
  • file: pytorch_model.bin;
  • SHA-256: a63082132ba4f97a80bea76823f544493bffa8082296d62d71581a4feff1576f.

Ask:

  1. What licence governs this exact trained weight file?
  2. Does the OpenAI CLIP MIT licence apply to it, or is there a separate licence or set of terms?
  3. May RawCull convert the weights into an Apple Core AI representation and publicly redistribute that converted derivative as an optional desktop-app download, including in a commercial application?
  4. Which copyright notice, attribution, model card, use limitation, or other terms must accompany the derivative?
  5. Is the model card’s statement that deployed uses are out of scope guidance, or does OpenAI intend it as a legal restriction on deployment or redistribution?

Required technical work

After licence clearance, re-export from the exact local source file and revision above. Record the conversion command and bind the new output to the source SHA-256. Update the provenance record and rebuild the AAR.

Sufficient resolution

The pack may pass this gate only with:

  • an authoritative written statement identifying terms that permit the exact intended conversion and redistribution; or
  • a written legal opinion accepting a clearly documented alternative basis.

A response that merely restates the code repository’s MIT licence without addressing the trained weights is not sufficient. If OpenAI does not answer or the answer does not permit the proposed distribution, omit OpenAI CLIP or use a replacement checkpoint with explicit weight-level terms.

Meta SAM 3 clearance

Current position

The SAM License dated November 19, 2025 defines the trained weights as SAM Materials and grants rights to use, reproduce, distribute, modify, and create derivatives. Distribution must remain under that agreement and a copy of the agreement must accompany the materials. RawCull now packages the complete agreement and requires acceptance of its verified text.

The separate unresolved question arises because Meta’s official checkpoint is gated. The model page requires a user to log in and share contact information, and Meta’s repository instructs users to request access and authenticate before downloading. Hugging Face documents that a gated model’s authors control access. The licence text appears permissive about redistribution, but a public GitHub download would let downstream users obtain the derivative without going through Meta’s upstream access flow.

Official references:

Who to contact

  1. Email Meta Open Source at opensource@meta.com. Meta publishes this contact address on its official Open Source terms page. Ask that the request be routed to the SAM 3 model/licensing owner.
  2. Open a narrowly scoped issue in facebookresearch/sam3 and/or a discussion on facebook/sam3. Link the public question from the private email so the project team can answer in its preferred channel.
  3. Contact Hugging Face only for clarification of how its gate operates. Hugging Face is the hosting platform; its support cannot substitute for permission or interpretation from Meta as the model owner.
  4. If Meta does not give a clear response, obtain a written opinion from qualified counsel or omit SAM 3 from public hosting.

Questions to ask Meta

Provide this exact identity and delivery proposal:

  • repository: facebook/sam3;
  • revision: 3c879f39826c281e95690f02c7821c4de09afae7;
  • file: model.safetensors;
  • SHA-256: 6d06f0a5f84e435071fe6603e61d0b4cc7b40e0d39d487cfd4d67d8cc11cc14a;
  • transformation: float16 Apple Core AI derivative for local inference;
  • delivery: optional RawCull Managed Background Asset from a public GitHub Release;
  • licence handling: complete SAM License packaged with the derivative and explicit in-app acceptance tied to the licence-text SHA-256.

Ask:

  1. Does section 1 of the SAM License permit this converted derivative to be distributed from a public, ungated GitHub Release?
  2. Must every downstream RawCull user independently request access through Meta’s Hugging Face gate before receiving the converted derivative?
  3. If downstream gating is required, what information and approval must RawCull collect, and may RawCull technically administer that gate?
  4. Is packaging the complete agreement and requiring verified in-app acceptance sufficient to distribute under the same agreement?
  5. Are there additional attribution, branding, reporting, geographic, trade control, or prohibited-use measures RawCull must implement?
  6. Does the answer apply to both free and commercial distribution of RawCull?

Possible outcomes

Meta confirms public ungated redistribution. Preserve the response, comply with every stated condition, re-export from the pinned source, update the provenance record, and have counsel review any material ambiguity before release.

Meta requires each recipient to pass its gate. Do not publish SAM 3 as a public GitHub Release asset. Managed Background Assets with an anonymous public origin will not reproduce individualized Hugging Face approval. Omit SAM 3 or design a separate authenticated acquisition flow and review it independently.

Meta declines, gives an unclear answer, or does not respond. Keep SAM 3 blocked. Obtain a legal opinion or omit it. Do not treat the licence’s general redistribution wording as resolving a separately imposed access condition without an accountable decision.

Required technical work after clearance

Re-export from the recorded revision and model.safetensors SHA-256, recording the complete conversion command and environment. Preserve the full SAM License with the pack, maintain explicit acceptance against its checksum, and rebuild the AAR. Re-check the official licence immediately before publication because the agreement states that Meta may modify it.

Contact-request template

Use a real name and reply-capable address. Do not send an archive, proprietary source, access token, or confidential material unless the recipient requests it through an appropriate channel.

Subject: Written clarification requested for redistribution of [MODEL] in RawCull

Hello,

I maintain RawCull, a macOS photo-culling application:
https://github.com/rsyncOSX/RawCull

I have not published or uploaded the model derivative described below. I am
seeking written clarification before doing so.

Upstream repository: [URL]
Immutable revision: [COMMIT]
Source weight file: [FILENAME]
Source SHA-256: [SHA256]

RawCull converts this file to a float16 Apple Core AI model for local inference.
The proposed delivery is an optional Managed Background Asset downloaded from a
public GitHub Release. The archive will include the applicable complete licence,
notices, attribution, provenance, and checksums. [Describe explicit acceptance,
if applicable.] RawCull is [free/paid; choose the accurate description].

Please confirm:

1. Which licence or terms govern this exact trained weight file?
2. May it be converted into this runtime representation?
3. May the converted derivative be publicly redistributed in this manner,
   including as part of a commercial application if applicable?
4. What notices, acceptance flow, attribution, gating, use restrictions, or
   other conditions must RawCull implement?

I would appreciate a response from the model owner or a person authorized to
clarify its licensing and distribution conditions. If another team handles
this request, please route it or identify the correct contact.

Thank you,
[NAME]
[CONTACT DETAILS]

For SAM 3, append the six SAM-specific questions above. For OpenAI CLIP, append the exact question about whether the MIT licence covers the trained weights and whether the model card’s deployment language is guidance or a restriction. For DataComp, ask whether the repository’s MIT designation covers the exact selected file.

When to involve a lawyer

Engage a Norwegian lawyer before publication if any of these apply:

  • the model owner does not answer;
  • the answer is informal, conditional, or ambiguous;
  • a licence permits redistribution but an upstream gate suggests a different access policy;
  • commercial use, downstream acceptance, trade controls, prohibited uses, or indemnification requires interpretation;
  • the responder’s authority to bind the rights holder is uncertain; or
  • RawCull will distribute in multiple jurisdictions.

Look for counsel with experience in copyright, software and open-source licensing, technology transactions, AI model weights, EEA law, and US/EU trade controls. The Norwegian Bar Association member search can verify professional membership. The Norwegian Industrial Property Office also publishes guidance on choosing an IP adviser.

Give counsel a private evidence bundle containing:

  • the exact model card and licence captured on the download date;
  • upstream repository, revision, source filename, SHA-256, and download method;
  • the conversion recipe and explanation of what the derivative contains;
  • complete notice catalogs;
  • every upstream response with message headers, dates, ticket IDs, and links;
  • RawCull’s intended countries, free or paid status, distribution channels, end-user flow, and licence-acceptance UI; and
  • the proposed GitHub release and Managed Background Assets architecture.

Ask for a written conclusion for each model covering conversion, redistribution, commercial use, notices, downstream terms, gating, and any required technical controls. Keep privileged advice private. Record only the resulting release decision and non-confidential obligations in the repository, unless counsel approves broader disclosure.

Evidence and decision record

Maintain a private register with at least these fields:

FieldRequired content
ModelExact upstream owner and repository
Source identityImmutable revision, filename, byte size, SHA-256
Licence identityName/version, official URL, captured file SHA-256, retrieval date
Contact recordOrganization, channel, date, ticket/issue ID, responder and stated authority
Permission scopeConversion, derivative redistribution, public access, commercial use, territories
ConditionsNotices, attribution, acceptance, gating, use restrictions, trade controls
Legal reviewCounsel, date, private matter/reference number, approved/blocked conclusion
ConversionScript/commit, command, dependencies, toolchain, timestamp, output hashes
PackExplicit selector manifest, AAR byte size and SHA-256, notice verification
DecisionReady, blocked, replaced, or omitted; responsible approver and date

Do not mark a contact item complete merely because a message was sent. Record the actual answer and whether it addresses the exact file and proposed delivery.

Final release checklist

A pack can change from blocked to ready only when every applicable item is complete:

  • Exact upstream owner, repository, immutable revision, and source file are recorded.
  • Source byte size and SHA-256 were computed before conversion.
  • The licence is captured from an official source and its checksum is recorded.
  • The licence demonstrably covers the trained weights and converted derivative.
  • Public and commercial redistribution is permitted for RawCull’s actual delivery model.
  • Any gated-access question is answered by the owner or resolved by qualified counsel.
  • All required notices, agreement copies, attribution, acceptance, and use controls are implemented.
  • The model was re-exported from the pinned local source under a recorded command and environment.
  • Converted output fingerprints and runtime hashes are recorded.
  • PhotoAIKit validation passes.
  • A new AAR was built with explicit selectors and inspected.
  • The AAR byte size and SHA-256 are recorded.
  • PROVENANCE.json, NOTICE.md, and the application catalogue agree.
  • A responsible human has signed and dated the release decision.

If even one required item remains open, keep that pack blocked or omit it.

Publication sequence after clearance

RawCull’s current policy is to wait until all three model decisions are resolved. A decision to omit or replace a model counts as resolution only when it is documented and the omitted asset is absent from the manifest.

After the decisions are complete:

  1. Re-export every approved model from its pinned and hashed source.
  2. Update the notice catalogs and change only genuinely approved catalogue descriptors to ready.
  3. Rebuild and inspect the AARs; record their new hashes and sizes.
  4. Generate and inspect the self-hosted download manifest with a non-beta or corrected ba-package toolchain.
  5. Create the dedicated RawCull-AI-Models release as a draft and upload only approved packs, their required remote asset names, the manifest, and public evidence.
  6. Verify every URL, redirect, byte size, and checksum while authenticated to the draft if necessary.
  7. Run download, acceptance, validation, removal, and licence-change tests against a non-production environment.
  8. Publish the release only after a final human review confirms that the manifest contains no blocked model and all obligations are satisfied.

Official-source summary

The following sources were checked on 2026-08-02:

Recheck all licence text, model-card metadata, gates, contacts, and official links immediately before release because they can change.

4 - Evaluating CLIP Models

Evaluating CLIP Models Against Source-Framework Cosine Values and RawCullFB

Evaluating CLIP Models Against Source-Framework Cosine Values and RawCullFB

This document is the complete repeatable procedure for validating CLIP model bundles used by RawCullFB. It covers both supported candidates:

  • OpenAI CLIP ViT-B/32 at 224 × 224.
  • OpenCLIP DataComp ViT-B/32-256 with datacomp_s34b_b86k weights.

SigLIP2 is outside the current evaluation scope.

The procedure has two independent validation layers:

  1. Numerical parity: compare embeddings produced by the Core AI bundle with reference embeddings produced by the original Hugging Face Transformers or OpenCLIP implementation.
  2. Product behavior: use RawCullFB to build a real catalog index, execute the same 77 semantic queries, inspect image-similarity neighborhoods, and measure labeled retrieval quality.

A model must pass both layers. Numerical parity proves that the conversion and integration reproduce the source model; it does not prove that the model retrieves useful photographs. Conversely, plausible search results do not prove that the converted model is correct.

1. What cosine parity means

For a reference embedding r and Core AI embedding c, CLIPBench calculates:

cosine(r, c) = dot(r, c) / (norm(r) × norm(c))

It also reports the largest element-wise difference:

max_absolute_error = max(abs(r[i] - c[i]))

Both source and exported models produce L2-normalized embeddings. Identical normalized vectors have cosine 1.0. Float16 conversion, platform image decoding, and framework implementation details can produce small differences.

Use these gates:

Test pathMinimum cosinePurpose
Text embeddings0.999Tokenizer, text encoder, output selection, and normalization
Canonical lossless image inputs0.999Resize, crop, normalization, image encoder, and output selection
End-to-end JPEG/PNG fixtures0.998Real platform decoding plus preprocessing and inference

The existing ten-image fixture set contains JPEG files and one PNG. Its release gate is therefore 0.998. A strict 0.999 run is useful diagnostically but can fail because Apple ImageIO and Pillow decode some JPEG pixels differently. Do not lower a threshold to hide a broad or unexplained mismatch.

Raw semantic-search scores are different from parity cosine values:

  • Parity cosine compares the same embedding from two implementations.
  • Semantic score compares a text embedding with an image embedding.
  • Image-similarity score compares two image embeddings.

Do not apply the parity threshold to semantic search or duplicate detection.

2. Current validated components

Record the live values again whenever this procedure is run. As of 2026-08-12, the relevant local state is:

ComponentCurrent identity/path
PhotoAIKit used by RawCullFBrevision 6e3216027b267c27ccaf99d334807b18ea1aaec9
OpenAI bundle/Users/thomas/ModelAssets/Release/Models/CLIP-OpenAI
OpenAI source revision3d74acf9a28c67741b2f4f2ea7635f0aaf6f0268
DataComp bundle/Users/thomas/ModelAssets/Release/Models/CLIP-DataComp
DataComp presetViT-B-32-256 / datacomp_s34b_b86k
CLIPBench/Users/thomas/GitHubCLIPtests/CLIPBench
RawCullFB/Users/thomas/GitHub/RawCull/RawCullFB
PhotoAIKit source tools/Users/thomas/GitHub/RawCull/PhotoAIKit
Fixture set/Users/thomas/Library/Mobile Documents/com~apple~CloudDocs/TestPhotos/CLIPParityFixtures
Semantic catalog/Users/thomas/Library/Mobile Documents/com~apple~CloudDocs/TestPhotos/testphotos
Canonical 77 queries/Users/thomas/GitHub/RawCull/RawCullFB/semantictest.txt

The DataComp metadata now explicitly identifies datacomp_s34b_b86k; older bundles containing only a generic open_clip_model.safetensors label must not be reused without proving their weight identity.

3. Requirements

  • Apple Silicon Mac.
  • macOS 27 or the version required by the current projects.
  • Xcode 27 and its command-line tools.
  • uv for the pinned Python environments declared in the PhotoAIKit scripts.
  • Access to the Hugging Face/OpenCLIP weights when regenerating references or bundles.
  • Enough disk space for Python model caches, Core AI bundles, Xcode DerivedData, and two catalog indexes.
  • The exact same image bytes for source-framework and Core AI parity.

The Python reference scripts declare pinned package versions in their inline uv metadata. Do not change those dependencies during a comparison.

4. Establish one evaluation workspace

Open a new zsh terminal and define the paths once:

RAW_CULL_ROOT=/Users/thomas/GitHub/RawCull/RawCull
RAW_CULL_FB_ROOT=/Users/thomas/GitHub/RawCull/RawCullFB
PHOTO_AI_KIT_ROOT=/Users/thomas/GitHub/RawCull/PhotoAIKit
CLIP_BENCH_ROOT=/Users/thomas/GitHubCLIPtests/CLIPBench

FIXTURE_ROOT='/Users/thomas/Library/Mobile Documents/com~apple~CloudDocs/TestPhotos/CLIPParityFixtures'
REFERENCE_ROOT="$FIXTURE_ROOT/reference"
CATALOG_ROOT='/Users/thomas/Library/Mobile Documents/com~apple~CloudDocs/TestPhotos/testphotos'
QUERY_FILE="$RAW_CULL_FB_ROOT/semantictest.txt"

OPENAI_BUNDLE=/Users/thomas/ModelAssets/Release/Models/CLIP-OpenAI
DATACOMP_BUNDLE=/Users/thomas/ModelAssets/Release/Models/CLIP-DataComp

EVALUATION_ROOT='/Users/thomas/Library/Mobile Documents/com~apple~CloudDocs/TestPhotos/CLIPModelEvaluations'
mkdir -p "$REFERENCE_ROOT" "$EVALUATION_ROOT"

Keep these variables in the same terminal session. If a path changes, update it here rather than editing individual commands later.

Create a run identifier so reports are not overwritten:

RUN_ID="$(date -u +%Y%m%dT%H%M%SZ)"
RUN_ROOT="$EVALUATION_ROOT/$RUN_ID"
mkdir -p "$RUN_ROOT"

5. Record the software and model identities

Record source revisions before building or generating references:

{
  date -u
  sw_vers
  xcodebuild -version
  xcrun swift --version
  uv --version
  git -C "$RAW_CULL_FB_ROOT" rev-parse HEAD
  git -C "$PHOTO_AI_KIT_ROOT" rev-parse HEAD
  git -C "$CLIP_BENCH_ROOT" rev-parse HEAD
} > "$RUN_ROOT/environment.txt"

Save the model metadata and hashes:

cp "$OPENAI_BUNDLE/metadata.json" "$RUN_ROOT/openai-metadata.json"
cp "$DATACOMP_BUNDLE/metadata.json" "$RUN_ROOT/datacomp-metadata.json"

shasum -a 256 \
  "$OPENAI_BUNDLE/metadata.json" \
  "$DATACOMP_BUNDLE/metadata.json" \
  > "$RUN_ROOT/model-metadata-sha256.txt"

Verify the important fields manually:

sed -n '1,130p' "$OPENAI_BUNDLE/metadata.json"
sed -n '1,130p' "$DATACOMP_BUNDLE/metadata.json"

Required OpenAI identity:

source_model: openai/clip-vit-base-patch32
source_revision: 3d74acf9a28c67741b2f4f2ea7635f0aaf6f0268
architecture: ViT-B-32
input: 1 × 3 × 224 × 224
resize/crop/interpolation: shortest-side / center / bicubic
embedding dimensions: 512
token context: 77
padding token: 49407

Required DataComp identity:

source_model: mlfoundations/open_clip
architecture: ViT-B-32-256
pretrained: datacomp_s34b_b86k
input: 1 × 3 × 256 × 256
resize/crop/interpolation: shortest-side / center / bicubic
embedding dimensions: 512
token context: 77
padding token: 0

Stop if a bundle’s checkpoint, input size, tokenizer, normalization, function names, or preprocessing metadata differs from the source reference that will be generated.

5.1 Optional: recreate the Core AI bundles

Skip this subsection when testing the existing release bundles. Use it when the converted assets themselves must be rebuilt. Export into an isolated staging directory; do not overwrite the release bundles before parity succeeds.

STAGING_MODEL_ROOT=/Users/thomas/ModelAssets/Staging/CLIP-Model-Evaluation
mkdir -p "$STAGING_MODEL_ROOT"

cd "$PHOTO_AI_KIT_ROOT"

uv run Tools/export_clip.py \
  --model openai \
  --dtype float16 \
  --output-dir "$STAGING_MODEL_ROOT" \
  --bundle-name CLIP-OpenAI

uv run Tools/export_clip.py \
  --model openclip-datacomp \
  --architecture ViT-B-32-256 \
  --pretrained datacomp_s34b_b86k \
  --dtype float16 \
  --output-dir "$STAGING_MODEL_ROOT" \
  --bundle-name CLIP-DataComp

The DataComp exporter verifies tokenizer IDs against OpenCLIP before completing. Inspect both staged metadata files and run the entire parity procedure against the staged paths. Promote a staged bundle only after it passes. If a staging bundle already exists, choose a new staging directory or intentionally use the exporter’s --overwrite option after verifying the exact target.

To evaluate the staged bundles in the remaining commands, replace the definitions from Section 4:

OPENAI_BUNDLE="$STAGING_MODEL_ROOT/CLIP-OpenAI"
DATACOMP_BUNDLE="$STAGING_MODEL_ROOT/CLIP-DataComp"

6. Verify PhotoAIKit and RawCullFB use the same implementation

RawCullFB currently pins PhotoAIKit remotely. Confirm the project and resolved lock file agree:

rg -n -C 5 'PhotoAIKit|photoaikit' \
  "$RAW_CULL_FB_ROOT/RawCullFB.xcodeproj/project.pbxproj" \
  "$RAW_CULL_FB_ROOT/RawCullFB.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved"

CLIPBench currently uses /Users/thomas/GitHubCLIPtests/PhotoAIKit through ../PhotoAIKit. Confirm that checkout is the same revision and contains the Pillow-compatible preprocessing fix:

git -C /Users/thomas/GitHubCLIPtests/PhotoAIKit rev-parse HEAD
rg -n 'pillowBicubicPreprocessingVersion' \
  /Users/thomas/GitHubCLIPtests/PhotoAIKit/Sources/CoreAICLIPBackend/CoreAICLIPProvider.swift

The revision must equal the PhotoAIKit revision resolved by RawCullFB. If it does not, update the dependency or checkout before proceeding. Otherwise CLIPBench and RawCullFB would test different implementations.

7. Verify the immutable fixture set

The fixture set must contain ten exact images plus manifest.json:

find "$FIXTURE_ROOT/images" -maxdepth 1 -type f -print | sort
sed -n '1,220p' "$FIXTURE_ROOT/manifest.json"

Expected images:

01-dog-outdoors.jpg
02-person-portrait.jpg
03-car-road.jpg
04-city-night.jpg
05-mountain-landscape.png
06-beetle-macro.jpg
07-kitchen-interior.jpg
08-motion-blur.jpg
09-sign-text.jpg
10-yellow-sunflower.jpg

Compute and save their hashes:

shasum -a 256 "$FIXTURE_ROOT"/images/0*.* \
  "$FIXTURE_ROOT"/images/10-yellow-sunflower.jpg \
  > "$RUN_ROOT/fixture-sha256.txt"

Compare every hash with manifest.json. Stop if a file is missing or changed. A regenerated reference from different image bytes is a different benchmark.

The ten prompts come from manifest.json and must remain in the same order as the images.

8. Test PhotoAIKit before model parity

Run the focused preprocessing/text tests and then the complete package suite:

cd "$PHOTO_AI_KIT_ROOT"
swift test --filter CLIPTextCapabilityTests
swift test

Required result: all tests pass. In particular, the suite must cover:

  • tokenizer construction and padding behavior;
  • center crop rather than stretch;
  • integer-floor crop origin;
  • Pillow-compatible bicubic pixels;
  • top-to-bottom orientation;
  • output shape and L2 normalization; and
  • preprocessing-version cache invalidation.

Do not start model parity if these tests fail.

9. Generate the OpenAI Hugging Face reference

Use the fixture-specific script because it writes:

  • normalized embeddings consumed by CLIPBench;
  • exact preprocessed pixel tensors;
  • token IDs and attention masks;
  • raw and normalized image/text embeddings;
  • the 10 × 10 image-to-text cosine matrix; and
  • reference rankings.

Generate all artifacts:

cd "$FIXTURE_ROOT"

uv run Scripts/generate_openai_clip_reference.py \
  --manifest "$FIXTURE_ROOT/manifest.json" \
  --output-directory "$REFERENCE_ROOT"

Expected outputs:

openai-clip-reference.json
openai-clip-reference-details.json
openai-clip-reference-tensors.npz

Verify that the Hugging Face revision recorded in the details file exactly equals the bundle’s source_revision:

rg -n 'model_id|model_revision|transformers_version|torch_version' \
  "$REFERENCE_ROOT/openai-clip-reference-details.json"

rg -n 'source_model|source_revision' "$OPENAI_BUNDLE/metadata.json"

If the revisions differ, the reference and Core AI bundle do not represent the same source model. Stop and regenerate one side from the intended pinned revision.

Save reference hashes:

shasum -a 256 \
  "$REFERENCE_ROOT/openai-clip-reference.json" \
  "$REFERENCE_ROOT/openai-clip-reference-details.json" \
  "$REFERENCE_ROOT/openai-clip-reference-tensors.npz" \
  > "$RUN_ROOT/openai-reference-sha256.txt"

10. Generate the DataComp OpenCLIP reference

DataComp weights may be hosted through model registries, but the authoritative runtime behavior for this model is the pinned OpenCLIP implementation—not the OpenAI Hugging Face Transformers class. Use the same architecture and pretrained tag used to export the Core AI bundle.

First record the OpenCLIP registry configuration:

cd "$PHOTO_AI_KIT_ROOT"

uv run --with open_clip_torch==3.2.0 python -c \
  'import json, open_clip; print(json.dumps(open_clip.get_pretrained_cfg("ViT-B-32-256", "datacomp_s34b_b86k"), indent=2, default=str))' \
  > "$RUN_ROOT/datacomp-openclip-registry.json"

Generate the reference directly under the fixture reference directory so all image paths remain valid relative paths:

uv run Tools/generate_clip_reference.py \
  --model openclip-datacomp \
  --architecture ViT-B-32-256 \
  --pretrained datacomp_s34b_b86k \
  --image "$FIXTURE_ROOT/images/01-dog-outdoors.jpg" \
  --image "$FIXTURE_ROOT/images/02-person-portrait.jpg" \
  --image "$FIXTURE_ROOT/images/03-car-road.jpg" \
  --image "$FIXTURE_ROOT/images/04-city-night.jpg" \
  --image "$FIXTURE_ROOT/images/05-mountain-landscape.png" \
  --image "$FIXTURE_ROOT/images/06-beetle-macro.jpg" \
  --image "$FIXTURE_ROOT/images/07-kitchen-interior.jpg" \
  --image "$FIXTURE_ROOT/images/08-motion-blur.jpg" \
  --image "$FIXTURE_ROOT/images/09-sign-text.jpg" \
  --image "$FIXTURE_ROOT/images/10-yellow-sunflower.jpg" \
  --text "a woman with a dog outdoors" \
  --text "a portrait of a woman wearing sunglasses" \
  --text "an old car driving on a country road" \
  --text "an illuminated city building at night" \
  --text "mountains surrounding a large lake" \
  --text "a macro photograph of an insect" \
  --text "the interior of a kitchen" \
  --text "a colorful amusement ride with motion blur" \
  --text "a green road sign with white text" \
  --text "a yellow sunflower" \
  --output "$REFERENCE_ROOT/datacomp-clip-reference.json"

Verify identity, counts, and relative paths:

sed -n '1,25p' "$REFERENCE_ROOT/datacomp-clip-reference.json"
rg -c '"path"' "$REFERENCE_ROOT/datacomp-clip-reference.json"
rg -c '"text"' "$REFERENCE_ROOT/datacomp-clip-reference.json"

Required source block:

model: openclip-datacomp
architecture: ViT-B-32-256
pretrained: datacomp_s34b_b86k

Required counts: 10 images and 10 texts.

Each image path must resolve from REFERENCE_ROOT. Test all paths before parity:

cd "$REFERENCE_ROOT"
test -f ../images/01-dog-outdoors.jpg
test -f ../images/10-yellow-sunflower.jpg

Do not reuse a DataComp reference containing paths to a deleted Downloads/CLIPParityFixtures directory or the obsolete missing _DSC7115.ARW.png fixture.

Save its hash:

shasum -a 256 "$REFERENCE_ROOT/datacomp-clip-reference.json" \
  > "$RUN_ROOT/datacomp-reference-sha256.txt"

11. Build CLIPBench from the intended PhotoAIKit revision

Verify its dependency first:

cd "$CLIP_BENCH_ROOT"
sed -n '1,45p' Package.swift
git -C ../PhotoAIKit rev-parse HEAD

Then test and build:

swift test
swift build -c release --product clipbench

Use the freshly built executable:

/Users/thomas/GitHubCLIPtests/CLIPBench/.build/release/clipbench

Inspect both bundles through the same runtime that will perform parity:

.build/release/clipbench inspect-model --model "$OPENAI_BUNDLE" \
  | tee "$RUN_ROOT/openai-model-inspection.txt"

.build/release/clipbench inspect-model --model "$DATACOMP_BUNDLE" \
  | tee "$RUN_ROOT/datacomp-model-inspection.txt"

Confirm that the printed source, architecture, pretrained tag, input geometry, tokenizer version, embedding dimensions, and model fingerprint match the recorded metadata.

12. Run OpenAI Core AI parity

Run the release gate and preserve its output:

cd "$CLIP_BENCH_ROOT"
set -o pipefail

.build/release/clipbench parity \
  --reference "$REFERENCE_ROOT/openai-clip-reference.json" \
  --model "$OPENAI_BUNDLE" \
  --minimum-cosine 0.998 \
  | tee "$RUN_ROOT/openai-parity.txt"

Expected characteristics from the corrected implementation:

  • all text rows approximately 1.0000;
  • nine image rows at or above approximately 0.999;
  • minimum image cosine approximately 0.9988 for the existing JPEG-heavy fixture set; and
  • command exits successfully at the 0.998 threshold.

Also run the strict diagnostic threshold:

.build/release/clipbench parity \
  --reference "$REFERENCE_ROOT/openai-clip-reference.json" \
  --model "$OPENAI_BUNDLE" \
  --minimum-cosine 0.999 \
  > "$RUN_ROOT/openai-parity-strict.txt" 2>&1

This strict end-to-end command may return nonzero because of the known JPEG decoder variance. Preserve the output; do not treat that specific documented result as a model failure if the 0.998 release gate passes and lossless tests remain green.

13. Run DataComp Core AI parity

Use its independently generated OpenCLIP reference:

cd "$CLIP_BENCH_ROOT"
set -o pipefail

.build/release/clipbench parity \
  --reference "$REFERENCE_ROOT/datacomp-clip-reference.json" \
  --model "$DATACOMP_BUNDLE" \
  --minimum-cosine 0.998 \
  | tee "$RUN_ROOT/datacomp-parity.txt"

Required result:

  • command exits successfully;
  • every text row is at least 0.999;
  • end-to-end minimum across all rows is at least 0.998; and
  • no individual maximum-absolute-error value is unexpectedly large relative to the other fixtures.

Run the strict diagnostic threshold as well:

.build/release/clipbench parity \
  --reference "$REFERENCE_ROOT/datacomp-clip-reference.json" \
  --model "$DATACOMP_BUNDLE" \
  --minimum-cosine 0.999 \
  > "$RUN_ROOT/datacomp-parity-strict.txt" 2>&1

Do not assume DataComp inherits OpenAI’s numerical result merely because both use the same PhotoAIKit preprocessing code. It has different weights, input resolution, padding behavior, and exported functions.

14. Diagnose parity failures

Use the failure pattern to select the first investigation:

PatternLikely first checks
Text and images failWrong checkpoint, stale CLIPBench build, wrong model asset, or wrong output tensor
Text fails; images passToken IDs, padding token, EOT handling, attention mask, text output, or normalization
Text passes; all images failResize dimension, crop origin, interpolation, color order, mean/std, orientation, image output, or normalization
Only JPEG images are slightly below 0.999Apple ImageIO versus Pillow decoding
One image fails badlyMissing/corrupt fixture, EXIF orientation, unsupported decoding, or stale relative path
Correct cosine but changed top-k on a fixture matrixNear-tie or ranking instability; inspect exact cosine matrix

For OpenAI, inspect openai-clip-reference-tensors.npz and the details JSON to isolate:

  1. preprocessed pixels;
  2. token IDs and masks;
  3. raw model outputs;
  4. normalized outputs; and
  5. the cosine/ranking matrix.

For DataComp, extend the reference generator to emit equivalent intermediate tensors if a parity failure cannot be localized from embeddings alone.

Parity failure blocks RawCullFB quality evaluation. Fix the integration before judging semantic retrieval.

15. Build and test RawCullFB

Confirm the project has no unexpected changes and still resolves the intended PhotoAIKit revision:

cd "$RAW_CULL_FB_ROOT"
git status --short
rg -n -C 5 'PhotoAIKit|photoaikit' \
  RawCullFB.xcodeproj/project.pbxproj \
  RawCullFB.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

Run its tests:

xcodebuild \
  -project RawCullFB.xcodeproj \
  -scheme RawCullFB \
  -destination 'platform=macOS,arch=arm64' \
  test

Build an unsigned Release product for local validation:

xcodebuild \
  -project RawCullFB.xcodeproj \
  -scheme RawCullFB \
  -configuration Release \
  -destination 'platform=macOS,arch=arm64' \
  -derivedDataPath /tmp/RawCullFB-CLIP-Evaluation \
  CODE_SIGNING_ALLOWED=NO \
  build

Expected product:

/tmp/RawCullFB-CLIP-Evaluation/Build/Products/Release/RawCullFB.app

CODE_SIGNING_ALLOWED=NO validates compilation only. Use the project’s normal signing, archive, notarization, and DMG workflow for distribution.

16. Freeze the semantic test catalog and queries

Both models must use:

  • the exact same catalog root;
  • the exact same image bytes;
  • the exact same query file;
  • the same result limit;
  • a complete model-specific index; and
  • the same RawCullFB/PhotoAIKit revisions.

Copy the canonical query file into the catalog root under the required name:

cp "$QUERY_FILE" "$CATALOG_ROOT/semantictest.txt"
wc -l "$CATALOG_ROOT/semantictest.txt"
cmp "$QUERY_FILE" "$CATALOG_ROOT/semantictest.txt"

Expected query count: 77 non-comment queries.

Record catalog files without including RawCullFB’s hidden index or generated reports:

find "$CATALOG_ROOT" -type f \
  ! -path '*/.clipbench/*' \
  ! -name '*-semantic-test-results.txt' \
  ! -name 'semantictest.txt' \
  -print | sort > "$RUN_ROOT/catalog-files.txt"

wc -l "$RUN_ROOT/catalog-files.txt"
shasum -a 256 "$CATALOG_ROOT/semantictest.txt" \
  > "$RUN_ROOT/semantic-query-sha256.txt"

For strict reproducibility, compute content hashes for the complete catalog and save them with the run. This can be slow for a large photo collection.

17. RawCullFB test for OpenAI CLIP

Launch the validated RawCullFB build, then:

  1. Open RawCullFB > Settings > CLIP.
  2. Choose /Users/thomas/ModelAssets/Release/Models/CLIP-OpenAI.
  3. Wait until verification reports a valid model and note its fingerprint.
  4. Select CATALOG_ROOT as the photo-folder root.
  5. Choose Index Selected Folder or Update Index.
  6. Wait until indexing completes with no unexplained failures.
  7. Confirm search is enabled and the index belongs to the selected model.
  8. Set Maximum results to 50.
  9. Choose Run Model Test in the main toolbar.
  10. Wait for all 77 queries and the image-similarity evaluation to finish.

RawCullFB writes:

<model-name>-semantic-test-results.txt

in the selected catalog root. Immediately preserve it under the run directory:

find "$CATALOG_ROOT" -maxdepth 1 -type f \
  -name '*semantic-test-results.txt' -print

cp "$CATALOG_ROOT/ViT-B-32-semantic-test-results.txt" \
  "$RUN_ROOT/openai-rawcullfb-results.txt"

If the generated filename differs, use the filename RawCullFB reports rather than assuming the example name.

18. RawCullFB test for DataComp CLIP

Repeat the workflow without changing catalog or query settings:

  1. Open RawCullFB > Settings > CLIP.
  2. Choose /Users/thomas/ModelAssets/Release/Models/CLIP-DataComp.
  3. Wait for valid model verification and record its distinct fingerprint.
  4. Keep the same CATALOG_ROOT selected.
  5. Choose Index Selected Folder or Update Index.
  6. Confirm RawCullFB creates/uses a DataComp-specific .clipbench/clip-<model-hash>.clipindex.
  7. Wait for the complete DataComp index; never evaluate a partial or stale index.
  8. Keep the result limit at 50.
  9. Choose Run Model Test.
  10. Wait for all semantic queries and image-similarity anchors to complete.

Preserve the report:

cp "$CATALOG_ROOT/datacomp_s34b_b86k-semantic-test-results.txt" \
  "$RUN_ROOT/datacomp-rawcullfb-results.txt"

Again, use the actual filename if RawCullFB emits a different model label.

OpenAI and DataComp indexes must remain separate. A model fingerprint, preprocessing version, or dimension mismatch must invalidate reuse. If switching models does not require its own index, stop and inspect index identity handling.

19. Validate RawCullFB result-file integrity

Inspect both headers:

for REPORT in \
  "$RUN_ROOT/openai-rawcullfb-results.txt" \
  "$RUN_ROOT/datacomp-rawcullfb-results.txt"
do
  echo "--- $REPORT"
  sed -n '1,18p' "$REPORT"
done

Require:

  • correct model name;
  • correct and distinct model fingerprint;
  • identical catalog path;
  • status equal to completed;
  • completed_queries equal to 77;
  • total_queries equal to 77;
  • identical result limit, normally 50; and
  • similarity_status equal to completed.

Count query and anchor sections:

for REPORT in \
  "$RUN_ROOT/openai-rawcullfb-results.txt" \
  "$RUN_ROOT/datacomp-rawcullfb-results.txt"
do
  printf '%s\tqueries=' "$REPORT"
  rg -c '^QUERY\t' "$REPORT"
  printf '%s\tanchors=' "$REPORT"
  rg -c '^ANCHOR\t' "$REPORT"
done

The established catalog contains 453 similarity anchors. If the catalog changes, record and explain the new anchor count; do not compare it silently with an older run.

20. Semantic sanity checks before labeling

Calculate or inspect at least:

  • distinct top-1 images across 77 queries;
  • most frequent top-1 image and its frequency;
  • unique images appearing across all top-50 results;
  • mean top-1 minus top-2 score margin;
  • mean shared top-10 results across all query pairs;
  • mean shared top-10 results within the five paraphrase groups;
  • first-query time; and
  • warmed mean, median, and P95 query time.

The five paraphrase groups are queries 63–77:

  1. dog on a beach;
  2. city street at night;
  3. sharp portrait;
  4. blurry photograph; and
  5. beautiful landscape.

Expected healthy behavior:

  • unrelated prompts do not all return the same small pool;
  • no single universal image wins a large fraction of unrelated queries;
  • paraphrase overlap is materially higher than unrelated-query overlap;
  • object prompts return visibly plausible objects in top positions; and
  • query latency stabilizes after model warm-up.

The corrected OpenAI integration previously produced 53 distinct top-1 images, a maximum hub frequency of 4/77, and substantially higher paraphrase than general overlap. Treat a return to the old collapsed pattern as an integration or index problem.

These are sanity checks, not accuracy metrics.

21. Create one pooled relevance-label set

For every query, pool the first five results from both models:

OpenAI top five union DataComp top five

Deduplicate identical query/image pairs and, if practical, hide the originating model during review. Create a CSV:

query_id,query,file,path,openai_rank,datacomp_rank,relevance,notes

Use the same graded scale for both models:

RelevanceDefinition
2Clearly and directly satisfies the query
1Partially relevant, ambiguous, or missing an important attribute/relation
0Irrelevant

Inspect the actual image. Do not label from filenames, cosine scores, or which model retrieved it.

Calculate for each model:

  • Precision@1;
  • Precision@5;
  • mean reciprocal rank (MRR);
  • nDCG@5 using relevance 0/1/2; and
  • metrics by query category.

Categories should include:

  • objects and scenes;
  • colors and attributes;
  • actions and relations;
  • photographic quality/style;
  • composition;
  • count, spatial relation, and negation; and
  • paraphrases.

With only 77 queries, report raw counts as well as averages. Do not select a model from a tiny aggregate difference without reviewing category failures and uncertainty.

22. Evaluate image similarity separately

RawCullFB’s anchor neighborhoods are useful for finding obvious failures but are not a calibrated duplicate benchmark. Build a labeled pair set containing:

  • exact duplicates;
  • resized/re-encoded duplicates;
  • color and exposure edits;
  • crops;
  • adjacent burst frames;
  • same subject in a different photograph;
  • semantically related but visually different hard negatives; and
  • unrelated negatives.

For each model, calculate:

  • ROC-AUC;
  • precision-recall AUC;
  • false-positive rate at the chosen recall;
  • false-negative rate at the chosen threshold; and
  • a confusion matrix for the proposed action.

Choose separate thresholds for OpenAI and DataComp. Their cosine distributions are not interchangeable. A threshold suitable for suggestions is not automatically safe for grouping, deletion, or another destructive action.

23. Compare operational behavior

Use the same Mac, OS, app revision, catalog, and measurement method for both models. Record:

  • model bundle size;
  • cold model-load/first-query latency;
  • warmed P50/P95/P99 search latency;
  • image-indexing throughput;
  • peak memory during indexing and searching;
  • complete index size; and
  • energy impact if available.

Run at least 30 unmeasured warm-up queries before measuring steady-state latency. DataComp uses 256 × 256 inputs versus OpenAI’s 224 × 224 and may have a different indexing cost even though both produce 512-dimensional embeddings.

24. Decision table

Complete this table and save it with the run:

Gate or metricOpenAI ViT-B/32DataComp ViT-B/32-256Decision
Source identity verified
Minimum text parity
Minimum end-to-end image parity
Precision@1
Precision@5
MRR
nDCG@5
Weakest query category
Largest semantic hub
Paraphrase top-10 overlap
Similarity PR-AUC
Similarity FPR at selected recall
Cold first-query latency
Warm P95 latency
Indexing throughput
Peak memory
Bundle size
Index size

Selection rules:

  • Reject any model whose source identity or numerical parity is unresolved.
  • Reject any model showing semantic collapse or unacceptable labeled quality.
  • Never select a model because it produces numerically higher raw similarity scores.
  • Prefer DataComp only if it delivers a material labeled-quality improvement at acceptable operational cost.
  • If quality is effectively tied, OpenAI is the simpler established control.
  • It is acceptable to ship OpenAI and keep DataComp experimental.

25. Archive the complete evidence package

At minimum, RUN_ROOT should contain:

environment.txt
fixture-sha256.txt
semantic-query-sha256.txt
catalog-files.txt
openai-metadata.json
datacomp-metadata.json
model-metadata-sha256.txt
openai-reference-sha256.txt
datacomp-reference-sha256.txt
datacomp-openclip-registry.json
openai-model-inspection.txt
datacomp-model-inspection.txt
openai-parity.txt
openai-parity-strict.txt
datacomp-parity.txt
datacomp-parity-strict.txt
openai-rawcullfb-results.txt
datacomp-rawcullfb-results.txt
relevance-labels.csv
semantic-metrics.json
similarity-pairs.csv
similarity-metrics.json
decision.md

Also preserve the exact source reference JSON files or their immutable hashes. The reference JSON stores paths relative to its own directory; moving only the JSON without its fixture images can break future parity runs.

26. Final release checklist

Shared implementation

  • PhotoAIKit focused and full tests pass.
  • CLIPBench and RawCullFB use the same PhotoAIKit revision.
  • RawCullFB tests and Release build pass.
  • Model metadata and asset fingerprints are archived.

OpenAI

  • Hugging Face revision matches bundle source_revision.
  • Ten-image/ten-text reference regenerated from immutable fixtures.
  • Text parity is at least 0.999.
  • End-to-end fixture parity is at least 0.998.
  • RawCullFB completes 77 queries and all anchors using a fresh model-specific index.

DataComp

  • Architecture and datacomp_s34b_b86k identity are recorded.
  • OpenCLIP registry configuration is archived.
  • DataComp reference is regenerated from the same preset and fixtures.
  • All reference image paths resolve.
  • Text parity is at least 0.999.
  • End-to-end fixture parity is at least 0.998.
  • RawCullFB completes 77 queries and all anchors using a fresh model-specific index.

Product quality

  • Pooled top-five results are labeled consistently.
  • Precision@1, Precision@5, MRR, and nDCG@5 are reported.
  • Category-level failures are reviewed.
  • No universal semantic hub exists.
  • Paraphrases are more consistent than unrelated prompts.
  • Similarity positive and hard-negative pairs are labeled.
  • Model-specific similarity thresholds meet the approved false-positive ceiling.
  • Latency, throughput, memory, bundle size, and index size are acceptable.
  • The final OpenAI/DataComp decision is documented.

27. Short execution order

When repeating the evaluation later:

  1. Define paths and create RUN_ROOT.
  2. Record software revisions and model metadata.
  3. Verify immutable fixture hashes.
  4. Test PhotoAIKit.
  5. Generate the pinned OpenAI Hugging Face reference.
  6. Generate the exact DataComp OpenCLIP reference.
  7. Build CLIPBench from the matching PhotoAIKit revision.
  8. Run and archive both parity tests.
  9. Test and compile RawCullFB.
  10. Freeze the catalog and 77-query file.
  11. Build a clean OpenAI index and run the RawCullFB model test.
  12. Build a clean DataComp index and run the same test.
  13. Validate report integrity and collapse diagnostics.
  14. Label the pooled top-five results.
  15. Calculate semantic and image-similarity metrics.
  16. Compare operational costs.
  17. Complete the decision table and release checklist.

The result is defensible only when the archived evidence links the exact source checkpoint, converted asset, preprocessing implementation, fixture bytes, catalog, query file, RawCullFB build, and human relevance labels.

5 - New RawCull AI models

Release runbook for publishing DataComp CLIP, OpenAI CLIP, and Meta SAM 3.

Publishing new RawCull AI models

This runbook publishes exactly three optional models:

ModelAsset-pack IDInstalled destination
DataComp CLIPno.blogspot.RawCull.models.clip-datacompModels/CLIP-DataComp
OpenAI CLIPno.blogspot.RawCull.models.clip-openaiModels/CLIP-OpenAI
Meta SAM 3no.blogspot.RawCull.models.sam3Models/SAM3

SigLIP2 and EfficientSAM are deliberately excluded. Do not add them to the release staging tree, packaging manifests, download manifest, production catalogue, settings UI, notices, or tests.

The examples use the v2 release in RawCull-AI-Models. Tag names and GitHub release URLs are case-sensitive. If another tag is chosen, replace v2 everywhere and keep both RawCull manifest URLs identical.

The current v2 manifest publishes DataComp CLIP and OpenAI CLIP only. Meta SAM 3 remains blocked and must not be uploaded or added to the deployable manifest until its redistribution review is complete.

1. Build the three canonical bundles

Follow AI Model Download Service for the pinned source checks, exact PhotoAIKit commands, runtime validation, and packaging layout. The canonical release outputs are:

CLIP-DataComp/
├── metadata.json
├── tokenizer/
└── ViT-B-32-256-datacomp_s34b_b86k_float16_static.aimodel/

CLIP-OpenAI/
├── metadata.json
├── tokenizer/
└── clip-vit-base-patch32_float16_static.aimodel/

SAM3/
├── metadata.json
├── tokenizer/
└── sam3_float16.aimodel/

Use PhotoAIKit revision 6e3216027b267c27ccaf99d334807b18ea1aaec9, or document and review the exact later revision actually used. Release CLIP bundles must use Float16, static batch dimensions, metadata version 0.4, separate image_encoder and text_encoder functions, and corrected bicubic preprocessing. The SAM 3 bundle currently uses metadata version 0.2.

DataComp must be exported with:

uv run --script Tools/export_clip.py \
  --model openclip-datacomp \
  --architecture ViT-B-32-256 \
  --pretrained datacomp_s34b_b86k \
  --output-dir /path/to/export \
  --bundle-name CLIP-DataComp \
  --dtype float16

Do not use --pretrained open_clip_model.safetensors. The historical bundle with open_clip_model.safetensors in its runtime name produced collapsed semantic rankings. The corrected tested bundle uses the registered datacomp_s34b_b86k identity and runtime filename.

Keep every *_source.aimodel as private conversion evidence. Never select it in a packaging manifest or place it in a downloadable pack.

2. Validate model behavior before packaging

For each bundle:

  1. Confirm metadata.json selects the optimized runtime in assets.main.
  2. Recompute the runtime directory fingerprint with PhotoAIKit/Tools/model_fingerprint.py and compare it with asset_fingerprints.main.
  3. Run swift test for the exact PhotoAIKit revision used to convert it.
  4. Install the complete candidate in a clean RawCull or RawCullFB model path.
  5. Rebuild all model-specific indexes after every model or fingerprint change.

For both CLIP models, run PyTorch/Core AI parity and then the same RawCullFB semantictest.txt. The report must include semantic-search rankings and image-to-image similarity comparisons. Review rankings, score spread, repeated results, failures, and elapsed time. Do not compare models against a reused index.

For SAM 3, test text prompts, mask dimensions and placement, licence acceptance, relaunch, and removal.

3. Clear release blockers

Do not mark a model .ready because it works locally.

DataComp CLIP

Confirm that the new archive uses the pinned DataComp checkpoint and corrected runtime filename. Replace the existing archive checksum and byte count whenever the bundle or packaging changes. Update its notice and provenance records; do not reuse values from the old custom-named runtime.

OpenAI CLIP

Before enabling the OpenAI pack:

  1. bind the pinned checkpoint revision and source SHA-256 to the conversion evidence;
  2. confirm that the applicable terms cover the exact trained weights and their redistribution, not only source code and tokenizer files;
  3. record the PhotoAIKit revision, command, tokenizer checksum, runtime fingerprint, archive checksum, and byte count in the external release catalogue;
  4. include the complete required notices in Notices/CLIP-OpenAI.

Leave the descriptor blocked and omit its pack from a public manifest while weight-level redistribution clearance is unresolved.

Meta SAM 3

Before enabling the SAM 3 pack:

  1. preserve the accepted gated-source revision and source SHA-256 as private evidence;
  2. confirm that an ungated GitHub release of the converted derivative complies with the SAM License and Meta’s gated checkpoint access conditions;
  3. record the PhotoAIKit revision, command, tokenizer checksum, runtime fingerprint, archive checksum, and byte count in the external release catalogue;
  4. include the complete SAM License and notices in Notices/SAM3;
  5. keep explicit in-app licence acceptance enabled.

Technical provenance does not resolve the redistribution decision. Omit SAM 3 from the public manifest until that review is complete.

4. Prepare release staging

The staging tree must contain only the three approved runtime bundles and their notice catalogues:

ModelAssets/Release/
├── Models/
│   ├── CLIP-DataComp/
│   ├── CLIP-OpenAI/
│   └── SAM3/
├── Notices/
│   ├── CLIP-DataComp/
│   ├── CLIP-OpenAI/
│   └── SAM3/
├── Packaging/
│   ├── clip-datacomp.json
│   ├── clip-openai.json
│   └── sam3.json
└── Output/

Each packaging manifest selects only:

  • the bundle’s metadata.json;
  • its tokenizer directory;
  • its optimized runtime .aimodel directory;
  • its matching notice directory.

The DataComp selector must be:

Models/CLIP-DataComp/ViT-B-32-256-datacomp_s34b_b86k_float16_static.aimodel

If any checked-in packaging manifest still selects ViT-B-32-256-open_clip_model.safetensors_float16_static.aimodel, it is stale and must be corrected before packaging.

5. Generate and record the asset-pack archives

Check the selected Xcode tool before every release:

xcrun --find ba-package
xcrun ba-package --version
xcrun ba-package help package

Run one package operation per ready model from the staging root. For the current v2 republish, package the two CLIP models only:

cd /Users/thomas/ModelAssets/Release

xcrun ba-package package Packaging/clip-datacomp.json \
  --output-path Output/clip-datacomp.aar
xcrun ba-package package Packaging/clip-openai.json \
  --output-path Output/clip-openai.aar

shasum -a 256 Output/*.aar
stat -f '%N %z bytes' Output/*.aar

Run the SAM 3 operation only after its redistribution review is complete and its status changes from blocked to ready:

xcrun ba-package package Packaging/sam3.json \
  --output-path Output/sam3.aar

The .aar suffix here means an Apple Archive produced for Managed Background Assets; it is not an Android library.

Do not copy old archive checksums into the application catalogue. A changed model, metadata file, tokenizer, notice, selector, or packaging tool can change the archive. Record the newly generated SHA-256 and exact byte count for every pack. Keep these archive-level values in the generated download manifest, RawCull’s production download catalogue, release documentation, and GitHub release metadata. Do not embed an archive checksum or size in a provenance file inside that same archive; doing so creates a self-referential checksum.

6. Generate the download manifest

The catalogue used in this chapter is the release staging directory:

/Users/thomas/ModelAssets/Release

Run every ba-package command in chapter 6 from that directory. Relative paths such as Output/clip-datacomp.aar and Packaging/clip-datacomp.json are resolved from the current directory; running the commands from /Users/thomas/ModelAssets or a RawCull source checkout selects the wrong paths.

Create a self-hosted manifest from the exact approved AAR files in /Users/thomas/ModelAssets/Release/Output. The generated manifest must contain only packs that have passed both technical and legal release gates. It may contain one, two, or all three packs while reviews are in progress; RawCull must expose only the same ready set.

Verify every entry before publishing:

  • exact asset-pack ID;
  • monotonically increasing asset-pack version;
  • exact download byte count;
  • tag-pinned HTTPS archive URL;
  • correct destination selected by RawCull’s catalogue.

Do not use GitHub’s /releases/latest/download/ redirect. It excludes prereleases and makes the resolved version less explicit. Use URLs under:

https://github.com/rsyncOSX/RawCull-AI-Models/releases/download/v2/

6.1. How to create the manifest

Create the manifest with the same Xcode installation used to package the AAR files. First verify the working directory and inputs. These checks must list the release packaging catalogues and the AAR files generated in chapter 5:

cd /Users/thomas/ModelAssets/Release
pwd
ls -l Packaging/*.json Output/*.aar

Pass only approved archives. ba-package matches version numbers to archive paths by position: the first value after --asset-pack-versions applies to the first AAR path, the second value to the second AAR path, and so on. Supply exactly one version for each AAR. Version 2 is used below for the new v2 release; replace it with the next monotonically increasing integer for any pack that has already used that version.

The base URL is the tag-pinned release directory, including its trailing slash:

DOWNLOAD_BASE_URL="https://github.com/rsyncOSX/RawCull-AI-Models/releases/download/v2/"

xcrun ba-package download-manifest create \
  Output/clip-datacomp.aar \
  --asset-pack-versions 2 \
  --macos \
  --download-base-url "$DOWNLOAD_BASE_URL" \
  --output-path Output/manifest.json

Generate Output/manifest.json with one of the commands below. Do not edit the generated JSON to add a blocked model. If the ready set changes, regenerate the manifest from exactly that set of AAR files.

After generation, validate the JSON, print the included IDs, and inspect every generated pack entry before publishing:

jq empty Output/manifest.json
jq -r '.assetPacks[].id' Output/manifest.json
jq -r '.assetPacks[] | [.id, (.version | tostring), (.downloadSize | tostring), .url] | @tsv' \
  Output/manifest.json

The generated key is id, not assetPackID; assetPackID is used only in the packaging catalogue. Verify the version, archive URL, and download size in each entry. The Background Assets tool constructs the URL by appending the pack ID to DOWNLOAD_BASE_URL. Consequently, the GitHub release asset must be named exactly like the final URL component, for example no.blogspot.RawCull.models.clip-datacomp, with no .aar suffix. Chapter 7 prepares those upload names. Do not hand-edit the generated JSON or URL.

6.2. Manifest for both CLIP models

Use this manifest after DataComp CLIP and OpenAI CLIP have both passed their technical and legal gates, while SAM 3 remains blocked:

xcrun ba-package download-manifest create \
  Output/clip-datacomp.aar \
  Output/clip-openai.aar \
  --asset-pack-versions 2 2 \
  --macos \
  --download-base-url "$DOWNLOAD_BASE_URL" \
  --output-path Output/manifest.json

The result must contain exactly these asset-pack IDs:

no.blogspot.RawCull.models.clip-datacomp
no.blogspot.RawCull.models.clip-openai

6.3. Manifest for all three models

Use this manifest only after both CLIP models and SAM 3 have passed all release gates:

xcrun ba-package download-manifest create \
  Output/clip-datacomp.aar \
  Output/clip-openai.aar \
  Output/sam3.aar \
  --asset-pack-versions 2 2 2 \
  --macos \
  --download-base-url "$DOWNLOAD_BASE_URL" \
  --output-path Output/manifest.json

The result must contain exactly these asset-pack IDs:

no.blogspot.RawCull.models.clip-datacomp
no.blogspot.RawCull.models.clip-openai
no.blogspot.RawCull.models.sam3

7. Publish the GitHub release

Chapter 7 uses two different locations:

  • local files come from /Users/thomas/ModelAssets/Release/Output;
  • the release itself is in the GitHub repository rsyncOSX/RawCull-AI-Models.

gh release view does not read a local model or packaging catalogue. Because the command supplies --repo rsyncOSX/RawCull-AI-Models, it can be run from any directory. Change to the release staging directory anyway so the later upload paths resolve correctly:

cd /Users/thomas/ModelAssets/Release
gh auth status
gh repo view rsyncOSX/RawCull-AI-Models --json nameWithOwner,url

7.1. Create or inspect v2

gh release view only inspects an existing release; it does not create the tag or release. To inspect v2, use this one-line form, which also avoids shell errors caused by spaces after a line-continuation backslash:

gh release view v2 --repo rsyncOSX/RawCull-AI-Models --json tagName,isDraft,isPrerelease,isImmutable

For the existing v2 release, the command returns:

{
  "isDraft": false,
  "isImmutable": false,
  "isPrerelease": true,
  "tagName": "v2"
}

isImmutable:false means GitHub is not currently enforcing immutable-release protection for this repository. Normally treat a release as immutable once clients have downloaded or consumed its manifest. The narrowly scoped republish procedure in section 7.4 is permitted only when the release is known to be unused and the entire affected payload-and-manifest set is replaced.

If the command fails, interpret the error before changing its arguments:

  • accepts at most 1 arg(s) usually means a copied multiline command lost a continuation backslash; use the one-line command above;
  • release not found or Could not resolve to a Release means v2 does not exist in the repository selected by --repo;
  • an authentication error requires gh auth login or a valid GH_TOKEN;
  • error connecting to api.github.com is a network or proxy failure, not a problem with v2, --repo, or --json.

If GitHub reports release not found, create the release explicitly from the repository’s main branch. Choose --prerelease only when that is the intended publication state:

gh release create v2 --repo rsyncOSX/RawCull-AI-Models \
  --target main \
  --title "RawCull AI models v2" \
  --prerelease \
  --notes "RawCull AI model asset packs for manifest version 2."

Do not run gh release create when gh release view v2 already succeeds. At verification time, tagName must be v2 and isDraft must be false. A tag-pinned URL can use a published prerelease, but record that decision.

7.2. Prepare the exact release asset names

The local package filenames end in .aar, but the generated manifest URLs do not. Make extensionless upload copies whose basenames exactly match the id values in Output/manifest.json:

cd /Users/thomas/ModelAssets/Release
mkdir -p Output/Upload

cp Output/clip-datacomp.aar \
  Output/Upload/no.blogspot.RawCull.models.clip-datacomp
cp Output/clip-openai.aar \
  Output/Upload/no.blogspot.RawCull.models.clip-openai
# Only when SAM 3 is present in Output/manifest.json:
cp Output/sam3.aar \
  Output/Upload/no.blogspot.RawCull.models.sam3

Before uploading, confirm that every manifest URL basename has a matching local file and that its byte count equals downloadSize:

jq -r '.assetPacks[] | [.id, (.downloadSize | tostring)] | @tsv' \
  Output/manifest.json
stat -f '%N %z' Output/Upload/no.blogspot.RawCull.models.*

Do not upload clip-datacomp.aar or clip-openai.aar under those local names; they would produce URLs that do not match the generated manifest.

7.3. Upload and verify

Upload files in this order:

  1. all approved AAR payloads under their exact extensionless manifest IDs;
  2. download each payload anonymously and verify its checksum and size;
  3. upload manifest.json last;
  4. download and inspect the published manifest anonymously.

For the current two-CLIP manifest, upload the extensionless assets explicitly:

gh release upload v2 \
  Output/Upload/no.blogspot.RawCull.models.clip-datacomp \
  Output/Upload/no.blogspot.RawCull.models.clip-openai \
  --repo rsyncOSX/RawCull-AI-Models

Do not use --clobber during a normal new-tag publication. Verify the remote filenames:

gh release view v2 --repo rsyncOSX/RawCull-AI-Models \
  --json assets \
  --jq '.assets[] | [.name, (.size | tostring), .url] | @tsv'

Download the asset-pack URLs from the generated manifest, compare them with the local upload copies, and only then upload the manifest:

curl --fail --location --output /tmp/no.blogspot.RawCull.models.clip-datacomp \
  https://github.com/rsyncOSX/RawCull-AI-Models/releases/download/v2/no.blogspot.RawCull.models.clip-datacomp
curl --fail --location --output /tmp/no.blogspot.RawCull.models.clip-openai \
  https://github.com/rsyncOSX/RawCull-AI-Models/releases/download/v2/no.blogspot.RawCull.models.clip-openai

shasum -a 256 \
  Output/Upload/no.blogspot.RawCull.models.clip-datacomp \
  /tmp/no.blogspot.RawCull.models.clip-datacomp
shasum -a 256 \
  Output/Upload/no.blogspot.RawCull.models.clip-openai \
  /tmp/no.blogspot.RawCull.models.clip-openai

gh release upload v2 Output/manifest.json \
  --repo rsyncOSX/RawCull-AI-Models

Each local/remote checksum pair must match. Finally, download and inspect the published manifest anonymously:

curl --fail --location --output /tmp/rawcull-v2-manifest.json \
  https://github.com/rsyncOSX/RawCull-AI-Models/releases/download/v2/manifest.json
jq empty /tmp/rawcull-v2-manifest.json
jq -r '.assetPacks[] | [.id, (.version | tostring), (.downloadSize | tostring), .url] | @tsv' \
  /tmp/rawcull-v2-manifest.json

If clients might already have consumed the release, never replace files under its tag. Publish a corrective tag, for example v2.0.1, and update both RawCull manifest URLs. If the release is confirmed unused, follow section 7.4 instead.

7.4. Republish unused models on the same v2 tag

Use this exception only when all of the following are true:

  • no user or client has downloaded or cached the model release;
  • the GitHub release reports isImmutable:false;
  • the replacement archives, manifest, RawCull catalogue, tests, notices, and documentation have already been updated and verified together;
  • every model omitted for legal or technical reasons remains omitted. For the current republish, SAM 3 remains blocked and absent.

First verify the local replacement set. The manifest must contain exactly the two ready CLIP packs, and each downloadSize must match its extensionless upload file:

AssetExpected v2 bytesExpected SHA-256
no.blogspot.RawCull.models.clip-datacomp282,966,632cf433dcd199b44635a4ff0260bd8e79177e4907a4cfcb2f72043066b8cbe4ef7
no.blogspot.RawCull.models.clip-openai282,866,068e9181157c2d4012db2e6478949488f9906696a4ed78ecaa10235d9762621136c
manifest.json783d9c58b6ff6752f5ae4e3d692a6d6d5839edca733d7d60da6ed4f54eca336d8a6
cd /Users/thomas/ModelAssets/Release

jq -r '.assetPacks[] | [.id, (.version | tostring), (.downloadSize | tostring), .url] | @tsv' \
  Output/manifest.json
stat -f '%N %z' \
  Output/Upload/no.blogspot.RawCull.models.clip-datacomp \
  Output/Upload/no.blogspot.RawCull.models.clip-openai
shasum -a 256 \
  Output/Upload/no.blogspot.RawCull.models.clip-datacomp \
  Output/Upload/no.blogspot.RawCull.models.clip-openai \
  Output/manifest.json

Confirm that the manifest includes no SAM 3 entry:

test "$(jq -r '.assetPacks[].id' Output/manifest.json | sort | tr '\n' ' ')" = \
  "no.blogspot.RawCull.models.clip-datacomp no.blogspot.RawCull.models.clip-openai "

Inspect the current remote assets and record their names, sizes, and digests before deleting anything:

gh release view v2 --repo rsyncOSX/RawCull-AI-Models \
  --json tagName,isDraft,isPrerelease,isImmutable,assets \
  --jq '{tagName,isDraft,isPrerelease,isImmutable,assets:[.assets[]|{name,size,digest,url}]}'

Delete the old manifest.json first. This prevents a client from discovering the old manifest while its referenced payloads are being replaced. Then delete the two old CLIP uploads by exact name. Do not delete or recreate the release or tag itself:

gh release delete-asset v2 manifest.json \
  --repo rsyncOSX/RawCull-AI-Models --yes
gh release delete-asset v2 no.blogspot.RawCull.models.clip-datacomp \
  --repo rsyncOSX/RawCull-AI-Models --yes
gh release delete-asset v2 no.blogspot.RawCull.models.clip-openai \
  --repo rsyncOSX/RawCull-AI-Models --yes

Verify those three names are absent. If any deletion failed, stop and resolve it before uploading replacements:

gh release view v2 --repo rsyncOSX/RawCull-AI-Models \
  --json assets --jq '.assets[].name'

Upload the two replacement payloads first. SAM 3 must not be included:

gh release upload v2 \
  Output/Upload/no.blogspot.RawCull.models.clip-datacomp \
  Output/Upload/no.blogspot.RawCull.models.clip-openai \
  --repo rsyncOSX/RawCull-AI-Models

Download both published payloads anonymously and compare their byte counts and SHA-256 values with the exact local upload files. Upload manifest.json only after both comparisons succeed:

curl --fail --location --output /tmp/no.blogspot.RawCull.models.clip-datacomp \
  https://github.com/rsyncOSX/RawCull-AI-Models/releases/download/v2/no.blogspot.RawCull.models.clip-datacomp
curl --fail --location --output /tmp/no.blogspot.RawCull.models.clip-openai \
  https://github.com/rsyncOSX/RawCull-AI-Models/releases/download/v2/no.blogspot.RawCull.models.clip-openai

cmp Output/Upload/no.blogspot.RawCull.models.clip-datacomp \
  /tmp/no.blogspot.RawCull.models.clip-datacomp
cmp Output/Upload/no.blogspot.RawCull.models.clip-openai \
  /tmp/no.blogspot.RawCull.models.clip-openai
shasum -a 256 \
  Output/Upload/no.blogspot.RawCull.models.clip-datacomp \
  /tmp/no.blogspot.RawCull.models.clip-datacomp \
  Output/Upload/no.blogspot.RawCull.models.clip-openai \
  /tmp/no.blogspot.RawCull.models.clip-openai

gh release upload v2 Output/manifest.json \
  --repo rsyncOSX/RawCull-AI-Models

Finally, download the new manifest anonymously and verify it byte-for-byte, then cross-check every remote asset against its manifest entry and local catalogue:

curl --fail --location --output /tmp/rawcull-v2-manifest.json \
  https://github.com/rsyncOSX/RawCull-AI-Models/releases/download/v2/manifest.json
cmp Output/manifest.json /tmp/rawcull-v2-manifest.json
jq -r '.assetPacks[] | [.id, (.version | tostring), (.downloadSize | tostring), .url] | @tsv' \
  /tmp/rawcull-v2-manifest.json
gh release view v2 --repo rsyncOSX/RawCull-AI-Models \
  --json assets \
  --jq '.assets[] | [.name, (.size | tostring), .digest, .url] | @tsv'

Do not leave v2 without a manifest longer than necessary. If replacement or verification fails after deletion, keep the manifest absent until the two payloads are complete and verified; never restore a manifest that references a missing or mismatched archive.

8. Update RawCull’s production catalogue

The current two-CLIP v2 release uses:

static let includeOpenAICLIP = true
static let includeDataCompCLIP = true
static let includeSAM3 = false

SAM 3 remains blocked. Change its switch only after its redistribution review and all technical release gates are complete. The eventual three-model configuration is:

static let includeOpenAICLIP = true
static let includeDataCompCLIP = true
static let includeSAM3 = true

For every rebuilt archive, update its production descriptor with actual values:

upstreamRevision: "<verified immutable source revision>",
expectedArchiveSHA256: "<64 lowercase hexadecimal characters>",
downloadByteCount: <exact AAR bytes>,
installedByteCount: <exact installed bytes or nil>,
releaseReadiness: .ready,

Do not change .blocked to .ready until the corresponding pack is present in the published manifest and every release gate is complete. Keep the verified SAM 3 licence resource, SHA-256, and requiresExplicitAcceptance: true synchronized with its notice catalogue.

9. Point both configurations to the release

Update both locations to the same exact URL:

https://github.com/rsyncOSX/RawCull-AI-Models/releases/download/v2/manifest.json

The locations are:

  • RawCullAIModelDownloadSource.productionManifestURL in RawCullAIModelDownloadService.swift;
  • BAManifestURL in RawCull-Info.plist.

Keep BAUsesAppleHosting false for GitHub self-hosting. Do not point RawCull to the new manifest until every referenced archive is anonymously downloadable and verified.

10. Update provenance, documentation, and tests

For every released pack, update ModelAssets/Notices/<model>/PROVENANCE.json and NOTICE.md with the source revision, source checksum, PhotoAIKit revision, command, runtime filename, model fingerprint, and review status. Keep archive SHA-256 and byte-count values in RawCull’s production download catalogue, release documentation, generated manifest, and release-host metadata—not in the provenance packaged inside that archive. Update ModelAssets/README.md to list the exact release URL and available packs.

Tests must verify:

  • the catalogue contains exactly the enabled ready models;
  • every ready model has an exact archive checksum and byte count;
  • bundled licence texts match their recorded checksums;
  • both manifest URL locations use the same tag;
  • provenance and notice records describe the generated archives;
  • excluded models do not appear in settings or the production catalogue.

Run the focused download and release-metadata tests, then the full suite:

xcodebuild \
  -project RawCull.xcodeproj \
  -scheme RawCull \
  -destination 'platform=macOS' \
  -only-testing:RawCullTests/RawCullAIModelDownloadsTests \
  -only-testing:RawCullTests/ReleaseMetadataTests \
  test

xcodebuild \
  -project RawCull.xcodeproj \
  -scheme RawCull \
  -destination 'platform=macOS' \
  test

11. Perform a clean-machine test

Before shipping:

  1. confirm Settings lists only the enabled subset of DataComp, OpenAI, and SAM 3;
  2. download each enabled model through Managed Background Assets;
  3. rebuild each CLIP index and test semantic search and similarity;
  4. verify that DataComp and OpenAI artifacts never cross-load;
  5. after SAM 3 becomes ready and is published, accept its licence and verify segmentation;
  6. relaunch and verify installation and selection persistence;
  7. remove each pack independently;
  8. interrupt a download and verify retry;
  9. verify Vision feature-print similarity remains the safe fallback when no CLIP model is available.

Release checklist

  • The supported model scope remains DataComp CLIP, OpenAI CLIP, and SAM 3.
  • The current manifest contains exactly the ready subset (both CLIP packs for v2).
  • DataComp uses datacomp_s34b_b86k, not the custom filename preset.
  • Exact upstream revisions and source checksums are recorded.
  • The exact PhotoAIKit revision and conversion commands are recorded.
  • Runtime fingerprints match metadata.json.
  • CLIP parity, semantic search, and image similarity are reviewed.
  • SAM 3 segmentation and licence acceptance are tested.
  • OpenAI redistribution blockers are resolved before its release.
  • SAM 3 remains blocked and absent, or its redistribution review is complete.
  • Only optimized runtime assets are packaged.
  • Fresh archive checksums and byte counts are in RawCull, the generated manifest, and release records.
  • Archives are uploaded and verified before manifest.json.
  • Both RawCull manifest URLs point to the same tag-pinned manifest.
  • Inclusion flags and readiness values match the published manifest.
  • Focused, full, and clean-machine tests pass.

Rollback

Before an app release, restore both manifest URLs and catalogue readiness to the last known-good release if the new model release fails. After clients have consumed a publication, do not mutate its tag. Publish a corrected tag, verify it, and update both RawCull URLs together. The same-tag procedure in section 7.4 is only for a confirmed-unused release.

6 - CLIP Model Evaluation Results

Detailed comparison of OpenAI CLIP ViT-B/32 and OpenCLIP DataComp ViT-B/32-256 using RawCullFB semantic-search and image-similarity reports.

CLIP Model Evaluation Results

Report date: 2026-08-12

This report compares the product-behavior results produced by RawCullFB for:

  • OpenAI CLIP ViT-B/32 at 224 × 224; and
  • OpenCLIP DataComp ViT-B/32-256 using the datacomp_s34b_b86k checkpoint.

The runs follow the product-behavior portion of Evaluating CLIP Models. They cover the canonical 77 semantic queries and a complete all-pairs image-similarity pass over 453 indexed images.

The reports show that both integrations completed cleanly, neither model shows semantic collapse, and both produce coherent image-neighbourhood structures. DataComp is modestly faster and more consistent across paraphrases. Those are promising sanity-check results, but they are not labeled accuracy metrics.

No final release-model selection can be made from these two reports alone. Source-framework parity evidence, pooled relevance labels, a calibrated labeled similarity benchmark, and full operational measurements are still required by the evaluation procedure.

1. Evidence analyzed

EvidenceOpenAIDataComp
RawCullFB reportViT-B-32-semantic-test-results.txtdatacomp_s34b_b86k-semantic-test-results.txt
Model labelViT-B-32datacomp_s34b_b86k
Run started, UTC2026-08-09 15:56:462026-08-10 05:07:09
Report updated, UTC2026-08-09 15:56:532026-08-10 05:07:16
Catalog recorded by report/Users/thomas/Downloads/testphotos/Users/thomas/Downloads/testphotos
Result limit5050

The complete model fingerprints recorded in the reports are:

OpenAI
clip:clip-vit-base-patch32_float16_static:openai/clip-vit-base-patch32:clip-vit-base-patch32_float16_static.aimodel:0.4:directory-tree-sha256-v1:a71ceca116e13b334b0679c95bd85d7ecda14fd68bd7a3ea84b83c286005f45b

DataComp
clip:ViT-B-32-256-datacomp_s34b_b86k_float16_static:mlfoundations/open_clip:ViT-B-32-256-datacomp_s34b_b86k_float16_static.aimodel:0.4:directory-tree-sha256-v1:6a3639a2049b8a4ea23fe04c3083e199a4f505433f7c8bd0748b3c8d4fcb1572

The fingerprints are distinct, as required. Each result set therefore identifies a separate model asset and embedding space.

This analysis did not receive the run’s environment record, catalog hashes, query hash, model metadata, parity outputs, or relevance-label CSV. Matching catalog paths and image counts support comparability, but do not independently prove that every catalog byte and software revision was unchanged between the two run times.

2. Report-integrity checks

Integrity checkOpenAIDataCompResult
Overall statuscompletedcompletedPass
Completed queries77/7777/77Pass
Result limit5050Pass
Similarity statuscompletedcompletedPass
Similarity anchors453453Pass
Pair comparisons102,378102,378Pass
Incompatible pairs00Pass

For 453 images, the number of unique unordered pairs is:

453 × 452 / 2 = 102,378

The reported pair count is therefore complete for both models. There is no evidence of a partial query run, partial similarity run, incompatible vector, or accidental reuse of one model fingerprint for both reports.

3. Semantic sanity-check methodology

The following report-derived diagnostics were calculated independently for each model:

  • distinct images returned at rank one across all 77 queries;
  • the most frequently reused rank-one image;
  • unique images appearing anywhere in the 77 top-50 result sets;
  • mean rank-one minus rank-two score margin;
  • mean shared images between the top ten of every pair of unrelated and related queries;
  • mean shared top-ten images within the five three-query paraphrase groups;
  • first-query latency; and
  • warmed mean, median, and P95 latency, excluding the first query.

These are collapse, diversity, consistency, and operational diagnostics. They do not establish whether an image is relevant to a query. Relevance must be judged from the image itself using the pooled labeling procedure in the main evaluation guide.

4. Semantic retrieval results

4.1 Diversity and hub behavior

MetricOpenAIDataCompInterpretation
Distinct rank-one images53/7758/77DataComp uses a slightly broader set of winners
Largest rank-one hub4/774/77Neither model has a universal winner
Unique images across all top-50 results418/453423/453Both search broadly across the catalog
Mean top-one minus top-two margin0.007370.00994DataComp has wider within-model separation on this run
Median top-one minus top-two margin0.005330.00708Same direction as the mean

The most frequently returned rank-one image for OpenAI is 62480371.jpg, selected for four queries. The most frequent DataComp winner is 2132732266.jpg, also selected for four queries.

Both results are consistent with healthy retrieval diversity. The established OpenAI sanity reference in the evaluation procedure is 53 distinct top-one images and a maximum hub frequency of 4/77; this run reproduces those exact values. DataComp slightly increases rank-one and top-50 coverage without creating a larger semantic hub.

The score-margin values may be compared between ranks from the same model, but not treated as calibrated confidence or compared directly as accuracy between models. The two models produce different embedding spaces and score distributions.

4.2 Paraphrase consistency

Across all 2,926 pairs of queries, the mean number of shared images in two top-ten result lists is low, as expected:

MetricOpenAIDataComp
Mean shared top-ten images, all query pairs0.7240.703
Median shared top-ten images, all query pairs00
Mean shared top-ten images, paraphrase pairs4.935.60
Median shared top-ten images, paraphrase pairs55
Paraphrase/general overlap ratio6.82×7.97×

Paraphrases are materially more consistent than unrelated prompts for both models. This is an important sanity check: changing the wording while retaining the intent produces strongly overlapping results, without causing unrelated queries to collapse onto a common result pool.

The group-level mean shared top-ten counts are:

Paraphrase groupOpenAIDataComp
Dog on a beach6.006.33
City street at night7.337.67
Sharp portrait3.334.67
Blurry photograph3.674.33
Beautiful landscape4.335.00

DataComp has higher top-ten overlap in all five groups. Its largest advantage is on the sharp-portrait group. This is the clearest positive signal for DataComp in the current unlabeled reports.

Top-one identity is less stable than top-ten membership for both models. That is not necessarily a failure: several images can have near-equal relevance, and a small score change can reorder the first few results. Graded relevance labels and nDCG@5 are needed to distinguish harmless reordering from a real quality difference.

4.3 Agreement between models

The models return the same rank-one image for 19 of 77 queries, or 24.7%. Their top-ten lists contain an average of 4.69 identical images, or 46.9% of a ten-result list.

Query categoryQueriesSame rank oneMean shared top-ten images
Objects and scenes1665.63
Colors and attributes945.56
Actions and relations725.00
Photographic quality and style1324.54
Composition and subjective judgment912.44
Count, spatial relation, and negation833.63
Paraphrases1515.07

Concrete objects, scenes, colors, and attributes show the strongest agreement. Composition and subjective photographic judgments show the weakest agreement, followed by count, spatial, and negative-language prompts.

The strongest top-ten agreement includes:

  • a yellow flower: 10/10 shared results;
  • a lake surrounded by mountains: 9/10;
  • food on a plate: 8/10;
  • dog on a beach: 8/10; and
  • canine at the seaside: 8/10.

The most model-sensitive prompts include:

  • a subject positioned using the rule of thirds: 0/10 shared results;
  • a photograph with strong leading lines: 1/10;
  • a candid emotional moment: 1/10;
  • a reflection of a person in a mirror: 1/10;
  • sharp portrait: 1/10; and
  • a photograph where the subject is not sharply focused: 1/10.

Disagreement is not itself proof that either result is wrong. It identifies the queries that should receive the closest attention during blind relevance labeling.

5. Query latency

MetricOpenAIDataCompDataComp difference
First query157 ms130 ms17.2% faster
Warm mean67.25 ms65.67 ms2.3% faster
Warm median66 ms65 ms1.5% faster
Warm P9572 ms69 ms4.2% faster
Warm range63–130 ms61–129 msSimilar

DataComp is consistently but only modestly faster in this report. The first query is the largest difference. Warmed latency is effectively in the same operational class for both models.

These timings are useful observations, not the complete operational benchmark required by the evaluation procedure. The reports do not establish that 30 unmeasured warm-up queries were run, and they do not contain P99 latency, model load time, indexing throughput, peak memory, energy impact, bundle size, or index size.

6. Image-similarity results

MetricOpenAIDataComp
Similarity pass duration861 ms842 ms
Mutual top-one pairs122124
Unique top-one targets298318
Maximum top-one target reuse79
Nearest-distance minimum0.000820.00586
Nearest-distance median0.171910.30235
Nearest-distance mean0.162900.27934
Nearest-distance P950.306120.50179
Nearest-distance maximum0.452590.78558

DataComp completes the all-pairs similarity pass 19 ms, or 2.2%, faster. It produces two more mutual top-one pairs and a broader set of unique top-one targets. OpenAI has a lower maximum target-reuse count.

OpenAI’s distances are numerically lower throughout the distribution. This does not mean that OpenAI is more accurate: distance and cosine values are calibrated differently in the two embedding spaces. A threshold selected for one model must never be copied to the other model.

As an additional, non-gating diagnostic, 50 obvious two-file groups were identified from shared numeric filename prefixes under images/. This gives 100 eligible anchors. Both models retrieved the apparent partner at the same rates:

Heuristic paired-file recoveryOpenAIDataComp
Recall at rank 198/10098/100
Recall within rank 399/10099/100
Recall within rank 5100/100100/100

This heuristic is encouraging, but filenames are not ground-truth labels. It must not replace the labeled pair set prescribed by the evaluation procedure: exact duplicates, re-encodes, edits, crops, burst frames, same-subject images, hard negatives, and unrelated negatives.

7. What the reports establish

The two reports establish that:

  1. RawCullFB completed the same 77-query and 453-anchor workload for both distinct model fingerprints.
  2. No incompatible image pairs were encountered.
  3. Neither model exhibits obvious semantic collapse or a universal top-one image.
  4. Both models distinguish paraphrases from unrelated queries.
  5. DataComp has stronger paraphrase overlap in this run.
  6. DataComp has slightly broader retrieval diversity.
  7. DataComp is modestly faster for semantic queries and the all-pairs similarity pass.
  8. The models disagree most on composition, subjective photographic quality, counting, spatial relations, and negation.

The reports do not establish that:

  • either converted bundle matches its source framework numerically;
  • one model has higher semantic Precision@1, Precision@5, MRR, or nDCG@5;
  • one model has a better duplicate-detection ROC-AUC or precision-recall AUC;
  • a shared image-similarity threshold is valid;
  • all catalog bytes, query bytes, software revisions, and index identities were frozen and archived; or
  • either model has acceptable indexing throughput, memory, energy, and storage costs for release.

8. Provisional decision table

Gate or metricOpenAI ViT-B/32DataComp ViT-B/32-256Current decision
Distinct model identity in reportPassPassBoth fingerprints recorded
Complete RawCullFB reportPassPassBoth completed
Source identity verified against archiveNot providedNot providedOpen
Minimum text parityNot providedNot providedOpen
Minimum end-to-end image parityNot providedNot providedOpen
Semantic collapse diagnosticPassPassBoth healthy
Distinct semantic rank-one images5358DataComp signal
Largest semantic hub4/774/77Tie
Mean paraphrase top-ten overlap4.935.60DataComp signal
Precision@1Not labeledNot labeledOpen
Precision@5Not labeledNot labeledOpen
MRRNot labeledNot labeledOpen
nDCG@5Not labeledNot labeledOpen
Similarity PR-AUCNot labeledNot labeledOpen
Similarity FPR at selected recallNot labeledNot labeledOpen
First-query latency157 ms130 msDataComp signal
Warm P95 latency72 ms69 msSmall DataComp signal
Indexing throughputNot providedNot providedOpen
Peak memoryNot providedNot providedOpen
Bundle and index sizeNot providedNot providedOpen

9. Recommendation and next steps

DataComp is the more promising candidate in this unlabeled RawCullFB run. It is more paraphrase-consistent, slightly more diverse, and modestly faster, while retaining healthy similarity-neighbourhood behavior.

The current evidence is not sufficient to prefer DataComp for release. The evaluation procedure explicitly requires a material labeled-quality improvement before replacing the established OpenAI control. If labeled quality is effectively tied, OpenAI remains the simpler established choice and DataComp may remain experimental.

Complete the following work before making the model decision:

  1. Archive the environment, source revisions, model metadata, fingerprints, catalog manifest, catalog hashes, and semantic-query hash for this run.
  2. Attach the OpenAI Hugging Face and DataComp OpenCLIP parity results, proving text cosine of at least 0.999 and end-to-end fixture cosine of at least 0.998.
  3. Pool and blind-label the union of both models’ top five results for every query using relevance grades 0, 1, and 2.
  4. Calculate Precision@1, Precision@5, MRR, and nDCG@5 overall and by query category.
  5. Prioritize manual review of composition, subjective-quality, count, spatial, and negation prompts because those categories differ most strongly.
  6. Build the labeled image-pair benchmark and calculate ROC-AUC, PR-AUC, false-positive rate, false-negative rate, and a model-specific threshold.
  7. Measure indexing throughput, peak memory, P99 latency, bundle size, index size, and energy impact under controlled conditions.
  8. Revisit the provisional decision using the completed decision table.

Until those steps are complete, the defensible conclusion is:

Both models pass the RawCullFB report-integrity and semantic-collapse sanity checks. DataComp shows better paraphrase consistency and small operational advantages, but the release choice remains open pending parity evidence and blinded relevance and similarity labels.