Skip to content

Commit 82de46c

Browse files
committed
simulate: lint fix, always emit gh group markers, health tuning
- staticcheck QF1012: fmt.Fprintf(&b, ...) over WriteString(Sprintf) - drop the gh/plain output split and isGitHubActions gating: group markers are a useful delimiter everywhere, and job ids now appear in the group titles - agent health: tolerate 3 not-joined timeouts, require 2 fatal log markers (a single one can be an isolated job crash), name the marker list pythonFatalMarkers (JS will need its own) - open the log pane automatically when the agent is detected broken - prune obvious comments
1 parent 213b7ad commit 82de46c

7 files changed

Lines changed: 82 additions & 107 deletions

File tree

cmd/lk/perf.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,13 @@ func agentLoadTest(ctx context.Context, cmd *cli.Command) error {
276276
}
277277

278278
params := loadtester.AgentLoadTestParams{
279-
URL: pc.URL,
280-
APIKey: pc.APIKey,
281-
APISecret: pc.APISecret,
282-
Rooms: int(cmd.Int("rooms")),
283-
AgentName: cmd.String("agent-name"),
284-
EchoSpeechDelay: cmd.Duration("echo-speech-delay"),
285-
Duration: cmd.Duration("duration"),
279+
URL: pc.URL,
280+
APIKey: pc.APIKey,
281+
APISecret: pc.APISecret,
282+
Rooms: int(cmd.Int("rooms")),
283+
AgentName: cmd.String("agent-name"),
284+
EchoSpeechDelay: cmd.Duration("echo-speech-delay"),
285+
Duration: cmd.Duration("duration"),
286286
ParticipantAttributes: participantAttributes,
287287
}
288288

cmd/lk/simulate.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,8 @@ func writeGeneratedScenariosTemp(run *livekit.SimulationRun) (string, error) {
114114
return f.Name(), nil
115115
}
116116

