A platform for researching real-time conversational AI interruptions. Connect with your researcher to test custom personas in a live voice environment.
secrets.json file in the root folder containing your OPENAI_API_KEY, ELEVEN_API_KEY, or GEMINI_API_KEY before running the server.
SET_SIZE personas forms one evaluation block. For example, if SET_SIZE = 3, personas 1-3 are compared in one survey, 4-6 in the next, etc. This means you typically list the same character multiple times with different interruption configs (one per turn-taking policy).
| Field | Required | Description |
|---|---|---|
name | Yes | Display name shown to testers. Use a suffix like "- Style A" to differentiate conditions in comparative mode. |
persona | Yes | The full system prompt: character description + scenario goal. Should encourage the tester to interrupt. |
quadrant | No | Metadata label for the Interpersonal Circumplex quadrant (e.g., "Q1_HighAgency_LowCommunion"). Not used by the system, but useful for your analysis. |
llm_provider | No | Override the global LLM provider for this persona. Values: "openai", "gemini". |
tts_provider | No | Override the global TTS provider. Values: "openai", "elevenlabs", "gemini". |
voice_id | No | Voice ID or name for the chosen TTS provider (e.g., "coral" for OpenAI, "Aoede" for Gemini, or an ElevenLabs voice ID). |
interruption_config | No | Per-persona override for interruption behavior. See interruption_config.json docs below for the full schema. |
{
"personas": [
{
"name": "Grumpy Tavern Keeper - Always Yield",
"persona": "You are a grumpy tavern keeper... Scenario: ...",
"quadrant": "Q1_HighAgency_LowCommunion",
"interruption_config": {
"MODE": "deterministic",
"STRATEGY_MATRIX": {
"COMPETITIVE": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0},
"COOPERATIVE": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0},
"TOPIC_CHANGE": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0},
"BACKCHANNEL": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0},
"TERMINATE": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0}
}
}
},
{
"name": "Grumpy Tavern Keeper - Probabilistic",
"persona": "You are a grumpy tavern keeper... Scenario: ...",
"interruption_config": {
"MODE": "probabilistic",
"STRATEGY_MATRIX": {
"COMPETITIVE": {"RESUME": 50, "BRIDGE": 15, "YIELD": 10, "OVERRIDE": 25}
}
}
},
{
"name": "Grumpy Tavern Keeper - Autonomous",
"persona": "You are a grumpy tavern keeper... Scenario: ...",
"interruption_config": { "MODE": "autonomous" }
}
]
}
Tip: In individual mode, each persona gets its own survey. Just list one entry per character with no suffix needed.
| Field | Default | Description |
|---|---|---|
MAX_TURNS | 5 | Number of user turns before the session auto-ends. The bot's greeting does not count. |
SURVEY_MODE | "written" | Reserved for future use (e.g., voice surveys). |
SURVEY_TYPE | "individual" | "individual" = one survey per persona. "comparative" = one survey after a set of SET_SIZE personas, comparing them side-by-side. |
SET_SIZE | 1 | How many consecutive personas form one evaluation block. Only used when SURVEY_TYPE = "comparative". |
SET_LABELS | [] | Display labels for each condition in the comparative survey (e.g., ["Style A", "Style B", "Style C"]). Must match SET_SIZE in length. |
SURVEY_QUESTIONS | [] | Array of question objects. See question types below. |
| Type | Mode | Description | Extra Fields |
|---|---|---|---|
matrix_likert | Comparative | Rate each condition (SET_LABEL) side-by-side on a scale. Renders as a matrix table. | allowed_options: scale values (e.g., ["1","2","3","4","5"]). reverse_coded: true to flag reverse-scored items. source: optional citation note. |
forced_choice | Comparative | Pick exactly one of the SET_LABELS as the best. Renders as radio buttons with the label names. | allowed_options: leave empty [], labels are auto-populated. |
options | Both | Select from a predefined list of choices. | allowed_options: array of string choices. |
number | Both | Numeric input, validated against allowed values. | allowed_options: valid number strings. |
text | Both | Free-form text response. Renders as a textarea. | None required. |
{
"MAX_TURNS": 5,
"SURVEY_TYPE": "individual",
"SET_SIZE": 1,
"SURVEY_QUESTIONS": [
{ "question": "How natural was the bot? (1-5)", "expected_type": "number", "allowed_options": ["1","2","3","4","5"] },
{ "question": "Did it stay in character?", "expected_type": "options", "allowed_options": ["yes", "no"] }
]
}
Example: Comparative mode (paper replication)
{
"MAX_TURNS": 5,
"SURVEY_TYPE": "comparative",
"SET_SIZE": 3,
"SET_LABELS": ["Style A", "Style B", "Style C"],
"SURVEY_QUESTIONS": [
{
"question": "The reaction felt human-like and natural.",
"expected_type": "matrix_likert",
"allowed_options": ["1","2","3","4","5"],
"source": "Reaction Naturalness"
},
{
"question": "Which style was best for this character?",
"expected_type": "forced_choice",
"allowed_options": []
},
{
"question": "Explain your choice.",
"expected_type": "text",
"allowed_options": []
}
]
}
llm_provider, tts_provider, and voice_id fields.
| Field | Default | Description |
|---|---|---|
LLM_PROVIDER | "gemini" | Default LLM provider for dialogue generation. Values: "openai", "gemini". |
OPENAI_MODEL | "gpt-4o-mini" | OpenAI model ID used when LLM_PROVIDER = "openai". |
GEMINI_MODEL | "gemini-2.5-flash" | Gemini model ID used when LLM_PROVIDER = "gemini". |
TTS_PROVIDER | "openai" | Default TTS provider for voice synthesis. Values: "openai", "elevenlabs", "gemini". |
OPENAI_TTS_MODEL | "gpt-4o-mini-tts" | OpenAI TTS model ID. |
OPENAI_DEFAULT_VOICE | "coral" | Default OpenAI voice name. |
GEMINI_TTS_MODEL | "gemini-2.5-flash-preview-tts" | Gemini TTS model ID. |
GEMINI_DEFAULT_VOICE | "Aoede" | Default Gemini voice name. |
ELEVENLABS_DEFAULT_VOICE_ID | "JBFqnCBsd6RMkjVDRZzb" | Default ElevenLabs voice ID. |
ELEVENLABS_MODEL_ID | "eleven_flash_v2_5" | ElevenLabs model ID. |
{
"LLM_PROVIDER": "gemini",
"OPENAI_MODEL": "gpt-4o-mini",
"GEMINI_MODEL": "gemini-2.5-flash",
"TTS_PROVIDER": "openai",
"OPENAI_TTS_MODEL": "gpt-4o-mini-tts",
"OPENAI_DEFAULT_VOICE": "coral",
"GEMINI_TTS_MODEL": "gemini-2.5-flash-preview-tts",
"GEMINI_DEFAULT_VOICE": "Aoede",
"ELEVENLABS_DEFAULT_VOICE_ID": "JBFqnCBsd6RMkjVDRZzb",
"ELEVENLABS_MODEL_ID": "eleven_flash_v2_5"
}
interruption_config field.
| Field | Description |
|---|---|
MODE |
Turn-taking policy mode. Determines how the strategy is selected:"autonomous" - The LLM dynamically selects its own strategy based on persona and context. The STRATEGY_MATRIX is ignored."probabilistic" - The system randomly samples a strategy from the STRATEGY_MATRIX based on the classified intent's probability weights."deterministic" - Same as probabilistic, but typically configured with 100% weights to force a single strategy (e.g., always yield).
|
INTENTS |
A dictionary mapping intent labels to natural-language descriptions used by the LLM classifier:"COMPETITIVE" - User contradicts, corrects, or overrides the agent."COOPERATIVE" - User adds relevant info or agrees without derailing."TOPIC_CHANGE" - User forcefully pivots to a new subject."BACKCHANNEL" - Short affirmations like "uh-huh", "right"."TERMINATE" - User wants to end the conversation.
|
STRATEGY_MATRIX |
For each intent, a dictionary of strategy weights (must sum to 100):"RESUME" (Hold) - Ignore the interruption, finish the current utterance."BRIDGE" (Soft Hold) - Briefly acknowledge user, then finish the thought."YIELD" (Switch) - Abandon current utterance, respond to user's new input."OVERRIDE" (Forced Hold) - Like RESUME but the user cannot interrupt the bot's response this turn. The bot still hears what the user said and processes it on the next turn.
|
{
"MODE": "deterministic",
"STRATEGY_MATRIX": {
"COMPETITIVE": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0},
"COOPERATIVE": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0},
"TOPIC_CHANGE": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0},
"BACKCHANNEL": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0},
"TERMINATE": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0}
}
}
Example: Literature-grounded probabilistic (Q1: High Agency / Low Communion)
{
"MODE": "probabilistic",
"STRATEGY_MATRIX": {
"COMPETITIVE": {"RESUME": 50, "BRIDGE": 15, "YIELD": 10, "OVERRIDE": 25},
"COOPERATIVE": {"RESUME": 15, "BRIDGE": 45, "YIELD": 30, "OVERRIDE": 10},
"TOPIC_CHANGE": {"RESUME": 30, "BRIDGE": 30, "YIELD": 20, "OVERRIDE": 20},
"BACKCHANNEL": {"RESUME": 80, "BRIDGE": 10, "YIELD": 0, "OVERRIDE": 10},
"TERMINATE": {"RESUME": 0, "BRIDGE": 0, "YIELD": 100, "OVERRIDE": 0}
}
}
Example: Autonomous LLM-driven
{
"MODE": "autonomous"
}
| Key | Required For | Description |
|---|---|---|
OPENAI_API_KEY | OpenAI LLM & TTS | Your OpenAI API key (starts with sk-proj-...). |
ELEVEN_API_KEY | ElevenLabs TTS + Transcription | Your ElevenLabs API key. Also used for speech-to-text transcription (Scribe v2). |
GEMINI_API_KEY | Gemini LLM & TTS | Your Google Gemini API key (starts with AIza...). |
{
"OPENAI_API_KEY": "sk-proj-...",
"ELEVEN_API_KEY": "sk_...",
"GEMINI_API_KEY": "AIza..."
}
Upload your modified JSON files here. Testers mapped to your account will automatically use them.
| Tester | Persona | Timestamp | Answers |
|---|
PROGRESS
Please read the following scenario carefully before starting.
When you are ready, click below to begin the conversation.