2026-02-15 09:47:24 +01:00
2026-02-14 15:14:12 +01:00
2026-02-14 15:14:12 +01:00
2026-02-14 15:14:12 +01:00
2026-02-14 15:14:12 +01:00
2026-02-14 15:14:12 +01:00
2026-02-15 09:47:24 +01:00
2026-02-08 10:42:01 +01:00
2026-02-14 15:14:12 +01:00
2026-02-07 08:23:18 +01:00
2026-02-14 15:14:12 +01:00
2026-02-07 08:23:18 +01:00
fix
2026-02-08 10:43:28 +01:00
2026-02-07 08:23:18 +01:00
2026-02-07 08:23:18 +01:00
2026-02-14 15:14:12 +01:00
2026-02-14 15:14:12 +01:00
2026-02-14 15:14:12 +01:00
2026-02-14 15:14:12 +01:00
2026-02-07 08:23:18 +01:00
2026-02-07 08:23:18 +01:00
2026-02-14 15:14:12 +01:00
2026-02-14 15:14:12 +01:00

Laravel Logo

Build Status Total Downloads Latest Stable Version License

About Laravel

Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:

Laravel is accessible, powerful, and provides tools required for large, robust applications.

Learning Laravel

Laravel has the most extensive and thorough documentation and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework. You can also check out Laravel Learn, where you will be guided through building a modern Laravel application.

If you don't feel like reading, Laracasts can help. Laracasts contains thousands of video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.

Laravel Sponsors

We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel Partners program.

Premium Partners

Contributing

Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the Laravel documentation.

Code of Conduct

In order to ensure that the Laravel community is welcoming to all, please review and abide by the Code of Conduct.

Security Vulnerabilities

If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via taylor@laravel.com. All security vulnerabilities will be promptly addressed.

Vision & AI Auto-Tagging Integration

Upload UI Feature Flag (uploads.v2)

The new React upload wizard is behind a feature flag and is disabled by default.

  • Flag env var: SKINBASE_UPLOADS_V2
  • Config key: features.uploads_v2
  • Client flags source: window.SKINBASE_FLAGS

Default behavior

  • SKINBASE_UPLOADS_V2=false → legacy upload UI is rendered.
  • SKINBASE_UPLOADS_V2=trueUploadWizard is rendered.

Setup

In .env (or .env.example for project defaults):

SKINBASE_UPLOADS_V2=false

Enable explicitly when ready:

SKINBASE_UPLOADS_V2=true

After changing env values, clear/reload config as usual:

php artisan config:clear

The system intentionally keeps legacy upload as the default until the flag is explicitly turned on.

Upload Moderation UI Flow

Admin moderation for draft uploads is available through a dedicated queue page.

  • Page route: /admin/uploads/moderation
  • Access: authenticated users with role=admin or role=moderator
  • Data source: GET /api/admin/uploads/pending

Queue behavior

  1. The page loads pending draft uploads (moderation_status=pending).
  2. Moderators can enter an optional note per upload.
  3. Approve action calls:
  • POST /api/admin/uploads/{id}/approve
  • Sets moderation to approved and records moderator + timestamp.
  1. Reject action calls:
  • POST /api/admin/uploads/{id}/reject
  • Sets upload status/processing state to rejected and stores note.

Publish gate

  • Normal users can publish only when moderation_status=approved.
  • Admin users can publish with override behavior.

Similar Artworks Analytics (A/B Evaluation)

The artwork page similar-items block emits two event types:

  • impression (block rendered)
  • click (item clicked)

Events are stored in similar_artwork_events and aggregated daily into similar_artwork_daily_metrics by algo_version.

  • Ingest endpoint: POST /api/analytics/similar-artworks
  • Aggregation command: php artisan analytics:aggregate-similar-artworks --date=YYYY-MM-DD
  • Scheduler: runs daily at 03:10

Personalized Discovery Foundation (Phase 8)

This foundation adds versioned, async-only ingestion and profile normalization for personalized discovery.

  • Tables:
    • user_interest_profiles
    • user_discovery_events
    • user_recommendation_cache
  • Ingest endpoint: POST /api/discovery/events (auth required)
  • Supported event types: view, click, favorite, download
  • Processing model: non-blocking queue job (IngestUserDiscoveryEventJob)
  • Normalization: recency-decay + score normalization in UserInterestProfileService

No feed ranking/UI behavior is introduced in this foundation step.

Feed Endpoint Skeleton

The backend now exposes a personalized feed API skeleton:

  • Endpoint: GET /api/v1/feed (auth required)
  • Query params:
    • limit (1-50, default 24)
    • cursor (opaque cursor token for pagination)
    • algo_version (optional override)
  • Response includes data items and meta.next_cursor for cursor pagination.

