Skip to content

Commit

Permalink
Return errors as strings, not resources
Browse files Browse the repository at this point in the history
This change proposes a different approach to simplifying errors than PR
 WebAssembly#75 (errors as records). Here we take that approach one step further and
completely remove the error code portion entirely, returning only a
`string` in the error case. Is this an anti-pattern, though? Some would
argue that this is the case, since users might have to resort to
string-matching for custom error handling. Others, however, might argue
that the error codes were low value anyways since we expect to only see
one or maybe two variants as failures to a single call. In any case,
this PR offers an alternative to WebAssembly#75 for discussion sake.
  • Loading branch information
abrown committed Aug 13, 2024
1 parent 917bf47 commit da2a864
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 139 deletions.
105 changes: 10 additions & 95 deletions ml.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ Then, the user passes <em>tensor</em> inputs to the <em>graph</em>, computes the
<li>Imports:
<ul>
<li>interface <a href="#wasi_nn_tensor_0_2_0_rc_2024_06_25"><code>wasi:nn/[email protected]</code></a></li>
<li>interface <a href="#wasi_nn_errors_0_2_0_rc_2024_06_25"><code>wasi:nn/[email protected]</code></a></li>
<li>interface <a href="#wasi_nn_inference_0_2_0_rc_2024_06_25"><code>wasi:nn/[email protected]</code></a></li>
<li>interface <a href="#wasi_nn_graph_0_2_0_rc_2024_06_25"><code>wasi:nn/[email protected]</code></a></li>
</ul>
Expand Down Expand Up @@ -90,94 +89,13 @@ containing a single value, use <code>[1]</code> for the tensor dimensions.</p>
<ul>
<li><a name="method_tensor_data.0"></a> <a href="#tensor_data"><a href="#tensor_data"><code>tensor-data</code></a></a></li>
</ul>
<h2><a name="wasi_nn_errors_0_2_0_rc_2024_06_25"></a>Import interface wasi:nn/[email protected]</h2>
<p>TODO: create function-specific errors (https://github.com/WebAssembly/wasi-nn/issues/42)</p>
<hr />
<h3>Types</h3>
<h4><a name="error_code"></a><code>enum error-code</code></h4>
<h5>Enum Cases</h5>
<ul>
<li>
<p><a name="error_code.invalid_argument"></a><code>invalid-argument</code></p>
<p>Caller module passed an invalid argument.
</li>
<li>
<p><a name="error_code.invalid_encoding"></a><code>invalid-encoding</code></p>
<p>Invalid encoding.
</li>
<li>
<p><a name="error_code.timeout"></a><code>timeout</code></p>
<p>The operation timed out.
</li>
<li>
<p><a name="error_code.runtime_error"></a><code>runtime-error</code></p>
<p>Runtime Error.
</li>
<li>
<p><a name="error_code.unsupported_operation"></a><code>unsupported-operation</code></p>
<p>Unsupported operation.
</li>
<li>
<p><a name="error_code.too_large"></a><code>too-large</code></p>
<p>Graph is too large.
</li>
<li>
<p><a name="error_code.not_found"></a><code>not-found</code></p>
<p>Graph not found.
</li>
<li>
<p><a name="error_code.security"></a><code>security</code></p>
<p>The operation is insecure or has insufficient privilege to be performed.
e.g., cannot access a hardware feature requested
</li>
<li>
<p><a name="error_code.unknown"></a><code>unknown</code></p>
<p>The operation failed for an unspecified reason.
</li>
</ul>
<h4><a name="error"></a><code>resource error</code></h4>
<hr />
<h3>Functions</h3>
<h4><a name="constructor_error"></a><code>[constructor]error: func</code></h4>
<h5>Params</h5>
<ul>
<li><a name="constructor_error.code"></a><code>code</code>: <a href="#error_code"><a href="#error_code"><code>error-code</code></a></a></li>
<li><a name="constructor_error.data"></a><code>data</code>: <code>string</code></li>
</ul>
<h5>Return values</h5>
<ul>
<li><a name="constructor_error.0"></a> own&lt;<a href="#error"><a href="#error"><code>error</code></a></a>&gt;</li>
</ul>
<h4><a name="method_error_code"></a><code>[method]error.code: func</code></h4>
<p>Return the error code.</p>
<h5>Params</h5>
<ul>
<li><a name="method_error_code.self"></a><code>self</code>: borrow&lt;<a href="#error"><a href="#error"><code>error</code></a></a>&gt;</li>
</ul>
<h5>Return values</h5>
<ul>
<li><a name="method_error_code.0"></a> <a href="#error_code"><a href="#error_code"><code>error-code</code></a></a></li>
</ul>
<h4><a name="method_error_data"></a><code>[method]error.data: func</code></h4>
<p>Errors can propagated with backend specific status through a string value.</p>
<h5>Params</h5>
<ul>
<li><a name="method_error_data.self"></a><code>self</code>: borrow&lt;<a href="#error"><a href="#error"><code>error</code></a></a>&gt;</li>
</ul>
<h5>Return values</h5>
<ul>
<li><a name="method_error_data.0"></a> <code>string</code></li>
</ul>
<h2><a name="wasi_nn_inference_0_2_0_rc_2024_06_25"></a>Import interface wasi:nn/[email protected]</h2>
<p>An inference &quot;session&quot; is encapsulated by a <a href="#graph_execution_context"><code>graph-execution-context</code></a>. This structure binds a
<a href="#graph"><code>graph</code></a> to input tensors before <code>compute</code>-ing an inference:</p>
<hr />
<h3>Types</h3>
<h4><a name="error"></a><code>type error</code></h4>
<p><a href="#error"><a href="#error"><code>error</code></a></a></p>
<p>
#### <a name="tensor"></a>`type tensor`
[`tensor`](#tensor)
<h4><a name="tensor"></a><code>type tensor</code></h4>
<p><a href="#tensor"><a href="#tensor"><code>tensor</code></a></a></p>
<p>
#### <a name="tensor_data"></a>`type tensor-data`
[`tensor-data`](#tensor_data)
Expand All @@ -197,7 +115,7 @@ e.g., cannot access a hardware feature requested
</ul>
<h5>Return values</h5>
<ul>
<li><a name="method_graph_execution_context_set_input.0"></a> result&lt;_, own&lt;<a href="#error"><a href="#error"><code>error</code></a></a>&gt;&gt;</li>
<li><a name="method_graph_execution_context_set_input.0"></a> result&lt;_, <code>string</code>&gt;</li>
</ul>
<h4><a name="method_graph_execution_context_compute"></a><code>[method]graph-execution-context.compute: func</code></h4>
<p>Compute the inference on the given inputs.</p>
Expand All @@ -210,7 +128,7 @@ https://github.com/WebAssembly/wasi-nn/issues/43.</p>
</ul>
<h5>Return values</h5>
<ul>
<li><a name="method_graph_execution_context_compute.0"></a> result&lt;_, own&lt;<a href="#error"><a href="#error"><code>error</code></a></a>&gt;&gt;</li>
<li><a name="method_graph_execution_context_compute.0"></a> result&lt;_, <code>string</code>&gt;</li>
</ul>
<h4><a name="method_graph_execution_context_get_output"></a><code>[method]graph-execution-context.get-output: func</code></h4>
<p>Extract the outputs after inference.</p>
Expand All @@ -221,18 +139,15 @@ https://github.com/WebAssembly/wasi-nn/issues/43.</p>
</ul>
<h5>Return values</h5>
<ul>
<li><a name="method_graph_execution_context_get_output.0"></a> result&lt;own&lt;<a href="#tensor"><a href="#tensor"><code>tensor</code></a></a>&gt;, own&lt;<a href="#error"><a href="#error"><code>error</code></a></a>&gt;&gt;</li>
<li><a name="method_graph_execution_context_get_output.0"></a> result&lt;own&lt;<a href="#tensor"><a href="#tensor"><code>tensor</code></a></a>&gt;, <code>string</code>&gt;</li>
</ul>
<h2><a name="wasi_nn_graph_0_2_0_rc_2024_06_25"></a>Import interface wasi:nn/[email protected]</h2>
<p>A <a href="#graph"><code>graph</code></a> is a loaded instance of a specific ML model (e.g., MobileNet) for a specific ML
framework (e.g., TensorFlow):</p>
<hr />
<h3>Types</h3>
<h4><a name="error"></a><code>type error</code></h4>
<p><a href="#error"><a href="#error"><code>error</code></a></a></p>
<p>
#### <a name="tensor"></a>`type tensor`
[`tensor`](#tensor)
<h4><a name="tensor"></a><code>type tensor</code></h4>
<p><a href="#tensor"><a href="#tensor"><code>tensor</code></a></a></p>
<p>
#### <a name="graph_execution_context"></a>`type graph-execution-context`
[`graph-execution-context`](#graph_execution_context)
Expand Down Expand Up @@ -274,7 +189,7 @@ graph IR in parts (e.g., OpenVINO stores its IR and weights separately).</p>
</ul>
<h5>Return values</h5>
<ul>
<li><a name="method_graph_init_execution_context.0"></a> result&lt;own&lt;<a href="#graph_execution_context"><a href="#graph_execution_context"><code>graph-execution-context</code></a></a>&gt;, own&lt;<a href="#error"><a href="#error"><code>error</code></a></a>&gt;&gt;</li>
<li><a name="method_graph_init_execution_context.0"></a> result&lt;own&lt;<a href="#graph_execution_context"><a href="#graph_execution_context"><code>graph-execution-context</code></a></a>&gt;, <code>string</code>&gt;</li>
</ul>
<h4><a name="load"></a><code>load: func</code></h4>
<p>Load a <a href="#graph"><code>graph</code></a> from an opaque sequence of bytes to use for inference.</p>
Expand All @@ -286,7 +201,7 @@ graph IR in parts (e.g., OpenVINO stores its IR and weights separately).</p>
</ul>
<h5>Return values</h5>
<ul>
<li><a name="load.0"></a> result&lt;own&lt;<a href="#graph"><a href="#graph"><code>graph</code></a></a>&gt;, own&lt;<a href="#error"><a href="#error"><code>error</code></a></a>&gt;&gt;</li>
<li><a name="load.0"></a> result&lt;own&lt;<a href="#graph"><a href="#graph"><code>graph</code></a></a>&gt;, <code>string</code>&gt;</li>
</ul>
<h4><a name="load_by_name"></a><code>load-by-name: func</code></h4>
<p>Load a <a href="#graph"><code>graph</code></a> by name.</p>
Expand All @@ -299,5 +214,5 @@ range from simple to complex (e.g., URLs?) and caching mechanisms of various kin
</ul>
<h5>Return values</h5>
<ul>
<li><a name="load_by_name.0"></a> result&lt;own&lt;<a href="#graph"><a href="#graph"><code>graph</code></a></a>&gt;, own&lt;<a href="#error"><a href="#error"><code>error</code></a></a>&gt;&gt;</li>
<li><a name="load_by_name.0"></a> result&lt;own&lt;<a href="#graph"><a href="#graph"><code>graph</code></a></a>&gt;, <code>string</code>&gt;</li>
</ul>
50 changes: 6 additions & 44 deletions wit/wasi-nn.wit
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ world ml {
import tensor;
import graph;
import inference;
import errors;
}

/// All inputs and outputs to an ML inference are represented as `tensor`s.
Expand Down Expand Up @@ -61,13 +60,12 @@ interface tensor {
/// A `graph` is a loaded instance of a specific ML model (e.g., MobileNet) for a specific ML
/// framework (e.g., TensorFlow):
interface graph {
use errors.{error};
use tensor.{tensor};
use inference.{graph-execution-context};

/// An execution graph for performing inference (i.e., a model).
resource graph {
init-execution-context: func() -> result<graph-execution-context, error>;
init-execution-context: func() -> result<graph-execution-context, string>;
}

/// Describes the encoding of the graph. This allows the API to be implemented by various
Expand Down Expand Up @@ -96,20 +94,19 @@ interface graph {
type graph-builder = list<u8>;

/// Load a `graph` from an opaque sequence of bytes to use for inference.
load: func(builder: list<graph-builder>, encoding: graph-encoding, target: execution-target) -> result<graph, error>;
load: func(builder: list<graph-builder>, encoding: graph-encoding, target: execution-target) -> result<graph, string>;

/// Load a `graph` by name.
///
/// How the host expects the names to be passed and how it stores the graphs for retrieval via
/// this function is **implementation-specific**. This allows hosts to choose name schemes that
/// range from simple to complex (e.g., URLs?) and caching mechanisms of various kinds.
load-by-name: func(name: string) -> result<graph, error>;
load-by-name: func(name: string) -> result<graph, string>;
}

/// An inference "session" is encapsulated by a `graph-execution-context`. This structure binds a
/// `graph` to input tensors before `compute`-ing an inference:
interface inference {
use errors.{error};
use tensor.{tensor, tensor-data};

/// Bind a `graph` to the input and output tensors for an inference.
Expand All @@ -118,51 +115,16 @@ interface inference {
/// (https://github.com/WebAssembly/wasi-nn/issues/43)
resource graph-execution-context {
/// Define the inputs to use for inference.
set-input: func(name: string, tensor: tensor) -> result<_, error>;
set-input: func(name: string, tensor: tensor) -> result<_, string>;

/// Compute the inference on the given inputs.
///
/// Note the expected sequence of calls: `set-input`, `compute`, `get-output`. TODO: this
/// expectation could be removed as a part of
/// https://github.com/WebAssembly/wasi-nn/issues/43.
compute: func() -> result<_, error>;
compute: func() -> result<_, string>;

/// Extract the outputs after inference.
get-output: func(name: string) -> result<tensor, error>;
}
}

/// TODO: create function-specific errors (https://github.com/WebAssembly/wasi-nn/issues/42)
interface errors {
enum error-code {
// Caller module passed an invalid argument.
invalid-argument,
// Invalid encoding.
invalid-encoding,
// The operation timed out.
timeout,
// Runtime Error.
runtime-error,
// Unsupported operation.
unsupported-operation,
// Graph is too large.
too-large,
// Graph not found.
not-found,
// The operation is insecure or has insufficient privilege to be performed.
// e.g., cannot access a hardware feature requested
security,
// The operation failed for an unspecified reason.
unknown
}

resource error {
constructor(code: error-code, data: string);

/// Return the error code.
code: func() -> error-code;

/// Errors can propagated with backend specific status through a string value.
data: func() -> string;
get-output: func(name: string) -> result<tensor, string>;
}
}

0 comments on commit da2a864

Please sign in to comment.