Skip to content

Commit

Permalink
Merge pull request #80 from mikkelhegn/main
Browse files Browse the repository at this point in the history
Updating with new JS SDK and SpinKube deployment for Magic-8-ball
  • Loading branch information
mikkelhegn authored Sep 14, 2024
2 parents bef25d6 + b3291b1 commit cd2e167
Show file tree
Hide file tree
Showing 136 changed files with 26,718 additions and 8,685 deletions.
36 changes: 21 additions & 15 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,51 @@
# it avoids downloading any platform-specific binaries, and installs the required
# tools either through Debian's package manager, or through installation scripts
# that download the appropriate binaries.
FROM mcr.microsoft.com/devcontainers/base:jammy
FROM mcr.microsoft.com/devcontainers/base:ubuntu

# provided by docker
ARG TARGETARCH
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

RUN apt-get update && apt-get install -y \
nodejs \
npm \
python3-pip
# Install Python
RUN sudo apt update
RUN sudo apt install python3.10-venv -y

# Install Go
ARG GO_VERSION="1.22.1"
ARG GO_VERSION="1.23.1"
RUN wget -O go.tar.gz "https://go.dev/dl/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz" && \
tar -C /usr/local -xzf go.tar.gz && \
rm go.tar.gz
ENV PATH="$PATH:/usr/local/go/bin"

# Install TinyGo
ARG TINYGO_VERSION="0.31.1"
ARG TINYGO_VERSION="0.33.0"
RUN wget "https://github.com/tinygo-org/tinygo/releases/download/v${TINYGO_VERSION}/tinygo_${TINYGO_VERSION}_${TARGETARCH}.deb" && \
dpkg -i "tinygo_${TINYGO_VERSION}_${TARGETARCH}.deb"

# Install Rust
ENV PATH="$PATH:/home/$USERNAME/.cargo/bin"
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- \
-y --target wasm32-wasi

# Set the current user before node and spin install for plugins/templates
USER $USERNAME

# Install Spin
ARG SPIN_VERSION="v2.3.1"
ARG SPIN_VERSION="v2.7.0"
RUN mkdir -p /opt/spin && \
cd /opt/spin && \
curl -fsSL https://developer.fermyon.com/downloads/install.sh | bash -s -- -v $SPIN_VERSION && \
ln -s /opt/spin/spin /usr/local/bin/spin

# Set the current user before spin install for plugins/templates
USER $USERNAME

# Install Rust
ENV PATH="$PATH:/home/$USERNAME/.cargo/bin"
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | bash -s -- \
-y --target wasm32-wasi
# Install Node and NPM
ARG NODE_VERSION=20
ENV NVM_DIR=/home/${USERNAME}/.nvm
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
RUN . "$NVM_DIR/nvm.sh" && nvm install ${NODE_VERSION}
ENV PATH="/root/.nvm/versions/node/v${NODE_VERSION}/bin/:${PATH}"

# Install Spin Plugins & Templates
RUN spin plugins update && \
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

Welcome to the home for all Fermyon workshops!

Complete a workshop, submit a survey, and claim a ![**Slat KeyCap**](./media/slats-keycap-cropped.png)
The below workshops are available for you to go and run on your own, or use in an instructor-led setting. Feel free to have fun and share with the world :-)

Best - The Fermyon Team

## Workshops

Expand Down
53 changes: 29 additions & 24 deletions magic-8-ball/01-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ This document assumes you have followed [the setup guide](./00-setup.md) and hav

```bash
$ spin --version
spin 2.3.0 (ee6706c8 2024-02-29)
spin 2.7.0 (a111517 2024-07-30)
```

