Integrating TensorFlow with Appwrite

The TensorFlow API allows you to create powerful machine learning models for various tasks. This tutorial will guide you through the process of setting up a TensorFlow-based text generation model and integrating it into your Appwrite project.

We'll create a function that uses TensorFlow to generate text completions based on a given prompt. Using Appwrite functions, we'll build a user interface that allows users to input text and see the generated completion.

Prerequisites

  • An Appwrite Project

  • Basic knowledge of Python and TensorFlow

1

Create new function

Head to the Appwrite Console, click on Functions in the left sidebar, and then click on the Create Function button.

Create function screen

Create function screen

  1. In the Appwrite Console's sidebar, click Functions.

  2. Click Create function.

  3. Under Connect Git repository, select your provider.

  4. After connecting to GitHub, under Quick start, select the Python ML starter template.

  5. In the Variables step, add any necessary variables like APPWRITE_API_KEY, APPWRITE_ENDPOINT, and APPWRITE_FUNCTION_PROJECT_ID.

  6. Follow the step-by-step wizard to create the function.

2

Add TensorFlow and necessary packages

Once the function is created, navigate to the freshly created repository and clone it to your local machine.

Add the necessary dependencies in the requirements.txt file:

tensorflow
numpy

Install these packages by running:

Bash
pip install -r requirements.txt
3

Train the TensorFlow model

Create a src/train.py file to train your TensorFlow model. This script will download a dataset, preprocess it, and train a model.

Python
import tensorflow as tf
import numpy as np
import os

def main():
    path_to_file = tf.keras.utils.get_file(
        "shakespeare.txt",
        "https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt",
    )
    text = open(path_to_file, "rb").read().decode(encoding="utf-8")
    vocab = sorted(set(text))
    char2idx = {u: i for i, u in enumerate(vocab)}
    idx2char = np.array(vocab)

    text_as_int = np.array([char2idx[c] for c in text])
    seq_length = 100
    char_dataset = tf.data.Dataset.from_tensor_slices(text_as_int)
    sequences = char_dataset.batch(seq_length + 1, drop_remainder=True)

    def split_input_target(chunk):
        input_text = chunk[:-1]
        target_text = chunk[1:]
        return input_text, target_text

    dataset = sequences.map(split_input_target)
    BATCH_SIZE = 64
    BUFFER_SIZE = 10000
    dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)

    vocab_size = len(vocab)
    embedding_dim = 256
    rnn_units = 1024

    model = tf.keras.Sequential(
        [
            tf.keras.layers.Embedding(
                vocab_size, embedding_dim, batch_input_shape=[BATCH_SIZE, None]
            ),
            tf.keras.layers.GRU(
                rnn_units,
                return_sequences=True,
                stateful=True,
                recurrent_initializer="glorot_uniform",
            ),
            tf.keras.layers.Dense(vocab_size),
        ]
    )

    def loss(labels, logits):
        return tf.keras.losses.sparse_categorical_crossentropy(
            labels, logits, from_logits=True
        )

    model.compile(optimizer="adam", loss=loss)

    EPOCHS = 10
    checkpoint_dir = "./training_checkpoints"
    checkpoint_prefix = f"{checkpoint_dir}/ckpt_{{epoch}}"

    checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
        filepath=checkpoint_prefix, save_weights_only=True
    )

    model.fit(dataset, epochs=EPOCHS, callbacks=[checkpoint_callback])

    model.save("text_generation_model.h5")
    np.save("char2idx.npy", char2idx)
    np.save("idx2char.npy", idx2char)

    os.remove(path_to_file)

if __name__ == "__main__":
    main()
4

Create utility functions

Create a src/utils.py file with utility functions to handle file retrieval and error handling.

Python
import os

__dirname = os.path.dirname(os.path.abspath(__file__))
static_folder = os.path.join(__dirname, "../static")

def get_static_file(file_name: str) -> str:
    file_path = os.path.join(static_folder, file_name)
    with open(file_path, "r") as file:
        return file.read()

def throw_if_missing(obj: object, keys: list[str]) -> None:
    missing = [key for key in keys if key not in obj or not obj[key]]
    if missing:
        raise ValueError(f"Missing required fields: {', '.join(missing)}")
5

Handle GET request

Write the GET request handler in the src/main.py file. This handler will return a static HTML page.

Python
import tensorflow as tf
import numpy as np
from .utils import get_static_file, throw_if_missing

def main(context):
    if context.req.method == "GET":
        return context.res.text(
            get_static_file("index.html"),
            200,
            {"content-type": "text/html; charset=utf-8"},
        )
6

Handle POST request

