Skip to content

Commit

Permalink
feat: add example to send mail
Browse files Browse the repository at this point in the history
  • Loading branch information
nichenqin committed Feb 18, 2025
1 parent 2605ae6 commit 6fdd0c1
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ YoMo support multiple LLM providers, like Ollama, Mistral, Llama, Azure OpenAI,
- [node-tool-currency-converter](./node-tool-currency-converter): Currency Calculator by 3rd party API.
- [node-tool-get-utc-time](./node-tool-get-utc-time): Get the UTC time by city name.
- [node-tool-get-ip-and-latency](./node-tool-get-ip-and-latency): Get IP and Latency by give website name like "Nike" and "Amazone" by `ping` command.
- [node-tool-send-mail](./node-tool-send-mail): Send email by `nodemailer` and `maildev`.

### Golang

Expand All @@ -22,6 +23,7 @@ YoMo support multiple LLM providers, like Ollama, Mistral, Llama, Azure OpenAI,
- [golang-tool-get-utc-time](./golang-tool-get-utc-time): Get the UTC time by city name.
- [golang-tool-timezone-calculator](./golang-tool-timezone-calculator): Calculate the timezone for a specific time.
- [golang-tool-get-ip-and-latency](./golang-tool-get-ip-and-latency): Get IP and Latency by give website name like "Nike" and "Amazone" by `ping` command.
- [golang-tool-send-mail](./golang-tool-send-mail): Send email by `nodemailer` and `maildev`.

## Self Hosting

Expand Down
6 changes: 6 additions & 0 deletions golang-tool-send-mail/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
YOMO_SFN_NAME=my_first_llm_function_tool
YOMO_SFN_ZIPPER="zipper.vivgrid.com:9000"
YOMO_SFN_CREDENTIAL=<your-yomo-sfn-credential>
SMTP_HOST=localhost
SMTP_PORT=1025
FROM_EMAIL=[email protected]
71 changes: 71 additions & 0 deletions golang-tool-send-mail/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# LLM Function Calling - Send Email

This is a serverless function for sending emails.

## Prerequisites

### 1. Environment Variables

```sh
YOMO_SFN_NAME=my_first_llm_function_tool
YOMO_SFN_ZIPPER=zipper.vivgrid.com:9000
YOMO_SFN_CREDENTIAL=<your-yomo-sfn-credential>
```