Behavior:

  • Reads user_recommendation_cache by user_id + algo_version.
  • On cache miss/stale, returns immediate fallback results and dispatches async regeneration job.
  • Regeneration runs in queue (RegenerateUserRecommendationCacheJob) and writes refreshed cache.
  • Includes cold-start fallback (popular + similar) and a diversity guard to avoid near-duplicates.

Feed Analytics Instrumentation

Feed analytics now track:

  • feed_impression
  • feed_click

Payload dimensions:

  • user_id (derived from auth session)
  • artwork_id
  • position
  • algo_version
  • source (personalized, cold_start, fallback)

Optional:

  • dwell_seconds (for click dwell bucket metrics)

Endpoints:

  • Ingest: POST /api/analytics/feed (auth required)
  • Daily aggregation: php artisan analytics:aggregate-feed --date=YYYY-MM-DD
  • Admin report: GET /api/admin/reports/feed-performance

Daily metrics include CTR, save-rate, and dwell buckets.

For non-blocking client transport, use navigator.sendBeacon with fetch(..., { keepalive: true }) fallback. Reference helper: resources/js/lib/feedAnalytics.js.

Phase 8B: Ranking Weight Tuning (Manual + Data-Driven)

Discovery ranking now supports versioned blend weights per algo_version in config/discovery.php.

  • Blend terms: w1 interest, w2 recency, w3 popularity, w4 novelty
  • Per-algo sets: discovery.ranking.algo_weight_sets
  • Safe rollout: deterministic traffic split by algo_version with config gates (g10, g50, g100)
  • Emergency rollback: DISCOVERY_FORCE_ALGO_VERSION=clip-cosine-v1

Offline evaluator and A/B helper:

  • Evaluate objective across one/all algos:
    • php artisan analytics:evaluate-feed-weights --from=YYYY-MM-DD --to=YYYY-MM-DD
    • Optional: --algo=clip-cosine-v1
  • Baseline vs candidate comparison:
    • php artisan analytics:compare-feed-ab clip-cosine-v1 clip-cosine-v2 --from=YYYY-MM-DD --to=YYYY-MM-DD

Objective score uses feed_daily_metrics and configurable objective weights in discovery.evaluation.objective_weights.

Temporary production policy: set DISCOVERY_EVAL_SAVE_RATE_INFORMATIONAL=true to keep save_rate visible but excluded from objective score until save-event ingestion is verified.

Operational runbook: docs/feed-rollout-runbook.md.

Operations / Runbooks

  • Upload UI v2 rollout, post-deploy monitoring, and rollback: docs/ui/upload-v2-rollout-runbook.md
  • Feed rollout and rollback: docs/feed-rollout-runbook.md

No automatic tuning is enabled in this phase.

Skinbase uses asynchronous AI tagging via AutoTagArtworkJob. The job calls external vision services (CLIP and optional YOLO), normalizes tags, and attaches them through TagService as AI tags with confidence values.

Critical Safety Rule

⚠️ Publish must never depend on vision services.

  • Upload/publish flow dispatches AI tagging to queue after publish work.
  • Vision failures, timeouts, or service outages must not block artwork publish.
  • If AI tagging fails, artwork remains published and can be tagged later (retry/manual/batch).

Environment Variables (Vision)

Set these in .env (all are optional; defaults are in config/vision.php):

Global

  • VISION_ENABLED (default: true)
    • Master switch for all AI auto-tagging.
  • VISION_QUEUE (default: default)
    • Queue name used by AutoTagArtworkJob.
  • VISION_IMAGE_VARIANT (default: md)
    • Derivative variant sent to vision services (e.g. md, lg).

