Skip to content

itaisinai/track-lab

Repository files navigation

Track Lab

Track Lab is an AI-powered music intelligence workstation for DJs, producers, and music researchers. It enriches track metadata, searches for remix candidates, and keeps each investigation grounded in provider evidence from sources such as Spotify, GetSongBPM, Beatport, SoundCloud, Wikipedia, and saved results.

The system combines deterministic provider pipelines with focused LLM judgement: providers fetch evidence, application code owns orchestration and persistence, and AI helps classify ambiguous dance-music signals, synthesize metadata, judge remix relevance, and drive the track-focused chat assistant.

Structure

  • apps/api - Express API.
  • apps/web - React/Vite UI.
  • packages/agent-chat - AI chat orchestration, session routing, and user-facing tool execution for track analysis and remix search.
  • packages/metadata-enrichment - metadata enrichment strategy, provider planning, provider evidence merging, and AI-assisted metadata synthesis.
  • packages/providers - external data providers. Providers fetch evidence only.
  • packages/remix-search - Remix discovery providers, deterministic scoring, and compact LLM judging.
  • packages/datastore - Prisma/PostgreSQL datastore repositories for track results, remix results, agent sessions, and jobs.

Architecture

The canonical architecture diagram is docs/system-architecture.md.

Pipeline Strategy

The current strategy doc is packages/track-analysis/src/strategy/pipeline-strategy.md.

The workflow graph is packages/track-analysis/src/strategy/pipeline-workflow.md.

The agent workflow graph is docs/agent-workflow.md.

flowchart TD
  A[User request] --> B[Normalize request]
  B --> C{Operation}

  C -->|analyze / enrich| D[Load known metadata]
  D --> E[Run Spotify identity search]
  E --> F[Run GetSongBPM if BPM or key is missing]
  F --> G{Deterministic EDM signals clear?}
  G -->|yes| H[Run Beatport + SoundCloud in code]
  G -->|no| I[Skip EDM providers]
  G -->|ambiguous| J[Ask LLM EDM classifier]
  J --> H
  H --> K[Normalize / dedupe evidence]
  I --> K
  K --> L[metadata synthesis]
  L --> M[Final metadata]

  C -->|remix_search| N[Resolve original track]
  N --> O[Build remix queries]
  O --> P[Run remix providers in code]
  P --> Q[Deterministic scoring + dedupe]
  Q --> R[Compact LLM judge batches]
  R --> S{Batch yielded results?}
  S -->|yes| T[Select final candidates]
  S -->|no| U[Try one more batch]
  U --> V{Any candidates?}
  V -->|yes| T
  V -->|no| W[Deterministic fallback]

  M --> X[Store result / review queue]
  T --> X
  W --> X
Loading

The LLM appears only at decision points where deterministic code is not enough: ambiguous EDM provider planning, metadata synthesis from evidence, remix candidate judging, and the conversational agent's user-facing tool selection. Provider-specific calls remain inside application code.

Run

yarn install
yarn dev
yarn dev:web

Docker setup: docker/README.md

Docker Development

Run the full local stack with:

yarn dev:docker

This starts the API, worker, web app, PostgreSQL, and the one-shot migration service that applies Prisma migrations.

Useful checks:

yarn typecheck
yarn workspace @track-lab/web build

Environment

Create .env in the repo root from .env.example.

cp .env.example .env

Set DATABASE_URL to the real connection string you want everywhere, such as your RDS URL with sslmode=no-verify. QUEUE_PROVIDER=database keeps the current database-backed job queue. QUEUE_PROVIDER=sqs uses AWS SQS for job delivery, while track_analysis_jobs remains the source of truth for job state and history. When using QUEUE_PROVIDER=sqs from Docker, mount your host ~/.aws directory into the api and worker containers and set AWS_PROFILE in .env so the SDK can read your local AWS credentials or SSO cache.

Provider credentials are optional for local wiring, but real enrichment quality depends on them. OPENAI_API_KEY enables the AI-backed chat assistant, ambiguous EDM planning, metadata synthesis, and remix judging. Without it, deterministic wiring can still run locally, but AI-backed paths will be unavailable or degraded. Beatport currently uses public web search only and may be blocked by Cloudflare. SoundCloud remix discovery can use SoundCloud's public search fallback directly. SEARXNG_SEARCH_URL is optional and can provide another free search source.

Run SearXNG locally with Docker:

docker run --rm -p 8080:8080 searxng/searxng

Persistence

