Skip to content

Commit fd57a15

Browse files
committed
simulate: condense verbose comments
1 parent 82de46c commit fd57a15

6 files changed

Lines changed: 51 additions & 100 deletions

File tree

cmd/lk/simulate.go

Lines changed: 14 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,7 @@ var simulateCommand = &cli.Command{
8888
}
8989

9090
// writeGeneratedScenariosTemp writes a generated run's scenarios to a temp
91-
// scenarios.yaml and returns its path, so the simulate command can print where
92-
// they landed (mirroring how it prints the agent log path). Returns "" when the
93-
// run carries no generated scenarios.
91+
// scenarios.yaml; "" when the run carries none.
9492
func writeGeneratedScenariosTemp(run *livekit.SimulationRun) (string, error) {
9593
group := run.GetScenarioGroup()
9694
if group == nil || len(group.GetScenarios()) == 0 {
@@ -137,9 +135,8 @@ func scenarioGroupToYAML(group *livekit.ScenarioGroup) ([]byte, error) {
137135
return yaml.Marshal(f)
138136
}
139137

140-
// scenariosFile mirrors a scenarios.yaml (the source of truth for scenarios).
141-
// It maps field-for-field onto livekit.ScenarioGroup; `userdata` is written as a
142-
// nested mapping here and JSON-encoded into the proto's string field.
138+
// scenariosFile mirrors a scenarios.yaml; `userdata` is a nested mapping here
139+
// and JSON-encoded into the proto's string field.
143140
type scenariosFile struct {
144141
Name string `yaml:"name"`
145142
Scenarios []yamlScenario `yaml:"scenarios"`
@@ -153,7 +150,6 @@ type yamlScenario struct {
153150
Userdata map[string]any `yaml:"userdata"`
154151
}
155152

156-
// simulateConfig holds all parameters needed to run a simulation in either TUI or CI mode.
157153
type simulateConfig struct {
158154
ctx context.Context
159155
client *lksdk.AgentSimulationClient
@@ -169,16 +165,13 @@ type simulateConfig struct {
169165
scenariosPath string // path to the --scenarios file (empty when generating from source)
170166
}
171167

172-
// simulateMode represents how scenarios are sourced.
173168
type simulateMode int
174169

175170
const (
176171
modeScenarios simulateMode = iota
177172
modeGenerateFromSource
178173
)
179174

180-
// loadScenarioGroup reads a scenarios.yaml into a livekit.ScenarioGroup, JSON-encoding
181-
// each scenario's nested `userdata` mapping into the proto's string field.
182175
func loadScenarioGroup(path string) (*livekit.ScenarioGroup, error) {
183176
data, err := os.ReadFile(path)
184177
if err != nil {
@@ -239,9 +232,8 @@ func runSimulate(ctx context.Context, cmd *cli.Command) error {
239232
return err
240233
}
241234

242-
// The scenarios file must be specified explicitly via --scenarios; we never
243-
// auto-discover one. When provided, those scenarios are the source of truth;
244-
// otherwise scenarios are generated from the agent's source.
235+
// never auto-discovered: an explicit --scenarios file is the source of
236+
// truth, otherwise scenarios are generated from the agent's source
245237
scenariosPath := cmd.String("scenarios")
246238

247239
var scenarioGroup *livekit.ScenarioGroup
@@ -259,8 +251,6 @@ func runSimulate(ctx context.Context, cmd *cli.Command) error {
259251
mode = modeGenerateFromSource
260252
}
261253

262-
// Generating from source uploads the agent's code to LiveKit Cloud, so make
263-
// the user agree to it explicitly before anything is sent.
264254
if mode == modeGenerateFromSource {
265255
if err := confirmSourceUpload(cmd, projectDir); err != nil {
266256
return err
@@ -297,8 +287,8 @@ func isInteractive() bool {
297287
return isatty.IsTerminal(os.Stdin.Fd()) && isatty.IsTerminal(os.Stdout.Fd())
298288
}
299289

300-
// confirmSourceUpload makes the user explicitly agree that their agent's source
301-
// code will be uploaded to LiveKit Cloud before generating scenarios from it.
290+
// confirmSourceUpload makes the user agree before their agent's source is
291+
// uploaded to LiveKit Cloud.
302292
func confirmSourceUpload(cmd *cli.Command, projectDir string) error {
303293
if cmd.Bool("yes") {
304294
return nil
@@ -333,9 +323,9 @@ func confirmSourceUpload(cmd *cli.Command, projectDir string) error {
333323

334324
// --- Shared lifecycle functions used by both TUI and CI modes ---
335325

336-
// agentLauncher owns the agent subprocess lifecycle around the TUI, which only
337-
// observes the start via Wait. Stop kills the worker even when the TUI quits
338-
// mid-start; a leaked worker keeps its port bound and breaks the next run.
326+
// agentLauncher owns the agent subprocess lifecycle around the TUI. Stop kills
327+
// the worker even when the TUI quits mid-start; a leaked worker keeps its port
328+
// bound and breaks the next run.
339329
type agentLauncher struct {
340330
done chan struct{}
341331
proc *AgentProcess
@@ -351,14 +341,13 @@ func launchSimulationAgent(c *simulateConfig) *agentLauncher {
351341
return l
352342
}
353343

354-
// Wait blocks until the start attempt finishes and returns its result.
355344
func (l *agentLauncher) Wait() (*AgentProcess, error) {
356345
<-l.done
357346
return l.proc, l.err
358347
}
359348

360-
// Stop kills the agent once the start attempt finishes (bounded so a stuck
361-
// start can't hang the exit path) and returns it for post-exit reporting.
349+
// Stop kills the agent once the start attempt finishes (bounded wait) and
350+
// returns it for post-exit reporting.
362351
func (l *agentLauncher) Stop() *AgentProcess {
363352
select {
364353
case <-l.done:
@@ -395,9 +384,8 @@ func startSimulationAgent(c *simulateConfig, forwardOutput io.Writer) (*AgentPro
395384
"--simulation",
396385
},
397386
Env: []string{
398-
// force the agent to register under the dispatch name regardless of any
399-
// agent_name hardcoded in the user's code (see LIVEKIT_AGENT_NAME_OVERRIDE
400-
// precedence in livekit-agents worker.py).
387+
// register under the dispatch name regardless of any agent_name
388+
// hardcoded in the user's code
401389
"LIVEKIT_AGENT_NAME_OVERRIDE=" + c.agentName,
402390
"LIVEKIT_URL=" + c.pc.URL,
403391
"LIVEKIT_API_KEY=" + c.pc.APIKey,
@@ -417,8 +405,6 @@ func createSimulationRun(ctx context.Context, c *simulateConfig) (string, *livek
417405
req.Concurrency = &c.concurrency
418406
}
419407
if c.mode == modeScenarios {
420-
// Run the scenarios from the yaml. When unset, the server generates
421-
// num_simulations scenarios from the uploaded source.
422408
req.ScenarioGroup = c.scenarioGroup
423409
}
424410

cmd/lk/simulate_ci.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,7 @@ func runSimulateCI(ctx context.Context, config *simulateConfig) error {
144144
}
145145
fmt.Fprintf(os.Stderr, "Warning: poll failed: %v\n", err)
146146
} else {
147-
// The worker is failing systemically (crashing on job startup, never
148-
// joining), not failing a scenario. Stop early and surface its log.
147+
// the worker is failing systemically: stop early and surface its log
149148
if !brokenAgent && agentBroken(run, agent) {
150149
brokenAgent = true
151150
report.BrokenAgent()
@@ -197,8 +196,8 @@ func runSimulateCI(ctx context.Context, config *simulateConfig) error {
197196
return nil
198197
}
199198

200-
// writeRunResults writes the per-job results and the run summary, using GitHub
201-
// Actions group markers (harmless and a useful delimiter outside GitHub too).
199+
// writeRunResults writes the per-job results and the run summary, with GitHub
200+
// group markers (a useful delimiter outside GitHub too).
202201
func writeRunResults(w io.Writer, run *livekit.SimulationRun, ap *AgentProcess) {
203202
if run == nil {
204203
return

cmd/lk/simulate_health.go

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,9 @@ import (
2020
"github.com/livekit/protocol/livekit"
2121
)
2222

23-
// A simulation can stall because the agent worker is broken (crashed on startup,
24-
// never joins a room) rather than because a scenario failed. When that happens
25-
// the CLI cancels the run instead of waiting out a 30s timeout per scenario, and
26-
// surfaces the worker's own error. The detection has to separate a broken agent
27-
// from transient connection pacing, which can cause an isolated "no agent joined"
28-
// timeout even when the agent is healthy.
23+
// Detects a broken agent worker (crashed on startup, never joins) so the CLI
24+
// can cancel the run and surface the worker's error, while tolerating
25+
// transient "no agent joined" timeouts from connection pacing.
2926

3027
// pythonFatalMarkers are livekit-agents (Python) log lines that only appear on
3128
// a fatal, non-transient worker error. The JS framework will need its own set.
@@ -35,17 +32,14 @@ var pythonFatalMarkers = []string{
3532
"closing due to unrecoverable error",
3633
}
3734

38-
// minFatalMarkers is how many fatal log lines it takes to call the worker
39-
// broken; a single one can be an isolated job crash.
35+
// a single fatal log line can be an isolated job crash
4036
const minFatalMarkers = 2
4137

42-
// maxAgentNotJoined is how many "no agent joined" timeouts to tolerate before
43-
// treating the worker as broken.
38+
// "no agent joined" timeouts tolerated before the worker counts as broken
4439
const maxAgentNotJoined = 3
4540

4641
// agentBroken reports whether the worker is failing systemically. A completed
47-
// scenario proves the agent works, so it is never broken in that case; otherwise
48-
// repeated fatal log markers or "no agent joined" timeouts mark it broken.
42+
// scenario proves the agent works.
4943
func agentBroken(run *livekit.SimulationRun, ap *AgentProcess) bool {
5044
completed, notJoined := 0, 0
5145
for _, job := range run.GetJobs() {
@@ -68,9 +62,8 @@ func agentBroken(run *livekit.SimulationRun, ap *AgentProcess) bool {
6862
}
6963
}
7064

71-
// agentErrorContext is the worker output to surface for a broken agent: the whole
72-
// block from the last fatal marker to the end of the log, so the full traceback
73-
// survives, or the recent tail when no marker is present.
65+
// agentErrorContext is the worker output to surface for a broken agent: from
66+
// the last fatal marker to the end (full traceback), or the recent tail.
7467
func agentErrorContext(ap *AgentProcess) []string {
7568
logs := ap.RecentLogs(0)
7669
if i := lastFatalMarker(logs); i >= 0 {
@@ -85,8 +78,6 @@ func agentErrorContext(ap *AgentProcess) []string {
8578
return out
8679
}
8780

88-
// lastFatalMarker returns the index of the last log line matching a fatal
89-
// marker, or -1 if none.
9081
func lastFatalMarker(logs []string) int {
9182
last := -1
9283
for i, line := range logs {

cmd/lk/simulate_report.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@ import (
2424
"github.com/livekit/protocol/livekit"
2525
)
2626

27-
// simLog is the single source of the plain-text simulation output: CI writes
28-
// it to the terminal (results on out, transient progress on info) and the
29-
// TUI's runReporter writes the same calls to the report file.
27+
// simLog is the single source of the plain-text simulation output, shared by
28+
// the CI mode (terminal) and the TUI's runReporter (report file).
3029
type simLog struct {
3130
out io.Writer
3231
info io.Writer
@@ -82,7 +81,6 @@ func (l *simLog) RunCreated(runID, dashboardURL string) {
8281
fmt.Fprintln(l.out)
8382
}
8483

85-
// RunUpdate logs run-status transitions and job progress from a poll result.
8684
func (l *simLog) RunUpdate(run *livekit.SimulationRun, configuredN int32) {
8785
_, done, _, _ := simulationJobCounts(run)
8886
switch run.Status {
@@ -129,8 +127,8 @@ func writeBrokenAgentNote(w io.Writer, ap *AgentProcess) {
129127
}
130128
}
131129

132-
// asciiWriter transliterates the output glyphs to plain ASCII, keeping the
133-
// report file free of special characters without forking the simLog strings.
130+
// asciiWriter keeps the report file free of special characters without
131+
// forking the simLog strings.
134132
type asciiWriter struct{ w io.Writer }
135133

136134
var asciiGlyphs = strings.NewReplacer(
@@ -162,7 +160,6 @@ func newRunReporter() *runReporter {
162160
return &runReporter{simLog: newSimLog(w, w), f: f}
163161
}
164162

165-
// Finish appends the results and trailer, closes the file, returns its path.
166163
func (r *runReporter) Finish(run *livekit.SimulationRun, ap *AgentProcess, brokenAgent bool, dashboardURL string) string {
167164
if r.f == nil {
168165
return ""

cmd/lk/simulate_subprocess.go

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,8 @@ type AgentStartConfig struct {
138138
// start/dev/console/simulate subcommands under `python -m livekit.agents`.
139139
const thinCLIMinVersion = "1.6.0"
140140

141-
// agentExitDetail surfaces the agent's own output (the real error) and a
142-
// pointer to the full log, for when the worker exits early or never registers.
141+
// agentExitDetail surfaces the agent's own output and the log path when the
142+
// worker exits early or never registers.
143143
func agentExitDetail(ap *AgentProcess) string {
144144
var b strings.Builder
145145
if tail := lastNonEmptyLines(ap.RecentLogs(0), 12); len(tail) > 0 {
@@ -175,20 +175,16 @@ func startAgent(cfg AgentStartConfig) (*AgentProcess, error) {
175175
return nil, err
176176
}
177177

178-
// Reuse the SDK-version reader (parses the project's deps) to fail fast with a
179-
// friendly message when livekit-agents is older than the thin-CLI baseline.
178+
// fail fast when livekit-agents is older than the thin-CLI baseline
180179
if err := agentfs.CheckSDKVersion(cfg.Dir, cfg.ProjectType, map[string]string{
181180
"python-min-sdk-version": thinCLIMinVersion,
182181
"node-min-sdk-version": thinCLIMinVersion,
183182
}); err != nil {
184183
return nil, err
185184
}
186185

187-
// Launch via the framework CLI module rather than running the user's file
188-
// directly: python -m livekit.agents SUBCOMMAND ENTRYPOINT FLAGS. The framework
189-
// discovers the AgentServer from the entrypoint and drives the thin CLI. Requires a
190-
// livekit-agents that supports start/console under -m livekit.agents; older versions
191-
// only expose download-files there.
186+
// python -m livekit.agents SUBCOMMAND ENTRYPOINT FLAGS: the framework
187+
// discovers the AgentServer from the entrypoint and drives the thin CLI.
192188
args := append(prefixArgs, "-m", "livekit.agents")
193189
if len(cfg.CLIArgs) > 0 {
194190
args = append(args, cfg.CLIArgs[0]) // subcommand: start | console
@@ -393,9 +389,9 @@ func extractLogRoom(line string) string {
393389
return ""
394390
}
395391

396-
// Kill terminates the worker quickly: a short SIGINT grace (an idle worker
397-
// exits cleanly; one draining jobs would take minutes), then SIGKILL to the
398-
// whole process group, waiting for the exit so the port is free on return.
392+
// Kill gives the worker a short SIGINT grace (an idle one exits cleanly; one
393+
// draining jobs would take minutes), then SIGKILLs the whole process group and
394+
// waits so the port is free on return.
399395
func (ap *AgentProcess) Kill() {
400396
if ap.cmd.Process == nil {
401397
return

0 commit comments

Comments
 (0)