<p align="center">
  <img src="assets/shift-icon.png" alt="SHIFT" width="200" />
</p>

<h1 align="center">SHIFT</h1>

<p align="center">
  <strong>Smart Hybrid Input Filtering & Transformation</strong>
</p>

<p align="center">
  A multimodal preflight layer that automatically adapts inputs before they reach an AI model.
</p>

---

## What it does

SHIFT sits between your application and the model API. Every request passes through a pipeline that **inspects**, **evaluates**, and **transforms** media inputs so they conform to provider constraints.

<p align="center">
  <img src="assets/flow.svg" alt="SHIFT pipeline flow" width="900" />
</p>

**Before SHIFT:** oversized images, unsupported formats, and bloated payloads cause hard failures (400 errors, token waste, session crashes).

**After SHIFT:** every request is valid, optimized, and tuned to your cost/quality preference.

## Token savings

SHIFT estimates per-provider token savings for every run. Both OpenAI and Anthropic charge tokens based on image dimensions — resizing images before they hit the API reduces cost.

| Scenario | Before | After | OpenAI tokens | Anthropic tokens |
|---|---|---|---|---|
| 4000×3000 hero image (balanced) | 4000×3000 | 2048×1536 | 765 → 765 | 1,568 → 1,568 |
| 4000×3000 hero image (economy) | 4000×3000 | 1024×768 | 765 → 765 | 1,568 → 1,082 (−31%) |
| 1254×1254 app icon (economy) | 1254×1254 | 1024×1024 | 765 → 765 | 1,568 → 1,405 (−10%) |
| SVG diagram → rasterized PNG | SVG | 512×256 PNG | 255 → 255 | 0 → 98 |

*Token estimates based on published provider formulas. OpenAI uses tile-based counting (GPT-4o/4.1 family, 512×512 tiles); Anthropic uses pixel-based (`w×h/750`, 1568px long-edge cap for standard models). Actual billing may vary by model — newer OpenAI models use patch-based counting, and Anthropic Opus 4.7 supports higher resolution (2576px, 4784 max tokens).*

### Sample report

```
$ cat request.json | shift-ai -m economy -o report

=== SHIFT Report ===
Images found:      1
Images modified:   1
Images dropped:    0
Original size:     42262 bytes
Transformed size:  17018 bytes
Size reduction:    59.7%

Token Savings (estimated):
  OpenAI:    765 -> 765 tokens  (0.0% saved)
  Anthropic: 1,568 -> 1,082 tokens  (31.0% saved)

Per-image breakdown:
  [0] 4000x3000 -> 1024x768  (OpenAI: 765 -> 765, Anthropic: 1,568 -> 1,082)

Actions:
  [image 0] resize — 4000x3000 -> 1024x768
```

### Cumulative tracking

SHIFT automatically records run statistics to `~/.shift/stats.jsonl`. View cumulative savings with `shift-ai gain`:

```
$ shift-ai gain

=== SHIFT Cumulative Savings ===

Runs:     42
Images:   156 processed, 89 modified
Bytes:    247.3 MB saved

Token Savings (estimated):
  OpenAI:    52,400 -> 12,300 tokens  (76.5% saved)
  Anthropic: 84,200 -> 28,100 tokens  (66.6% saved)
```

```
$ shift-ai gain --daily

=== SHIFT Daily Token Savings ===

Date          Runs  Images    OpenAI saved Anthropic saved
----------------------------------------------------------
2026-04-20       8      24           3,200           5,400
2026-04-21      12      42           4,800           8,200
2026-04-22      22      90          12,100          18,500
```

Use `shift-ai gain --format json` for machine-readable output.

## Install

### From crates.io

```bash
cargo install shift-preflight-cli
```

### From GitHub (latest)

```bash
cargo install --git https://github.com/alohaninja/shift shift-preflight-cli
```

### From source

```bash
git clone https://github.com/alohaninja/shift.git
cd shift
cargo install --path shift-cli
```

### Build without installing

```bash
git clone https://github.com/alohaninja/shift.git
cd shift
cargo build --release
# Binary at target/release/shift-ai
```

### Pre-built binaries

