Movatterモバイル変換


[0]ホーム

URL:


pdk

packagemodule
v1.1.3Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2025 License:BSD-3-ClauseImports:4Imported by:42

Details

Repository

github.com/extism/go-pdk

Links

README

Extism Go PDK

This library can be used to writeExtism Plug-ins in Go.

Install

Include the library with Go get:

go get github.com/extism/go-pdk

Reference Documentation

You can find the reference documentation for this library onpkg.go.dev.

Getting Started

The goal of writing anExtism plug-in is to compile your Gocode to a Wasm module with exported functions that the host application caninvoke. The first thing you should understand is creating an export. Let's writea simple program that exports agreet function which will take a name as astring and return a greeting string. Paste this into yourmain.go:

package mainimport ("github.com/extism/go-pdk")//go:wasmexport greetfunc greet() int32 {input := pdk.Input()greeting := `Hello, ` + string(input) + `!`pdk.OutputString(greeting)return 0}

Some things to note about this code:

  1. The//go:wasmexport greet comment is required. This marks the greet function as anexport with the namegreet that can be called by the host.
  2. Exports in the Go PDK are coded to the raw ABI. You get parameters from thehost by callingpdk.Input* functions andyou send returns back with thepdk.Output* functions.
  3. An Extism export expects an i32 return code.0 is success and1 is afailure.

Install thetinygo compiler:

Seehttps://tinygo.org/getting-started/install/ for instructions for yourplatform.

Note: while the core Go toolchain has support to target WebAssembly, we findtinygo to work well for plug-in code. Please open issues on this repositoryif you try building withgo build instead & have problems!

Compile this with the command:

tinygo build -o plugin.wasm -target wasip1 -buildmode=c-shared main.go

We can now testplugin.wasm using theExtism CLI'srun command:

extism call plugin.wasm greet --input "Benjamin" --wasi# => Hello, Benjamin!

Note: Currentlywasip1 must be provided for all Go plug-ins even if theydon't need system access, however this will eventually be optional.

Note: We also have a web-based, plug-in tester called theExtism Playground

More Exports: Error Handling

Suppose we want to re-write our greeting module to never greet Benjamins. We canusepdk.SetError orpdk.SetErrorString:

//go:wasmexport greetfunc greet() int32 {name := string(pdk.Input())if name == "Benjamin" {pdk.SetError(errors.New("Sorry, we don't greet Benjamins!"))return 1}greeting := `Hello, ` + name + `!`pdk.OutputString(greeting)return 0}

Now when we try again:

extism call plugin.wasm greet --input="Benjamin" --wasi# => Error: Sorry, we don't greet Benjamins!# => returned non-zero exit code: 1echo $? # print last status code# => 1extism call plugin.wasm greet --input="Zach" --wasi# => Hello, Zach!echo $?# => 0
Json

Extism export functions simply take bytes in and bytes out. Those can bewhatever you want them to be. A common and simple way to get more complex typesto and from the host is with json:

type Add struct {A int `json:"a"`B int `json:"b"`}type Sum struct {Sum int `json:"sum"`}//go:wasmexport addfunc add() int32 {params := Add{}// use json input helper, which automatically unmarshals the plugin input into your structerr := pdk.InputJSON(&params)if err != nil {pdk.SetError(err)return 1}sum := Sum{Sum: params.A + params.B}// use json output helper, which automatically marshals your struct to the plugin output_, err := pdk.OutputJSON(sum)if err != nil {pdk.SetError(err)return 1}return 0}
extism call plugin.wasm add --input='{"a": 20, "b": 21}' --wasi# => {"sum":41}

Configs

Configs are key-value pairs that can be passed in by the host when creating aplug-in. These can be useful to statically configure the plug-in with some datathat exists across every function call. Here is a trivial example usingpdk.GetConfig:

//go:wasmexport greetfunc greet() int32 {user, ok := pdk.GetConfig("user")if !ok {pdk.SetErrorString("This plug-in requires a 'user' key in the config")return 1}greeting := `Hello, ` + user + `!`pdk.OutputString(greeting)return 0}

To test it, theExtism CLI has a--configoption that lets you pass inkey=value pairs:

extism call plugin.wasm greet --config user=Benjamin# => Hello, Benjamin!

Variables

Variables are another key-value mechanism but it's a mutable data store thatwill persist across function calls. These variables will persist as long as thehost has loaded and not freed the plug-in.

//go:wasmexport countfunc count() int32 {count := pdk.GetVarInt("count")count = count + 1pdk.SetVarInt("count", count)pdk.OutputString(strconv.Itoa(count))return 0}

