Skip to content

Commit

Permalink
Merge pull request #55 from fermyon/sections
Browse files Browse the repository at this point in the history
Fix section headers in magic-8-ball workshop
  • Loading branch information
calebschoepp committed Mar 6, 2024
2 parents 6b097b1 + 42f7c86 commit 80d3c6a
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 91 deletions.
26 changes: 15 additions & 11 deletions magic-8-ball/00-setup.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Setup

There are a few ways to set up the development environment in order to follow this workshop:

- [A: configuring your local environment](#configuring-your-local-environment)
- [B: using a local dev container](#using-a-local-dev-container-with-vs-code)
- [C: using GitHub Codespaces](#using-github-codespaces)
Expand All @@ -11,7 +12,7 @@ If working on your machine (i.e. not using GitHub Codespaces), first clone the r
$ git clone https://github.com/fermyon/workshops && cd workshops/magic-8-ball
```

### Option A: Configuring your local environment
## Option A: Configuring your local environment

First, you have to configure [Spin](https://fermyon.com/spin) by following the [instructions for your operating system from the Spin documentation](https://developer.fermyon.com/spin/install).

Expand All @@ -22,7 +23,7 @@ brew tap fermyon/tap
brew install fermyon/tap/spin
```

For Windows (not WSL2), see here:
For Windows (not WSL2), see here:

Alternatively, you can [manually install from a GitHub release](https://github.com/fermyon/spin/releases), or you can [build Spin from source](https://developer.fermyon.com/spin/contributing-spin).

Expand All @@ -49,7 +50,7 @@ Depending on the programming languages you want to use, you will need to configu
- [Go](https://go.dev/doc/install) and [TinyGo](https://tinygo.org/getting-started/install)
- [.NET](https://dotnet.microsoft.com/en-us/download/dotnet/7.0)

### Option B: Using a local dev container with VS Code
## Option B: Using a local dev container with VS Code

This repository contains the necessary files to open the project and develop inside a container, using [Visual Studio Code Dev Containers](https://code.visualstudio.com/docs/devcontainers/containers).

Expand All @@ -59,27 +60,30 @@ After [following the VS Code documentation](https://code.visualstudio.com/docs/d

![Open the workshops repository using a VS Code Dev Container](../media/dev-container.png)

### Option C: Using GitHub Codespaces
## Option C: Using GitHub Codespaces

You can complete this workshop using only your browser using [GitHub Codespaces](https://github.com/features/codespaces). To achieve this, navigate to the GitHub repository for this workshop, https://github.com/fermyon/workshops, then click on the "Clone, open, or download button", then select "Codespaces", click "Create codespace on main`, then follow the instructions:

![Open the repository using GitHub Codespaces](../media/gh-codespace.png)

### Troubleshooting
## Troubleshooting

#### Q: I cannot build my Rust application with `spin build`.
### Q: I cannot build my Rust application with `spin build`.

A: Make sure you have [configured your Rust toolchain](https://www.rust-lang.org/tools/install), and have added the `wasm32-wasi` target using `rustup target add wasm32-wasi`.

#### Q: I cannot build my JavaScript or TypeScript application with `spin build`.
### Q: I cannot build my JavaScript or TypeScript application with `spin build`.

A: Make sure you have [configured Node.js and `npm`](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm), and that you have executed `npm install` in the directory with your component's `package.json` file that contains the dependencies.

### Learning Summary
## Learning Summary

In this section you learned how to:

- [x] Install the latest Spin CLI version (canary)
- [x] Install the latest Spin templates
- [x] Install the latest Spin templates
- [x] Install the latest Spin plugins (canary for JS)

### Navigation
* Proceed to [01 - Getting started with Spin](./01-getting-started.md)
## Navigation

- Proceed to [01 - Getting started with Spin](./01-getting-started.md)
8 changes: 4 additions & 4 deletions magic-8-ball/01-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ $ spin templates list

You are now ready to create your first Spin applications. Depending on what language you are familiar with, you can choose to follow the rest of the guide in Rust or JavaScript/TypeScript.

### a. Building your first Spin application with Rust
## a. Building your first Spin application with Rust

Rust has excellent [support for WebAssembly](https://www.rust-lang.org/what/wasm), and Spin has an SDK for Rust that simplifies building Spin applications in Rust.

Expand Down Expand Up @@ -133,7 +133,7 @@ You are now ready to expand your application. You can follow the [guide for buil

> Note: you can find the complete applications used in this workshop in the [`apps` directory](./apps/).
### b. Building your first Spin application with JavaScript or TypeScript
## 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.

Expand Down Expand Up @@ -245,7 +245,7 @@ You are now ready to expand your application. You can follow the [guide for buil

> Note: you can find the complete applications used in this workshop in the [`apps` directory](./apps/).
### Learning Summary
## Learning Summary

In this section you learned how to:

Expand All @@ -255,7 +255,7 @@ In this section you learned how to:
- [x] Run your application locally with `spin up`
- [x] Modify an HTTP trigger's route and entrypoint

### Navigation
## Navigation

- Go back to [00 - Setup](00-setup.md) if you still have questions on previous section
- Otherwise, proceed to [02 - Building a Magic 8 Ball JSON API with Spin](02-json-api.md)
4 changes: 2 additions & 2 deletions magic-8-ball/02-json-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,13 +156,13 @@ $ curl localhost:3000/magic-8

> Note: you can find the complete applications used in this workshop in the [`apps` directory](./apps/).
### Learning Summary
## Learning Summary

In this section you learned how to:

- [x] Host a JSON API with a Spin app which uses an HTTP trigger

### Navigation
## Navigation

- Go back to [01 - Getting started with Spin](01-getting-started.md) if you still have questions on previous section
- Otherwise, proceed to [03 - Using Fermyon Serverless AI](03-spin-ai.md)
45 changes: 22 additions & 23 deletions magic-8-ball/03-spin-ai.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,18 +107,17 @@ We need to modify our `magic-8-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, update the request handler to get the question from the request body and return an error if
the body is empty:

```ts
const question = decoder.decode(request.body)
if (question.length == 0) {
return {
status: 400,
body: encoder.encode("No question asked").buffer
}
}
const question = decoder.decode(request.body);
if (question.length == 0) {
return {
status: 400,
body: encoder.encode("No question asked").buffer,
};
}
```

Next, update the `answer` function to use the LLM instead of pulling a random response from a list.
Expand All @@ -127,28 +126,28 @@ should give along with the user provided question.

```ts
function answer(question: string): string {
const prompt = `<s>[INST] <<SYS>>
const prompt = `<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
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)
const answerPrefix = "Answer:";
response = response.trim();
if (response.startsWith(answerPrefix)) {
response = response.substring(answerPrefix.length);
}
return response
return response;
}
```

Expand Down Expand Up @@ -184,7 +183,7 @@ Let's ask a question (make sure to use your Spin application's domain name, disc

```bash
$ curl -d "Will I win the lottery?" https://{url}/magic-8
{"answer": "Signs point to yes!"}
{"answer": "Signs point to yes!"}
```

## (Optional) Run Locally
Expand Down Expand Up @@ -213,7 +212,7 @@ Let's ask a question

```bash
$ curl -d "Will I win the lottery?" http://127.0.0.1:3000/magic-8
{"answer": "Signs point to yes!"}
{"answer": "Signs point to yes!"}
```

> Note: you can find the complete applications used in this workshop in the [`apps`
Expand Down
30 changes: 16 additions & 14 deletions magic-8-ball/04-frontend.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Magic 8 Ball Frontend

Let's make our Magic 8 Ball application more interactive by adding a frontend where you can submit your question to the omniscient 8 ball! To do this, we want to add a new component to our Spin application that can serve the frontend of our application.
Fortunately, Spin has a [static file server component](https://github.com/fermyon/spin-fileserver) that can be added to any Spin application using `spin add`. Since this will be the base of our application, overwrite the default component trigger with the wildcard `/...` to match all routes.
Let's make our Magic 8 Ball application more interactive by adding a frontend where you can submit your question to the omniscient 8 ball! To do this, we want to add a new component to our Spin application that can serve the frontend of our application.
Fortunately, Spin has a [static file server component](https://github.com/fermyon/spin-fileserver) that can be added to any Spin application using `spin add`. Since this will be the base of our application, overwrite the default component trigger with the wildcard `/...` to match all routes.

We also need to tell the fileserver which frontend files to serve. Choose the default `assets` when asked for the 'Directory containing the files to serve'. In your Spin app's directory create a folder named `assets`. For the purposes of this workshop, we've already prepared an HTML, CSS and JS [frontend](apps/frontend/) that you can reuse. Download these files into the `assets` folder.
We also need to tell the fileserver which frontend files to serve. Choose the default `assets` when asked for the 'Directory containing the files to serve'. In your Spin app's directory create a folder named `assets`. For the purposes of this workshop, we've already prepared an HTML, CSS and JS [frontend](apps/frontend/) that you can reuse. Download these files into the `assets` folder.

```bash
$ spin add static-fileserver fileserver
Expand All @@ -25,32 +25,34 @@ route = "/..."
Let's look at the frontend implementation. The user asks a question and calls our Magic 8 Ball `magic-8` component to get the response. This is done in the `fetch('../magic-8')` call in the JS portion of the snippet below. You can see that we are also passing the question in the body of the request. We will use this in a later step of the workshop. Don't worry about copying the code snippet below. This is just for illustrative purposes.

```js
const btn= document.getElementById("btn");
btn.addEventListener('click', function(){
var question = document.getElementById("question").value;
fetch('../magic-8', { method: 'POST', body: question }).then(response => response.json()).then(data => {
document.getElementById("triangle").style.display = 'inline-block';
document.getElementById("circle").style.display = 'inline-block';
document.getElementById("triangle").innerHTML = '<br><br>' + data.answer;
})
const btn = document.getElementById("btn");
btn.addEventListener("click", function () {
var question = document.getElementById("question").value;
fetch("../magic-8", { method: "POST", body: question })
.then((response) => response.json())
.then((data) => {
document.getElementById("triangle").style.display = "inline-block";
document.getElementById("circle").style.display = "inline-block";
document.getElementById("triangle").innerHTML = "<br><br>" + data.answer;
});
});
```

Now, we are ready to build and run our application. Run this command and click on the URL to see the Magic 8 Ball frontend.
Now, we are ready to build and run our application. Run this command and click on the URL to see the Magic 8 Ball frontend.

```bash
spin build --up
```

### Learning Summary
## Learning Summary

In this section you learned how to:

- [x] Add a new component to an existing Spin app using `spin add`
- [x] Use the Spin static fileserver component
- [x] Call one Spin component from another Spin component

### Navigation
## Navigation

- Go back to [03 - Building a Magic AIght Ball JSON API with Spin](03-spin-ai.md) if you still have questions on previous section
- Otherwise, proceed to [05 - Persisting Magic 8 Ball Responses](05-spin-kv.md)
30 changes: 14 additions & 16 deletions magic-8-ball/05-spin-kv.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ We will need to:
1. Change the component to expect a body with a text question (`"Should I get a new haircut?"`).
1. Enable a default key/value store for our component
1. Check if that question has already been asked by looking it up in the key/value store.
- a: If previously asked, return the previous answer.*
- b: If not previously asked, generate an answer, store the answer in the KV store, and return it.
> `*` if the previous answer was "Ask again later", follow step b.
- a: If previously asked, return the previous answer.\*
- b: If not previously asked, generate an answer, store the answer in the KV store, and return it.
> `*` if the previous answer was "Ask again later", follow step b.
## 1: Giving our component access to a KV store

Expand All @@ -29,26 +29,24 @@ route = "/magic-8"
command = "cargo build --target wasm32-wasi --release"
```

Here's how `spin.toml` would look like in the **TypeScript** component
Here's how `spin.toml` would look like in the **TypeScript** component

```typescript
[[component]]
id = "magic-8-ball"
source = "target/magic-8-ball.wasm"
exclude_files = ["**/node_modules"]
key_value_stores = ["default"]
[component.trigger]
route = "/magic-8"
[component.build]
command = "npm run build"
[[component]];
id = "magic-8-ball";
source = "target/magic-8-ball.wasm";
exclude_files = ["**/node_modules"];
key_value_stores = ["default"][component.trigger];
route = "/magic-8"[component.build];
command = "npm run build";
```

## 2: Storing questions and answers in our key/value store

The Spin SDK surfaces the Spin key value store interface to your language with operations such as `open` `get` `set` `delete` and more. The [Spin KV store API guide](https://developer.fermyon.com/spin/kv-store-api-guide) can be used to set and check previous question-answer pairs. Here's the code snippet in **Rust** to do this
The Spin SDK surfaces the Spin key value store interface to your language with operations such as `open` `get` `set` `delete` and more. The [Spin KV store API guide](https://developer.fermyon.com/spin/kv-store-api-guide) can be used to set and check previous question-answer pairs. Here's the code snippet in **Rust** to do this

```rust
// Checks key/value store to see if the question has already been answered.
// Checks key/value store to see if the question has already been answered.
// If not answered, generates an answer, sets it in the KV store and returns it.
fn get_or_set_answer(question: &str) -> Result<String> {
// Open the default key/value store
Expand Down Expand Up @@ -95,7 +93,7 @@ function getOrSetAnswer(question: string): string {
}
```

In both code snippets, the answer is retrieved from the key value store, or if the key does not exist, it stores both the question and reponse. There is also a separate check if the Magic 8 Ball's response to a stored question was previously "Ask me again later", in which case the
In both code snippets, the answer is retrieved from the key value store, or if the key does not exist, it stores both the question and response. There is also a separate check if the Magic 8 Ball's response to a stored question was previously "Ask me again later", in which case the
new response is stored along with the question. The full code for the KV store can be [found here](https://github.com/fermyon/workshops/tree/main/spin/apps/05-spin-kv)

The [key value store tutorial](https://developer.fermyon.com/spin/kv-store-tutorial) is a helpful resource for a deep-dive into data persistence.
Expand Down
12 changes: 6 additions & 6 deletions magic-8-ball/06-deploy-fermyon-cloud.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ Available Routes:
fileserver: https://magic-8-ball-xyz.fermyon.app (wildcard)
```

In case you have not logged into your Fermyon account, you can run `spin login` and follow the steps. More details on [deploying to the Fermyon Cloud can be found here](https://developer.fermyon.com/cloud/deploy)
In case you have not logged into your Fermyon account, you can run `spin login` and follow the steps. More details on [deploying to the Fermyon Cloud can be found here](https://developer.fermyon.com/cloud/deploy)

#### Custom Domains
## Custom Domains

Fermyon Cloud also provides a way for you to use your own domain names to provide your users with human-friendly access to your Spin applications. If you do not own a domain, you can purchase one through a domain registrar such as Namecheap and GoDaddy and then point your Spin application to the new custom domain. [This tutorial](https://developer.fermyon.com/cloud/custom-domains-tutorial ) runs you through the steps to apply a custom domain to your Spin application running on Fermyon Cloud.
Fermyon Cloud also provides a way for you to use your own domain names to provide your users with human-friendly access to your Spin applications. If you do not own a domain, you can purchase one through a domain registrar such as Namecheap and GoDaddy and then point your Spin application to the new custom domain. [This tutorial](https://developer.fermyon.com/cloud/custom-domains-tutorial) runs you through the steps to apply a custom domain to your Spin application running on Fermyon Cloud.

### Learning Summary
## Learning Summary

In this section you learned how to:

- [x] Deploy a Spin app to Fermyon Cloud using `spin cloud deploy`
- [x] Learnt about custom domains on Fermyon Cloud
- [x] Learnt about custom domains on Fermyon Cloud

### Navigation
## Navigation

- Go back to [05 - Persisting Magic 8 Ball Responses](05-spin-kv.md) if you still have questions on previous section
- Otherwise, proceed to [07 - Storing data in an external database](07-external-db.md)
6 changes: 3 additions & 3 deletions magic-8-ball/07-external-db.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ Follow the Spin [Redis documentation](https://developer.fermyon.com/cloud/data-r

Follow the Spin [Redis documentation](https://developer.fermyon.com/cloud/data-redis.md#redis) to persist questions & answers using a free database from RedisLabs. Reference the Spin [`examples` repository](https://github.com/fermyon/spin-js-sdk/tree/main/examples/typescript/outbound_redis) for an example TypeScript application that does outbound Redis requests.

### Learning Summary
## Learning Summary

In this section you learned how to:

- [x] Connect to an external database (in this case, Redis) to persist data from your Spin app using the Redis Spin API

### Navigation
## Navigation

- Go back to [06 - Deploying to Fermyon Cloud](06-deploy-fermyon-cloud.md) if you still have questions on previous section
- Otherwise, proceed to [08 - Deploying a Spin Application on Kubernetes](08-kubernetes.md)
- Otherwise, proceed to [08 - Deploying a Spin Application on Kubernetes](08-kubernetes.md)
Loading

0 comments on commit 80d3c6a

Please sign in to comment.