· 10 min read

Polyglot Notebooks: JavaScript LangChain Azure OpenAI

Polyglot Notebooks, früher bekannt als NET Interactive Notebooks, sind ähnlich wie Jupyter Notebooks. Was ist das und wie verwendet man diese Notebooks? Ist das nur etwas für Data Scientists? Kann man auch JavaScript einsetzen und damit die Library LangChain.js ansprechen, wie man es in diversen Videos sieht? Dort allerdings wird immer die Python Variante von LangChain zusammen mit Jupyter verwendet. Notebooks werden gerne zum Vorzeigen, zum Lehren benutzt. Könnte ich das für ein Azure Tutorial einsetzen? All dies wollte ich herausfinden und das Ergebnis fasse ich in diesem Artikel zusammen.

0. Motivation

Als ich nach Möglichkeiten suchte, wie man OpenAI in eigenen Programmen einbinden kann, stieß ich auf viele Videos, in denen Jupyter Notebooks verwendet werden. Dort werden Python-Codeausschnitte ausgeführt und die Ergebnisse entweder im selben Dokument angezeigt oder im nächsten Codeabschnitt weiterverarbeitet. Bei den Updates von Visual Studio Code bemerkte ich auch immer wieder Verbesserungen, die für Notebooks gemacht wurden. Ich hatte schon seit einiger Zeit vor, mir das genauer anzuschauen, also habe ich mich darüber informiert. Mein Ziel war es, LangChain mit JavaScript in einem solchen Notebook verwenden zu können. LangChain ist eine Bibliothek, die ich auch in meinem TypeScript-Programm nutze, um KIs anzusprechen. Dies wäre praktisch, um es vorzuführen, aber auch während des Entwicklungsprozesses einer KI-gesteuerten Anwendung sehr hilfreich. ( siehe vorigen Blog Eintrag: Microsoft To Do + Artificial Intelligence in To Do Vo Do).

Das zugehörige YouTube Video, zeigt nur mein Hauptanliegen, wie man LangChain JavaScript mit Polyglot Notebook verwendet.

Mit Polyglot Notebooks LangChain JavaScript verwenden:

1. Was ist Polyglot Notebook

Notebooks, wie Jupyter Notebooks oder Polyglot Notebooks, sind Dokumente, die Abschnitte, sogenannte Zellen, mit ausführbarem Programmcode, Markdown Abschnitte zum Dokumentieren enthalten und auch der Output der Programmteile wird gleich in dem einen Dokument angezeigt. Somit ist das sehr gut geeignet zum Erlernen von Programmieren, denn man kann den Programmcode anpassen, wieder ausführen und die zugehörigen Erklärungen bzw. Anleitungen lesen. Dies wird zum Beispiel in Learn to code C#: .NET Interactive Notebooks for C# verwendet.

Polyglot Notebooks ist eine Erweiterung von Microsoft für VS Code (Visual Studio Code), es basiert auf .NET Interactive und man kann mit diversen Programmiersprachen arbeiten, darunter auch JavaScript. Die kurze Beschreibung in der Extension erklärt die Grundlagen sehr gut: Polyglot Notebooks.

Ich habe ein GitHub Repo angelegt, wo man meine Versuche gut nachvollziehen kann. Ich hoffe, das hilft einem beim Starten mit Polyglot Notebooks und JavaScript weiter.

PolyglotNotebook-1

PolyglotNotebook-2-Cell-Language

Anmerkung: Data Scientists arbeiten gerne mit vielen Zwischenschritten. Sie laden sich die Daten, dann werden sie angezeigt und mit anderen Daten verknüpft und wieder angezeigt und gefiltert usw. Diese Vorgangsweise wird von Notebooks sehr gut unterstützt und daher ist es kein Wunder, dass Juypiter Notebooks mit Python dort stark verbreitet sind.

2. Verwendung von Polyglot Notebook

Ich will hier nur einige Tipps und Hintergrundwissen geben, die einen den Start erleichtern.

Visual Studio Code Extension Polyglot Notebooks installieren.

Es werden zwei Arten von Filetypen unterstützt das mit Jupyter kompatible ipynb File (Json Format) und das Polyglot eigene dib File (Markdown Format).

Das *.dib File speichert keine Lösungen und dadurch war das mit git einfacher zu benutzen, da sich das File nicht ständig ändert und so habe ich meist dieses Format verwendet.

