Help us shape the future of Go! Take the 2026 Go Developer Survey

agentworker

package module
v0.1.21 Latest Latest
Warning

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

Go to latest
Published: Oct 20, 2026 License: MIT Imports: 13 Imported by: 4

README

agentworker

The webtyp agent inside a Web Worker: models from OPFS, device check, downloads with progress, a decision cache kept between starts, and a page-side client with typed events.

It has two halves. The Worker binary (written by the application, because its tools, templates and memory live there) calls Serve. The page calls Start from the click that wakes the assistant and receives events. Design: docs/ARCHITECTURE.md.

The Worker main

//go:build wasm

package main

import (
	"webtyp.com/agent"
	"webtyp.com/agentworker"
	"webtyp.com/lfm"
	"webtyp.com/qwen"
)

func main() {
	agentworker.Serve(agentworker.Setup{
		Dir:     "cote", // this module's OPFS directory
		Decider: agentworker.DeciderSpec{Weights: "decider-0.8b", Merges: "decider-0.8b-merges", Decoder: qwen.Qwen35_08B, Temperature: 1.03},
		Writer:  &agentworker.WriterSpec{Weights: "writer-lfm-350m", Merges: "writer-lfm-350m-merges", Decoder: lfm.LFM25_350M},
		AgentConfig: func(m agentworker.Models) (agent.Config, error) {
			// Decider, Writer and Tokens are set by agentworker from m.
			return agent.Config{Texts: texts, Memory: agent.NewMemMemory(), IDGen: ids, ToolIndex: agent.NewMemToolIndex(), LocalTools: tools}, nil
		},
	})
}

The page

// From the wake-up click's goroutine: Start blocks on browser promises.
c, err := agentworker.Start(agentworker.Scripts{Plain: "/cote.worker.js", SIMD: "/cote.simd.worker.js"},
	func(e agentworker.Event) {
		switch e.Kind {
		case agentworker.EventProgress: // e.Artifact, e.Done of e.Total bytes
		case agentworker.EventReady: // e.WriterOff: answers come from templates/data; !e.Persisted: warn the disk may evict the models
		case agentworker.EventAsleep: // e.Shortfalls: "secure", "space", "tier", "speed"
		case agentworker.EventReply: // e.Text; e.Pending → show them, then c.Confirm or c.Decline
		case agentworker.EventFailed: // e.Text
		}
	})
if err != nil {
	return err
}
c.Run("session-1", "¿A qué hora abren?")

Start asks device.Persist() itself: Workers do not have navigator.storage.persist, and the browser wants a user gesture for it.

The artifacts it expects

The application's /artifacts.json (webtyp.com/artifacts, written by sitec from the project's ArtifactSources()) lists the decider's weights and merges and, optionally, the writer's. Each artifact's needs decides: when the decider's needs are not met the assistant sleeps; when the writer's are not met, or both models do not fit, only the writer is dropped.

min_rate is in runs per second of the benchmark kernel: one nn.MatVecQ8Block32 over a 3584×1024 int8 matrix, measured for BenchBudgetMs (200 ms). Under TinyGo 0.41 on the machine of nn/docs/PERFORMANCE.md it runs ≈ 770 times per second plain and ≈ 1640 with SIMD.

Testing an application

Start needs a built Worker script, so the end-to-end test (page → Worker → reply) belongs to the application. This library tests the Worker logic natively with fakes (core_internal_test.go).

Documentation

Index

Constants

View Source
const (
	// DecisionCacheFile is where the decision cache is kept, in Setup.Dir (D27).
	DecisionCacheFile = "decision.cache"
	// BenchBudgetMs is how long the Worker measures its kernel: one nn.MatVecQ8Block32 over a
	// 3584×1024 int8 matrix. A manifest's min_rate is in runs per second of that kernel (≈ 770
	// plain, ≈ 1640 SIMD under TinyGo 0.41 on the machine of nn/docs/PERFORMANCE.md).
	BenchBudgetMs = 200
)

Variables

This section is empty.

Functions

This section is empty.

Types

type DeciderSpec added in v0.1.0