Saved results are stored in PostgreSQL through Prisma by default. The datastore uses normalized Title + Artists keys for uniqueness.

DATASTORE_PROVIDER=prisma
DATABASE_URL=postgresql://track_lab:track_lab@your-rds-endpoint:5432/track_lab?sslmode=no-verify

Worker Jobs

Background work is stored in the track_analysis_jobs datastore table and exposed as TrackAnalysisJob API objects.

Job operations:

  • analyze - enrich track metadata, preferring saved datastore results.
  • enrich - fresh metadata enrichment using optional known metadata.
  • remix_search - find remix candidates for a track or Spotify URL.

Job statuses:

  • queued - waiting for the worker to claim it.
  • processing - claimed by the worker; attemptCount has been incremented.
  • completed - worker stored a JSON result and completedAt.
  • failed - reserved retryable failure status.
  • dead_lettered - exhausted maxAttempts; stores errorMessage and completedAt.

Job payloads:

type TrackAnalysisPayload =
  | {
      operation: "analyze" | "enrich";
      track: { title: string; artists: string };
      knownMetadata?: {
        album?: string | null;
        bpm?: number | null;
        genre?: string | null;
        subGenre?: string | null;
        key?: string | null;
        spotifyUrl?: string | null;
      };
      source?: "manual" | "saved_result" | "bulk_saved_results";
    }
  | {
      operation: "remix_search";
      request: {
        title?: string | null;
        artists?: string | null;
        spotifyUrl?: string | null;
        genre?: string | null;
      };
    };

Job API shape:

type TrackAnalysisJob = {
  id: number;
  operation: "analyze" | "enrich" | "remix_search";
  status: "queued" | "processing" | "completed" | "failed" | "dead_lettered";
  payload: TrackAnalysisPayload;
  result: unknown | null;
  errorMessage: string | null;
  attemptCount: number;
  maxAttempts: number;
  createdAt: string;
  updatedAt: string;
  completedAt: string | null;
  notificationReadAt: string | null;
  resolvedAt: string | null;
};

Persisted columns use snake_case names: id, operation, status, payload_json, result_json, error_message, attempt_count, max_attempts, created_at, updated_at, completed_at, notification_read_at, and resolved_at. The worker claims the oldest queued job, writes result_json on success, and requeues failures until attempt_count >= max_attempts.

Behavior

  • The chat assistant can understand natural language requests such as "analyze Strobe by deadmau5", "find bass remixes for this track", or "analyze Levels by Avicii". It keeps follow-ups in the current track-focused session and starts a new session when the user explicitly names a different track.
  • The metadata enrichment pipeline returns Title, Artists, Album, BPM, Genre, SubGenre, Key, summary, provider status, provider URLs, and errors.
  • AI-assisted synthesis turns provider evidence into a concise final metadata result while deterministic code keeps provider execution, dedupe, and storage predictable.
  • Remix search combines provider candidates, deterministic scoring, and compact LLM judging to identify likely remixes, edits, bootlegs, VIPs, flips, and reworks.
  • By default, enrichment checks saved results first.
  • The UI can skip saved results to force a fresh enrichment.
  • Saved results can be viewed, re-enriched, saved again, or removed.

Terminology

See docs/ai-terminology.md for the project naming rules. In short: providers fetch external evidence, planners decide execution paths, pipelines run multi-step flows, and tools are only capabilities directly callable by an LLM or agent orchestrator.

API

  • POST /track-analysis - enqueue analyze/enrich metadata work.
  • POST /agent - legacy compatibility alias for enqueueing analyze/enrich metadata work. New code should use /track-analysis.
  • POST /remix-search - search remix candidates.
  • GET /track-analysis/jobs - list worker jobs; supports status, unresolved=true, and unread=true.
  • GET /track-analysis/jobs/:id - get one worker job.
  • POST /track-analysis/jobs/:id/retry - requeue a failed or dead-lettered job.
  • POST /track-analysis/jobs/:id/resolve - mark a terminal job resolved.
  • POST /track-analysis/jobs/:id/notification-read - mark a job notification read.
  • GET /results - list saved results.
  • GET /results/:id - get one saved result.
  • POST /results - save an enrichment response.
  • POST /results/:id/enrich - fresh enrichment for a saved result.
  • DELETE /results/:id - remove a saved result.

Notes

The web action icons use inline SVGs from Heroicons, which is MIT licensed.

About

AI-powered music intelligence workstation built with TypeScript, AWS, PostgreSQL, Spotify, and OpenAI.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages