FlexTime-AI / README.md
HNS8273's picture
fix: git merge conflict markers in README preventing HF space parsing
2be4d4d
|
Raw
History Blame Contribute Delete
11.5 kB
metadata
title: FlexTime AI
emoji: ๐Ÿ•
colorFrom: green
colorTo: blue
sdk: docker
app_port: 7860
tags:
  - openenv
  - scheduling
  - reinforcement-learning
  - workforce
  - optimization
  - real-world
pinned: false
license: mit
short_description: Real-world AI workforce scheduling OpenEnv environment.

๐Ÿ• FlexTime โ€” AI Workforce Scheduling Environment

OpenEnv Python FastAPI License: MIT


FlexTime is a fully-featured OpenEnv-compliant environment where AI agents learn to solve the real-world workforce scheduling problem: assigning employees to shifts while satisfying hard operational constraints (skill matching, availability, maximum hours) and optimizing soft objectives (fairness, employee preferences, demand coverage).

This is a problem that operations managers in retail, healthcare, logistics, and hospitality face every week โ€” affecting millions of workers worldwide. FlexTime models it faithfully, making it both a genuine benchmark and a practical tool for developing AI scheduling assistants.


๐Ÿ—บ Environment Overview

Property Value
Domain Workforce Scheduling / Operations Research
Tasks 3 (Easy โ†’ Medium โ†’ Hard)
Episode horizon 20 / 60 / 120 steps
Reward Dense, shaped (โ€“1.0 to +1.0)
Action space Discrete: assign, remove, swap, noop
Observation Structured JSON: employees, shifts, assignments, metrics
Constraints 4 hard (H1โ€“H4) + 4 soft (S1โ€“S4)
Baseline agent Greedy (rule-based) + LLM (OpenAI API)

โšก Quick Start

Docker

git clone https://huggingface.co/spaces/your-org/flextime
cd flextime

docker build -t flextime .
docker run -p 7860:7860 flextime

# Environment live at http://localhost:7860

Local (no Docker)

pip install -r requirements.txt
uvicorn app.main:app --host 0.0.0.0 --port 7860 --reload

Run Baseline

# Rule-based greedy baseline (no API key needed)
python -m scripts.baseline

# LLM-based baseline (requires OpenAI key)
export OPENAI_API_KEY=sk-...
python -m scripts.baseline --llm --model gpt-4o-mini

# Single task
python -m scripts.baseline --task task_hard

๐Ÿ“‹ Task Definitions

Task 1 โ€” Basic Shift Coverage (Easy)

Target score: 1.0

Assign employees to 5 open morning shifts for a single day. All employees are available; skills match every shift. The agent needs to fill all slots without creating overlaps.

Employees: 5    Shifts: 5    Max steps: 20
Constraints: H1 (no overlap), H2 (skill match)

Task 2 โ€” Weekly Schedule with Constraints (Medium)

Target score: 0.85

Build a complete weekly schedule for 8 employees across 30 shifts. Must respect:

  • Skill requirements (cashier, supervisor, inventory)
  • Availability windows per employee per day
  • 40-hour maximum working week per employee
  • Soft fairness distribution (ยฑ4h between min/max hours)
Employees: 8    Shifts: 30    Max steps: 60
Constraints: H1โ€“H4 (hard) + S1โ€“S2 (soft)

Task 3 โ€” Fair Optimization Under Pressure (Hard)

Target score: 0.75 on ALL sub-metrics simultaneously

The hardest task: 12 employees, 50 shifts, 3 pre-seeded conflicts to resolve. The grader applies a joint threshold โ€” all of coverage, fairness, constraint satisfaction, and demand satisfaction must exceed 0.75 simultaneously. Missing any single threshold triggers a penalty: score = min(sub_scores) ร— 0.9.

Employees: 12    Shifts: 50    Max steps: 120
Constraints: H1โ€“H4 + S1โ€“S4 (all 8 active)
Pre-seeded conflicts: 3
Skills: 5 (cashier, supervisor, inventory, customer_service, technician)