Anmerkung: .NET Interactive ist ein Notebook Kernel. Man könnte daher in Jupyter ein in Polyglot erstelltes ipynb File hochladen und dort dann als Kernel .NET Interaktiv auswählen und das Notebook wird funktionieren. - Das habe ich aber nicht getestet.

Eine Datei anlegen

Man beginnt jetzt nicht eine leere Datei mit entsprechender Extension anzulegen, sondern lässt das die Polyglot VS Code Erweiterung machen, indem man die Command Palette öffnet und dort dann Polyglot Notebook: Create new blank notebook auswählt (Command Palette: Ctrl+Shift+P in Windows oder Cmd+Shift+P in MacOS). Ctrl+Shift+Alt+N unter Windows ist eine Abkürzung.

Magic Command

Magic Commands - diese Befehle beginnen mit: #! (Bei Jupiter Python: %) Mit Magic Commands kann man z.B. Imports in C# machen, die verwendete Sprache der Zelle verändern oder auch Variablen von einer Sprache zur anderen transportieren.

#r "nuget: Microsoft.DotNet.Interactive.Kql, *-*"
#!connect kql --kernel-name samples_database --cluster "https://help.kusto.windows.net" --database "Samples"

#!html
#!js  oder #!javascript

#!share
#!set

Ausführung einer Programm Zelle dauert ungewöhnlich lange

Beim Laden einer Library soll man dem System Zeit lassen, dass kann schon mal dauern, bis alles geladen ist.

Trotzdem kommt es immer wieder mal vor, dass man den Vorgang abbrechen muss. Dazu gibt es zwar einen Button ganz oben, aber der funktioniert häufig nicht. Den Button daneben: Kernel neu starten, funktioniert schon besser. Ich habe aber auch die Erfahrung gemacht, vor allem beim Inkludieren von anderen Notebook Files und Umgebungsvariablen ist ein Neustart von VS Code oft hilfreich.

PolyglotNotebook-3-Restart

REPL vs Notebooks

Repl, dieser Begriff wird immer wieder mal im Zusammenhang mit Notebooks genannt. Eine gute Erklärung fand ich in der .NET Interactive FAQ: Eine “Read-Eval-Print-Loop” oder REPL ist eine interaktive textbasierte Schnittstelle zum inkrementellen Erstellen eines Programms, zum Bereitstellen von Eingaben und zum Anzeigen seiner Ausgabe. Während man in der Vergangenheit normalerweise über ein Terminal mit einer REPL interagiert, gibt es auch eine Reihe von GUI-basierten REPLs. Notizbücher können als Beispiel betrachtet werden.

3. Polyglot Notebooks und JavaScript Libraries

Bei der Verwendung von JavaScript als Programmiersprache ist man etwas enttäuscht. Die Autovervollständigung funktioniert aktuell leider nicht. Auch scheinen keine Typen überprüft zu werden, wie man es inzwischen von VS Code gewohnt ist, wenn es Zugriff auf die Type Definitions von TypeScript hat. Die Unterstützung von TypeScript wäre grundsätzlich sehr wünschenswert.

Besonders schwierig ist das Laden von Libraries. Das ist zum einen ein grundsätzliches Problem von JavaScript, dass es für jeden Anwendungsfall unterschiedliche Möglichkeiten gibt (Browser, Node.js, ..), aber auch dass die Libraries nicht alle Arten anbieten.

Polyglot Notebooks unterstützt zwei Arten ( .NET Interactive Using JavaScript in Polyglot Notebooks):

EcmaScript Module mit import

uuid = await import("https://cdn.jsdelivr.net/npm/uuid@latest/+esm");

const id = uuid.v4();
console.log(id);

RequireJS

function loadLodash() {
    return new Promise((resolve, reject) => {
      require.config({
        paths: {
          lodash: 'https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min'
        },
      });
      require(['lodash'], (lodash) => {
        resolve(lodash);
      }, reject);
    });
  }
  
async function loadDependencies() {
  try {
    _ = await loadLodash();
  } catch (error) {
    console.error('Failed to load lodash:', error);
  }
}

await loadDependencies();

const input = "Gurke Tomate Fisch";
kC2 = _.kebabCase(input);
console.log(kC2);

LangChain JavaScript