Note: Use the untyped variantspdk.SetVar(string, []byte)andpdk.GetVar(string) []byteto handle your own types.

Logging

Because Wasm modules by default do not have access to the system, printing tostdout won't work (unless you use WASI). Extism provides a simplelogging function that allowsyou to use the host application to log without having to give the plug-inpermission to make syscalls.

//go:wasmexport log_stufffunc logStuff() int32 {pdk.Log(pdk.LogInfo, "An info log!")pdk.Log(pdk.LogDebug, "A debug log!")pdk.Log(pdk.LogWarn, "A warn log!")pdk.Log(pdk.LogError, "An error log!")return 0}

FromExtism CLI:

extism call plugin.wasm log_stuff --wasi --log-level=debug2023/10/12 12:11:23 Calling function : log_stuff2023/10/12 12:11:23 An info log!2023/10/12 12:11:23 A debug log!2023/10/12 12:11:23 A warn log!2023/10/12 12:11:23 An error log!

Note: From the CLI you need to pass a level with--log-level. If you arerunning the plug-in in your own host using one of our SDKs, you need to makesure that you callset_log_file to"stdout" or some file location.

HTTP

Sometimes it is useful to let a plug-inmake HTTP calls.See this example

//go:wasmexport http_getfunc httpGet() int32 {// create an HTTP Request (withuot relying on WASI), set headers as neededreq := pdk.NewHTTPRequest(pdk.MethodGet, "https://jsonplaceholder.typicode.com/todos/1")req.SetHeader("some-name", "some-value")req.SetHeader("another", "again")// send the request, get response back (can check status on response via res.Status())res := req.Send()pdk.OutputMemory(res.Memory())return 0}

By default, Extism modules cannot make HTTP requests unless you specify whichhosts it can connect to. You can use--alow-host in the Extism CLI to setthis:

extism call plugin.wasm http_get --wasi --allow-host='*.typicode.com'# => { "userId": 1, "id": 1, "title": "delectus aut autem", "completed": false }

Imports (Host Functions)

Like any other code module, Wasm not only let's you export functions to theoutside world, you can import them too. Host Functions allow a plug-in to importfunctions defined in the host. For example, if you host application is writtenin Python, it can pass a Python function down to your Go plug-in where you caninvoke it.

This topic can get fairly complicated and we have not yet fully abstracted theWasm knowledge you need to do this correctly. So we recommend reading ourconcept doc on Host Functionsbefore you get started.

A Simple Example

Host functions have a similar interface as exports. You just need to declarethem as extern on the top of your main.go. You only declare the interface as itis the host's responsibility to provide the implementation:

//go:wasmimport extism:host/user a_python_funcfunc aPythonFunc(uint64) uint64

We should be able to call this function as a normal Go function. Note that weneed to manually handle the pointer casting:

//go:wasmexport hello_from_pythonfunc helloFromPython() int32 {    msg := "An argument to send to Python"    mem := pdk.AllocateString(msg)    defer mem.Free()    ptr := aPythonFunc(mem.Offset())    rmem := pdk.FindMemory(ptr)    response := string(rmem.ReadBytes())    pdk.OutputString(response)    return 0}
Testing it out

We can't really test this from the Extism CLI as something must provide theimplementation. So let's write out the Python side here. Check out thedocs for Host SDKs to implement ahost function in a language of your choice.

from extism import host_fn, Plugin@host_fn()def a_python_func(input: str) -> str:    # just printing this out to prove we're in Python land    print("Hello from Python!")    # let's just add "!" to the input string    # but you could imagine here we could add some    # applicaiton code like query or manipulate the database    # or our application APIs    return input + "!"

Now when we load the plug-in we pass the host function:

manifest = {"wasm": [{"path": "/path/to/plugin.wasm"}]}plugin = Plugin(manifest, functions=[a_python_func], wasi=True)result = plugin.call('hello_from_python', b'').decode('utf-8')print(result)
python3 app.py# => Hello from Python!# => An argument to send to Python!

Reactor modules

Since TinyGo version 0.34.0, the compiler has native support forReactor modules.

Make sure you invoke the compiler with the-buildmode=c-shared flagso that libc and the Go runtime are properly initialized:

cd example/reactortinygo build -target wasip1 -buildmode=c-shared -o reactor.wasm ./tiny_main.goextism call ./reactor.wasm read_file --input "./test.txt" --allow-path . --wasi --log-level info# => Hello World!
Note on TinyGo 0.33.0 and earlier

TinyGo versions below 0.34.0 do not supportReactor modules.If you want to use WASI inside your Reactor module functions (exported functions otherthanmain). You can however import thewasi-reactor module to ensure that libcand go runtime are initialized as expected:

Moreover, older versions may not provide the special//go:wasmexportdirective, and instead use//export.

package mainimport ("os""github.com/extism/go-pdk"_ "github.com/extism/go-pdk/wasi-reactor")//export read_filefunc read_file() {name := pdk.InputString()content, err := os.ReadFile(name)if err != nil {pdk.Log(pdk.LogError, err.Error())return}pdk.Output(content)}func main() {}
tinygo build -target wasip1 -o reactor.wasm ./tiny_main.goextism call ./reactor.wasm read_file --input "./test.txt" --allow-path . --wasi --log-level info# => Hello World!

Note: this is not required if you only have themain function.

Generating Bindings

It's often very useful to define a schema to describe the function signaturesand types you want to use between Extism SDK and PDK languages.

XTP Bindgen is an open sourceframework to generate PDK bindings for Extism plug-ins. It's used by theXTP Platform, but can be used outside of the platformto define any Extism compatible plug-in system.

1. Install thextp CLI.

See installation instructionshere.

2. Create a schema using our OpenAPI-inspired IDL:
version: v1-draftexports:   CountVowels:      input:           type: string          contentType: text/plain; charset=utf-8      output:          $ref: "#/components/schemas/VowelReport"          contentType: application/json# components.schemas defined in example-schema.yaml...

See an example inexample-schema.yaml, or a full"kitchen sink" example onthe docs page.

3. Generate bindings to use from your plugins:
xtp plugin init --schema-file ./example-schema.yaml    1. TypeScript                        > 2. Go                                  3. Rust                                4. Python                              5. C#                                  6. Zig                                 7. C++                                 8. GitHub Template                     9. Local Template

This will create an entire boilerplate plugin project for you to get startedwith:

package main// returns VowelReport (The result of counting vowels on the Vowels input.)func CountVowels(input string) (VowelReport, error) {// TODO: fill out your implementation herepanic("Function not implemented.")}

Implement the empty function(s), and runxtp plugin build to compile yourplugin.

For more information about XTP Bindgen, see thedylibso/xtp-bindgen repository andthe officialXTP Schema documentation.

Reach Out!

Have a question or just want to drop in and say hi?Hop on the Discord!

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

funcGetConfig

func GetConfig(keystring) (string,bool)

GetConfig returns the config string associated with `key` (if any).

funcGetVar

func GetVar(keystring) []byte

GetVar returns the byte slice (if any) associated with `key`.

funcGetVarInt

func GetVarInt(keystring)int

GetVarInt returns the int associated with `key` (or 0 if none).

funcInput

func Input() []byte

Input returns a slice of bytes from the host.

funcInputJSONadded inv1.0.2

func InputJSON(vany)error

InputJSON returns unmartialed JSON data from the host "input".

funcInputString

func InputString()string

InputString returns the input data from the host as a UTF-8 string.

funcJSONFromadded inv1.0.2

func JSONFrom(offsetuint64, vany)error

JSONFrom unmarshals a `Memory` block located at `offset` from the hostinto the provided data `v`.

funcLog

func Log(levelLogLevel, sstring)

Log logs the provided UTF-8 string `s` on the host using the provided log `level`.

funcLogMemory

func LogMemory(levelLogLevel, mMemory)

LogMemory logs the `memory` block on the host using the provided log `level`.

funcOutput

func Output(data []byte)

Output sends the `data` slice of bytes to the host output.

funcOutputJSONadded inv1.0.2

func OutputJSON(vany)error

OutputJSON marshals the provided data `v` as output to the host.

funcOutputMemory

func OutputMemory(memMemory)

OutputMemory sends the `mem` Memory to the host output.Note that the `mem` is _NOT_ freed and is your responsibility to free when finished with it.

funcOutputString

func OutputString(sstring)

OutputString sends the UTF-8 string `s` to the host output.

funcParamBytesadded inv1.0.3

func ParamBytes(offsetuint64) []byte

ParamBytes returns bytes from Extism host memory given an offset.

funcParamStringadded inv1.0.3

func ParamString(offsetuint64)string

ParamString returns UTF-8 string data from Extism host memory given an offset.

funcParamU32added inv1.0.3

func ParamU32(offsetuint64)uint32

ParamU32 returns a uint32 from Extism host memory given an offset.

funcParamU64added inv1.0.3

func ParamU64(offsetuint64)uint64

ParamU64 returns a uint64 from Extism host memory given an offset.

funcRemoveVar

func RemoveVar(keystring)

