# Tools

Compared to [actions](./actions.md), tools are more **lightweight** listeners, run
while iterating through the wasot [events](./architecture.md). They only have access to one event at a
time. This means that they don't have access to a block, which spans multiple
events (selectable via a stack). The job of tools is to set variables, either local
ones or global ones.

## Context: Global and Scoped

There are two places where tools can place their variables: In the global context
which is never dropped and to the current scope. As with scopes in programming
languages, only tools of later blocks on the same level and their children have
access to the scoped variables. Scoped variables are dropped as soon as the scope
ends, which happens when a block finishes.

See [Wasot and XML](./wasot_and_xml.md) and the [architecture](./architecture.md)
for more intuition.

## Tool Definition

Tools are implemented in different programming languages and not part of wasot itself.
You have to register your tools with wasot.

To see how a tool is defined, look for the `struct Tool` in `src/tools/tool.rs`. It is basically just a name, a bunch of selectors, and a callback to be called when an event matches the selector.

Before wasot starts, you have to register your tools. This creates a bunch of instances of the `struct Tool`.
The tools parse their selectors, which are given as a list of strings, and store the parsed ones inside.
Then, for every event, all selectors of all tools are checked and the callback of the tool is called.
This acts as an adapter and may call into other programming languages like python.
A serializable context with all global and scoped variables is passed.
The tool does its thing and changes the context.
The context is defined in `src/context/context.rs` via `struct Context`.

## Bridging the language barrier

Tools can be provided in any language.
Wasot is used as a main application, e.g., via the command line. The tools are plugins.
So how can wasot load the plugins?

Different mechanisms come to mind:

  - Running the plugins in their own process
      - communicating via stdin, stdout: Not suitable, as we have request/response
      - via sockets
  - Loading the plugins as a library via FFI
      - Using C FFI

FFI would be nice for performance and simplicity but is a headache for languages like Python.
So we need to communicate via sockets, starting a new process for every plugin.
But starting processes is expensive; thus, they should act as a server.
Sockets are too low-level; thus, we start a gRPC server with a socket.

## Different Channels, Independent of transportation, Interface is just a rust callback

It is nice that gRPC does not depend on the channel, a socket in our case.
Each plugin implements a gRPC interface, which is kind of small. It does:

  - provide meta-data which is called once after starting the plugin to register the tools.
    it provides a name and selectors so that we can build the `struct Tool` in wasot
  - handle a meta-line and the context, returning an updated context.

It basically implements a tool.

But gRPC is independent of the channel. We can swap the socket channel with one that works
on Windows, Mac, or Android. We can even implement one that works in the browser and load wasm tools
in a wasm version of wasot. We can run the tools on a web server and call them on another host.

We aren't even coupled to gRPC. It is only one adapter type, which implements a tool.
Remember that a tool is just a struct with a name, selectors, and a rust callback. The gRPC part
is just an adapter to implement that callback using a gRPC call. There can be other adapters, like
one that uses REST or one that uses FFI. In this way, we are independent of gRPC.
This is nice because we can use FFI for ad-hoc plugins: If you use wasot for example as a Python library,
you can just define your own tools and register them using pyo3. As you are calling rust in your
python code, it lives in the same process, and Rust can call back into the Python code for a tool.

The same goes with the standard library, which implements the tool callback directly in rust and is
part of the wasot binary / library. In short: callbacks are just adapters, and we are flexible in
implementing them.

But this means that when we define a tool, we need to set the adapter and its required metadata.

## Being minimalistic, avoid chatter

Having tools in another process means that calling them is quite expensive compared to function calls.
This is true for socket calls, which are fast but still slow compared to function calls.
But this is especially true for tool calls over the network, which gRPC and the independence of the
transport mechanism enables.

If you have different services, like we have with the microkernel and plugins, you normally try to
reduce chatter, reducing the calls. This is kind of hard, as we have a lot of events for every wasot
file, each block triggering an event.

**Selectors to express in which events you are interested in:**
The first technique is the selectors which each tool has to specify on registration. The tool is
then called only on events which match at least one of the selectors. This removes most of the
calls. Wasot itself can use this to determine if an event is interesting to a plugin and does not have
to ask the plugin, which is expensive.