You can find other environment variables in the serverless page of [vivgrid dashboard](https://dashboard.vivgrid.com/).

## Development

### 1. Install YoMo CLI

```bash
curl -fsSL https://get.yomo.run | sh
```

For detailed CLI usage, check [Doc: YoMo CLI](https://yomo.run/docs/cli).

### 2. Start Mail Development Server

```bash
docker run -p 1080:1080 -p 1025:1025 maildev/maildev
```

### 3. Add Environment Variables

Add the following environment variables to your `.env` file:

```bash
SMTP_HOST=localhost
SMTP_PORT=1025
[email protected]
```

### 4. Test the Function

You can test the email sending functionality with the following curl command:

```bash
curl --request POST \
--url https://api.vivgrid.com/v1/chat/completions \
--header 'Authorization: Bearer <token>' \
--header 'content-type: application/json' \
--data '{
"model": "gpt-4o",
"messages": [
{
"role": "assistant",
"content": "send an email to [email protected], subject is '\''hello'\'', body is '\''world'\''"
}
]
}'
```

### 4. Connect this Function to Your LLM Bridge

```bash
yomo run app.go -n my_first_llm_function_tool
```

## Web Interface

After starting the mail development server, you can access the web interface at `http://localhost:1080` to view sent emails.
65 changes: 65 additions & 0 deletions golang-tool-send-mail/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"fmt"
"log/slog"
"net/smtp"
"os"

"github.com/yomorun/yomo/serverless"
)

// Description describes the functionality of this Function Calling
func Description() string {
return `This function is called when users need to send emails. You need to determine if the user's input contains complete email information (recipient, subject, content).
If the information is incomplete, you should ask for the missing information.`
}

// Parameter defines the required parameters for sending emails
type Parameter struct {
To string `json:"to" jsonschema:"description=Recipient's email address,[email protected]"`
Subject string `json:"subject" jsonschema:"description=Email subject"`
Body string `json:"body" jsonschema:"description=Email content"`
}

func InputSchema() any {
return &Parameter{}
}

// Handler processes the email sending logic
func Handler(ctx serverless.Context) {
var msg Parameter
ctx.ReadLLMArguments(&msg)

// Get email configuration from environment variables
smtpHost := os.Getenv("SMTP_HOST")
smtpPort := os.Getenv("SMTP_PORT")
fromEmail := os.Getenv("FROM_EMAIL")

// Construct email content
emailBody := fmt.Sprintf("Subject: %s\r\n\r\n%s", msg.Subject, msg.Body)

// Send email
err := smtp.SendMail(
smtpHost+":"+smtpPort,
nil,
fromEmail,
[]string{msg.To},
[]byte(emailBody),
)

if err != nil {
slog.Error("Failed to send email", "error", err)
ctx.WriteLLMResult("Failed to send email, please try again later")
return
}

ctx.WriteLLMResult(fmt.Sprintf("Email has been successfully sent to %s", msg.To))
}

// DataTags specifies the data tags to which this serverless function
// subscribes, essential for data reception. Upon receiving data with these
// tags, the Handler function is triggered.
func DataTags() []uint32 {
return []uint32{0x65}
}
12 changes: 12 additions & 0 deletions golang-tool-send-mail/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module llm-fn-get-weather

go 1.22.3

require github.com/yomorun/yomo v1.18.11

require (
github.com/caarlos0/env/v6 v6.10.1 // indirect
github.com/lmittmann/tint v1.0.4 // indirect
github.com/sashabaranov/go-openai v1.27.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
)
18 changes: 18 additions & 0 deletions golang-tool-send-mail/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II=
github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/lmittmann/tint v1.0.4 h1:LeYihpJ9hyGvE0w+K2okPTGUdVLfng1+nDNVR4vWISc=
github.com/lmittmann/tint v1.0.4/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sashabaranov/go-openai v1.27.0 h1:L3hO6650YUbKrbGUC6yCjsUluhKZ9h1/jcgbTItI8Mo=
github.com/sashabaranov/go-openai v1.27.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/yomorun/yomo v1.18.11 h1:lWA+YtRnm/ppQKPztoV2XekmCcQVRHJajyYSFu49h+g=
github.com/yomorun/yomo v1.18.11/go.mod h1:aDnZBSmXMCBH/73jnqtUdYvzVDeqGx25Z87y80cOU34=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
6 changes: 6 additions & 0 deletions node-tool-send-mail/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
YOMO_SFN_NAME=my_first_llm_function_tool
YOMO_SFN_ZIPPER="zipper.vivgrid.com:9000"
YOMO_SFN_CREDENTIAL=<your-yomo-sfn-credential>
SMTP_HOST=localhost
SMTP_PORT=1025
FROM_EMAIL=[email protected]
71 changes: 71 additions & 0 deletions node-tool-send-mail/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# LLM Function Calling - Send Email

This is a serverless function for sending emails.

## Prerequisites

### 1. Environment Variables

```sh
YOMO_SFN_NAME=my_first_llm_function_tool
YOMO_SFN_ZIPPER=zipper.vivgrid.com:9000
YOMO_SFN_CREDENTIAL=<your-yomo-sfn-credential>
```

You can find other environment variables in the serverless page of [vivgrid dashboard](https://dashboard.vivgrid.com/).

## Development

### 1. Install YoMo CLI

```bash
curl -fsSL https://get.yomo.run | sh
```

For detailed CLI usage, check [Doc: YoMo CLI](https://yomo.run/docs/cli).

### 2. Start Mail Development Server

```bash
docker run -p 1080:1080 -p 1025:1025 maildev/maildev
```

### 3. Add Environment Variables

Add the following environment variables to your `.env` file:

```bash
SMTP_HOST=localhost
SMTP_PORT=1025
[email protected]
```

### 4. Test the Function

You can test the email sending functionality with the following curl command:

```bash
curl --request POST \
--url https://api.vivgrid.com/v1/chat/completions \
--header 'Authorization: Bearer <token>' \
--header 'content-type: application/json' \
--data '{
"model": "gpt-4o",
"messages": [
{
"role": "assistant",
"content": "send an email to [email protected], subject is '\''hello'\'', body is '\''world'\''"
}
]
}'
```

### 4. Connect this Function to Your LLM Bridge

```bash
yomo run app.go -n my_first_llm_function_tool
```

## Web Interface

After starting the mail development server, you can access the web interface at `http://localhost:1080` to view sent emails.
52 changes: 52 additions & 0 deletions node-tool-send-mail/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import * as dotenv from 'dotenv';
import * as nodemailer from 'nodemailer';

dotenv.config();

// Description outlines the functionality for the LLM Function Calling feature
export const description = `This function is called when users need to send emails. You need to determine if the user's input contains complete email information (recipient, subject, content).
If the information is incomplete, you should ask for the missing information.`;

// Define the parameter structure for the LLM Function Calling
interface Argument {
to: string;
subject: string;
body: string;
}

// Tag specifies the data tag that this serverless function
// subscribes to, which is essential for data reception. When data with this
// tag is received, the Handler function will be triggered.
export const tag = 0x65;

async function sendEmail(args: Argument): Promise<string> {
try {
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT || '1025'),
secure: false,
});

await transporter.sendMail({
from: process.env.FROM_EMAIL,
to: args.to,
subject: args.subject,
text: args.body,
});

return `Email has been successfully sent to ${args.to}`;
} catch (error) {
console.error('Failed to send email:', error);
return 'Failed to send email, please try again later';
}
}

/**
* Handler orchestrates the core processing logic of this function.
* @param args - LLM Function Calling Arguments.
* @returns The result of the email sending operation.
*/
export async function handler(args: Argument): Promise<string> {
const result = await sendEmail(args);
return result;
}
21 changes: 21 additions & 0 deletions node-tool-send-mail/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "tool-node-send-mail",
"version": "1.0.0",
"description": "LLM Function Calling - Send Email",
"main": ".wrapper.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/node": "^22.10.1",
"@types/nodemailer": "^6.4.14",
"typescript": "^5.7.2"
},
"dependencies": {
"@yomo/sfn": "^1.0.5",
"dotenv": "^16.4.7",
"nodemailer": "^6.9.12"
}
}
11 changes: 11 additions & 0 deletions node-tool-send-mail/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
},
"include": [".wrapper.ts"]
}

0 comments on commit 6fdd0c1

Please sign in to comment.