RemoveVar removes (and frees) the host variable associated with `key`.

funcResultBytesadded inv1.0.3

func ResultBytes(d []byte)uint64

ResultBytes allocates bytes and returns the offset in Extism host memory.

funcResultStringadded inv1.0.3

func ResultString(sstring)uint64

ResultString allocates a UTF-8 string and returns the offset in Extism host memory.

funcResultU32added inv1.0.3

func ResultU32(duint32)uint64

ResultU32 allocates a uint32 and returns the offset in Extism host memory.

funcResultU64added inv1.0.3

func ResultU64(duint64)uint64

ResultU64 allocates a uint64 and returns the offset in Extism host memory.

funcSetError

func SetError(errerror)

SetError sets the host error string from `err`.

funcSetErrorString

func SetErrorString(errstring)

SetErrorString sets the host error string from `err`.

funcSetVar

func SetVar(keystring, value []byte)

SetVar sets the host variable associated with `key` to the `value` byte slice.

funcSetVarInt

func SetVarInt(keystring, valueint)

SetVarInt sets the host variable associated with `key` to the `value` int.

Types

typeHTTPMethodadded inv1.0.1

type HTTPMethodint32

HTTPMethod represents an HTTP method.

const (MethodGetHTTPMethod =iotaMethodHeadMethodPostMethodPutMethodPatch//RFC 5789MethodDeleteMethodConnectMethodOptionsMethodTrace)

func (HTTPMethod)Stringadded inv1.0.1

func (mHTTPMethod) String()string

typeHTTPRequest

type HTTPRequest struct {// contains filtered or unexported fields}

HTTPRequest represents an HTTP request sent by the host.

funcNewHTTPRequest

func NewHTTPRequest(methodHTTPMethod, urlstring) *HTTPRequest

NewHTTPRequest returns a new `HTTPRequest`.

func (*HTTPRequest)Send

func (r *HTTPRequest) Send()HTTPResponse

Send sends the `HTTPRequest` from the host and returns the `HTTPResponse`.

func (*HTTPRequest)SetBody

func (r *HTTPRequest) SetBody(body []byte) *HTTPRequest

SetBody sets an HTTP request body to the provided byte slice.

func (*HTTPRequest)SetHeader

func (r *HTTPRequest) SetHeader(keystring, valuestring) *HTTPRequest

SetHeader sets an HTTP header `key` to `value`.

typeHTTPRequestMeta

type HTTPRequestMeta struct {URLstring            `json:"url"`Methodstring            `json:"method"`Headers map[string]string `json:"headers"`}

HTTPRequestMeta represents the metadata associated with an HTTP request on the host.

typeHTTPResponse

type HTTPResponse struct {// contains filtered or unexported fields}

HTTPResponse represents an HTTP response returned from the host.

func (HTTPResponse)Body

func (rHTTPResponse) Body() []byte

Body returns the body byte slice (if any) from the `HTTPResponse`.

func (*HTTPResponse)Headersadded inv1.1.0

func (r *HTTPResponse) Headers() map[string]string

Headers returns a map containing the HTTP response headers

func (HTTPResponse)Memory

func (rHTTPResponse) Memory()Memory

Memory returns the memory associated with the `HTTPResponse`.

func (HTTPResponse)Status

func (rHTTPResponse) Status()uint16

Status returns the status code from the `HTTPResponse`.

typeLogLevel

type LogLevelint

LogLevel represents a logging level.

const (LogTraceLogLevel =iotaLogDebugLogInfoLogWarnLogError)

typeMemory

type Memory =memory.Memory

Memory represents memory allocated by (and shared with) the host.

funcAllocate

func Allocate(lengthint)Memory

funcAllocateBytes

func AllocateBytes(data []byte)Memory

funcAllocateJSONadded inv1.0.4

func AllocateJSON(vany) (Memory,error)

AllocateJSON allocates and saves the type `any` into Memory on the host.

funcAllocateString

func AllocateString(datastring)Memory

AllocateString allocates and saves the UTF-8 string `data` into Memory on the host.

funcFindMemory

func FindMemory(offsetuint64)Memory

FindMemory finds the host memory block at the given `offset`.

funcNewMemoryadded inv1.1.1

func NewMemory(offsetuint64, lengthuint64)Memory

Source Files

View all Source files

Directories

PathSynopsis
examplemodule
httpcommand
reactorcommand
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f orF : Jump to
y orY : Canonical URL
go.dev uses cookies from Google to deliver and enhance the quality of its services and to analyze traffic.Learn more.

[8]ページ先頭

©2009-2025 Movatter.jp