In der Doku findet man, immer wieder Beispiele, wie Libraries geladen werden, indem der CDN (Content Delivery Network) cdn.jsdelivr.net verwendet wird, so wie ich das auch oben mit der uuid Library gezeigt habe. Leider scheint es mit LangChain dort aktuell Schwierigkeiten zu geben.

https://cdn.jsdelivr.net/npm/langchain@latest/+esm

-> Error: Error: The requested module ‘/npm/openai@3.3.0/+esm’ does not provide an export named ‘Configuration’

Doch der CDN esm.sh kann die Library und ihre Abhängigkeiten richtig ausliefern:

// Importing Langchain library
langchain = await import('https://esm.sh/langchain@latest');
langchainEmbeddings = await import('https://esm.sh/langchain@0.0.105/embeddings');

Wie kommt man zur Library

In den Browser die gewünschte Lib eingeben: Z.B. für langchain/vectorstores/memory: Also:

https://esm.sh/langchain/vectorstores/memory

Wenn man weitergeleitet wird, indem die aktuelle Version dazugefügt wird, dann sollte dieser Link funktionieren. =>

https://esm.sh/langchain@0.0.105/vectorstores/memory

Statt der Version kann man immer auch das Wort latest verwenden.

https://esm.sh/langchain@latest/vectorstores/memory

Anmerkung: Diese library langchain/vectorstores/memory habe ich jetzt nicht in meinen Beispielen verwende. Beim Importieren gibt es aber zumindest keinen Fehler.

OpenAI API und Azure OpenAI Service

Die API kann dann, wie in LangChain JS dokumentiert, verwendet werden.

Meine einfachen Beispiele aus dem GitHub Repo: hlc-polyglot-samples:

OpenAI API

// Importing Langchain library
langchain = await import('https://esm.sh/langchain@latest');

// Creating a Langchain instance
const temperature = 0.9;
const modelName = "gpt-3.5-turbo";
const maxTokens = 2000;

langchainInstance = new langchain.OpenAI({
    openAIApiKey: env.openaiApiKey, 
    temperature: temperature,
    modelName: modelName,
    maxTokens: maxTokens,
 });

// Using Langchain methods
const res = await langchainInstance.call(
    "What would be a good company name a company that makes colorful socks?"
  );
console.log(res);

Azure API Service

// Importing Langchain library
langchain = await import('https://esm.sh/langchain@latest');

// Creating a Langchain instance

const instanceName = "hlc-azure-openai-test-001";
const deploymentName = "test-gpt-35-turbo";
const apiVersion = "2023-05-15";

const temperature = 0.9;
const maxTokens = 2000;

langchainInstance = new langchain.OpenAI({

    azureOpenAIApiKey: env.azureOpenaiApiKey,
    azureOpenAIApiInstanceName: instanceName,
    azureOpenAIApiDeploymentName: deploymentName,
    azureOpenAIApiVersion: apiVersion,

    temperature: temperature,
    maxTokens: maxTokens,
});

// Using Langchain methods
const res = await langchainInstance.call(
    "What would be a good company name a company that makes colorful socks?"
  );
console.log(res);

4. Environment

Ich habe zwei Möglichkeiten entwickelt, um Secrets zu speichern. Eine Methode besteht darin, sie in den Systemumgebungsvariablen abzulegen, wie es oft in Python-Präsentationen gezeigt wird. Die andere Möglichkeit besteht darin, die Secrets in einer separaten JSON-Datei zu speichern, die nicht in das Versionskontrollsystem eingecheckt wird.

Eine dritte Variante besteht darin, einen Benutzereingabe auszulösen, was zu einem Dialog führt, in dem man etwas eingeben kann. Diese Methode wird in der Polyglot Notebook-Dokumentation erklärt.

Variante 1: Umgebungsvariable

Da man process.env, wie in Node.js nicht zur Verfügung hat, habe ich den Umweg über C# gemacht:

common-read-env.dib:

C# Zelle:

// In PowerShell: set environment variables permanent:
// Setx ENV_POLYGLOT__OPENAI_API_KEY "sk-..."
// Setx ENV_POLYGLOT__AZURE_OPENAI_API_KEY "07..."

string computerName = Environment.GetEnvironmentVariable("COMPUTERNAME");
string openaiApiKey = Environment.GetEnvironmentVariable("ENV_POLYGLOT__OPENAI_API_KEY");
string azureOpenaiApiKey = Environment.GetEnvironmentVariable("ENV_POLYGLOT__AZURE_OPENAI_API_KEY");