Add the methods necessary to integrate with the TensorFlow model. For now, call a placeholder function generate_text that returns the prompt as is.

Python
def main(context):
    if context.req.method == "GET":
        return context.res.text(
            get_static_file("index.html"),
            200,
            {"content-type": "text/html; charset=utf-8"},
        )

    try:
        throw_if_missing(context.req.body, ["prompt"])
    except ValueError as err:
        return context.res.json({"ok": False, "error": err.message}, 400)

    prompt = context.req.body["prompt"]
    generated_text = generate_text(prompt)
    return context.res.json({"ok": True, "completion": generated_text}, 200)
7

Build the generate_text function

Create the generate_text function in the src/main.py file to generate text completions using the TensorFlow model.

Python
model = tf.keras.models.load_model("text_generation_model.h5")
char2idx = np.load("char2idx.npy", allow_pickle=True).item()
idx2char = np.load("idx2char.npy", allow_pickle=True)

def generate_text(prompt):
    input_eval = [char2idx[s] for s in prompt]
    input_eval = tf.expand_dims(input_eval, 0)

    text_generated = []
    temperature = 1.0

    model.reset_states()
    for _ in range(1000):
        predictions = model(input_eval)
        predictions = tf.squeeze(predictions, 0)
        predictions = predictions / temperature
        predicted_id = tf.random.categorical(predictions, num_samples=1)[-1, 0].numpy()

        input_eval = tf.expand_dims([predicted_id], 0)
        text_generated.append(idx2char[predicted_id])

    return prompt + "".join(text_generated)
8

Create web page

Create a HTML web page that the function will serve. Create a new file at static/index.html with some HTML boilerplate:

HTML
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Generate with TensorFlow demo</title>

    <script>
      async function onSubmit(prompt) {
        const response = await fetch("/", {
          method: "POST",
          body: JSON.stringify({ prompt }),
          headers: {
            "Content-Type": "application/json",
          },
        });

        const json = await response.json();

        if (!json.ok || json.error) {
          alert(json.error);
        }

        return json.completion;
      }
    </script>

    <script src="//unpkg.com/alpinejs" defer></script>

    <link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink" />
    <link rel="stylesheet" href="https://unpkg.com/@appwrite.io/pink-icons" />
  </head>
  <body class="theme-dark">
    <main class="main-content">
      <div class="top-cover u-padding-block-end-56">
        <div class="container">
          <div
            class="u-flex u-gap-16 u-flex-justify-center u-margin-block-start-16"
          >
            <h1 class="heading-level-1">Generate with TensorFlow demo</h1>
            <code class="u-un-break-text"></code>
          </div>
          <p
            class="body-text-1 u-normal u-margin-block-start-8"
            style="max-width: 50rem"
          >
            Use this page to test your implementation with TensorFlow. Enter
            text and receive the model output as a response.
          </p>
        </div>
      </div>
      <div
        class="container u-margin-block-start-negative-56"
        x-data="{ prompt: '', answer: '', loading: false }"
      >
        <div class="card u-flex u-gap-24 u-flex-vertical">
          <div class="u-flex u-cross-center u-gap-8">
            <div
              class="input-text-wrapper is-with-end-button u-width-full-line"
            >
              <input x-model="prompt" type="search" placeholder="Question" />
              <div class="icon-search" aria-hidden="true"></div>
            </div>

            <button
              class="button"
              x-bind:disabled="loading"
              x-on:click="async () => { loading = true; answer = ''; try { answer = await onSubmit(prompt) } catch(err) { console.error(err); } finally { loading = false; } }"
            >
              <span class="text">Submit</span>
            </button>
          </div>
          <template x-if="answer">
            <div class="u-flex u-flex-vertical u-gap-12">
              <div class="u-flex u-flex-vertical u-gap-12 card">
                <div class="u-flex u-gap-12">
                  <h5 class="eyebrow-heading-2">TensorFlow Model:</h5>
                </div>

                <div style="overflow-x: hidden; line-break: anywhere">
                  <p class="u-color-text-gray" x-text="answer"></p>
                </div>
              </div>
            </div>
          </template>
        </div>
      </div>
    </main>
  </body>
</html>

The form will allow users to submit their text to the Appwrite function through a POST request. The Appwrite function will call the TensorFlow model and return the generated text to the user.

9

Test the function

Now that the function is deployed, test it by visiting the function URL in your browser. This should show the UI created earlier. To test it, write a prompt and click the submit button. After a brief moment, you should see the generated text from the TensorFlow model.

This concludes the tutorial on integrating TensorFlow with Appwrite. You now have a working example of a text generation model integrated with Appwrite functions!