MCPcopy
hub / github.com/promptfoo/promptfoo / PythonWorker

Class PythonWorker

src/python/worker.ts:19–310  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

17export { MAX_STDERR_BUFFER_LENGTH } from './stderr';
18
19export class PythonWorker {
20 private process: PythonShell | null = null;
21 private ready: boolean = false;
22 private busy: boolean = false;
23 private shuttingDown: boolean = false;
24 private crashCount: number = 0;
25 private stderrLogger = new PythonStderrLogger('Python worker stderr: ');
26 private readonly maxCrashes: number = 3;
27 private pendingRequest: {
28 responseFile: string;
29 resolve: (result: unknown) => void;
30 reject: (error: Error) => void;
31 } | null = null;
32 private requestTimeout: NodeJS.Timeout | null = null;
33
34 constructor(
35 private scriptPath: string,
36 private functionName: string,
37 private pythonPath?: string,
38 private timeout: number = getRequestTimeoutMs(),
39 private onReady?: () => void,
40 ) {}
41
42 async initialize(): Promise<void> {
43 return this.startWorker();
44 }
45
46 private async startWorker(): Promise<void> {
47 const wrapperPath = path.join(getWrapperDir('python'), 'persistent_wrapper.py');
48
49 // Validate and resolve Python path using smart detection (tries python3, then python)
50 const resolvedPythonPath = await validatePythonPath(
51 this.pythonPath || 'python',
52 typeof this.pythonPath === 'string',
53 );
54
55 this.process = new PythonShell(wrapperPath, {
56 mode: 'text',
57 pythonPath: resolvedPythonPath,
58 args: [this.scriptPath, this.functionName],
59 stdio: ['pipe', 'pipe', 'pipe'],
60 });
61
62 // Listen for READY signal
63 return new Promise((resolve, reject) => {
64 const readyTimeout = setTimeout(() => {
65 // Kill the process to prevent orphaned Python processes
66 // and avoid triggering handleCrash() which would retry
67 this.shuttingDown = true;
68 if (this.process) {
69 this.process.kill('SIGTERM');
70 this.process = null;
71 }
72 reject(new Error('Worker failed to become ready within timeout'));
73 }, 30000);
74
75 this.process!.on('message', (message: string) => {
76 if (message.trim() === 'READY') {

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…