> Note: The workshop was last tested with Spin 2.7.0
[Spin](https://github.com/fermyon/spin) is an open source framework for building, distributing, and running serverless applications and microservices with WebAssembly (or Wasm).

Spin uses Wasm because of its portability, sandboxed execution environment, and near-native speed. [More and more languages have support for WebAssembly](https://www.fermyon.com/wasm-languages/webassembly-language-support), so you should be able to use your favorite language to build your first serverless application with Wasm.
Expand Down Expand Up @@ -135,7 +137,7 @@ You are now ready to expand your application. You can follow the [guide for buil
## b. Building your first Spin application with JavaScript or TypeScript

JavaScript is one of the most popular programming languages. Spin has an [experimental SDK and toolchain](https://github.com/fermyon/spin-js-sdk) for JavaScript and TypeScript which is based on [QuickJS](https://bellard.org/quickjs/), a small embeddable JavaScript runtime.
JavaScript is one of the most popular programming languages. Spin has an [experimental SDK and toolchain](https://github.com/fermyon/spin-js-sdk) for JavaScript and TypeScript.

> Note: you can [read more about how the Spin SDK for JavaScript and TypeScript is built on Fermyon's blog](https://www.fermyon.com/blog/spin-js-sdk).
Expand All @@ -144,25 +146,29 @@ Let's create a new Spin application in TypeScript, based on the HTTP template. T
```bash
$ spin new hello-typescript -t http-ts --accept-defaults && cd hello-typescript
$ tree
|-- README.md
|-- package.json
|-- spin.toml
|-- src
| -- index.ts
|-- tsconfig.json
|-- webpack.config.js
.
├── knitwit.json
├── package.json
├── spin.toml
├── src
│   ├── index.ts
│   └── spin.ts
├── tsconfig.json
└── webpack.config.js
```

> You can `sudo apt-get install tree` if you do not have `tree` installed.
Let's explore the `spin.toml` file. This is the Spin manifest file, which tells Spin what events should trigger what components. In our case we have a HTTP trigger at the route `/...` — a wildcard that matches any request sent to this application. The trigger sends requests to our component `hello-rust`. In more complex applications, you can define multiple components that are triggered for requests on different routes.

```toml
spin_manifest_version = 2

[application]
authors = ["Fermyon Engineering <[email protected]>"]
description = ""
name = "hello-typescript"
version = "0.1.0"
authors = ["Fermyon Engineering <[email protected]>"]
description = "Hello World Rust Application"

[[trigger.http]]
route = "/..."
Expand Down Expand Up @@ -211,32 +217,31 @@ The command will start Spin on port 3000. You can now access the application by

```bash
$ curl localhost:3000
Hello from TS-SDK
hello universe
```

That response is coming from the handler function for this component — in the case of a TypeScript component, defined in the index file from `src/index.ts`. Our entire application consists of a single function, `handleRequest`, which takes the HTTP request as an argument and returns an HTTP response.

Let's change the message body to "Hello, WebAssembly!":

```typescript
export const handleRequest: HandleRequest = async function (
request: HttpRequest
): Promise<HttpResponse> {
return {
status: 200,
headers: { "content-type": "text/plain" },
body: "Hello, WebAssembly!",
};
};
import { ResponseBuilder } from "@fermyon/spin-sdk";

export async function handler(req: Request, res: ResponseBuilder) {
console.log(req);
res.send("Hello, WebAssembly!");
}
```

We can now run `spin build` again, which will compile our component, and we can use the `--up` flag to automatically start the application, then send another request:

```bash
$ spin build --up
$ curl -v localhost:3000
< HTTP/1.1 200 OK
< content-type: text/plain
$ curl -i localhost:3000
HTTP/1.1 200 OK
content-length: 19
content-type: text/plain;charset=UTF-8
date: Thu, 12 Sep 2024 11:19:37 GMT

Hello, WebAssembly!
```
Expand Down
31 changes: 15 additions & 16 deletions magic-8-ball/02-json-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,36 +103,35 @@ We will create another Spin application in TypeScript based on the HTTP template
$ spin new magic-eight-ball -t http-ts
Description: A Magic 8 Ball App
HTTP path: /magic-8
Enable AoT Compilation [y/N]: N

$ cd magic-eight-ball
```

We will now write a HTTP component in our `index.ts` file that returns a Magic 8 Ball responses. Your `index.ts` file should look like this:

```typescript
import { HandleRequest, HttpRequest, HttpResponse } from "@fermyon/spin-sdk";

const encoder = new TextEncoder();

export const handleRequest: HandleRequest = async function (
request: HttpRequest
): Promise<HttpResponse> {
let answerJson = `{\"answer\": \"${answer()}\"}`;
return {
status: 200,
headers: { "Content-Type": "application/json" },
body: answerJson,
};
import { ResponseBuilder } from "@fermyon/spin-sdk";

interface Answer {
answer: String;
};

function answer(): string {
export async function handler(req: Request, res: ResponseBuilder) {
res.statusCode = 200;
res.headers.append("Content-Type", "application/json");
res.send(JSON.stringify(answer()));
}

function answer(): Answer {
let answers = [
"Ask again later.",
"Absolutely!",
"Unlikely",
"Simply put, no",
];
let idx = Math.floor(Math.random() * answers.length);
return answers[idx];
return { answer: answers[idx] };
}
```

Expand All @@ -151,7 +150,7 @@ Your Magic 8 Ball app is now running locally! Remember, we earlier had set our `

```bash
$ curl localhost:3000/magic-8
{"answer": "Absolutely!"}%
{"answer": "Absolutely!"}
```

> Note: you can find the complete applications used in this workshop in the [`apps` directory](./apps/).
Expand Down
85 changes: 45 additions & 40 deletions magic-8-ball/03-spin-ai.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,9 @@ fn answer(question: &str) -> Result<String> {
let answer = llm::infer_with_options(
model,
&prompt,
llm::InferencingParams {
llm::InferencingParams
max_tokens: 20,
repeat_penalty: 1.5,
repeat_penalty_last_n_token_count: 20,
repeat_penalty: 1.5, repeat_penalty_last_n_token_count: 20,
temperature: 0.25,
top_k: 5,
top_p: 0.25,
Expand All @@ -98,6 +97,15 @@ Now, build your application and see the [deploy to Fermyon Cloud section](#deplo
spin build
```

### Configure access to an AI model

By default, a given component of a Spin application will not have access to any Serverless AI models. Access must be provided explicitly via the Spin application’s manifest (the `spin.toml` file). We can give our `magic-8` component access to the llama2-chat model by adding the following ai_models configuration inside its `[[component]]` section:

```toml
[[component.magic-8-ball]]
ai_models = ["llama2-chat"]
```

## b. Building your Magic 8 Ball application with TypeScript

We need to modify our `magic-eight-ball` component to:
Expand All @@ -106,22 +114,20 @@ We need to modify our `magic-eight-ball` component to:
1. Get a yes/no question from the body of the HTTP request
1. Use the `Llm.infer` function Spin `Llm` library to generate a response to the question

First, create a `TextDecoder` at the top level:

```ts
const decoder = new TextDecoder();
```

Then, update the `handleRequest` function to get the question from the request body and return an error if
the body is empty:
Then, update the `handler` function to get the question from the request body. We also need to add `Llm` as an import from the Spin SDK:

```ts
const question = decoder.decode(request.body);
if (question.length == 0) {
return {
status: 400,
body: encoder.encode("No question asked").buffer,
};
import { ResponseBuilder, Llm } from "@fermyon/spin-sdk";

export async function handler(req: Request, res: ResponseBuilder) {
const response: Answer = await req.text().then(data => {
console.log(data);
return answer(data);
});
console.log(response);
res.statusCode = 200;
res.headers.append("Content-Type", "application/json");
res.send(JSON.stringify(response));
}
```

Expand All @@ -130,29 +136,30 @@ Use the `Llm.infer` function, passing in a prompt to tell the system what type o
should give along with the user provided question.

```ts
function answer(question: string): string {
const prompt = `<s>[INST] <<SYS>>
function answer(question: string): Answer {
const prompt: string = `<s>[INST] <<SYS>>
You are acting as a Magic 8 Ball that predicts the answer to a questions about events now or in the future.
Your tone should be expressive yet polite.
Your answers should be 10 words or less.
Prefix your response with 'Answer:'.
<</SYS>>
User: ${question}[/INST]"`;
let response = Llm.infer(InferencingModels.Llama2Chat, prompt, {
maxTokens: 20,
repeatPenalty: 1.5,
repeatPenaltyLastNTokenCount: 20,
temperature: 0.25,
topK: 5,
topP: 0.25,
}).text;
// Parse the response to remove the expected `Answer:` prefix from the response
const answerPrefix = "Answer:";
response = response.trim();
if (response.startsWith(answerPrefix)) {
response = response.substring(answerPrefix.length);
}
return response;
let response: Answer = { answer: Llm.infer(Llm.InferencingModels.Llama2Chat, prompt, {
maxTokens: 20,
repeatPenalty: 1.5,
repeatPenaltyLastNTokenCount: 20,
temperature: 0.25,
topK: 5,
topP: 0.25,
}).text};
// Parse the response to remove the expected `Answer:` prefix from the response
console.log(response.answer);
const answerPrefix = "Answer:";
response.answer = response.answer.trim();
if (response.answer.startsWith(answerPrefix)) {
response.answer = response.answer.substring(answerPrefix.length);
};
return response;
}
```

Expand All @@ -163,13 +170,12 @@ npm install
spin build
```

## Configure access to an AI model
### Configure access to an AI model

By default, a given component of a Spin application will not have access to any Serverless AI models. Access must be provided explicitly via the Spin application’s manifest (the `spin.toml` file). We can give our `magic-8` component access to the llama2-chat model by adding the following ai_models configuration inside its `[[component]]` section:

```toml
[[component]]
id = "magic-8-ball"
[[component.magic-8-ball]]
ai_models = ["llama2-chat"]
```

Expand Down Expand Up @@ -232,13 +238,12 @@ Now, build your application.
$ spin build
```

## Configure access to an AI model
### Configure access to an AI model

By default, a given component of a Spin application will not have access to any Serverless AI models. Access must be provided explicitly via the Spin application’s manifest (the `spin.toml` file). We can give our `magic-8` component access to the llama2-chat model by adding the following `ai_models` configuration inside its `[[component]]` section:

```toml
[[component]]
id = "magic-8-ball"
[[component.magic-8-ball]]
ai_models = ["llama2-chat"]
```

Expand Down
Loading

0 comments on commit cd2e167

Please sign in to comment.