**Taking inspiration from the Gap-Buffer:**
Text editors have a similar problem. They need to be responsive but handle a lot of events fast, even
on very constrained systems. They use a Gap-Buffer, as mentioned in
[Writing a Text Editor - Computerphile](https://www.youtube.com/watch?v=g2hiVp6oPZc&t=1157s). This
takes advantage by knowing how it is used. A slow human is navigating through the file. It uses this
knowledge to optimize the data structure.
It is the same with wasot. We need to process an event fast, but the human working with a wasot file
is actually very slow, giving us a ton of time to do so if we are smart about it.
This is not a technical concrete solution, but rather one that takes the whole picture in when designing
components and **something** to keep in mind while coming up with solutions.
Be smart on when to do things, and we got a lot of time.

**Calls are stateless, thus can be cached:**
A tool call only depends on the event and the context passed in. A tool should not keep any state.
It should be a pure function. Thus, if the event and the context we pass in don't change, we don't
have to call the tool but can reuse the cached output.
Users only change files a tiny bit by a tiny bit between iterations. Thus, most of the context should
stay the same, avoiding most calls.

**Subset of the context as input:**
We can further improve the automatic caching if we require the tool to specify on which parts of the context
it depends. The context can get quite big, and tools normally need only one or a few items from it.
Thus the tool's result is the same output even if the context changes as long as the important fields don't
change. If we know which parts of the context the tool needs, we can improve the caching and reduce the
calls to the tools. The tool is then only called again if the relevant context changes, not when something
in the context changes. As an added benefit, we can reduce the message size to the tool by filtering the
context down to only the relevant entries, avoiding useless data on the wire.

**DAG and topological sorting:**
So, we declare on which entries we are dependent. We can also declare which entries we produce. Using this
information, we can build a directed acyclic graph of dependencies between tools. Using topological sorting,
we get an optimal execution plan for a given change to call tools.
Note: By specifying the inputs and outputs, we are effectively doing function signatures. You could even do
type checking, going further.
But most imporantly: A DAG will allow for parallel processing in a seemingly serial event-stream world!

**Daemon**:
A deaomon would prevent the overhead of starting a process. I am not sure if this is worth it as the tools
are stateless. It would add complexity and consume RAM that we are not using all the time.

## Steps

Good tool calling is the foundation of any plugin idea, which enables the core architectural idea of the
microkernel of wasot. Using this, we don't force anyone to anything, enabling freedom and room for
growth in unexpected directions in the future. We start with the basic mechanism of tool calling adapters
for gRPC and FFI-tool calling for the standard library and ad-hoc tools and expand from there in the
future for caching and defining relevant context with the DAG and maybe even type checking later.

## First Step: Implementing a gRPC adapter

To implement a gRPC service, we need to implement a `struct Tool` callback of `src/tools/tools.rs`.
We also need to inject ourself in the registration process.
The idea is to pass a list of gRPC plugins to wasot at the start, which registers them and then listens 
to the events when reading a wasot file. Looking in at the [architecture](./architecture.md) we could do 
this either using a transformer on in an consumer.

The gRPC interface of the plugin needs to:

- provide a list of tools, consisting of an identifying name and a list of strings as selectors.
- An enpoint to handle an event (which matches the one of the selector). A call contains the name of the tool,
  the context and the event itself. It expects an updated context back.

The context is a serializable version of `src/context/context.rs`.

## Defining a tool

A tool definition needs to specify the adapter-type, the name, selector and the adapter configuration.
There are different adapter-types:

- `local-ffi` which calls the tool via FFI and uses a registry for the tools
- `grpc-binary` with takes a path to an executable, runs it, supervised the process, binds it to a socket.


## A first iteration

```
class Tool {
  name: string
  library_name: string
  selectors: list<string>
  optional_context_keys: list<string> // Input
  required_context_keys: list<string> // Input - Tool is not called if these are not met
  maybe_produced_context_keys: list<string> // Output - Tool don't have to produce all of them, but can not produce more
}

class Library {
  name: string
  tools: list<Tool>
  config: Json
  adapter_key: string

  getTool(name) -> Tool? {
    for tool in tools {
      if tool.name == name {
        return tool
      }
    }
  }
}

// A registry
class Libraries {

  libraries: list<Library>

  getTool(library_name, name) -> Tool? {
    return getLibrary(libraryName)?.getTool(name)
  }

  getLibrary(name) -> Library? {
    for lib in libraries {
      if lib.name == name {
        return lib
      }
    }
  }

  register(library: Library) {
    libraries.push(library)
  }
}

LIBRARIES = Libraries() // Singleton

from src/context/context.rs import Context, ContextItem // Context is a collection of ContextItems
from src/events/events.rs import Event

protobuf message ToolCall {
  // the event
  event: Event

  // the state
  context: Context,

  // the subject of the call
  library: Library,
  tool: Tool,

}

interface Adapter {

  // Optional to implement.
  // Should return fast, use polling on ready to check status
  start() {}

  ready() -> bool {
    return true
  }


  // A tool takes an event and the state in  form the the context and returns an updated state
  callTool(toolCall: ToolCall) -> Context
}


class ContextChange {
  target: ContextChangeTarget,
  item: ContextItem
}

enum ContextChangeTarget {
  SCOPE,
  GLOBAL
}

// Manages the local gRPC processes (starting, restarting, callinng, retrying, etc.)
class Supervisor {

}

class LocalGrpcAdapter implements Adapter {

  callTool(toolCall: ToolCall) -> List<ContextChange> {
    // use the 
  }
}


// TODO: Process Supervisor

```


## Write your own plugin

For context and background-knowledge, see:

- [Plugin Architecture Overview](./plugin_architecture.md) Explains the general Idea of plugins via gRPC
- [Plugin Socket Communication](./plugin_socket_communication.md): Explains how the plugins communicate, the workflow

You can then write your plugin by following:

- [Rust Plugin Tutoiral](./rust_plugin_tutorial.md)