CLIP

  • CLIP_BASE_URL (default: empty)
    • Base URL for CLIP service (example: https://clip.internal).
    • If empty, CLIP call is skipped.
  • CLIP_ANALYZE_ENDPOINT (default: /analyze)
    • Path appended to CLIP_BASE_URL.
  • CLIP_TIMEOUT_SECONDS (default: 8)
    • Request timeout for CLIP calls.
  • CLIP_CONNECT_TIMEOUT_SECONDS (default: 2)
    • Connection timeout for CLIP calls.
  • CLIP_HTTP_RETRIES (default: 1)
    • HTTP retry attempts for CLIP requests.
  • CLIP_HTTP_RETRY_DELAY_MS (default: 200)
    • Delay between CLIP retries.

YOLO (optional)

  • YOLO_ENABLED (default: true)
    • Enables YOLO integration.
  • YOLO_BASE_URL (default: empty)
    • Base URL for YOLO service. If empty, YOLO call is skipped.
  • YOLO_ANALYZE_ENDPOINT (default: /analyze)
    • Path appended to YOLO_BASE_URL.
  • YOLO_TIMEOUT_SECONDS (default: 8)
    • Request timeout for YOLO calls.
  • YOLO_CONNECT_TIMEOUT_SECONDS (default: 2)
    • Connection timeout for YOLO calls.
  • YOLO_HTTP_RETRIES (default: 1)
    • HTTP retry attempts for YOLO requests.
  • YOLO_HTTP_RETRY_DELAY_MS (default: 200)
    • Delay between YOLO retries.
  • YOLO_PHOTOGRAPHY_ONLY (default: true)
    • When true, YOLO is called only for artworks in photography content type.

Expected CLIP Response Format

CLIP /analyze should return tags as either a direct list or under tags / data:

[
  { "tag": "cyberpunk", "confidence": 0.42 },
  { "tag": "city", "confidence": 0.31 }
]

Also accepted:

{
  "tags": [
    { "tag": "cyberpunk", "confidence": 0.42 }
  ]
}

or

{
  "data": [
    { "tag": "cyberpunk", "confidence": 0.42 }
  ]
}

Expected YOLO Response Format

YOLO may return the same tag list format as CLIP, or object detections:

{
  "objects": [
    { "label": "person", "confidence": 0.91 },
    { "label": "camera", "confidence": 0.67 }
  ]
}

label values are converted to tags, confidence is preserved when present.

AutoTagArtworkJob Behavior

  • Calls CLIP /analyze when VISION_ENABLED=true and CLIP_BASE_URL is set.
  • Optionally calls YOLO based on YOLO_ENABLED and YOLO_PHOTOGRAPHY_ONLY.
  • Merges CLIP + YOLO tags and keeps highest confidence for duplicates.
  • Normalizes tags before attach (lowercase, cleanup, slug-safe format).
  • Uses TagService::attachAiTags() to store pivot data:
    • source = ai
    • confidence = <float|null>
  • Runs with queue retry + timeout safety (tries, backoff, timeout).
  • Logs failures with reference/context for troubleshooting.
  • On non-retriable response scenarios (e.g. 4xx), job exits safely without blocking publish.

Queue / Worker Requirements (VISION_QUEUE)

  • Ensure a worker is running for the configured queue.
  • Example worker command:
php artisan queue:work --queue=default
  • If VISION_QUEUE=vision, run worker for that queue:
php artisan queue:work --queue=vision
  • In production, use Supervisor/systemd/Horizon to keep workers alive.
  • Without an active worker, auto-tagging jobs remain queued and will not execute.

Local vs Production Notes

Local development

  • For fully offline local work, set VISION_ENABLED=false.
  • Or set only CLIP_BASE_URL/YOLO_BASE_URL you can reach locally.
  • Prefer short timeouts to avoid slow dev feedback loops.

Production

  • Use internal/private service endpoints for CLIP/YOLO when possible.
  • Keep conservative timeouts and low retry counts to prevent queue congestion.
  • Monitor failed jobs and logs for vision service reliability.
  • Scale queue workers based on upload volume and service latency.

Verify Setup (Health + Test Call)

After configuring env vars and restarting workers, verify in this order:

Quick helper (PowerShell):

pwsh -File ./scripts/vision-smoke.ps1

Optional flags:

pwsh -File ./scripts/vision-smoke.ps1 -EnvFile ".env" -SampleImageUrl "https://files.skinbase.org/img/aa/bb/cc/md.webp"
pwsh -File ./scripts/vision-smoke.ps1 -SkipAnalyze
  1. Confirm queue worker is consuming VISION_QUEUE.
php artisan queue:work --queue=default
  1. Check CLIP/YOLO health endpoints (replace host/port as needed):
curl -fsS "$CLIP_BASE_URL/health"
curl -fsS "$YOLO_BASE_URL/health"
  1. Make a direct analyze test call (CLIP example):
curl -X POST "$CLIP_BASE_URL$CLIP_ANALYZE_ENDPOINT" \
  -H "Content-Type: application/json" \
  -d '{"image_url":"https://files.skinbase.org/img/aa/bb/cc/md.webp"}'
  1. Trigger an upload/publish and confirm:
  • Publish response succeeds even if CLIP/YOLO is down.
  • AutoTagArtworkJob is queued/executed asynchronously.
  • AI tags appear on the artwork when services are healthy.
  • Failures are logged, but publish is unaffected.

License

The Laravel framework is open-sourced software licensed under the MIT license.

Description
No description provided
Readme 78 MiB
Languages
JavaScript 47.8%
HTML 32.6%
CSS 14.5%
PHP 2.7%
Blade 1.1%
Other 1.2%