Files
Python-bindings-for-nodejs/INNER_WORKING.md
2025-11-18 11:02:39 +01:00

5.9 KiB

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:

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

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.