# Node.js Python Binding Class Documentation ## Overview This project provides a Node.js interface (`python_bindings.js`) to interact with a Python backend. The Python backend exposes an API via a Controller class (`controller.py`) and communicates with Node.js via JSON messages over stdin/stdout using `python-shell` in Node.js. The Node.js class `Bindings` wraps the interaction with the Python process and exposes asynchronous methods mapped dynamically to Python Controller methods. It supports property setting/getting, method calling with parameters, and streaming partial results via callbacks. --- ## Files and Components ### 1. `index.js` - Entry point example illustrating usage of the `ChatModel` (the default export from `python_bindings.js`). - Shows setting properties, calling methods, receiving streamed messages, and proper shutdown. - This file is intended for customization by users for their use case. Example usage: ```js import ChatModel from "./python_bindings.js"; const chat = new ChatModel({ "model": "something" }); await chat.setProperty("tokenizer", 123); const modelResponse = await chat.getModelPath(); console.log(modelResponse); // { model: "something" } const tokenizerResponse = await chat.getTokenizer(); console.log(tokenizerResponse); // { tokenizer: 123 } let response = await chat.increment({ by: 5 }); console.log("Incremented counter:", response.counter); chat.onMessage(function(data) { console.log(data); // receives streamed partial results }); chat.testStream(); chat.end(); ``` --- ### 2. `python_bindings.js` - Implements `Bindings` class which manages the Python subprocess using `python-shell`. - Sends JSON-formatted method calls with parameters to Python. - Receives JSON responses asynchronously. - Supports streaming JSON messages prefixed with `__STREAM__`. - Uses Proxy to dynamically route method calls to Python backend methods. - Provides: - `.setProperty(name, value)` to set Python controller properties. - `.getProperty(name)` to get Python controller properties. - `.onMessage(callback)` to register streaming data callback. - `.end()` to gracefully end the Python subprocess. Key methods: - `callMethod(method, ...args)`: sends a method call to Python and returns a promise resolving to the response. - `processSendQueue()`: ensures requests are sent sequentially. - `onMessage(callback)`: sets a callback for streaming partial data. --- ### 3. `python/controller.py` - Defines `Controller` class that inherits from `BaseController`. - Implements backend logic called by Node.js methods. - Example methods: - `getModelPath(params)` - `getTokenizer(params)` - `increment(params)` - `reset(params)` - `testStream(params)` — emits streaming partial data via `self.send()`. --- ### 4. `python/basecontroller.py` - Provides base class with common controller features: - `setProperty(params)`: sets attributes dynamically on controller. - `getProperty(params)`: gets attributes dynamically. - `send(data)`: sends partial streaming data through injected stream function. - `set_stream_func(stream_func)`: used to inject the stream callback for `send()`. --- ### 5. `python/router.py` - Acts as the Python message router. - Reads JSON messages from `stdin`, calls the appropriate `Controller` method, and writes JSON responses to `stdout`. - Supports streaming partial responses with the `__STREAM__` prefix. - Handles unknown methods gracefully by returning error JSON. --- ### 6. `python/index.py` - Runs the `Router` instance when executed as a script. - This is the Python entry point for the subprocess. --- ### 7. `router.js` - Alternative Node.js approach (not used directly by `Bindings`). - Spawns a Python process per method call. - Supports streaming messages. - More suited for stateless or separate process calls. --- ## How It Works (Workflow) 1. **Initialization** - `Bindings` starts a persistent Python subprocess running `python/index.py` via `python-shell`. - The Python process reads JSON messages from stdin and sends JSON responses on stdout. 2. **Method Calls** - Node.js sends JSON messages with `{ method: "methodName", params: { ... } }`. - Python calls the matching method in `Controller` class with params. - Python sends back `{ result: ... }` or `{ error: ... }`. 3. **Streaming** - Python can send partial results during long-running methods using the injected `send()` function. - These partial messages are prefixed with `__STREAM__` so Node.js can route them to the streaming callback. 4. **Dynamic Proxy** - The `Bindings` class uses a JavaScript Proxy to make any method call on the object automatically send the request to Python and return a Promise with the result. 5. **Property Setting/Getting** - Properties like `model` and `tokenizer` can be set or fetched through dedicated calls or via `.setProperty()`. 6. **Shutdown** - Calling `.end()` ends the Python process cleanly. --- ## Usage Summary - Import `Bindings` or `ChatModel`. - Instantiate with initial properties if desired. - Call any controller method asynchronously on the instance. - Use `.onMessage()` to handle streamed data. - Call `.end()` to terminate the backend. --- ## Notes for Customization - Edit `controller.py` to add your own Python logic and methods. - Update `index.js` for your application-specific flow. - Ensure Python methods return JSON serializable objects. - Implement streaming with `send()` and handle it in Node.js via `.onMessage()`. --- ## Example ```js import ChatModel from "./python_bindings.js"; const chat = new ChatModel({ model: "gpt" }); await chat.setProperty("tokenizer", 42); const info = await chat.getModelPath(); console.log(info); // { model: "gpt" } chat.onMessage(data => { console.log("Streamed partial data:", data); }); await chat.testStream(); await chat.end(); ``` --- This documentation provides a comprehensive understanding of the binding class design and usage to integrate Python backend logic into a Node.js environment efficiently.