type DeciderSpec struct {
	Weights     string         // artifact id of the weights, e.g. "decider-0.8b"
	Merges      string         // artifact id of the companion .merges file
	Decoder     decoder.Config // qwen.Qwen35_08B for decider-0.8b
	Temperature float64        // decision temperature (decider-0.8b: 1.03)
}

DeciderSpec names the decision model's files in the artifacts manifest and its shape.

type Event added in v0.1.0

type Event struct {
	Kind       EventKind
	Artifact   string   // EventProgress, EventAsleep: artifact id
	Done       int64    // EventProgress
	Total      int64    // EventProgress
	WriterOff  bool     // EventReady: the writer was dropped (answers come from templates/data)
	Persisted  bool     // EventReady: storage is persistent; false → the UI warns (D-PWA-9)
	Shortfalls []string // EventAsleep: device.Shortfall.String() of every blocking shortfall
	SessionID  string   // EventReply
	Text       string   // EventReply, EventFailed
	Pending    []Pending
}

Event is one message from the Worker to the page.

func DecodeEvent added in v0.1.0

func DecodeEvent(data []byte) (Event, error)

DecodeEvent reads an Event written by Encode.

func (Event) Encode added in v0.1.0

func (e Event) Encode() ([]byte, error)

Encode writes e as JSON.

type EventKind added in v0.1.0

type EventKind uint8

EventKind is what the Worker tells the page.

const (
	EventProgress EventKind = iota + 1 // Artifact, Done, Total
	EventReady                         // WriterOff, Persisted
	EventAsleep                        // Artifact, Shortfalls: the device cannot run the assistant
	EventReply                         // SessionID, Text, Pending
	EventFailed                        // Text: what went wrong
)

type Models added in v0.1.0

type Models struct {
	Decider llm.Decider
	Writer  llm.Client       // nil when the device could not take the writer (D-PWA-2)
	Tokens  llm.TokenCounter // the decider's tokenizer
}

Models are what agentworker built; the application's AgentConfig receives them.

type Pending added in v0.1.0

type Pending struct{ ID, Name, Input string }

Pending is a tool call waiting for the person (from llm.ToolCall).

type Request added in v0.1.0

type Request struct {
	Kind      RequestKind
	SessionID string
	Text      string // RequestRun only
	Persisted bool   // RequestStart only: what the page's device.Persist() answered (D-PWA-9)
}

Request is one message from the page to the Worker.

func DecodeRequest added in v0.1.0

func DecodeRequest(data []byte) (Request, error)

DecodeRequest reads a Request written by Encode.

func (Request) Encode added in v0.1.0

func (r Request) Encode() ([]byte, error)

Encode writes r as JSON.

type RequestKind added in v0.1.0

type RequestKind uint8

RequestKind is what the page asks the Worker.

const (
	RequestStart   RequestKind = iota + 1 // check the device, download, load; then Ready or Asleep
	RequestRun                            // a message from the person
	RequestConfirm                        // confirm the pending calls
	RequestDecline                        // decline them
)

type Scripts added in v0.1.5

type Scripts struct {
	Plain string // e.g. "/cote.worker.js"
	SIMD  string // e.g. "/cote.simd.worker.js"; "" = only the plain build exists
}

Scripts are the Worker scripts of one assistant: the same Worker main built twice (D16), by sitec from web/workers/<name> as "/<name>.worker.js" and "/<name>.simd.worker.js".

type Setup added in v0.1.0

type Setup struct {
	Dir     string      // OPFS directory of this module (D-PWA-10), e.g. "cote"
	Decider DeciderSpec // required
	Writer  *WriterSpec // optional; dropped when the device cannot hold or run it
	// AgentConfig returns the application's agent configuration (Texts, Templates, Guard, Memory,
	// IDGen, Clock, ToolIndex, LocalTools, MCPServers...). agentworker then sets Decider, Writer
	// and Tokens from m, overriding whatever the function put there.
	AgentConfig func(m Models) (agent.Config, error)
}

Setup is everything the Worker binary declares.

type WriterSpec added in v0.1.0

type WriterSpec struct {
	Weights string // e.g. "writer-lfm-350m"
	Merges  string
	Decoder decoder.Config // lfm.LFM25_350M
}

WriterSpec names the optional writer model's files and its shape.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL