API Reference¶
Complete API documentation for LayerCode Gym.
Core Classes¶
LayercodeClient¶
The main client for running voice agent conversations.
Constructor¶
LayercodeClient(
simulator: UserSimulatorProtocol,
settings: Settings | None = None,
turn_callback: TurnCallback | None = None,
conversation_callback: ConversationCallback | None = None
)
Parameters:
simulator: UserSimulator instance that generates user responsessettings: Optional Settings object (defaults to environment variables)turn_callback: Optional callback called after each turnconversation_callback: Optional callback called at conversation end
Methods¶
run()¶
Run the conversation and return the conversation ID.
Returns: str - Unique conversation ID
Example:
UserSimulator¶
Factory class for creating user simulators.
Factory Methods¶
from_text()¶
@classmethod
def from_text(
cls,
messages: list[str],
send_as_text: bool = True,
tts_engine: TTSEngineProtocol | None = None,
settings: Settings | None = None
) -> UserSimulatorProtocol
Create a simulator with fixed text messages.
Parameters:
messages: List of text messages to sendsend_as_text: If True, send as text; if False, convert to audio via TTStts_engine: Optional custom TTS engine (auto-created if None and send_as_text=False)settings: Optional Settings object
Returns: UserSimulatorProtocol instance
Example:
from_files()¶
Create a simulator that streams pre-recorded audio files.
Parameters:
files: List of Path objects pointing to audio files (WAV or MP3)
Returns: UserSimulatorProtocol instance
Example:
from pathlib import Path
simulator = UserSimulator.from_files(
files=[Path("audio1.wav"), Path("audio2.wav")]
)
from_agent()¶
@classmethod
def from_agent(
cls,
persona: Persona | None = None,
agent: Agent | None = None,
deps: Any = None,
model: str = "openai:gpt-4o-mini",
max_turns: int = 5,
send_as_text: bool = False,
tts_engine: TTSEngineProtocol | None = None,
settings: Settings | None = None
) -> UserSimulatorProtocol
Create an AI-driven simulator with dynamic responses.
Parameters:
persona: Persona object defining user background and intentagent: Optional custom PydanticAI Agent (overrides persona)deps: Optional dependencies for custom agentmodel: Model name (e.g., "openai:gpt-4o-mini", "anthropic:claude-3-5-sonnet")max_turns: Maximum number of turns before endingsend_as_text: If True, send as text; if False, use TTStts_engine: Optional custom TTS enginesettings: Optional Settings object
Returns: UserSimulatorProtocol instance
Example:
simulator = UserSimulator.from_agent(
persona=Persona(
background_context="You are a busy executive",
intent="You want quick answers"
),
model="openai:gpt-4o-mini",
max_turns=5
)
Persona¶
Defines user persona for AI-driven simulators.
Constructor¶
Parameters:
background_context: Description of user's background, personality, and situationintent: User's goal or intent for the conversation
Example:
persona = Persona(
background_context="""
You are Sarah, a 35-year-old small business owner.
You're tech-savvy but busy.
""",
intent="You want to understand pricing and features."
)
Settings¶
Configuration settings for LayerCode Gym.
Constructor¶
Settings(
server_url: str,
agent_id: str,
tts_model: str = "gpt-4o-mini-tts",
tts_voice: str = "coral",
tts_instructions: str = "",
chunk_ms: int = 100,
chunk_interval: float = 0.0,
output_root: str = "./conversations"
)
Parameters:
server_url: Your backend server URLagent_id: Your Layercode agent IDtts_model: OpenAI TTS model nametts_voice: TTS voice (alloy, echo, fable, onyx, nova, shimmer, coral)tts_instructions: Optional voice instructionschunk_ms: Audio chunk size in millisecondschunk_interval: Delay between chunks in secondsoutput_root: Directory for saving conversation results
Example:
settings = Settings(
server_url="http://localhost:8001",
agent_id="your_agent_id",
tts_voice="alloy"
)
Environment Variables:
Settings can be loaded from environment variables:
SERVER_URL="http://localhost:8001"
LAYERCODE_AGENT_ID="your_agent_id"
OPENAI_TTS_MODEL="gpt-4o-mini-tts"
OPENAI_TTS_VOICE="coral"
OPENAI_TTS_INSTRUCTIONS="Speak clearly"
LAYERCODE_CHUNK_MS="100"
LAYERCODE_CHUNK_INTERVAL="0.0"
LAYERCODE_OUTPUT_ROOT="./conversations"
LOGFIRE_TOKEN="..." # Optional: enable LogFire observability
Callbacks¶
create_judge_callback()¶
from layercode_gym.callbacks import create_judge_callback
def create_judge_callback(
criteria: list[str],
model: str = "openai:gpt-4o"
) -> TurnCallback
Create an LLM-as-judge callback for evaluating conversation quality.
Parameters:
criteria: List of evaluation criteria as questionsmodel: Model to use for evaluation (e.g., "openai:gpt-4o", "anthropic:claude-3-5-sonnet")
Returns: TurnCallback function
Example:
judge = create_judge_callback(
criteria=[
"Did the agent answer the user's question?",
"Was the agent polite?"
],
model="openai:gpt-4o"
)
client = LayercodeClient(
simulator=simulator,
turn_callback=judge
)
TurnCallback¶
from typing import Callable, Awaitable
TurnCallback = Callable[
[int, str, str, str],
Awaitable[None]
]
Callback function called after each conversation turn.
Parameters:
turn_number: int- Current turn number (0-indexed)user_message: str- User's message textagent_message: str- Agent's response textconversation_id: str- Unique conversation ID
Example:
async def my_callback(
turn_number: int,
user_message: str,
agent_message: str,
conversation_id: str
) -> None:
print(f"Turn {turn_number}: User said '{user_message}'")
ConversationCallback¶
from typing import Callable, Awaitable
from layercode_gym.models import ConversationLog
ConversationCallback = Callable[
[ConversationLog],
Awaitable[None]
]
Callback function called at the end of a conversation.
Parameters:
conversation_log: ConversationLog- Complete conversation data
Example:
async def my_callback(log: ConversationLog) -> None:
print(f"Conversation {log.conversation_id} complete")
print(f"Total turns: {log.stats['total_turns']}")
Protocols¶
UserSimulatorProtocol¶
Protocol for implementing custom user simulators.
Methods¶
get_response()¶
Generate the next user response.
Parameters:
request: UserRequest- Contains conversation context
Returns: UserResponse or None to end conversation
Example:
from layercode_gym.simulator import (
UserSimulatorProtocol,
UserRequest,
UserResponse
)
class MySimulator(UserSimulatorProtocol):
async def get_response(
self,
request: UserRequest
) -> UserResponse | None:
if request.turn_number > 5:
return None # End conversation
return UserResponse(
text="My response",
audio_path=None,
data=()
)
TTSEngineProtocol¶
Protocol for implementing custom TTS engines.
Methods¶
synthesize()¶
Convert text to speech audio file.
Parameters:
text: str- Text to synthesize**kwargs- Additional parameters
Returns: Path - Path to generated audio file
Example:
from pathlib import Path
from layercode_gym.simulator import TTSEngineProtocol
class MyTTSEngine(TTSEngineProtocol):
async def synthesize(self, text: str, **kwargs) -> Path:
# Call your TTS service
audio_data = await my_tts_api.generate(text)
# Save to file
output_path = Path("output.wav")
output_path.write_bytes(audio_data)
return output_path
Data Models¶
UserRequest¶
Request data passed to user simulator.
Attributes:
agent_transcript: list[str]- All agent messages so farturn_number: int- Current turn number (0-indexed)conversation_id: str- Unique conversation ID
UserResponse¶
Response from user simulator.
Attributes:
text: str- User message textaudio_path: Path | None- Optional path to audio filedata: tuple- Additional data (reserved for future use)
ConversationLog¶
Complete conversation data.
Attributes:
conversation_id: str- Unique IDagent_id: str- Agent IDstarted_at: str- ISO timestampended_at: str- ISO timestampturns: list[Turn]- All conversation turnsstats: dict- Aggregate statistics
Stats Dictionary:
{
"total_turns": int,
"duration_seconds": float,
"avg_latency_ms": float,
"avg_ttfab_ms": float,
"total_user_words": int,
"total_agent_words": int
}
Turn¶
Single conversation turn.
Attributes:
turn_number: int- Turn indexuser_message: Message- User's messageagent_message: Message- Agent's response
Message¶
Single message in conversation.
Attributes:
text: str- Message texttimestamp: str- ISO timestampaudio_path: str | None- Optional audio file pathttfab_ms: int | None- Time to first audio byte (agent messages only)
Type Hints¶
LayerCode Gym is fully typed with mypy --strict compatibility.
from layercode_gym import (
LayercodeClient,
UserSimulator,
Persona,
Settings
)
from layercode_gym.simulator import (
UserSimulatorProtocol,
TTSEngineProtocol,
UserRequest,
UserResponse
)
from layercode_gym.callbacks import (
TurnCallback,
ConversationCallback,
create_judge_callback
)
from layercode_gym.models import (
ConversationLog,
Turn,
Message
)
Run type checks:
Next Steps¶
- Examples - See practical usage examples
- Advanced Usage - LogFire integration and custom implementations
- Concepts - Understand the architecture