diff --git a/.changeset/sixty-timers-push.md b/.changeset/sixty-timers-push.md new file mode 100644 index 00000000..eff07517 --- /dev/null +++ b/.changeset/sixty-timers-push.md @@ -0,0 +1,6 @@ +--- +"@livekit/agents": patch +"livekit-agents-examples": minor +--- + +Maximize self-import compatibility diff --git a/README.md b/README.md index 3d80027c..e83b46f4 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ serve as a starting point for a completely custom agent application. To join a LiveKit room that's already active, you can use the `connect` command: ```bash -bash my_agent.ts connect --room +node my_agent.ts connect --room ``` ### FAQ diff --git a/agents/src/generator.ts b/agents/src/generator.ts index 0f5b5e09..18ea05a6 100644 --- a/agents/src/generator.ts +++ b/agents/src/generator.ts @@ -9,6 +9,20 @@ export interface Agent { prewarm?: (proc: JobProcess) => unknown; } +/** Helper to check if an object is an agent before running it. + * + * @internal + */ +export function isAgent(obj: unknown): obj is Agent { + return ( + typeof obj === 'object' && + obj !== null && + 'entry' in obj && + typeof (obj as Agent).entry === 'function' && + (('prewarm' in obj && typeof (obj as Agent).prewarm === 'function') || !('prewarm' in obj)) + ); +} + /** * Helper to define an agent according to the required interface. * @example A basic agent with entry and prewarm functions diff --git a/agents/src/ipc/job_main.ts b/agents/src/ipc/job_main.ts index 007e22c2..6436898f 100644 --- a/agents/src/ipc/job_main.ts +++ b/agents/src/ipc/job_main.ts @@ -7,7 +7,7 @@ import { fork } from 'child_process'; import { EventEmitter, once } from 'events'; import type { Logger } from 'pino'; import { fileURLToPath } from 'url'; -import type { Agent } from '../generator.js'; +import { type Agent, isAgent } from '../generator.js'; import type { RunningJobInfo } from '../job.js'; import { JobContext } from '../job.js'; import { JobProcess } from '../job.js'; @@ -93,7 +93,14 @@ if (process.send) { // [0] `node' // [1] import.meta.filename // [2] import.meta.filename of function containing entry file - const agent: Agent = await import(process.argv[2]).then((agent) => agent.default); + const moduleFile = process.argv[2]; + const agent: Agent = await import(moduleFile).then((module) => { + const agent = module.default; + if (agent === undefined || !isAgent(agent)) { + throw new Error(`Unable to load agent: Missing or invalid default export in ${moduleFile}`); + } + return agent; + }); if (!agent.prewarm) { agent.prewarm = defaultInitializeProcessFunc; } diff --git a/agents/src/worker.ts b/agents/src/worker.ts index b58018db..13f1fa68 100644 --- a/agents/src/worker.ts +++ b/agents/src/worker.ts @@ -178,6 +178,9 @@ export class WorkerOptions { logLevel?: string; }) { this.agent = agent; + if (!this.agent) { + throw new Error('No Agent file was passed to the worker'); + } this.requestFunc = requestFunc; this.loadFunc = loadFunc; this.loadThreshold = loadThreshold; diff --git a/examples/package.json b/examples/package.json index bfd66acb..02087d0e 100644 --- a/examples/package.json +++ b/examples/package.json @@ -4,7 +4,8 @@ "type": "module", "scripts": { "build": "tsc", - "lint": "eslint -f unix \"src/**/*.ts\"" + "lint": "eslint -f unix \"src/**/*.ts\"", + "minimal": "node dist/minimal_assistant.js dev" }, "devDependencies": { "typescript": "^5.0.0" @@ -14,6 +15,5 @@ "@livekit/agents-plugin-openai": "workspace:*", "@livekit/rtc-node": "^0.8.1", "zod": "^3.23.8" - }, - "version": null + } } diff --git a/examples/src/minimal_assistant.ts b/examples/src/minimal_assistant.ts index 6b04939d..2efad8c4 100644 --- a/examples/src/minimal_assistant.ts +++ b/examples/src/minimal_assistant.ts @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 import { type JobContext, WorkerOptions, cli, defineAgent } from '@livekit/agents'; import { OmniAssistant, defaultConversationConfig } from '@livekit/agents-plugin-openai'; +import { fileURLToPath } from 'node:url'; import { z } from 'zod'; export default defineAgent({ @@ -36,4 +37,4 @@ export default defineAgent({ }, }); -cli.runApp(new WorkerOptions({ agent: import.meta.filename })); +cli.runApp(new WorkerOptions({ agent: fileURLToPath(import.meta.url) }));