๐Ÿ”Œ API Reference

All endpoints are OpenEnv-compliant. Interactive docs at /docs.

Core Endpoints

POST /reset

Initialize or reset the environment for a given task.

// Request
{ "task_id": "task_medium", "seed": 42 }

// Response: Observation
{
  "week_id": "week-a3f912",
  "task_id": "task_medium",
  "employees": [...],
  "shifts": [...],
  "assignments": [],
  "unassigned_shifts": ["shf001", "shf002", ...],
  "conflicts": [],
  "metrics": {
    "total_shifts": 30,
    "assigned_shifts": 0,
    "coverage_rate": 0.0,
    "hard_violations": 0,
    "fairness_delta": 0.0,
    "fairness_score": 1.0,
    "demand_satisfaction": 0.0,
    "preference_satisfaction": 0.0
  },
  "done": false,
  "step_count": 0,
  "max_steps": 60
}

POST /step

Apply an action. Returns new observation, shaped reward, done flag, and info dict.

// Request โ€” Action
{
  "action_type": "assign",       // assign | remove | swap | noop
  "employee_id": "emp001",       // required for assign/remove/swap
  "shift_id": "shf042",          // required for assign/remove
  "target_employee_id": "emp005" // required for swap only
}

// Response: StepResult
{
  "observation": { ... },        // full Observation
  "reward": {
    "total": 0.15,
    "components": {
      "shift_covered": 0.225,
      "demand_signal": 0.043,
      "constraint_violated": 0.0,
      "fairness": 0.02
    },
    "info": { "result": "action=assign, ฮ”coverage=+0.033, hard_violations=0" }
  },
  "done": false,
  "info": { "step": "1", "coverage": "0.033" }
}

GET /state

Returns current observation without changing state.

GET /tasks

Returns all tasks with descriptions, schemas, and expected difficulty.

GET /grader

Returns normalized score (0.0โ€“1.0) for the current episode.

{
  "task_id": "task_medium",
  "score": 0.842,
  "breakdown": {
    "coverage_score": 0.9333,
    "fairness_score": 0.875,
    "constraint_score": 0.9,
    "demand_score": 0.891,
    "preference_score": 0.723
  },
  "passed": true,
  "summary": "Score 0.8420 (PASS) โ€” Coverage 93.3%, Fairness 87.5%, Hard violations: 1"
}

POST /baseline

Runs the baseline agent against all 3 tasks and returns reproducible scores.

// Query param: ?use_llm=true (requires OPENAI_API_KEY)
{
  "model": "GreedyBaseline",
  "results": [...],
  "mean_score": 0.717,
  "timestamp": "2026-03-30T12:00:00Z"
}

๐Ÿ“ฆ Observation Space

class Observation(BaseModel):
    week_id: str                    # Unique episode identifier
    task_id: str                    # Which task is active
    employees: List[Employee]       # Full roster with skills, availability, hours
    shifts: List[Shift]             # All shifts with day, period, skill, demand
    assignments: List[Dict]         # Active assignments [{employee_id, shift_id, hours}]
    unassigned_shifts: List[str]    # Shift IDs still needing coverage
    conflicts: List[ConstraintViolation]  # Active violations with type and severity
    metrics: ScheduleMetrics        # Computed KPIs
    done: bool                      # Episode termination flag
    step_count: int
    max_steps: int

๐ŸŽฎ Action Space

class Action(BaseModel):
    action_type: Literal["assign", "remove", "swap", "noop"]
    employee_id: Optional[str]       # emp001 ... emp012
    shift_id: Optional[str]          # shf001 ... shf050
    target_employee_id: Optional[str] # For swap only

๐Ÿ† Reward Function

Dense shaped reward at every step. Range: [โ€“1.0, +1.0]

