-
Notifications
You must be signed in to change notification settings - Fork 309
feat(agents): read simulator dispatch metadata #1757
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 1.5.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,6 +17,7 @@ import { AsyncLocalStorage } from 'node:async_hooks'; | |
| import * as os from 'node:os'; | ||
| import * as path from 'node:path'; | ||
| import type { Logger } from 'pino'; | ||
| import { ATTRIBUTE_SIMULATOR, ATTRIBUTE_SIMULATOR_DISPATCH } from './constants.js'; | ||
| import type { InferenceExecutor } from './ipc/inference_executor.js'; | ||
| import { log } from './log.js'; | ||
| import { flushOtelLogs, setupCloudTracer, uploadSessionReport } from './telemetry/index.js'; | ||
|
|
@@ -90,6 +91,49 @@ export type RunningJobInfo = { | |
| apiSecret?: string; | ||
| }; | ||
|
|
||
| export enum SimulationMode { | ||
| SIMULATION_MODE_UNSPECIFIED = 0, | ||
| SIMULATION_MODE_TEXT = 1, | ||
| SIMULATION_MODE_AUDIO = 2, | ||
| } | ||
|
|
||
| export type SimulationDispatch = { | ||
| simulationRunId?: string; | ||
| simulation_run_id?: string; | ||
| mode?: string | number; | ||
| scenario?: unknown; | ||
| }; | ||
|
|
||
| export class SimulationContext<ProcessUserData = Record<string, unknown>> { | ||
| #dispatch: SimulationDispatch; | ||
| #jobContext: JobContext<ProcessUserData>; | ||
|
|
||
| constructor(dispatch: SimulationDispatch, jobContext: JobContext<ProcessUserData>) { | ||
| this.#dispatch = dispatch; | ||
| this.#jobContext = jobContext; | ||
| } | ||
|
|
||
| get scenario(): unknown { | ||
| return this.#dispatch.scenario; | ||
| } | ||
|
|
||
| get simulationMode(): SimulationMode { | ||
| const mode = this.#dispatch.mode; | ||
| if (mode === SimulationMode.SIMULATION_MODE_AUDIO || mode === 'SIMULATION_MODE_AUDIO') { | ||
| return SimulationMode.SIMULATION_MODE_AUDIO; | ||
| } | ||
| if (mode === SimulationMode.SIMULATION_MODE_TEXT || mode === 'SIMULATION_MODE_TEXT') { | ||
| return SimulationMode.SIMULATION_MODE_TEXT; | ||
| } | ||
| // Simulations predating the mode field were text-only. | ||
| return SimulationMode.SIMULATION_MODE_TEXT; | ||
| } | ||
|
|
||
| get jobContext(): JobContext<ProcessUserData> { | ||
| return this.#jobContext; | ||
| } | ||
| } | ||
|
|
||
| /** Attempted to add a function callback, but the function already exists. */ | ||
| export class FunctionExistsError extends Error { | ||
| constructor(msg?: string) { | ||
|
|
@@ -119,6 +163,8 @@ export class JobContext<ProcessUserData = Record<string, unknown>> { | |
| } = {}; | ||
| #logger: Logger; | ||
| #inferenceExecutor: InferenceExecutor; | ||
| #simulationResolved = false; | ||
| #simulationContext?: SimulationContext<ProcessUserData>; | ||
|
|
||
| /** @internal */ | ||
| _primaryAgentSession?: AgentSession; | ||
|
|
@@ -172,6 +218,48 @@ export class JobContext<ProcessUserData = Record<string, unknown>> { | |
| return this.#info; | ||
| } | ||
|
|
||
| simulationContext(): SimulationContext<ProcessUserData> | undefined { | ||
| if (this.#simulationResolved) { | ||
| return this.#simulationContext; | ||
| } | ||
|
|
||
| this.#simulationResolved = true; | ||
|
Comment on lines
+222
to
+226
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔴 simulationContext() permanently caches undefined when called before room is connected or simulator participant has joined The Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback. |
||
|
|
||
| let metadata = ''; | ||
| for (const participant of this.#room.remoteParticipants.values()) { | ||
| if (!Object.hasOwn(participant.attributes, ATTRIBUTE_SIMULATOR)) { | ||
| continue; | ||
| } | ||
|
|
||
| metadata = participant.attributes[ATTRIBUTE_SIMULATOR_DISPATCH] || ''; | ||
| if (metadata) { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if (!metadata) { | ||
| // Older servers and fake job contexts placed the dispatch in job metadata. | ||
| metadata = (this.#info.job as proto.Job & { metadata?: string }).metadata || ''; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚩 Job metadata fallback uses type assertion for metadata field At Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| } | ||
| if (!metadata) { | ||
| return undefined; | ||
| } | ||
|
|
||
| let dispatch: SimulationDispatch; | ||
| try { | ||
| dispatch = JSON.parse(metadata) as SimulationDispatch; | ||
| } catch { | ||
| return undefined; | ||
| } | ||
|
|
||
| if (!(dispatch.simulationRunId || dispatch.simulation_run_id)) { | ||
| return undefined; | ||
| } | ||
|
|
||
| this.#simulationContext = new SimulationContext(dispatch, this); | ||
| return this.#simulationContext; | ||
| } | ||
|
|
||
| /** @returns The agent's participant if connected to the room, otherwise `undefined` */ | ||
| get agent(): LocalParticipant | undefined { | ||
| return this.#room.localParticipant; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚩 waitForParticipant does not filter simulator participants
The existing
waitForParticipantmethod atagents/src/job.ts:285-319filters outParticipantKind.AGENTbut does not filter out simulator participants (those with thelk.simulatorattribute). With this PR introducing simulation support, a simulator participant could potentially be returned bywaitForParticipant. This is likely fine if simulator participants useParticipantKind.AGENT(which would already be filtered), but if they use a different kind, they could be incorrectly returned as the "user" participant. Not flagged as a bug since we can't verify the participant kind used by simulators without access to the server code, and this is pre-existing behavior.(Refers to lines 285-319)
Was this helpful? React with 👍 or 👎 to provide feedback.