Skip to content

Commit

Permalink
add ModusPress recipe (#6)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnymontana authored Nov 14, 2024
1 parent 17afe82 commit 8ff4e1a
Show file tree
Hide file tree
Showing 14 changed files with 2,038 additions and 6 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ For more infromation on getting started with Modus, check out [the docs](https:/

The following recipes have associated recorded content:

| Code | Video |
|------|-------|
| [modus101](modus101/) | [Introducing ModusHack: Modus 101 livestream](https://www.youtube.com/watch?v=8vgXmZPKjbo) |
| [modus-getting-started](modus-getting-started/) |[Getting Started With Modus video](https://www.youtube.com/watch?v=3CcJTXTmz88) |
| [modushack-data-models](modushack-data-models/) | [ModusHack: Working With Data & AI Models livestream](https://www.youtube.com/watch?v=gB-v7YWwkCw&list=PLzOEKEHv-5e3zgRGzDysyUm8KQklHQQgi&index=3)

| Code | Video |
| ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| [modus101](modus101/) | [Introducing ModusHack: Modus 101 livestream](https://www.youtube.com/watch?v=8vgXmZPKjbo) |
| [modus-getting-started](modus-getting-started/) | [Getting Started With Modus video](https://www.youtube.com/watch?v=3CcJTXTmz88) |
| [modushack-data-models](modushack-data-models/) | [ModusHack: Working With Data & AI Models livestream](https://www.youtube.com/watch?v=gB-v7YWwkCw&list=PLzOEKEHv-5e3zgRGzDysyUm8KQklHQQgi&index=3) |
| [modus-press](modus-press/) | Coming soon |
15 changes: 15 additions & 0 deletions modus-press/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Ignore macOS system files
.DS_Store

# Ignore environment variable files
.env
.env.*

# Ignore build output directories
build/

# Ignore node_modules folders
node_modules/

# Ignore logs generated by as-test
logs/
3 changes: 3 additions & 0 deletions modus-press/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["assemblyscript-prettier"]
}
65 changes: 65 additions & 0 deletions modus-press/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# ModusPress

This example shows how to add LLM-backed features to a fictitious web-based blog platform using [Modus](https://docs.hypermode.com/modus), the open source framework for building intelligent APIs. Specifically, this example is a Modus app that exposes a GraphQL endpoint with the

- generate suggested blog post titles based on the blog post content, in the style of the author
- generate HTML meta tags optimized for SEO based on the blog post content

![](img/ModusPressArch.png)

## Setup

Install the Modus CLI (if not already installed):

```
npm i -g @hypermode/modus-cli
```

**Seed Postgres DB**

This example pulls author biography data from a Postgres database that represents the backend database for our blog platform. You'll need to create a Postgres database and seed it with author data using the following schema:

![](img/ModusPressPostgres.png)

**Set database credentials in .env**

If using a local Postgres database with defaults your conection credentials will look something like this:

```
MODUS_MODUSPRESSDB_USERNAME=<YOUR_USERNAME_HERE>
MODUS_MODUSPRESSDB_HOST=localhost
MODUS_MODUSPRESSDB_PORT=5432
MODUS_MODUSPRESSDB_DBNAME=<YOUR_DB_NAME_HERE>
```

**Connect to Hypermode model hosting**

This example uses the LLaMa open source LLM hosted on Hypermode. You can create a free Hypermode account to leverage model hosting using the Hypermode Platform with the `hyp` cli.

Install `hyp` cli:

```
npm i -g @hypermode/hyp-cli
```

Login to Hypermode:

```
hyp login
```

This command will open a web browser and prompt you to sign in or create a free Hypermode account, then select an organization. Once complete you will be able to use Hypermode hosted models in your Modus app.

## Run

```
modus dev
```

This will build your Modus app and launch a GraphQL API at `localhost:8686/graphql`

## Query

You can query the GraphQL endpoint using any GraphQL client or cURL. Here we use Postman to query for suggested titles based on the blog post content and author bio retrived from Postres and then passed to the LLM.

![](img/ModusPressGraphQL.png)
6 changes: 6 additions & 0 deletions modus-press/asconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "./node_modules/@hypermode/modus-sdk-as/plugin.asconfig.json",
"options": {
"transform": ["@hypermode/modus-sdk-as/transform", "json-as/transform"]
}
}
87 changes: 87 additions & 0 deletions modus-press/assembly/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
This example Modus app exposes two GraphQL Query fields to add LLM-backed features
to a hypothetical blogging platform capable of suggesting blog post titles and HTML
meta tags optimized for SEO based on the blog post content in the style of the
blog post author.
*/

import { models, postgresql } from "@hypermode/modus-sdk-as";

import {
OpenAIChatModel,
ResponseFormat,
SystemMessage,
UserMessage,
} from "@hypermode/modus-sdk-as/models/openai/chat";

// This function generates HTML meta description tag content optimized for SEO based on the blog post content
export function genSEO(postContent: string): string {
const suggestedTag = generateText(
"You are an SEO expert",
`Create the HTML meta description tag for a blog post with the following content, only return the meta tag value: ${postContent}`,
);

return suggestedTag;
}

// This function generates a suggested blog post title using the blog post content and category and leverages
// the author's biography data retrieved from a Postgres database to match the author's style
export function genTitle(
postContent: string,
postCategory: string,
authorName: string,
): string {
const author = getAuthorByName(authorName);

const suggestedTitle = generateText(
"You are a copyeditor",
`
Create a title for the following blog post, in the style of ${author.name}. Only return the title text.
Blog post content: ${postContent}
Blog post category: ${postCategory}
Author biography: ${author.bio}
`,
);

return suggestedTitle;
}

// Use our LLM to generate text based on an instruction and prompt
function generateText(instruction: string, prompt: string): string {
const model = models.getModel<OpenAIChatModel>("llama");

const input = model.createInput([
new SystemMessage(instruction),
new UserMessage(prompt),
]);

input.temperature = 0.7;
const output = model.invoke(input);

return output.choices[0].message.content.trim();
}

// The connection host for our Postgres database, as defined in modus.json
const host = "moduspressdb";

// Define a type to represent our author information
@json
class Author {
id: i32 = 0;
name!: string;
bio!: string;
}

// Query our database to find author information
function getAuthorByName(name: string): Author {
const query = "select * from authors where name = $1";

const params = new postgresql.Params();
params.push(name);

const response = postgresql.query<Author>(host, query, params);
return response.rows[0];
}
4 changes: 4 additions & 0 deletions modus-press/assembly/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "assemblyscript/std/assembly.json",
"include": ["./**/*.ts"]
}
11 changes: 11 additions & 0 deletions modus-press/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @ts-check

import eslint from "@eslint/js";
import tseslint from "typescript-eslint";
import aseslint from "@hypermode/modus-sdk-as/tools/assemblyscript-eslint";

export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
aseslint.config,
);
Binary file added modus-press/img/ModusPressArch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added modus-press/img/ModusPressGraphQL.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added modus-press/img/ModusPressPostgres.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions modus-press/modus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "https://schema.hypermode.com/modus.json",
"endpoints": {
"default": {
"type": "graphql",
"path": "/graphql",
"auth": "bearer-token"
}
},
"models": {
"llama": {
"sourceModel": "meta-llama/Meta-Llama-3.1-8B-Instruct",
"provider": "hugging-face",
"connection": "hypermode"
}
},
"connections": {
"moduspressdb": {
"type": "postgresql",
"connString": "postgresql://{{USERNAME}}@{{HOST}}:{{port}}/{{DBNAME}}"
}
}
}
Loading

0 comments on commit 8ff4e1a

Please sign in to comment.