Self-hosted service
mattn/go-tflite avatar
mattn/go-tflite

mattn/go-tflite: a cgo binding that keeps TensorFlow Lite's C API in Go's hands

Go binding for TensorFlow Lite

322 stars42 forksJupyter NotebookMIT

At a glance

What is it?
go-tflite wraps the TensorFlow Lite C API rather than reimplementing an interpreter, so your build depends on libtensorflowlite_c.so and the version pinned by the binding. It suits Go services that already ship a .tflite file and want inference in-process.
Who is it for?
Adopt go-tflite if your deployment already ships libtensorflowlite_c.so and you want inference inside a Go process without a separate runtime. Do not adopt it if you cannot pin the TensorFlow Lite C library to the version the binding expects, or if you want a pure-Go build with no cgo and no shared object.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 28 days ago.
What is it written in?
Mainly Jupyter Notebook, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The gap go-tflite fills: Go code that needs to call an existing .tflite file

TensorFlow Lite is distributed as a C library plus a set of language bindings built on top of it. Go is not one of the first-class targets. go-tflite exists to close that gap: it is a cgo binding, which means Go code calls into the TensorFlow Lite C API through libtensorflowlite_c.so instead of through a reimplementation of the interpreter in Go. The README states the scope plainly: go-tflite links against libtensorflowlite_c.so only, so there is no need to build the full TensorFlow library. That single sentence defines the audience. This is for teams that already have a trained model exported to the .tflite format and want to run inference from a Go binary, typically a service or a CLI tool, without standing up a Python process or a separate inference server. The typical caller is a Go backend that owns the request path and does not want a network hop to a model server. The repository also ships an iris.ipynb notebook and a Binder badge so the binding can be tried in a browser without a local install, and the same image is published as ghcr.io/mattn/go-tflite/gophernotes. That is a low-friction way to read the API before committing to a cgo build. What go-tflite is not: a trainer, a model converter, or a pure-Go runtime. If you need to produce the .tflite file itself, that work happens elsewhere.

What the interpreter lifecycle looks like in Go

The README's usage example is short and shows the whole shape of the API. You load a model with tflite.NewModelFromFile, create options with tflite.NewInterpreterOptions, construct an interpreter with tflite.NewInterpreter, call AllocateTensors, write into the input tensor, call Invoke, and read the output tensor. Every one of those constructors returns a value that carries its own Delete method, and the example defers each Delete immediately after the nil check. That pattern is not decoration. These are handles to C-side allocations, and the binding exposes explicit lifetime management rather than relying on a finalizer. If you forget model.Delete() or interpreter.Delete(), you leak the underlying TensorFlow Lite objects. The tensor accessors are typed: the example uses input.Float32s() to get a slice it can index into, and GetOutputTensor(0).Float32s()[0] to read the result. The example model is a sine approximation, and the input is converted with float64(1.2) * math.Pi / 180.0 before being stored as a float32. That detail is worth noting: the binding hands you the underlying slice, so you are responsible for writing values in the layout the model expects. There is no shape checking in the snippet, and no error return from Invoke. The README also points to the _example directory for more examples, which is where you would look for anything beyond the single-input, single-output case.

Building libtensorflowlite_c.so and pointing cgo at it

This is the part that decides whether the project is usable for you. The README states the release requires TensorFlow Lite 2.2.0-rc3, so the C library version is pinned by the binding, not chosen freely. Assuming the TensorFlow source is under /source/directory/tensorflow, the documented build command is bazel build --config opt --config monolithic //tensorflow/lite/c:libtensorflowlite_c.so. The monolithic config is what produces the shared object the binding links against. After that, two environment variables carry the paths. CGO_CFLAGS takes the include path, exported as CGO_CFLAGS=-I/source/directory/tensorflow, and CGO_LDFLAGS takes the library path, exported as CGO_LDFLAGS=-L/path/to/tensorflow/libaries (the README's own spelling). Without those, the Go toolchain will not find the headers or the shared object. The README offers an escape hatch for people who do not want to use Bazel: a Makefile.tflite that you place as Makefile in tensorflow/lite/c and run with make, with the caveat that it has not been tested on Linux or Mac. That caveat is the README's own wording, and it is worth taking at face value. Once the library is built and the variables are exported, the documented next step is to run go build on the examples. The Edge TPU delegate is a separate install: the README points to google-coral/edgetpu and to a deb package, and gives a shell sequence for x86 that clones the repository, copies libedgetpu.so.1.0 into /usr/local/lib, creates the .so.1 and .so symlinks, and copies edgetpu.h and edgetpu_c.h into the include path. The README notes the libraries should live in a system-wide library path like /usr/local/lib and the headers somewhere reachable from your CGO include path. That is an accelerator-specific setup on top of the base build, not a flag you flip.