Component Signal Value
shift_covered New shift filled +0.15 ร— ฮ”coverage
demand_signal Demand-weighted coverage +0.05 ร— ฮ”demand
constraint_violated New hard violation โ€“0.20 per violation
constraint_resolved Violation removed +0.10 per resolution
fairness Fairness score improved +0.03 ร— ฮ”fairness
conflict_resolved Pre-seeded conflict fixed +0.10 bonus
invalid_action Non-existent IDs, etc. โ€“0.05
noop No-operation 0.0

๐Ÿ”’ Constraint System

Hard Constraints (must not be violated)

ID Name Description
H1 No Overlapping Shifts One shift per employee per (day, period)
H2 Skillโ€“Role Match Employee skills โЇ shift required_skill
H3 Max Hours ฮฃ hours โ‰ค max_hours_per_week (typically 40h)
H4 Availability Employee must be available on shift day

Soft Constraints (penalized in objective)

ID Name Description
S1 Fair Distribution max(hours) โ€“ min(hours) โ‰ค 4h
S2 Preference Matching Assign preferred shift period when possible
S3 Min 11h Rest Gap โ‰ฅ 11h between consecutive shifts
S4 Consecutive Days โ‰ค 5 consecutive working days

๐Ÿ“Š Baseline Scores

Reproducible scores with seed=42, GreedyBaseline agent:

Task Score Steps Pass?
task_easy ~0.95 ~5 โœ…
task_medium ~0.72 ~28 โŒ (target 0.85)
task_hard ~0.48 ~52 โŒ (target 0.75)
Mean ~0.72

The gap between greedy (0.72) and target (0.85) on medium/hard provides excellent learning signal for RL agents.


๐Ÿงช Testing

pip install pytest pytest-asyncio httpx

# Run all tests
pytest tests/ -v

# Run specific test class
pytest tests/test_environment.py::TestSpecCompliance -v

# Run with coverage
pytest tests/ --cov=app --cov-report=html

๐Ÿ“ Project Structure

flextime/
โ”œโ”€โ”€ app/
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ”œโ”€โ”€ main.py          # FastAPI application, all endpoints
โ”‚   โ”œโ”€โ”€ engine.py        # Core environment: state, step, reward, grader
โ”‚   โ”œโ”€โ”€ models.py        # Pydantic typed models (Observation, Action, Reward)
โ”‚   โ””โ”€โ”€ static/
โ”‚       โ””โ”€โ”€ index.html   # Interactive demo UI
โ”œโ”€โ”€ scripts/
โ”‚   โ”œโ”€โ”€ __init__.py
โ”‚   โ””โ”€โ”€ baseline.py      # Greedy + LLM baseline agents & CLI
โ”œโ”€โ”€ tests/
โ”‚   โ””โ”€โ”€ test_environment.py  # Full pytest test suite
โ”œโ”€โ”€ openenv.yaml         # OpenEnv spec metadata
โ”œโ”€โ”€ requirements.txt
โ”œโ”€โ”€ Dockerfile
โ””โ”€โ”€ README.md

๐Ÿค— HuggingFace Spaces Deployment

  1. Create a new HF Space: Docker template, tagged openenv
  2. Push this repo to the Space
  3. The Space will auto-build and serve at https://huggingface.co/spaces/your-org/flextime

The HF Space will automatically:

  • Build the Docker image
  • Expose port 7860
  • Respond to reset() pings for OpenEnv validation

๐Ÿง  Why Workforce Scheduling?

Workforce scheduling is a genuine, high-value operations problem:

  • Scale: Affects billions of shift workers globally (retail, healthcare, logistics)
  • Complexity: Multi-constraint combinatorial optimization (NP-hard in general)
  • Real cost: Poor scheduling โ†’ $millions in overtime, turnover, and burnout
  • AI gap: Existing tools are rule-based; LLM/RL agents could outperform dramatically
  • Fairness stakes: Biased scheduling has real worker welfare consequences

FlexTime provides the first OpenEnv environment in this domain, enabling the community to benchmark and train agents on a problem with immediate real-world deployment value.


๐Ÿ“„ License

MIT ยฉ FlexTime Team