-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathapp.go
127 lines (107 loc) · 4.45 KB
/
app.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"log/slog"
"net/http"
"os"
"github.com/joho/godotenv"
"github.com/yomorun/yomo/serverless"
)
// Description outlines the functionality for the LLM Function Calling feature.
// It provides a detailed description of the function's purpose, essential for
// integration with LLM Function Calling. The presence of this function and its
// return value make the function discoverable and callable within the LLM
// ecosystem. For more information on Function Calling, refer to the OpenAI
// documentation at: https://platform.openai.com/docs/guides/function-calling
func Description() string {
return `if user asks currency exchange rate related questions, you should call this function. But if the source currency is other than USD (US Dollar), you should ignore calling tools.`
}
// Parameter defines the arguments for the LLM Function Calling.
type Parameter struct {
SourceCurrency string `json:"source" jsonschema:"description=The source currency to be queried in 3-letter ISO 4217 format"`
TargetCurrency string `json:"target" jsonschema:"description=The target currency to be queried in 3-letter ISO 4217 format"`
Amount float64 `json:"amount" jsonschema:"description=The amount of the currency to be converted to the target currency"`
}
// InputSchema defines the argument structure for LLM Function Calling. It
// utilizes jsonschema tags to detail the definition. For jsonschema in Go,
// see https://github.com/invopop/jsonschema.
func InputSchema() any {
return &Parameter{}
}
// 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 {
// try loading the API_KEY fo openexchangerates.org from ENV, if not found,
// try from .env file.
if _, ok := os.LookupEnv("API_KEY"); !ok {
err := godotenv.Load()
if err != nil {
log.Fatal("You have to set API_KEY in ENV or .env file")
os.Exit(-1)
}
}
return nil
}
// Handler orchestrates the core processing logic of this function.
// - ctx.ReadLLMArguments() parses LLM Function Calling Arguments.
// - ctx.WriteLLMResult() sends the retrieval result back to LLM.
func Handler(ctx serverless.Context) {
// parse the input data generated by llm tools_call
var msg Parameter
ctx.ReadLLMArguments(&msg)
// debug info
slog.Info("[sfn] << receive", "data", fmt.Sprintf("%+v", msg))
// if the source currency is not USD, ignore calling tools.
// openexchangerates.org free tier only supports USD as the base currency.
rate, err := fetchRate(msg.SourceCurrency, msg.TargetCurrency, msg.Amount)
if err != nil {
slog.Error("[sfn] >> fetchRate error", "err", err)
ctx.WriteLLMResult("can not get the target currency right now, please try later")
return
}
// request openexchangerates.org API to get the exchange rate
result := fmt.Sprintf("based on today's exchange rate: %f, %f %s is equivalent to approximately %f %s", rate, msg.Amount, msg.SourceCurrency, msg.Amount*rate, msg.TargetCurrency)
slog.Info("[sfn] >> result", "result", result)
if rate == 0 {
result = fmt.Sprintf("can not understand the target currency %s", msg.TargetCurrency)
}
// yomo will write tools_call result back to llm automatically
ctx.WriteLLMResult(result)
}
type Rates struct {
Rates map[string]float64 `json:"rates"`
}
// fetchRate fetches the exchange rate from openexchangerates.org
func fetchRate(sourceCurrency string, targetCurrency string, _ float64) (float64, error) {
resp, err := http.Get(fmt.Sprintf("https://openexchangerates.org/api/latest.json?app_id=%s&base=%s&symbols=%s", os.Getenv("API_KEY"), sourceCurrency, targetCurrency))
if err != nil {
return 0, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return 0, err
}
var rt *Rates
err = json.Unmarshal(body, &rt)
if err != nil {
return 0, err
}
return getRates(targetCurrency, rt)
}
func getRates(targetCurrency string, rates *Rates) (float64, error) {
if rates == nil {
return 0, fmt.Errorf("can not get the target currency, target currency is %s", targetCurrency)
}
if rate, ok := rates.Rates[targetCurrency]; ok {
return rate, nil
}
return 0, fmt.Errorf("can not get the target currency, target currency is %s", targetCurrency)
}