object env = new {
    computerName = computerName,
    openaiApiKey = openaiApiKey,
    azureOpenaiApiKey = azureOpenaiApiKey
};

JavaScript Zelle:

#!set --name env --value @csharp:env

Damit kann das Einlesen der Umgebungsvariablen bequem in anderen JavaScript files verwendet werden:

#!import ../common-read-env.dib
console.log(env.computerName);

Variante 2: JSON File einlesen

Das File mit den Api Keys wurde als JSON gespeichert:

.env:

{
    "key0": "value0 - ok",
    "openaiApiKey" : "sk-123",
    "azureOpenaiApiKey" : "456"
}

common-read-env-file.dib:

Value (Raw Value Storage) Zelle:

#!value --from-file .env --name envJson

JavaScript Zelle:

#!share envJson --from value
env = JSON.parse(envJson);

Hier hat man allerdings das Problem, dass man den Pfad zum File relativ zum aktuellen File angibt. D.h. wenn dieses Notebook File in einem anderen inkludiert wird, dann stimmt womöglich der Pfad zur Datei .env nicht mehr. Aus diesem Grund habe ich den Pfad absolut hinterlegt:

#!value --from-file E:\Sourcecode\hlc\hlc-polyglot-samples\.env --name envJson

Man sollte natürlich in seiner .gitignore Datei verhindern, dass die Datei .env eingecheckt wird.

5. Azure

Zum Ausführen von Azure CLI oder Azure PowerShell Commandlets ist die PowerShell Zelle geeignet.

Dazu muss natürlich entsprechendes vorher installiert sein ( Azure CLI bzw. Azure PowerShell).

Man kann PowerShell Files auch in VS Code step by step debuggen, daher ist diese Möglichkeit mit einem Notebook nur in Fällen von Lehre interessant (Verwenden von Visual Studio Code für die Entwicklung mit PowerShell).

Die in der Doku erwähnte Möglichkeit der Verwendung der Azure Cloud Shell direkt aus einem Notebook aus, hat bei mir nicht funktioniert.

6. Datenanalyse

Einen kleinen Data Science Ausflug habe ich auch gemacht. Beim Beispiel 4-Azure OpenAI\sample-azure-openai-2-embeddings.dib habe ich mir die Embeddings Daten anzeigen lassen.

PolyglotNotebook-4-Embeddings-Text

Und zwar habe ich für zwei kurze Texte die Embeddings erzeugen lassen und diese dann via Plotly, das D3 und jQuery benutzt, anzeigen lassen. Zunächst sieht das sehr ähnlich aus, doch wenn man nun einen Ausschnitt anzeigt, sieht man, dass diese beiden Texte doch sehr unterschiedlich sein müssen.

PolyglotNotebook-5-Embedding-Diagram

Für diese Datenanalyse Vorgänge sind Notebooks wirklich sehr praktisch.

7. Fazit

Das Problem, dass die LangChain Library nicht von cdn.jsdelivr.net, sondern von esm.sh eingebunden werden muss, das herauszufinden hat leider viele Stunden gekostet. Das ist wohl kein Problem von Polyglot Notebook, sondern von LangChain oder jsdelivr. Ich denke, es wäre klüger gewesen, die Zeit in Python zu investieren, wenn man mit LangChain arbeiten will.

Man findet wenige Informationen über JavaScript und Polyglot Notebooks. JavaScript wird stiefmütterlich von Polyglot unterstützt (z.B. Autovervollständigung), weil es in dem Umfeld wohl wenig benutzt wird. Ich hoffe, dass es bei einem nächsten Update Verbesserungen gibt. Ich hätte noch lieber einen TypeScript Unterstützung.

Grundsätzlich finde ich das Notebook Konzept sehr interessant. Für iterative Entwicklung, wenn man z.B. eine API testen will, eignet sich das hervorragend. Die Idee ist, dass man dann den durch Ausprobieren erzeugten Code in sein Programm übernimmt. Auch zum Lehren bzw. Vorzeigen, kann ich mir das gut vorstellen. Wenn man damit eine Sprache lernen will, bin ich mir nicht ganz so sicher, denn die Oberfläche ist doch ein wenig verwirrend, wenn man VS Code nicht gewohnt ist.

Helmbergers Let's Code - Instagram quad: Polyglot Notebooks mit JavaScript LangChain Azure OpenAI

Artikel in diesem Blog

Back to Blog