Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add example to send mail #7

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ 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-smtp](./node-tool-send-mail-smtp): Send email by `nodemailer` and `maildev`.
- [node-tool-send-mail-resend](./node-tool-send-mail-resend): Send email by `resend`.
- [node-tool-web-search](./node-tool-web-search): Search the web by Google Custom Search Engine.

### Golang

Expand All @@ -22,6 +25,8 @@ 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-smtp](./golang-tool-send-mail-smtp): Send email by smtp.
- [golang-tool-send-mail-resend](./golang-tool-send-mail-resend): Send email by `resend`.

## Self Hosting

Expand Down
5 changes: 5 additions & 0 deletions golang-tool-send-mail-resend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
YOMO_SFN_NAME=my_first_llm_function_tool
YOMO_SFN_ZIPPER="zipper.vivgrid.com:9000"
YOMO_SFN_CREDENTIAL=<your-yomo-sfn-credential>
[email protected]
RESEND_API_KEY=<your-resend-api-key>
67 changes: 67 additions & 0 deletions golang-tool-send-mail-resend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# LLM Function Calling - Send Email with Resend (Go)

This is a serverless function for sending emails using Resend, implemented in Go.

## 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/).

### 2. Resend API Key

1. Sign up for a [Resend](https://resend.com) account
2. Get your API key
3. Add the API key to your `.env` file:

```bash
[email protected]
RESEND_API_KEY=<your-resend-api-key>
```

## 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. Install Dependencies

```bash
go mod download
```

### 3. 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 '{
"messages": [
{
"role": "assistant",
"content": "send an email to [email protected], tell him I will attend the meeting"
}
]
}'
```

### 4. Connect Function to LLM Bridge

```bash
yomo run main.go -n my_first_llm_function_tool
```
92 changes: 92 additions & 0 deletions golang-tool-send-mail-resend/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package main

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

"github.com/joho/godotenv"
"github.com/resend/resend-go/v2"
"github.com/yomorun/yomo/serverless"
)

// Description outlines the functionality for the LLM Function Calling feature.
func Description() string {
return `Generate and send emails. Please provide the recipient's email address, and you should help generate appropriate subject and content. If no recipient address is provided, You should ask to add one. When you generate the subject and content, you should send it through the email sending function.`
}

// InputSchema defines the argument structure for LLM Function Calling
func InputSchema() any {
return &Parameter{}
}

var client *resend.Client

// Init is an optional function invoked during the initialization phase of the
// sfn instance. It's designed for setup tasks like global variable
// initialization, establishing database connections, or loading models into
// GPU memory. If initialization fails, the sfn instance will halt and
// terminate. This function can be omitted if no initialization tasks are
// needed.
func Init() error {
if _, ok := os.LookupEnv("RESEND_API_KEY"); !ok {
err := godotenv.Load()
if err != nil {
log.Fatal("You have to set RESEND_API_KEY in ENV or .env file")
os.Exit(-1)
}
}

client = resend.NewClient(os.Getenv("RESEND_API_KEY"))
return nil
}

// Parameter defines the arguments for the LLM Function Calling
type Parameter struct {
To string `json:"to" jsonschema:"description=The recipient's email address"`
Subject string `json:"subject" jsonschema:"description=The subject of the email"`
Body string `json:"body" jsonschema:"description=The content of the email"`
}

// Handler orchestrates the core processing logic of this function
func Handler(ctx serverless.Context) {
var args Parameter
ctx.ReadLLMArguments(&args)

result, err := sendEmail(args)
if err != nil {
ctx.WriteLLMResult(fmt.Sprintf("Failed to send email: %v", err))
return
}

ctx.WriteLLMResult(result)
slog.Info("send-email", "to", args.To, "result", result)
}

func sendEmail(args Parameter) (string, error) {
if err := godotenv.Load(); err != nil {
slog.Warn("Error loading .env file", "error", err)
}

slog.Info("send-email", "args", args)

params := &resend.SendEmailRequest{
From: os.Getenv("FROM_EMAIL"),
To: []string{args.To},
Subject: args.Subject,
Html: fmt.Sprintf("<p>%s</p>", args.Body),
}

resp, err := client.Emails.Send(params)
if err != nil {
return "", fmt.Errorf("failed to send email: %w", err)
}

return fmt.Sprintf("Email has been successfully sent to %s with ID: %s", args.To, resp.Id), nil
}

// DataTags specifies the data tags to which this serverless function subscribes
func DataTags() []uint32 {
return []uint32{0x67}
}
17 changes: 17 additions & 0 deletions golang-tool-send-mail-resend/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module github.com/yomo-mqtt/llm-function-calling/examples/golang-tool-send-mail-resend

go 1.21

require github.com/yomorun/yomo v1.18.11

require (
github.com/joho/godotenv v1.5.1
github.com/resend/resend-go/v2 v2.5.0
)

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
)
22 changes: 22 additions & 0 deletions golang-tool-send-mail-resend/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
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/resend/resend-go/v2 v2.5.0 h1:XzTtzQ9YB2LlGHWjS5AVyUqV9cVbDU+6Z4XgCKsJh4g=
github.com/resend/resend-go/v2 v2.5.0/go.mod h1:ihnxc7wPpSgans8RV8d8dIF4hYWVsqMK5KxXAr9LIos=
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 golang-tool-send-mail-smtp/.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
[email protected]
70 changes: 70 additions & 0 deletions golang-tool-send-mail-smtp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# LLM Function Calling - Send Email with SMTP

This is a serverless function for sending emails with SMTP.

## 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 '{
"messages": [
{
"role": "assistant",
"content": "send an email to [email protected], tell him I will attend the meeting"
}
]
}'
```

### 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.
66 changes: 66 additions & 0 deletions golang-tool-send-mail-smtp/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
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 `Generate and send emails. Please provide the recipient's email address, and you should help generate appropriate subject and content. If no recipient address is provided, You should ask to add one. When you generate the subject and content, you should send it through the email sending function.`
}

// 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)

slog.Info("send-mail", "msg", 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-smtp/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
)
Loading