Skip to content

masterlexus/RLHF_frontend

Repository files navigation

Neural RLHF Frontend

RSVP (Rapid Serial Visual Presentation) word flasher with LSL (Lab Streaming Layer) marker streaming for EEG experiments. Displays words one at a time with frame-accurate timing and pushes timestamped markers to LSL for downstream EEG epoch-locking.

Built for the London Neurotech Hackathon 2026 as part of the Neural RLHF system — using EEG-derived preference signals (N400, P300) for real-time language model evaluation.

How It Works

                 ┌─────────────┐
  --sentence     │  RSVP Loop  │    LSL "RSVPMarkers"
  --llm claude ──┤  (PsychoPy) ├──► outlet ──► LabRecorder / EEG pipeline
                 │  60Hz vsync │
                 └─────────────┘

  Per word:
    1. Set text, pre-draw to back buffer
    2. win.flip()  — blocks until vsync, word appears on screen
    3. push_sample() — LSL marker pushed (sub-ms after flip)
    4. Hold for remaining frames of word duration

Each word is shown for 500 ms followed by a 200 ms inter-word blank (~1.43 words/sec). Timing is frame-based, not clock-based, for sub-millisecond precision.

Requirements

  • Python 3.10 (required by psychopy-lib, pinned in .python-version)
  • uv package manager
  • Linux (X11) or Windows — macOS compositor introduces erratic frame timing

Installation

git clone <repo-url> && cd Neural_RLFH_frontend

# Install locked dependencies
uv sync

# Install PsychoPy without its heavy GUI deps (wxPython, PyQt6, psychtoolbox)
uv pip install psychopy-lib --no-deps

Verify the install

uv run python -c "from psychopy import visual, core, event; from pylsl import StreamInfo, StreamOutlet; print('OK')"

Warnings about missing psychtoolbox, wx, PyQt6, or ALSA are harmless and expected.

Usage

Present a sentence

uv run rsvp --sentence "The model generated a surprisingly coherent response"

Fetch text from an LLM and present it

# Requires ANTHROPIC_API_KEY or OPENAI_API_KEY in environment
uv run rsvp --llm claude --prompt "Explain in one sentence why the sky is blue."
uv run rsvp --llm chatgpt --prompt "What is neuroplasticity?"

Claude responses are automatically constrained to a single sentence (≤20 words) for RSVP suitability. Missing or invalid API keys produce clear error messages instead of raw tracebacks.

Windowed mode (debugging only)

uv run rsvp --no-fullscreen --sentence "Testing in a window"

Always use fullscreen for real data collection — windowed mode adds compositor jitter.

Monitor LSL markers (separate terminal)

uv run rsvp-receiver

Prints every marker as it arrives and reports inter-word jitter statistics after each sentence.

Drain buffered LSL markers

uv run python lsl_drain.py

Resolves the RSVPMarkers stream, drains all buffered samples, and exits. Useful for cleanup between runs.

Press Escape at any time to abort the presentation.

LSL Marker Format

All markers are pushed to the RSVPMarkers LSL stream (type Markers).

Marker When
FIXATION Fixation cross onset
SENTENCE_START Before word loop
idx:word:total Each word onset (e.g., 3:neural:12)
SENTENCE_END After word loop
ABORTED User pressed Escape

Package Structure

neural_rlfh_frontend/
    __init__.py          # Package marker, version string
    config.py            # All constants
    cli.py               # parse_args()
    llm.py               # fetch_from_claude, fetch_from_chatgpt, get_sentence
    lsl.py               # create_lsl_outlet, push_marker, receive_markers
    display.py           # create_window, measure_frame_rate, create_stimuli
    presentation.py      # show_fixation, present_words, present_chunks
    timing.py            # report_timing (post-hoc jitter stats)
    main.py              # main() entry point — orchestrates everything

Root-level rsvp_flasher.py and lsl_receiver.py are thin shims for backwards compatibility.

CLI Entry Points

Defined in pyproject.toml:

Command Target Description
uv run rsvp neural_rlfh_frontend.main:main Run the RSVP flasher
uv run rsvp-receiver neural_rlfh_frontend.lsl:receive_markers Monitor LSL markers

Configuration

All constants are in neural_rlfh_frontend/config.py:

Constant Default Description
WORD_DURATION_SEC 0.5 Word display duration (seconds)
BLANK_DURATION_SEC 0.2 Inter-word blank duration (seconds)
FIXATION_SEC 0.5 Fixation cross duration (seconds)
FONT_HEIGHT 0.12 PsychoPy height units (~7 deg visual angle)
FONT_COLOR white Stimulus color
BG_COLOR black Background color
FULLSCREEN True Fullscreen by default
LSL_STREAM_NAME RSVPMarkers LSL stream name

Downstream Integration

This package is a pure stimulus presenter. It outputs LSL markers consumed by:

  • LabRecorder — records all LSL streams to XDF
  • LSL sync hub — subscribes by stream name/type
  • Streamlit dashboard — reads RSVPMarkers for live word display
  • MNE-Python — epochs EEG data around word onset markers (tmin=-0.2, tmax=0.8)

Timing Notes

  • All timing is frame-based (vsync-locked win.flip() calls), never time.sleep()
  • At 60 Hz: 30 frames per word (500 ms) + 12 frames blank (200 ms) = 700 ms per cycle
  • LSL markers are pushed after win.flip() returns (flip is what puts the pixel on screen)
  • The presentation loop runs entirely on the main thread (PsychoPy's OpenGL context is not thread-safe)
  • Post-presentation timing report shows dropped frames and marker jitter

License

MIT

About

The frontend responsible for generating an llm response and displaying the timed fragmented response for the user

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages