

Discover more from hrbrmstr's Daily Drop
Drop #246 (2023-04-24): A Peek Behind The GPT App Curtain
ChartGPT App Deep Dive; Recharts; Ace, CodeMirror, and Monaco: A Comparison of the Code Editors You Use in the Browser
I've had a few requests to dig into how to best make use of LLMs over the past 2-3 months. So, today we pick apart a new app that is a wonderful showcase for how to use and develop with this new tech. We then follow up with a charting library that app uses, and close with a tie in topic to a recent Drop.
ChartGPT App Deep Dive
ChartGPT (GH) is a small application written by Kate Reznykova, self-described as a coder who is now, primarily, a venture capitalist (VC). Kate put together a really nice example of a focused, practical use of our nascent GPT overlords.
Kate's success as both a developer and VC has made it possible to showcase this example for free, and you can even use the hidden API for free (for now, anyway).
Why am I mentioning that “free” thing?
Well, I'm working on figuring out how to cover this space (LLMs, OpenAI GPTs, etc.) and not leave behind the talented humans who can't jump on the roller coaster for a thrill ride because they fail to meet the “you must be this tall…” critera — which, in this case, is being able to afford to use an OpenAPI API key or freemium AI services.
So, while I may include links to services or repos that do require a paid access key, the focus of including said resources will be more to point out how they're built, and also how the prompts were set up. It's bad enough that greedy corporations have, are, and will continue to (stupidly) replace capable humans with these stochastic automatons. I refuse to be part of the problem, and will continue to do my best to help bring more humans into the coding/tech fold, vs. marginalize them further.
In this example, the model being used costs $0.0200 per 1K tokens. I realize that's not going to break the bank. But, I ended up spending a dollar (yes, that's also not a bank-breaking number) on the examples I used in a previous Drop that demonstrated a CLI tool for generating shell commands from text descriptions. There weren't many commands in that post, and I haven't used it since, but daily use of it would have added up pretty quickly.
Now, let's see how Kate built this, so you can play along at home.
If you haven't dug in to this space on the API side, it may be beneficial to explain some of the tuning parameters that control the behavior of AI language model which Kate is using in the API calls.
temperature: this parameter controls the randomness of the model's output. A higher temperature (e.g., 1.0) will result in more diverse and creative text, while a lower temperature (e.g., 0.1) will produce more focused and deterministic text. By adjusting the temperature, you can balance between exploration (high temperature) and exploitation (low temperature) of the model's knowledge.
frequency penalty: this parameter penalizes tokens that occur frequently in the text. By increasing the frequency penalty, you can discourage the model from repeating common phrases and encourage it to generate more diverse and unique text. A value of 0 means no penalty, while a higher value (e.g., 1.0) will strongly penalize frequent tokens.
presence penalty: this parameter penalizes tokens that have already appeared in the text. Increasing the presence penalty helps prevent the model from repeating the same words or phrases in its output. A value of 0 means no penalty, while a higher value (e.g., 1.0) will strongly penalize tokens that have already been used.
logprobs: this parameter determines the number of most probable tokens (along with their probabilities) to return for each token generated by the model. Logprobs can be useful for analyzing the model's confidence in its output or for implementing custom sampling strategies. A value of 0 means that no logprob information will be returned, while a higher value (e.g., 10) will return the top 10 most probable tokens and their probabilities.
max tokens: This parameter sets an upper limit on the number of tokens generated by the model in its response. By setting max tokens, you can control the length of the output to fit specific use cases or constraints. Note that if the model generates fewer tokens than the max tokens value, it will stop generating text when it deems it has completed its response.
With all that out of the way, this is the prompt I used for this example:
i need a bar chart with the following values
that represent number of widgets sold (in millions)
Jan through Dec.
Here are the values: 39 68 98 29 8 18 20 15 9 41 50 22
There are two parts to how the application works. The first part identifies the chart type:
Source:
https://github.com/whoiskatrin/chart-gpt/blob/main/pages/api/get-type.ts
API endpoint:
https://www.chartgpt.dev/api/get-type
Kate developed the following prompt to bend the text-davinci-003 model to her will:
The following are the possible chart types supported by the code provided:
area, bar, line, composed, scatter, pie, radar, radialBar, treemap, and funnel.
Given the user input: ${inputData}, identify the chart type the user wants to display.
Return just one word.
The result from my prompt was (obviously):
"Bar"
The second part has the model put the data into the proper format:
Source:
https://github.com/whoiskatrin/chart-gpt/blob/main/pages/api/parse-graph.ts
API endpoint:
https://www.chartgpt.dev/api/parse-graph
This is the initialization prompt:
Generate a valid JSON in which each element is an object.
Strictly using this FORMAT and naming:
[{ \"name\": \"a\", \"value\": 12 }]
for the following description for Recharts.
i need a bar chart with the following values
that represent number of widgets sold (in millions)
Jan through Dec.
Here are the values: 39 68 98 29 8 18 20 15 9 41 50 22
And, this is the output from the model:
[
{ "name": "Jan", "value": 39 },
{ "name": "Feb", "value": 68 },
{ "name": "Mar", "value": 98 },
{ "name": "Apr", "value": 29 },
{ "name": "May", "value": 8 },
{ "name": "Jun", "value": 18 },
{ "name": "Jul", "value": 20 },
{ "name": "Aug", "value": 15 },
{ "name": "Sep", "value": 9 },
{ "name": "Oct", "value": 41 },
{ "name": "Nov", "value": 50 },
{ "name": "Dec", "value": 22 }
]
The entire app is pretty compact and laid out quite well, so it's not a bad one to riff from (if you can afford to). One riff idea: come up with a way to let the user specify colors/styles for the chart. Another is to have the chart be a bit smarter about the contents. e.g.,
This is also a decent example of how you can have these giant LLMs perform a task with some amount of trustable determinism. You just need to define the problem space pretty well and have the domain be fairly limited (unless you wire up your own data/context to the engines).
In the next section, we'll take a look at the charting library Kate chose to work with this model app.
Recharts
Recharts is a flexible and impressively intuitive charting React library powered by D3.js. It provides a simple yet versatile way to create a wide variety of charts, from basic bar and line charts to more advanced scatter plots, Sankey diagrams, and treemaps. While readers know I'm partial to other JS frameworks, React is super popular, and Recharts does show off how elegant React components can be.
As one might expect in 2023, Recharts sports a wide range of customization options, allowing us to easily tailor the appearance and behavior of the charts to fit specific requirements. This includes adjusting colors, fonts, axes, tooltips, etc.
The generated charts are fully responsive and automatically adjust based on the available space, making it easy to create data visualizations that look great on any device. Plus, they're fully interactive, and you can define what happens on hover and click/touch events.
You can see how to use it in the repo linked in the first section, but I think Substack won't mangle a small example too horribly here:
import React from 'react';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from 'recharts';
// this data should look familiar!
const data = [
{ name: "Jan", value: 39 },
{ name: "Feb", value: 68 },
{ name: "Mar", value: 98 },
{ name: "Apr", value: 29 },
{ name: "May", value: 8 },
{ name: "Jun", value: 18 },
{ name: "Jul", value: 20 },
{ name: "Aug", value: 15 },
{ name: "Sep", value: 9 },
{ name: "Oct", value: 41 },
{ name: "Nov", value: 50 },
{ name: "Dec", value: 22 }
];
const MyBarChart = () => (
<BarChart width={600} height={300} data={data}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="value" fill="#8884d8" />
</BarChart>
);
You can kind of see why Kate chose it as the medium, though, Observable Plot has a similar composable idiom.
If you use or have been learning React, this could be a fun way to get more proficient with it.
Ace, CodeMirror, and Monaco: A Comparison of the Code Editors You Use in the Browser
The weekend Bonu$ Drop (the content of which will trickle out to the M-F free editions) mentioned a relatively new and lightweight playground for javascript hacking that you can use online or self-host. It uses CodeMirror, a popular in-browser editor framework, to facilitate the input for the playground.
In my WebRIDERr WebR (WASM-ified R) experiment, I leaned into another in-browser editor framework from Microsoft — Monaco; and, RStudio, an app I use daily, is based on the Ace editor.
The folks at ReplIt — a far more advanced version of the tool I linked to — did a fine job deconstructing the three in “Ace, CodeMirror, and Monaco: A Comparison of the Code Editors You Use in the Browser”, and I thought this would be a nice follow-on from said bonus edition (and some good background material for when I post that section in a week or two).
FIN
Year progress: ▓▓▓▓▓░░░░░░░░░░ 31%
☮