117-
// scenarioGroupToYAML renders a ScenarioGroup as a scenarios.yaml document — the
118-
// inverse of loadScenarioGroup, decoding each scenario's JSON userdata string
119-
// back into a nested mapping.
117+
// scenarioGroupToYAML renders a ScenarioGroup as a scenarios.yaml document, the
118+
// inverse of loadScenarioGroup.
120119
func scenarioGroupToYAML(group *livekit.ScenarioGroup) ([]byte, error) {
121120
f := scenariosFile{Name: group.GetName()}
122121
for _, s := range group.GetScenarios() {

cmd/lk/simulate_ci.go

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -183,12 +183,10 @@ func runSimulateCI(ctx context.Context, config *simulateConfig) error {
183183

184184
_, _, _, failed := simulationJobCounts(run)
185185
if failed > 0 || run.Status == livekit.SimulationRun_STATUS_FAILED {
186-
if isGitHubActions() {
187-
if failed > 0 {
188-
fmt.Fprintf(os.Stdout, "::error::%d simulation(s) failed\n", failed)
189-
} else {
190-
fmt.Fprintf(os.Stdout, "::error::Simulation run failed: %s\n", run.Error)
191-
}
186+
if failed > 0 {
187+
fmt.Fprintf(os.Stdout, "::error::%d simulation(s) failed\n", failed)
188+
} else {
189+
fmt.Fprintf(os.Stdout, "::error::Simulation run failed: %s\n", run.Error)
192190
}
193191
if run.Status == livekit.SimulationRun_STATUS_FAILED && len(run.Jobs) == 0 {
194192
return fmt.Errorf("simulation failed: %s", run.Error)
@@ -199,10 +197,9 @@ func runSimulateCI(ctx context.Context, config *simulateConfig) error {
199197
return nil
200198
}
201199

202-
// writeRunResults writes the per-job results and the run summary. With gh set,
203-
// sections are wrapped in GitHub Actions group markers and failed jobs emit
204-
// ::error:: annotations on GitHub.
205-
func writeRunResults(w io.Writer, run *livekit.SimulationRun, ap *AgentProcess, gh bool) {
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).
202+
func writeRunResults(w io.Writer, run *livekit.SimulationRun, ap *AgentProcess) {
206203
if run == nil {
207204
return
208205
}
@@ -226,11 +223,7 @@ func writeRunResults(w io.Writer, run *livekit.SimulationRun, ap *AgentProcess,
226223
label = fmt.Sprintf("Job %d", i+1)
227224
}
228225

229-
if gh {
230-
fmt.Fprintf(w, "::group::%s %s\n", icon, label)
231-
} else {
232-
fmt.Fprintf(w, "%s %s (%s)\n", icon, label, job.Id)
233-
}
226+
fmt.Fprintf(w, "::group::%s %s (%s)\n", icon, label, job.Id)
234227

235228
if job.Instructions != "" {
236229
fmt.Fprintln(w, "Instructions:")
@@ -268,20 +261,16 @@ func writeRunResults(w io.Writer, run *livekit.SimulationRun, ap *AgentProcess,
268261
}
269262
}
270263

271-
if gh {
272-
fmt.Fprintln(w, "::endgroup::")
273-
} else {
274-
fmt.Fprintln(w)
275-
}
264+
fmt.Fprintln(w, "::endgroup::")
276265

277-
if job.Status == livekit.SimulationRun_Job_STATUS_FAILED && gh && isGitHubActions() {
266+
if job.Status == livekit.SimulationRun_Job_STATUS_FAILED {
278267
firstLine, _, _ := strings.Cut(job.Error, "\n")
279268
fmt.Fprintf(w, "::error::Job %d failed: %s\n", i+1, firstLine)
280269
}
281270
}
282271

283272
if run.Summary != nil {
284-
writeRunSummary(w, run, gh)
273+
writeRunSummary(w, run)
285274
} else {
286275
msg := "The summary for this run is not available"
287276
if run.Error != "" {
@@ -292,16 +281,12 @@ func writeRunResults(w io.Writer, run *livekit.SimulationRun, ap *AgentProcess,
292281
}
293282
}
294283

295-
func writeRunSummary(w io.Writer, run *livekit.SimulationRun, gh bool) {
284+
func writeRunSummary(w io.Writer, run *livekit.SimulationRun) {
296285
summary := run.Summary
297286
total, _, passed, failed := simulationJobCounts(run)
298287

299288
fmt.Fprintln(w)
300-
if gh {
301-
fmt.Fprintln(w, "::group::Summary")
302-
} else {
303-
fmt.Fprintln(w, "Summary")
304-
}
289+
fmt.Fprintln(w, "::group::Summary")
305290
fmt.Fprintf(w, "%d total, %d passed, %d failed\n", total, passed, failed)
306291

307292
if summary.GoingWell != "" {
@@ -331,9 +316,7 @@ func writeRunSummary(w io.Writer, run *livekit.SimulationRun, gh bool) {
331316
}
332317
}
333318

334-
if gh {
335-
fmt.Fprintln(w, "::endgroup::")
336-
}
319+
fmt.Fprintln(w, "::endgroup::")
337320
}
338321

339322
func writeChatHistory(w io.Writer, chatCtx *agent.ChatContext) {
@@ -376,7 +359,3 @@ func writeChatHistory(w io.Writer, chatCtx *agent.ChatContext) {
376359
}
377360
}
378361
}
379-
380-
func isGitHubActions() bool {
381-
return os.Getenv("GITHUB_ACTIONS") != ""
382-
}

cmd/lk/simulate_health.go

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,25 @@ import (
2727
// from transient connection pacing, which can cause an isolated "no agent joined"
2828
// timeout even when the agent is healthy.
2929

30-
// agentFatalMarkers are framework log lines that only appear on a fatal,
31-
// non-transient worker error.
32-
var agentFatalMarkers = []string{
30+
// pythonFatalMarkers are livekit-agents (Python) log lines that only appear on
31+
// a fatal, non-transient worker error. The JS framework will need its own set.
32+
var pythonFatalMarkers = []string{
3333
"unhandled exception while running the job task",
3434
"error initializing process",
3535
"closing due to unrecoverable error",
3636
}
3737

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.
40+
const minFatalMarkers = 2
41+
3842
// maxAgentNotJoined is how many "no agent joined" timeouts to tolerate before
3943
// treating the worker as broken.
40-
const maxAgentNotJoined = 1
44+
const maxAgentNotJoined = 3
4145

4246
// agentBroken reports whether the worker is failing systemically. A completed
4347
// scenario proves the agent works, so it is never broken in that case; otherwise
44-
// a fatal log marker, or more than one "no agent joined" timeout, marks it broken.
48+
// repeated fatal log markers or "no agent joined" timeouts mark it broken.
4549
func agentBroken(run *livekit.SimulationRun, ap *AgentProcess) bool {
4650
completed, notJoined := 0, 0
4751
for _, job := range run.GetJobs() {
@@ -57,7 +61,7 @@ func agentBroken(run *livekit.SimulationRun, ap *AgentProcess) bool {
5761
switch {
5862
case completed > 0:
5963
return false
60-
case ap != nil && lastFatalMarker(ap.RecentLogs(0)) >= 0:
64+
case ap != nil && countFatalMarkers(ap.RecentLogs(0)) >= minFatalMarkers:
6165
return true
6266
default:
6367
return notJoined > maxAgentNotJoined
@@ -81,17 +85,34 @@ func agentErrorContext(ap *AgentProcess) []string {
8185
return out
8286
}
8387

84-
// lastFatalMarker returns the index of the last log line matching an
85-
// agentFatalMarker, or -1 if none.
88+
// lastFatalMarker returns the index of the last log line matching a fatal
89+
// marker, or -1 if none.
8690
func lastFatalMarker(logs []string) int {
8791
last := -1
8892
for i, line := range logs {
89-
for _, marker := range agentFatalMarkers {
90-
if strings.Contains(ansiEscapeRe.ReplaceAllString(line, ""), marker) {
91-
last = i
92-
break
93-
}
93+
if isFatalMarker(line) {
94+
last = i
9495
}
9596
}
9697
return last
9798
}
99+
100+
func countFatalMarkers(logs []string) int {
101+
n := 0
102+
for _, line := range logs {
103+
if isFatalMarker(line) {
104+
n++
105+
}
106+
}
107+
return n
108+
}
109+
110+
func isFatalMarker(line string) bool {
111+
plain := ansiEscapeRe.ReplaceAllString(line, "")
112+
for _, marker := range pythonFatalMarkers {
113+
if strings.Contains(plain, marker) {
114+
return true
115+
}
116+
}
117+
return false
118+
}

cmd/lk/simulate_report.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func (l *simLog) BrokenAgent() {
117117
}
118118

119119
func (l *simLog) Results(run *livekit.SimulationRun, ap *AgentProcess) {
120-
writeRunResults(l.out, run, ap, true)
120+
writeRunResults(l.out, run, ap)
121121
}
122122

123123
func writeBrokenAgentNote(w io.Writer, ap *AgentProcess) {

cmd/lk/simulate_subprocess.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ func roomNamePrefix(roomName string) string {
353353

354354
// RecentRoomLogsByPrefix returns log lines for the most recent room matching
355355
// the prefix of the given room name. When a job is retried, each attempt gets
356-
// a new room with the same prefix — we show only the latest attempt's logs.
356+
// a new room with the same prefix; only the latest attempt's logs are shown.
357357
func (ap *AgentProcess) RecentRoomLogsByPrefix(n int, roomName string) []string {
358358
ap.mu.Lock()
359359
defer ap.mu.Unlock()

0 commit comments

Comments
 (0)