The cgo toolchain is the adoption cost, not the API

The Go code in the README is a dozen lines. The build is the hard part. Three separate things have to line up: the version of the TensorFlow Lite C library, the include path in CGO_CFLAGS, and the link path in CGO_LDFLAGS. Get the version wrong and you are linking against an API the binding does not match. The README pins 2.2.0-rc3, which is a release candidate, and that is a real constraint for anyone who needs a stable upstream library version. This also means your build is not portable by default. A Go binary that links a shared object is not the same as a static Go binary you can copy to another machine. The README's own instructions put the library in /usr/local/lib, a system-wide path, which tells you what the deployment model looks like: the shared object ships alongside the binary, or is installed on the host. If your team's whole reason for choosing Go is single-binary deployment with no runtime dependencies, this binding works against that goal. The Edge TPU path adds another shared object and another header set, so the deployment surface grows again. None of this is a defect in the binding. It is the direct consequence of choosing to call the C API rather than reimplement it, and the trade is explicit: you get the upstream interpreter's behaviour instead of a partial Go rewrite. The maintenance cost tracks TensorFlow Lite releases, not go-tflite's own release cadence, because the binding has to be rebuilt and retested whenever the C API moves.

Where calling into cgo is the wrong answer

If your model runs comfortably in a Go process with no C dependency, a pure-Go inference path avoids the shared-object problem entirely. The clearest example is the class of models that are just arithmetic over float32 slices: small regressions, fixed-shape classifiers, or the sine model in the README. Writing that forward pass in Go is a few dozen lines, it compiles to a static binary, and it never has to be rebuilt because a C library changed its ABI. The difference in approach is not performance, it is dependency shape. go-tflite gives you the TensorFlow Lite interpreter, its kernel implementations, and its delegate support, at the cost of a cgo build and a pinned shared library. A hand-written Go forward pass gives you a static binary and no version coupling, at the cost of supporting only the operators you implement. For anything with convolutions, quantization, or a model you did not design yourself, the hand-written route stops being reasonable quickly, and that is exactly when go-tflite becomes the better choice. The other case where go-tflite is the wrong tool is a team that has no path to building the C library at all, for example a build environment that cannot run Bazel or make inside a TensorFlow source tree. The README's Makefile.tflite fallback exists, but it comes with the untested-on-Linux-or-Mac note, so it is not a guarantee. If neither build route is available to you, the binding cannot be adopted regardless of how good the Go API looks.

Licence, releases, and what you inherit

go-tflite is MIT licensed, which is permissive and imposes few obligations on the binding's own source. That covers the Go code in this repository. It does not cover TensorFlow Lite itself, which you build and link separately, or the Edge TPU libraries from google-coral/edgetpu, which have their own terms. If you distribute a binary that links libtensorflowlite_c.so, the licence that matters for that shared object is the one attached to the TensorFlow Lite build you produced, not the MIT licence on the binding. This is not legal advice; check the terms of the components you actually ship. On release cadence, the repository shows v1.0.7 and v1.0.6 in August 2026 and v1.0.5 tagged buildkit-20240529 in May 2024, so the version tags do not map cleanly onto a fixed schedule. The material does not show what changed between those releases, so pin a specific tag rather than tracking the default branch if reproducibility matters to you. The primary language listed for the repository is Jupyter Notebook, which reflects the iris.ipynb notebook and the Binder setup rather than the binding itself; the Go source and the cgo layer are what you compile. That mismatch is worth knowing before you judge the project by its language breakdown.

Editorial conclusion

Adopt go-tflite if your deployment already ships libtensorflowlite_c.so and you want inference inside a Go process without a separate runtime. Do not adopt it if you cannot pin the TensorFlow Lite C library to the version the binding expects, or if you want a pure-Go build with no cgo and no shared object. Before writing any application code, build the model into libtensorflowlite_c.so, export CGO_CFLAGS and CGO_LDFLAGS, and compile the _example directory: if that does not link, nothing else in the binding matters.

Official sources

  1. Issues
  2. License: MIT
  3. mattn/go-tflite on GitHub
  4. README
  5. Releases
Community notes

Community notes