Skip to content

Commit

Permalink
servlet: google maps static images (#32)
Browse files Browse the repository at this point in the history
Signed-off-by: Edoardo Vacchi <[email protected]>
  • Loading branch information
evacchi authored Dec 23, 2024
1 parent b36be58 commit 39d14c0
Show file tree
Hide file tree
Showing 10 changed files with 656 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.work
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use (
./servlets/amadeus-flight-api
./servlets/crypto-hash
./servlets/github
./servlets/google-maps-image
./servlets/historical-flight-api
./servlets/tenor-gifs
./servlets/wordpress
Expand Down
2 changes: 2 additions & 0 deletions servlets/google-maps-image/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist/

17 changes: 17 additions & 0 deletions servlets/google-maps-image/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Google Maps Static Image API

Generates an image using [Google's static Map generation API](https://developers.google.com/maps/documentation/maps-static/start).

## Config

- `api-key`: see [docs](https://developers.google.com/maps/documentation/maps-static/get-api-key)

## Domains

- `maps.googleapis.com`

## Example

> can you draw santa's route on xmas eve on a map? start at the north pole, then show at least 15 waypoints! Make sure that the right-most waypoint is the first Santa encounters. Style the map in festive colors, and use red and green for the markers to celebrate the winter holidays! make it 2000x2000
![](imgs/santa-route.png)
5 changes: 5 additions & 0 deletions servlets/google-maps-image/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module google-maps-image

go 1.22.1

require github.com/extism/go-pdk v1.0.5
2 changes: 2 additions & 0 deletions servlets/google-maps-image/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/extism/go-pdk v1.0.5 h1:5d5yYkWBweBP84Z+H3DP5DsD0fwvf2anWXyypCXpSW8=
github.com/extism/go-pdk v1.0.5/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4=
Binary file added servlets/google-maps-image/imgs/santa-route.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
319 changes: 319 additions & 0 deletions servlets/google-maps-image/main.go

Large diffs are not rendered by default.

221 changes: 221 additions & 0 deletions servlets/google-maps-image/pdk.gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
// THIS FILE WAS GENERATED BY `xtp-go-bindgen`. DO NOT EDIT.
package main

import (
"errors"

pdk "github.com/extism/go-pdk"
)

//export call
func _Call() int32 {
var err error
_ = err
pdk.Log(pdk.LogDebug, "Call: getting JSON input")
var input CallToolRequest
err = pdk.InputJSON(&input)
if err != nil {
pdk.SetError(err)
return -1
}

pdk.Log(pdk.LogDebug, "Call: calling implementation function")
output, err := Call(input)
if err != nil {
pdk.SetError(err)
return -1
}

pdk.Log(pdk.LogDebug, "Call: setting JSON output")
err = pdk.OutputJSON(output)
if err != nil {
pdk.SetError(err)
return -1
}

pdk.Log(pdk.LogDebug, "Call: returning")
return 0
}

//export describe
func _Describe() int32 {
var err error
_ = err
output, err := Describe()
if err != nil {
pdk.SetError(err)
return -1
}

pdk.Log(pdk.LogDebug, "Describe: setting JSON output")
err = pdk.OutputJSON(output)
if err != nil {
pdk.SetError(err)
return -1
}

pdk.Log(pdk.LogDebug, "Describe: returning")
return 0
}

//
type BlobResourceContents struct {
// A base64-encoded string representing the binary data of the item.
Blob string `json:"blob"`
// The MIME type of this resource, if known.
MimeType *string `json:"mimeType,omitempty"`
// The URI of this resource.
Uri string `json:"uri"`
}

// Used by the client to invoke a tool provided by the server.
type CallToolRequest struct {
Method *string `json:"method,omitempty"`
Params Params `json:"params"`
}

// The server's response to a tool call.
//
// Any errors that originate from the tool SHOULD be reported inside the result
// object, with `isError` set to true, _not_ as an MCP protocol-level error
// response. Otherwise, the LLM would not be able to see that an error occurred
// and self-correct.
//
// However, any errors in _finding_ the tool, an error indicating that the
// server does not support tool calls, or any other exceptional conditions,
// should be reported as an MCP error response.
type CallToolResult struct {
Content []Content `json:"content"`
// Whether the tool call ended in an error.
//
// If not set, this is assumed to be false (the call was successful).
IsError *bool `json:"isError,omitempty"`
}

// A content response.
// For text content set type to ContentType.Text and set the `text` property
// For image content set type to ContentType.Image and set the `data` and `mimeType` properties
type Content struct {
Annotations *TextAnnotation `json:"annotations,omitempty"`
// The base64-encoded image data.
Data *string `json:"data,omitempty"`
// The MIME type of the image. Different providers may support different image types.
MimeType *string `json:"mimeType,omitempty"`
// The text content of the message.
Text *string `json:"text,omitempty"`
Type ContentType `json:"type"`
}

//
type ContentType string

const (
ContentTypeText ContentType = "text"
ContentTypeImage ContentType = "image"
ContentTypeResource ContentType = "resource"
)

func (v ContentType) String() string {
switch v {
case ContentTypeText:
return `text`
case ContentTypeImage:
return `image`
case ContentTypeResource:
return `resource`
default:
return ""
}
}

func stringToContentType(s string) (ContentType, error) {
switch s {
case `text`:
return ContentTypeText, nil
case `image`:
return ContentTypeImage, nil
case `resource`:
return ContentTypeResource, nil
default:
return ContentType(""), errors.New("unable to convert string to ContentType")
}
}

// Provides one or more descriptions of the tools available in this servlet.
type ListToolsResult struct {
// The list of ToolDescription objects provided by this servlet.
Tools []ToolDescription `json:"tools"`
}

//
type Params struct {
Arguments interface{} `json:"arguments,omitempty"`
Name string `json:"name"`
}

// The sender or recipient of messages and data in a conversation.
type Role string

const (
RoleAssistant Role = "assistant"
RoleUser Role = "user"
)

func (v Role) String() string {
switch v {
case RoleAssistant:
return `assistant`
case RoleUser:
return `user`
default:
return ""
}
}

func stringToRole(s string) (Role, error) {
switch s {
case `assistant`:
return RoleAssistant, nil
case `user`:
return RoleUser, nil
default:
return Role(""), errors.New("unable to convert string to Role")
}
}

// A text annotation
type TextAnnotation struct {
// Describes who the intended customer of this object or data is.
//
// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
Audience []Role `json:"audience,omitempty"`
// Describes how important this data is for operating the server.
//
// A value of 1 means "most important," and indicates that the data is
// effectively required, while 0 means "least important," and indicates that
// the data is entirely optional.
Priority float32 `json:"priority,omitempty"`
}

//
type TextResourceContents struct {
// The MIME type of this resource, if known.
MimeType *string `json:"mimeType,omitempty"`
// The text of the item. This must only be set if the item can actually be represented as text (not binary data).
Text string `json:"text"`
// The URI of this resource.
Uri string `json:"uri"`
}

// Describes the capabilities and expected paramters of the tool function
type ToolDescription struct {
// A description of the tool
Description string `json:"description"`
// The JSON schema describing the argument input
InputSchema interface{} `json:"inputSchema"`
// The name of the tool. It should match the plugin / binding name.
Name string `json:"name"`
}

// Note: leave this in place, as the Go compiler will find the `export` function as the entrypoint.
func main() {}
72 changes: 72 additions & 0 deletions servlets/google-maps-image/prepare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/bash

# Function to check if a command exists
command_exists () {
command -v "$1" >/dev/null 2>&1
}

missing_deps=0

# Check for Go
if ! (command_exists go); then
missing_deps=1
echo "❌ Go (supported version between 1.18 - 1.22) is not installed."
echo ""
echo "To install Go, visit the official download page:"
echo "👉 https://go.dev/dl/"
echo ""
echo "Or install it using a package manager:"
echo ""
echo "🔹 macOS (Homebrew):"
echo " brew install go"
echo ""
echo "🔹 Ubuntu/Debian:"
echo " sudo apt-get -y install golang-go"
echo ""
echo "🔹 Arch Linux:"
echo " sudo pacman -S go"
echo ""
fi

# Check for the right version of Go, needed by TinyGo (supports go 1.18 - 1.22)
if (command_exists go); then
compat=0
for v in `seq 18 22`; do
if (go version | grep -q "go1.$v"); then
compat=1
fi
done

if [ $compat -eq 0 ]; then
echo "❌ Supported Go version is not installed. Must be Go 1.18 - 1.22."
echo ""
fi
fi


ARCH=$(arch)

# Check for TinyGo
if ! (command_exists tinygo); then
missing_deps=1
echo "❌ TinyGo is not installed."
echo ""
echo "To install TinyGo, visit the official download page:"
echo "👉 https://tinygo.org/getting-started/install/"
echo ""
echo "Or install it using a package manager:"
echo ""
echo "🔹 macOS (Homebrew):"
echo " brew tap tinygo-org/tools"
echo " brew install tinygo"
echo ""
echo "🔹 Ubuntu/Debian:"
echo " wget https://github.com/tinygo-org/tinygo/releases/download/v0.31.2/tinygo_0.31.2_$ARCH.deb"
echo " sudo dpkg -i tinygo_0.31.2_$ARCH.deb"
echo ""
echo "🔹 Arch Linux:"
echo " pacman -S extra/tinygo"
echo ""
fi

go install golang.org/x/tools/cmd/goimports@latest
17 changes: 17 additions & 0 deletions servlets/google-maps-image/xtp.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
app_id = "app_01je4dgpcyfvgrz8f1ys3pbxas"

# This is where 'xtp plugin push' expects to find the wasm file after the build script has run.
bin = "dist/plugin.wasm"
extension_point_id = "ext_01je4jj1tteaktf0zd0anm8854"
name = "google-maps-image"

[scripts]

# xtp plugin build runs this script to generate the wasm file
build = "mkdir -p dist && tinygo build -target wasi -o dist/plugin.wasm ."

# xtp plugin init runs this script to format the plugin code
format = "go fmt && go mod tidy && goimports -w main.go"

# xtp plugin init runs this script before running the format script
prepare = "sh prepare.sh && go get ./..."

0 comments on commit 39d14c0

Please sign in to comment.