Creating JavaScript Callback Functions


Highcharts (JS) is known for how easy it is to configure and style beautiful, interactive data visualizations. One of the core tools it uses to achieve this is callback functions.

These can be used throughout your chart configuration, and let you write custom code that will do what you need it to do in specific situations. It is incredibly powerful!

However, Highcharts (JS) is a JavaScript suite, and that means that it only works with JavaScript callback functions. So if we’re using Highcharts for Python, how do we create JavaScript callback functions that Highcharts (JS) will know how to leverage?

The answer is simple: Highcharts for Python provides a special CallbackFunction class that you can use to create JavaScript callback functions. When using this class, you can either:

  1. Write your own JavaScript function and the CallbackFunction class will serialize it to JavaScript when needed, or

  2. You can write a callback function in Python, and rely on either OpenAI’s GPT or Anthropic’s Claude generative AI model to suggest an equivalent JavaScript function.

Because you’re using Highcharts for Python, let’s look at the AI-driven approach first, because we already know how to write Python code. No JavaScript needed!


Creating Callback Functions using Generative AI

The CallbackFunction class has a special helper class method called .from_python() which can automatically create a CallbackFunction instance containing the JavaScript function that you need.

Here’s how that works. Let’s imagine a scenario where we want a custom tooltip formatter function that customizes the content of each data point’s tooltips. We can write that function in Python like so:

def my_custom_formatter():
    return f'The value for <b>{this.x}</b> is <b>{this.y}</b>.'

Really pretty straightforward, right? Now we can produce its equivalent by passing my_custom_formatter as an argument to .from_python():

my_callback = CallbackFunction.from_python(my_custom_formatter)

What the .from_python() method call will do is:

  1. It will take the Python function’s source code, and pass it to the generative AI model of your choice.

  2. The AI will return a JavaScript function that the AI believes will do the same thing as your Python function.

  3. And it will then load that JavaScript function into a new CallbackFunction instance.

Now, when you use this CallbackFunction instance in your chart configuration, it will get serialized to its approrpriate JavaScript source code form when appropriate, for example when calling Chart.display() or Chart.to_js_literal().

Using Different Models

Highcharts for Python supports different models provided by OpenAI and Anthropic.

OpenAI’s models in particular differ based on the version of GPT that the model supports, as well as the number of tokens that they allow (more tokens mean they can convert more complicated/longer function). Most typical callback functions should be converted reasonably reliably using the default model gpt-3.5-turbo, though others are available:

  • OpenAI

    • 'gpt-3.5-turbo' (default)

    • 'gpt-3.5-turbo-16k'

    • 'gpt-4'

    • 'gpt-4-32k'

  • Anthropic

    • 'claude-instant-1'

    • 'claude-2'

To use a different model, simply pass the model argument to the .from_python() method:

my_callback = CallbackFunction.from_python(my_custom_formatter, model = "gpt-4")

Authenticating with Your AI Provider

Caution

Because this relies on the outside APIs exposed by OpenAI and Anthropic, if you wish to use one of their models you must supply your own API key. These are paid services which they provide, and so you will be incurring costs by using these generative AIs.

To use one of the supported AI models, you must have a valid user/customer account with either OpenAI or Anthropic. You must also have an API key to their respective platform that has permission to use the model you request. You can set your account up and get the relevant API key from each of the AI providers, respectively.

When you have the API key, you can pass it in as an argument (api_key) to the .from_python() method:

my_callback = CallbackFunction.from_python(my_custom_formatter, api_key = "YOUR-API-KEY-GOES-HERE")

However, if you do not supply an explicit api_key value, Highcharts for Python will look for the API key in your OPENAI_API_KEY or ANTHROPIC_API_KEY environment variables.

Tip

BEST PRACTICE: Treat your API key as a highly-sensitive piece of information. It should never be listed in your source code, or in your Jupyter Notebook. It should only be read from environment variables, which in turn should get set with as few places where your API key is visible/available as possible.

Reviewing Your JavaScript Code

Warning

Generating the JavaScript source code is not deterministic. That means that it may not be correct, and we STRONGLY recommend reviewing it before using it in a production application.

Every single generative AI is known to have issues - whether “hallucinations”, biases, or incoherence. We cannot stress enough:

DO NOT RELY ON AI-GENERATED CODE IN PRODUCTION WITHOUT HUMAN REVIEW.

That being said, for “quick and dirty” EDA, fast prototyping, etc. the functionality may be “good enough”.

Once you have created a CallbackFunction instance using the .from_python() method, you can review the JavaScript source code that was generated by calling str() on your CallbackFunction instance:

print(str(my_callback))

# Output:
# function my_custom_formatter() { return 'The value for <b>' + this.x + '</b> is <b>' + this.y + '</b>.'; }

We STRONGLY recommend reviewing the JavaScript source code that was generated before using it in production. Even if you are not a JavaScript expert, since you know Python and you know what your function should be doing, you can probably follow along close-enough to make sure the JavaScript code “looks right”.

Tip

BEST PRACTICE: Never let the AI generate JavaScript code based on user-entered Python code.

Doing so may introduce unintended security vulnerabilities into your application, and should be considered VERY bad practice.


Creating Callback Functions Directly

If you do not wish to use generative AI to create your callback functions, you can simply create CallbackFunction instances directly. You can do this by:

Instantiating the CallbackFunction Directly

my_callback = CallbackFunction(function_name = 'my_formatter',
                               arguments = None,
                               body = """return 'The value for <b>' + this.x + '</b> is <b>' + this.y + '</b>.';""")

When instantiating the callback function directly, you supply the body of the function as a string to the body argument. A best practice is to use Python’s triple-quote syntax to make it easier to handle quotation marks within your JavaScript code.

Using .from_js_literal()

If you have your JavaScript function in a string, you can use the CallbackFunction.from_js_literal() class method to create the callback function instance:

callback_as_str = """function my_formatter() {
   return 'The value for <b>' + this.x + '</b> is <b>' + this.y + '</b>.';
}"""

my_callback = CallbackFunction.from_js_literal(callback_as_str)

And that’s it! When your CallbackFunction instances are used in your chart configuration, they will automatically be serialized to the appropriate JavaScript syntax when needed.