Download from [GitHub Releases](https://github.com/alohaninja/shift/releases) — macOS (x86/arm) and Linux (x86/arm).

### Verify installation

```bash
shift-ai --version
shift-ai --help

# Quick validation — transform a sample payload
echo '{"model":"gpt-4o","messages":[{"role":"user","content":"hello"}]}' | shift-ai

# Check stats tracking
shift-ai gain
```

## Quick start

```bash
# Transform an OpenAI request (stdin/stdout pipe)
cat request.json | shift-ai -p openai -m balanced > safe_request.json

# Transform an Anthropic request from a file
shift-ai request.json -p anthropic -m economy > safe_request.json

# See what would change without modifying anything
shift-ai request.json --dry-run -o report

# Compose with curl
shift-ai request.json -p openai | curl -s -X POST \
  https://api.openai.com/v1/chat/completions \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @-
```

## Options

```
shift-ai [OPTIONS] [FILE]

Arguments:
  [FILE]  Input file (JSON request payload). Reads stdin if omitted.

Options:
  -p, --provider <PROVIDER>  Target provider [default: openai]
                              [openai, anthropic, claude]
  -m, --mode <MODE>          Drive mode [default: balanced]
                              [performance, balanced, economy]
      --svg-mode <MODE>      SVG handling [default: raster]
                              [raster, source, hybrid]
  -o, --output <FORMAT>      Output format [default: json]
                              [json, report, json-report, both]
      --dry-run              Show what would change without modifying
      --profile <FILE>       Custom provider profile JSON
      --model <MODEL>        Target model (overrides model in payload)
      --no-stats             Disable saving run statistics
  -v, --verbose              Verbose output

Commands:
  shift-ai gain                 Show cumulative token savings
  shift-ai gain --daily         Day-by-day breakdown
  shift-ai gain --format json   Machine-readable output for dashboards
```

## Drive modes

| Mode | What it does |
|---|---|
| **performance** | Minimal transforms. Only enforce hard provider limits (max dimension, max file size). Preserve original fidelity. |
| **balanced** | Moderate optimization. Resize oversized images, recompress bloated files. Remove obvious waste. **Default.** |
| **economy** | Aggressive optimization. Downscale everything to 1024px, drop excess images beyond provider limits, minimize token usage. |

## SVG handling

Most AI model APIs reject SVG. SHIFT detects SVG inputs and handles them based on `--svg-mode`:

| Mode | Behavior |
|---|---|
| **raster** | Rasterize SVG to PNG via `resvg` (default, provider-safe) |
| **source** | Replace the image with SVG XML as a text content block |
| **hybrid** | Rasterize to PNG and retain SVG source as text |

## Supported formats

**Detected and processed:**

| Category | Formats |
|---|---|
| Raster images | PNG, JPEG, GIF, WebP, BMP, TIFF |
| Vector images | SVG (auto-rasterized to PNG) |
| Encodings | base64 data URIs, raw base64, URL references |

BMP and TIFF are auto-converted to PNG. SVGs are rasterized. Everything else passes through if it meets provider constraints.

## Provider profiles

Built-in constraints for the two major multimodal providers:

| Provider | Max images | Max dimension | Max file size | Megapixel limit |
|---|---|---|---|---|
| **OpenAI** | 10 | 2048 px | 20 MB | -- |
| **Anthropic** | 20 | 8000 px | 5 MB | 1.15 MP |

Profiles include per-model overrides (gpt-4o, gpt-4.1, claude-sonnet-4, etc.) and fall back to provider defaults for unknown models.

Custom profiles can be loaded with `--profile custom.json`.

## Library usage

SHIFT is split into two crates: `shift-preflight` (library) and `shift-preflight-cli` (binary, installs as `shift-ai`). The library can be used directly in Rust applications:

```toml
# Cargo.toml
[dependencies]
shift-preflight = "0.1"
```

```rust
use shift_preflight::{pipeline, ShiftConfig, DriveMode};
use serde_json::json;

let payload = json!({
    "model": "gpt-4o",
    "messages": [{
        "role": "user",
        "content": [{
            "type": "image_url",
            "image_url": {"url": "data:image/png;base64,..."}
        }]
    }]
});

let config = ShiftConfig {
    mode: DriveMode::Balanced,
    provider: "openai".to_string(),
    ..Default::default()
};

let (safe_payload, report) = pipeline::process(&payload, &config).unwrap();
eprintln!("{}", report); // what changed and why
```

## How it works

1. **Inspect** -- Detect every image in the request payload. Extract format (via magic bytes), dimensions, file size, encoding type. Handles base64 data URIs, raw base64, and URL references (fetched automatically).

2. **Evaluate** -- Load the provider profile for the target API. Compare each image's metadata against the constraints. Apply mode-specific rules to determine what actions are needed (resize, recompress, convert, rasterize, drop).

3. **Transform** -- Execute the actions. Resize preserves aspect ratio using Lanczos3 filtering. SVGs are rasterized with `resvg` (supports gradients, text, viewBox). BMP/TIFF are converted to PNG. JPEG recompression uses mode-tuned quality levels.

4. **Reconstruct** -- Rebuild the original payload with transformed images slotted back in. Output is a valid JSON request ready to send to the API.

## Project structure

```
shift/
├── shift-core/          Library crate: shift-preflight (all processing logic)
│   └── src/
│       ├── inspector/   Format detection, metadata extraction
│       ├── policy/      Provider profiles, constraint evaluation, rules
│       ├── transformer/ Image resize, recompress, SVG rasterize, convert
│       ├── payload/     OpenAI + Anthropic message format parse/reconstruct
│       ├── pipeline.rs  Orchestrator: inspect -> policy -> transform
│       ├── cost.rs      Token estimation (OpenAI tile, Anthropic pixel)
│       ├── stats.rs     Persistent run statistics, gain summaries
│       ├── report.rs    Transformation report with token savings
│       └── mode.rs      DriveMode, SvgMode, ShiftConfig
├── shift-cli/           Binary crate: shift-preflight-cli → shift-ai
├── profiles/            Provider constraint JSON (embedded at compile time)
├── tests/
│   ├── fixtures/        Test images and sample payloads
│   └── docker/          Dockerfiles for cross-distro CI (Ubuntu, Arch)
└── .github/workflows/   CI + Linux distro tests
```

## Roadmap (v2+)

- **Video**: frame sampling, keyframe extraction, resolution downscale
- **Audio**: compression, transcription to text
- **Documents**: chunking, summarization, text extraction
- **Smart image selection**: near-duplicate detection, keep most informative
- **Caption fallback**: replace low-value images with text descriptions
- **Adaptive policies**: dynamic adjustment based on request size and latency targets

## License

Apache-2.0
