Timer Functionality for Autonomous Speech in ChocolateLMLite: Complete Implementation Guide
ChocolateLMLite automatically generates AI speech at configurable intervals through a background timer that injects system messages into the active persona's conversation history when EnableTimerGenerate is enabled.
ChocolateLMLite includes a sophisticated timer system that enables autonomous speech generation without user prompts. This feature, implemented in the gpsnmeajp/chocolatelmlite repository, allows the LLM to initiate conversations periodically based on settings defined in FileManager.cs and executed through the Persona class background loop.
Core Components of the Timer System
The autonomous speech feature relies on four key configuration values managed by FileManager.cs and consumed by the periodic task runner.
Global Toggle and Limits in FileManager.cs
EnableTimer(lines 178-188): The master switch stored insettings.yamlthat activates the entire timer functionality across all personas.TimerGenerateLimitMax(lines 101-102): A safety upper bound preventing runaway generation by limiting consecutive timer-triggered messages.TimerGenerateMessage(lines 101-103): The template text wrapped in<system>tags that precedes each autonomous generation.
Per-Persona Configuration
TimerCycleMinutes(lines 21-22): Defines the interval between autonomous speeches for each individual persona, stored inpersona_<id>.yaml.
Execution Engine
PerformPeriodicTasksinPersona.cs(lines 33-45): The core background method executed every second that checks timing conditions and triggers generation.
How the Timer Works Internally
The autonomous speech system operates through a continuous background loop that evaluates time-based conditions against the active persona's settings.
The Background Execution Loop
Every second, the Persona.PerformPeriodicTasks method performs the following validation sequence:
- Checks if
generalSettings.EnableTimerGenerateis true. - Retrieves the active persona configuration via
fileManager.GetActivePersonaSettings(). - Verifies that
TimerCycleMinutesis greater than zero. - Calculates the next scheduled execution time:
var nextGeneratedAt = lastGeneratedAt.AddMinutes(activePersonaSettings.TimerCycleMinutes);
This logic appears at lines 34-40 of Persona.cs.
Message Generation and Injection
When DateTime.UtcNow exceeds nextGeneratedAt, the timer fires and executes the following sequence (lines 63-70 of Persona.cs):
- Respects the
TimerGenerateLimitMaxconstraint to prevent excessive consecutive generations. - Skips execution if the LLM is already processing (
llm.IsGenerating()returns true). - Constructs a system entry using the configured message template:
string tt = $"<system>{fileManager.generalSettings.TimerGenerateMessage}</system>";
- Inserts the entry into the active persona's talk history with
TalkRole.ChocolateLMand the current Unix timestamp. - Invokes
llm.GenerateResponseAsync()to produce the autonomous speech. - Updates counters (
lastGeneratedAt,consecutiveTimerGenerations) for the next interval calculation.
Configuring Autonomous Speech
You can enable and customize timer-generated speech through YAML configuration files or the web interface defined in static/js/setting.js.
YAML Configuration (Manual)
Edit data/settings.yaml for global settings and data/persona_1.yaml (or your specific persona file) for interval definitions:
# data/settings.yaml
EnableTimerGenerate: true
TimerGenerateLimitMax: 30
TimerGenerateMessage: "タイマーイベント: 自由に独り言を言ったり、ツールを呼び出したりすることが出来ます。"
# data/persona_1.yaml
TimerCycleMinutes: 10
Web Interface Configuration
The front-end exposes these controls through static/js/setting.js (lines 151-164):
- Navigate to Settings → General.
- Enable Timer Generate to set
EnableTimerGenerateto true. - Enter the desired interval in the Timer Cycle (min) field to set
TimerCycleMinutes. - Click Save to persist values through the
/api/general/settingendpoint.
REST API Updates
Update timer settings programmatically via the active persona endpoint:
curl -X POST http://localhost:8010/api/persona/active/setting \
-H "Content-Type: application/json" \
-d '{
"timer_cycle_minutes": 15,
"enable_timer_generate": true
}'
This modifies the active persona's YAML file immediately; the background task will begin emitting messages every 15 minutes.
Implementation Code Examples
Minimal C# Timer Check Logic
The following snippet replicates the core validation logic from Persona.cs:
if (generalSettings.EnableTimerGenerate && activePersonaSettings.TimerCycleMinutes > 0)
{
var next = lastGeneratedAt.AddMinutes(activePersonaSettings.TimerCycleMinutes);
if (DateTime.UtcNow >= next && !llm.IsGenerating())
{
// Insert system entry
var sys = $"<system>{generalSettings.TimerGenerateMessage}</system>";
var entry = new TalkEntry {
Role = TalkRole.ChocolateLM,
Text = sys,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
await fileManager.UpsertTalkHistoryToActivePersona(entry);
await llm.GenerateResponseAsync(); // autonomous speech generation
}
}
Configuration File Structure
The timer system relies on specific YAML structures parsed by FileManager.cs:
- Global settings (
settings.yaml): ContainsEnableTimerGenerate,TimerGenerateLimitMax, andTimerGenerateMessage. - Persona files (
persona_*.yaml): ContainsTimerCycleMinutesand webhook settings.
Summary
- EnableTimerGenerate in
settings.yamlacts as the master switch for autonomous speech. - TimerCycleMinutes controls the interval between generations for each persona.
- PerformPeriodicTasks in
Persona.csexecutes every second to evaluate timing conditions. - TimerGenerateLimitMax prevents infinite generation loops by capping consecutive calls.
- The system injects TimerGenerateMessage wrapped in
<system>tags to trigger LLM responses without user input. - Configuration persists through YAML files or the web interface at
static/js/setting.js.
Frequently Asked Questions
How do I completely disable the autonomous speech timer?
Set EnableTimerGenerate: false in data/settings.yaml or uncheck the Enable Timer Generate option in the web UI. This prevents PerformPeriodicTasks from evaluating timer conditions entirely.
What happens if the LLM is still generating when the timer fires?
The timer checks llm.IsGenerating() before creating new entries. If generation is in progress, the timer skips the current cycle and rechecks after one second, ensuring no overlapping requests occur.
Can different personas have different autonomous speech intervals?
Yes. TimerCycleMinutes is stored per persona in individual persona_<id>.yaml files. When you switch active personas, fileManager.GetActivePersonaSettings() loads the specific interval for that personality.
What is the purpose of the <system> tag in TimerGenerateMessage?
The code at lines 63-70 of Persona.cs wraps TimerGenerateMessage in <system> tags to mark the entry as a system instruction rather than user input. This distinguishes timer-generated prompts from actual conversation, allowing the LLM to recognize it as an autonomous trigger event.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →