Skip to content

Commit

Permalink
Add api chain
Browse files Browse the repository at this point in the history
  • Loading branch information
hupe1980 committed Jul 26, 2023
1 parent fa001ff commit 31103c4
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 3 deletions.
174 changes: 174 additions & 0 deletions chain/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package chain

import (
"context"
"fmt"
"io"
"net/http"
"strings"

"github.com/hupe1980/golc"
"github.com/hupe1980/golc/prompt"
"github.com/hupe1980/golc/schema"
)

const defaultAPIURLTemplate = `You are given the below API Documentation:
{{.apiDoc}}
Using this documentation, generate the full API url to call for answering the user question.
You should build the API url in order to get a response that is as short as possible, while still getting the necessary information to answer the question. Pay attention to deliberately exclude any unnecessary pieces of data in the API call.
Question:{{.question}}
API url:`

const defaultAPIAnswerTemplate = defaultAPIURLTemplate + `{{.apiURL}}
Here is the response from the API:
{{.apiResponse}}
Summarize this response to answer the original question.
Summary:`

type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
}

// Compile time check to ensure API satisfies the Chain interface.
var _ schema.Chain = (*API)(nil)

type APIOptions struct {
*schema.CallbackOptions
InputKey string
OutputKey string
HTTPClient HTTPClient
Header map[string]string
}

type API struct {
apiRequestChain *LLM
apiAnswerChain *LLM
apiDoc string
opts APIOptions
}

func NewAPI(llm schema.Model, apiDoc string, optFns ...func(o *APIOptions)) (*API, error) {
opts := APIOptions{
InputKey: "question",
OutputKey: "output",
HTTPClient: http.DefaultClient,
CallbackOptions: &schema.CallbackOptions{
Verbose: golc.Verbose,
},
}

for _, fn := range optFns {
fn(&opts)
}

apiRequestChain, err := NewLLM(llm, prompt.NewTemplate(defaultAPIURLTemplate))
if err != nil {
return nil, err
}

apiAnswerChain, err := NewLLM(llm, prompt.NewTemplate(defaultAPIAnswerTemplate))
if err != nil {
return nil, err
}

return &API{
apiRequestChain: apiRequestChain,
apiAnswerChain: apiAnswerChain,
apiDoc: apiDoc,
opts: opts,
}, nil
}

// Call executes the API chain with the given context and inputs.
// It returns the outputs of the chain or an error, if any.
func (c *API) Call(ctx context.Context, inputs schema.ChainValues, optFns ...func(o *schema.CallOptions)) (schema.ChainValues, error) {
question, ok := inputs[c.opts.InputKey].(string)
if !ok {
return nil, fmt.Errorf("%w: no value for inputKey %s", ErrInvalidInputValues, c.opts.InputKey)
}

apiURL, err := golc.SimpleCall(ctx, c.apiRequestChain, schema.ChainValues{
"question": question,
"apiDoc": c.apiDoc,
})
if err != nil {
return nil, err
}

apiURL = strings.TrimSpace(apiURL)
if !strings.HasPrefix(apiURL, "https://") {
apiURL = fmt.Sprintf("https://%s", apiURL)
}

httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil)
if err != nil {
return nil, err
}

if c.opts.Header != nil {
for k, v := range c.opts.Header {
httpReq.Header.Set(k, v)
}
}

res, err := c.opts.HTTPClient.Do(httpReq)
if err != nil {
return nil, err
}

defer res.Body.Close()

apiResponse, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}

answer, err := golc.SimpleCall(ctx, c.apiAnswerChain, schema.ChainValues{
"question": question,
"apiDoc": c.apiDoc,
"apiURL": apiURL,
"apiResponse": string(apiResponse),
})
if err != nil {
return nil, err
}

return schema.ChainValues{
c.opts.OutputKey: strings.TrimSpace(answer),
}, nil
}

// Memory returns the memory associated with the chain.
func (c *API) Memory() schema.Memory {
return nil
}

// Type returns the type of the chain.
func (c *API) Type() string {
return "API"
}

// Verbose returns the verbosity setting of the chain.
func (c *API) Verbose() bool {
return c.opts.CallbackOptions.Verbose
}

// Callbacks returns the callbacks associated with the chain.
func (c *API) Callbacks() []schema.Callback {
return c.opts.CallbackOptions.Callbacks
}

// InputKeys returns the expected input keys.
func (c *API) InputKeys() []string {
return []string{c.opts.InputKey}
}

// OutputKeys returns the output keys the chain will return.
func (c *API) OutputKeys() []string {
return []string{c.opts.OutputKey}
}
77 changes: 77 additions & 0 deletions docs/content/en/docs/chains/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
title: Api
description: All about api chains.
weight: 50
---
```go
package main

import (
"context"
"fmt"
"log"
"os"

"github.com/hupe1980/golc"
"github.com/hupe1980/golc/chain"
"github.com/hupe1980/golc/model/llm"
)

func main() {
openai, err := llm.NewOpenAI(os.Getenv("OPENAI_API_KEY"), func(o *llm.OpenAIOptions) {
o.Temperature = 0
})
if err != nil {
log.Fatal(err)
}

api, err := chain.NewAPI(openai, apiDoc)
if err != nil {
log.Fatal(err)
}

answer, err := golc.SimpleCall(context.Background(), api, "What is the weather like right now in Munich, Germany in degrees Fahrenheit?")
if err != nil {
log.Fatal(err)
}

fmt.Println(answer)
}

const apiDoc = `BASE URL: https://api.open-meteo.com/
API Documentation
The API endpoint /v1/forecast accepts a geographical coordinate, a list of weather variables and responds with a JSON hourly weather forecast for 7 days. Time always starts at 0:00 today and contains 168 hours. All URL parameters are listed below:
Parameter Format Required Default Description
latitude, longitude Floating point Yes Geographical WGS84 coordinate of the location
hourly String array No A list of weather variables which should be returned. Values can be comma separated, or multiple &hourly= parameter in the URL can be used.
daily String array No A list of daily weather variable aggregations which should be returned. Values can be comma separated, or multiple &daily= parameter in the URL can be used. If daily weather variables are specified, parameter timezone is required.
current_weather Bool No false Include current weather conditions in the JSON output.
temperature_unit String No celsius If fahrenheit is set, all temperature values are converted to Fahrenheit.
windspeed_unit String No kmh Other wind speed speed units: ms, mph and kn
precipitation_unit String No mm Other precipitation amount units: inch
timeformat String No iso8601 If format unixtime is selected, all time values are returned in UNIX epoch time in seconds. Please note that all timestamp are in GMT+0! For daily values with unix timestamps, please apply utc_offset_seconds again to get the correct date.
timezone String No GMT If timezone is set, all timestamps are returned as local-time and data is returned starting at 00:00 local-time. Any time zone name from the time zone database is supported. If auto is set as a time zone, the coordinates will be automatically resolved to the local time zone.
past_days Integer (0-2) No 0 If past_days is set, yesterday or the day before yesterday data are also returned.
start_date
end_date String (yyyy-mm-dd) No The time interval to get weather data. A day must be specified as an ISO8601 date (e.g. 2022-06-30).
models String array No auto Manually select one or more weather models. Per default, the best suitable weather models will be combined.
Hourly Parameter Definition
The parameter &hourly= accepts the following values. Most weather variables are given as an instantaneous value for the indicated hour. Some variables like precipitation are calculated from the preceding hour as an average or sum.
Variable Valid time Unit Description
temperature_2m Instant °C (°F) Air temperature at 2 meters above ground
snowfall Preceding hour sum cm (inch) Snowfall amount of the preceding hour in centimeters. For the water equivalent in millimeter, divide by 7. E.g. 7 cm snow = 10 mm precipitation water equivalent
rain Preceding hour sum mm (inch) Rain from large scale weather systems of the preceding hour in millimeter
showers Preceding hour sum mm (inch) Showers from convective precipitation in millimeters from the preceding hour
weathercode Instant WMO code Weather condition as a numeric code. Follow WMO weather interpretation codes. See table below for details.
snow_depth Instant meters Snow depth on the ground
freezinglevel_height Instant meters Altitude above sea level of the 0°C level
visibility Instant meters Viewing distance in meters. Influenced by low clouds, humidity and aerosols. Maximum visibility is approximately 24 km.`
```
Output:
```text
The current temperature in Munich, Germany is 55.5°F.
```
2 changes: 1 addition & 1 deletion docs/content/en/docs/chains/conversation.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Conversation
description: All about conversation chains.
weight: 50
weight: 60
---

```go
Expand Down
2 changes: 1 addition & 1 deletion docs/content/en/docs/chains/llm_bash.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: LLMBash
description: All about llm bash chains.
weight: 60
weight: 70
---
```go
package main
Expand Down
2 changes: 1 addition & 1 deletion docs/content/en/docs/chains/sql.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: SQL
description: All about sql chains.
weight: 70
weight: 80
---

```go
Expand Down
66 changes: 66 additions & 0 deletions examples/api_chain/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import (
"context"
"fmt"
"log"
"os"

"github.com/hupe1980/golc"
"github.com/hupe1980/golc/chain"
"github.com/hupe1980/golc/model/llm"
)

func main() {
openai, err := llm.NewOpenAI(os.Getenv("OPENAI_API_KEY"), func(o *llm.OpenAIOptions) {
o.Temperature = 0
})
if err != nil {
log.Fatal(err)
}

api, err := chain.NewAPI(openai, apiDoc)
if err != nil {
log.Fatal(err)
}

answer, err := golc.SimpleCall(context.Background(), api, "What is the weather like right now in Munich, Germany in degrees Fahrenheit?")
if err != nil {
log.Fatal(err)
}

fmt.Println(answer)
}

const apiDoc = `BASE URL: https://api.open-meteo.com/
API Documentation
The API endpoint /v1/forecast accepts a geographical coordinate, a list of weather variables and responds with a JSON hourly weather forecast for 7 days. Time always starts at 0:00 today and contains 168 hours. All URL parameters are listed below:
Parameter Format Required Default Description
latitude, longitude Floating point Yes Geographical WGS84 coordinate of the location
hourly String array No A list of weather variables which should be returned. Values can be comma separated, or multiple &hourly= parameter in the URL can be used.
daily String array No A list of daily weather variable aggregations which should be returned. Values can be comma separated, or multiple &daily= parameter in the URL can be used. If daily weather variables are specified, parameter timezone is required.
current_weather Bool No false Include current weather conditions in the JSON output.
temperature_unit String No celsius If fahrenheit is set, all temperature values are converted to Fahrenheit.
windspeed_unit String No kmh Other wind speed speed units: ms, mph and kn
precipitation_unit String No mm Other precipitation amount units: inch
timeformat String No iso8601 If format unixtime is selected, all time values are returned in UNIX epoch time in seconds. Please note that all timestamp are in GMT+0! For daily values with unix timestamps, please apply utc_offset_seconds again to get the correct date.
timezone String No GMT If timezone is set, all timestamps are returned as local-time and data is returned starting at 00:00 local-time. Any time zone name from the time zone database is supported. If auto is set as a time zone, the coordinates will be automatically resolved to the local time zone.
past_days Integer (0-2) No 0 If past_days is set, yesterday or the day before yesterday data are also returned.
start_date
end_date String (yyyy-mm-dd) No The time interval to get weather data. A day must be specified as an ISO8601 date (e.g. 2022-06-30).
models String array No auto Manually select one or more weather models. Per default, the best suitable weather models will be combined.
Hourly Parameter Definition
The parameter &hourly= accepts the following values. Most weather variables are given as an instantaneous value for the indicated hour. Some variables like precipitation are calculated from the preceding hour as an average or sum.
Variable Valid time Unit Description
temperature_2m Instant °C (°F) Air temperature at 2 meters above ground
snowfall Preceding hour sum cm (inch) Snowfall amount of the preceding hour in centimeters. For the water equivalent in millimeter, divide by 7. E.g. 7 cm snow = 10 mm precipitation water equivalent
rain Preceding hour sum mm (inch) Rain from large scale weather systems of the preceding hour in millimeter
showers Preceding hour sum mm (inch) Showers from convective precipitation in millimeters from the preceding hour
weathercode Instant WMO code Weather condition as a numeric code. Follow WMO weather interpretation codes. See table below for details.
snow_depth Instant meters Snow depth on the ground
freezinglevel_height Instant meters Altitude above sea level of the 0°C level
visibility Instant meters Viewing distance in meters. Influenced by low clouds, humidity and aerosols. Maximum visibility is approximately 24 km.`

0 comments on commit 31103c4

Please sign in to comment.