Skip to content

Commit 3a3142b

Browse files
authored
simulate: audio subcommand with impairment flags (#943)
* simulate: pass the simulation mode into runSimulate * simulate: audio is a subcommand, with impairment flags * go.mod: bump protocol for the impairment fields server-sdk-go moves with it: protocol #1730 changed TransferSIPParticipant to return a response, which v2.18.1 cannot compile against.
1 parent 29c2686 commit 3a3142b

4 files changed

Lines changed: 289 additions & 284 deletions

File tree

autocomplete/fish_autocomplete

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,16 @@ complete -x -c lk -n '__fish_seen_subcommand_from agent a; and not __fish_seen_s
223223
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate' -f -l num-simulations -s n -r -d 'Number of scenarios to generate'
224224
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate' -f -l concurrency -r -d 'Max simulations running in parallel (default: server-side limit)'
225225
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate' -f -l scenarios -r -d 'Path to a scenarios `FILE` (yaml). If omitted, scenarios are generated from the agent\'s source'
226-
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate' -f -l audio -d 'Simulate speech-to-speech interactions using the agent\'s full audio pipeline. By default, simulations run in text-only mode.'
227226
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate' -f -l yes -s y -d 'Skip the source-upload confirmation prompt (required for non-interactive runs that generate from source)'
228227
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate' -f -l view -r -d 'Open a pre-existing simulation'
229228
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate' -f -l export -r -d 'Print the run with run `ID` and its exact per-job chat contexts as JSON. Nothing is run or polled: the run must already be finished'
230229
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate' -f -l agent-name -r -d 'Run against an already-running agent instead of spawning one locally. Pass the registered `NAME`, or "" to target the project\'s default agent (the one that auto-joins every room). Requires --scenarios.'
231230
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate' -f -l help -s h -d 'show help'
231+
complete -x -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate; and not __fish_seen_subcommand_from audio' -a 'audio' -d 'Simulate speech-to-speech interactions using the agent\'s full audio pipeline'
232+
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate; and __fish_seen_subcommand_from audio' -f -l background-noise -d 'Mix ambient noise into the simulated user\'s audio'
233+
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate; and __fish_seen_subcommand_from audio' -f -l low-quality-microphone -d 'Publish the simulated user\'s audio as a low-quality microphone would capture it'
234+
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate; and __fish_seen_subcommand_from audio' -f -l packet-loss -d 'Drop packets from the simulated user\'s audio track'
235+
complete -c lk -n '__fish_seen_subcommand_from agent a; and __fish_seen_subcommand_from simulate; and __fish_seen_subcommand_from audio' -f -l help -s h -d 'show help'
232236
complete -x -c lk -n '__fish_seen_subcommand_from agent a; and not __fish_seen_subcommand_from init create dockerfile config deploy promote status update restart rollback logs tail delete destroy versions list secrets update-secrets private-link start dev console daemon simulate help h' -a 'help' -d 'Shows a list of commands or help for one command'
233237
complete -c lk -n '__fish_seen_subcommand_from analytics' -f -l experimental -d 'Enable experimental features'
234238
complete -c lk -n '__fish_seen_subcommand_from analytics' -f -l help -s h -d 'show help'

cmd/lk/simulate.go

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ var simulateCommand = &cli.Command{
7070
simulateProjectConfig = pc
7171
return nil, nil
7272
},
73-
Action: runSimulate,
73+
Action: func(ctx context.Context, cmd *cli.Command) error {
74+
return runSimulate(ctx, cmd, livekit.SimulationMode_SIMULATION_MODE_TEXT)
75+
},
76+
Commands: []*cli.Command{simulateAudioCommand},
7477
Flags: []cli.Flag{
7578
&cli.IntFlag{
7679
Name: "num-simulations",
@@ -85,10 +88,6 @@ var simulateCommand = &cli.Command{
8588
Name: "scenarios",
8689
Usage: "Path to a scenarios `FILE` (yaml). If omitted, scenarios are generated from the agent's source",
8790
},
88-
&cli.BoolFlag{
89-
Name: "audio",
90-
Usage: "Simulate speech-to-speech interactions using the agent's full audio pipeline. By default, simulations run in text-only mode.",
91-
},
9291
&cli.BoolFlag{
9392
Name: "yes",
9493
Aliases: []string{"y"},
@@ -109,6 +108,34 @@ var simulateCommand = &cli.Command{
109108
},
110109
}
111110

111+
// simulateAudioCommand inherits every flag on `simulate`: flags there are
112+
// persistent (cli.FlagBase.Local defaults to false), so they parse on either
113+
// side of the subcommand name.
114+
var simulateAudioCommand = &cli.Command{
115+
Name: "audio",
116+
Usage: "Simulate speech-to-speech interactions using the agent's full audio pipeline",
117+
Description: "Options on lk agent simulate apply here too, e.g. --scenarios and --agent-name.",
118+
ArgsUsage: "[entrypoint]",
119+
HideHelpCommand: true,
120+
Action: func(ctx context.Context, cmd *cli.Command) error {
121+
return runSimulate(ctx, cmd, livekit.SimulationMode_SIMULATION_MODE_AUDIO)
122+
},
123+
Flags: []cli.Flag{
124+
&cli.BoolFlag{
125+
Name: "background-noise",
126+
Usage: "Mix ambient noise into the simulated user's audio",
127+
},
128+
&cli.BoolFlag{
129+
Name: "low-quality-microphone",
130+
Usage: "Publish the simulated user's audio as a low-quality microphone would capture it",
131+
},
132+
&cli.BoolFlag{
133+
Name: "packet-loss",
134+
Usage: "Drop packets from the simulated user's audio track",
135+
},
136+
},
137+
}
138+
112139
// writeGeneratedScenariosTemp writes a generated run's scenarios to a temp
113140
// scenarios.yaml; "" when the run carries none.
114141
func writeGeneratedScenariosTemp(run *livekit.SimulationRun) (string, error) {
@@ -189,6 +216,12 @@ type simulateConfig struct {
189216
viewModeRunID string // non-empty when --view opens a pre-existing run
190217
liveAgent bool // --agent-name: run against an already-running agent, don't spawn one
191218
warnings []string // config-level warnings surfaced at setup (e.g. ignored flags)
219+
220+
// impairments on the simulated user's audio, set only in SIMULATION_MODE_AUDIO
221+
backgroundNoise bool
222+
lowQualityMicrophone bool
223+
packetLoss bool
224+
192225
// TODO (steveyoon): add agent deployment support
193226
// agentDeployment string
194227
}
@@ -273,7 +306,7 @@ func buildTaskExists(projectDir string) (bool, error) {
273306
return ok, nil
274307
}
275308

276-
func runSimulate(ctx context.Context, cmd *cli.Command) error {
309+
func runSimulate(ctx context.Context, cmd *cli.Command, simulationMode livekit.SimulationMode) error {
277310
pc := simulateProjectConfig
278311

279312
// --export is a one-shot read of a finished run, so it short-circuits
@@ -368,11 +401,6 @@ func runSimulate(ctx context.Context, cmd *cli.Command) error {
368401

369402
simClient := lksdk.NewAgentSimulationClient(serverURL, pc.APIKey, pc.APISecret)
370403

371-
simulationMode := livekit.SimulationMode_SIMULATION_MODE_TEXT
372-
if cmd.Bool("audio") {
373-
simulationMode = livekit.SimulationMode_SIMULATION_MODE_AUDIO
374-
}
375-
376404
simCfg := &simulateConfig{
377405
ctx: ctx,
378406
client: simClient,
@@ -392,6 +420,12 @@ func runSimulate(ctx context.Context, cmd *cli.Command) error {
392420
warnings: simulateConfigWarnings(mode, numSimulations),
393421
}
394422

423+
if simulationMode == livekit.SimulationMode_SIMULATION_MODE_AUDIO {
424+
simCfg.backgroundNoise = cmd.Bool("background-noise")
425+
simCfg.lowQualityMicrophone = cmd.Bool("low-quality-microphone")
426+
simCfg.packetLoss = cmd.Bool("packet-loss")
427+
}
428+
395429
if !isInteractive() {
396430
return runSimulateCI(ctx, simCfg)
397431
}
@@ -522,9 +556,12 @@ func startSimulationAgent(c *simulateConfig, forwardOutput io.Writer) (*AgentPro
522556

523557
func createSimulationRun(ctx context.Context, c *simulateConfig) (string, *livekit.PresignedPostRequest, error) {
524558
req := &livekit.SimulationRun_Create_Request{
525-
AgentName: c.agentName,
526-
NumSimulations: c.numSimulations,
527-
Mode: c.simulationMode,
559+
AgentName: c.agentName,
560+
NumSimulations: c.numSimulations,
561+
Mode: c.simulationMode,
562+
BackgroundNoise: c.backgroundNoise,
563+
LowQualityMicrophone: c.lowQualityMicrophone,
564+
PacketLoss: c.packetLoss,
528565
}
529566
if c.concurrency > 0 {
530567
req.Concurrency = &c.concurrency

0 commit comments

Comments
 (0)