Skip to content

Commit da2a864

Browse files
committed
Return errors as strings, not resources
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.
1 parent 917bf47 commit da2a864

File tree

2 files changed

+16
-139
lines changed

2 files changed

+16
-139
lines changed

ml.md

Lines changed: 10 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ Then, the user passes <em>tensor</em> inputs to the <em>graph</em>, computes the
1010
<li>Imports:
1111
<ul>
1212
<li>interface <a href="#wasi_nn_tensor_0_2_0_rc_2024_06_25"><code>wasi:nn/[email protected]</code></a></li>
13-
<li>interface <a href="#wasi_nn_errors_0_2_0_rc_2024_06_25"><code>wasi:nn/[email protected]</code></a></li>
1413
<li>interface <a href="#wasi_nn_inference_0_2_0_rc_2024_06_25"><code>wasi:nn/[email protected]</code></a></li>
1514
<li>interface <a href="#wasi_nn_graph_0_2_0_rc_2024_06_25"><code>wasi:nn/[email protected]</code></a></li>
1615
</ul>
@@ -90,94 +89,13 @@ containing a single value, use <code>[1]</code> for the tensor dimensions.</p>
9089
<ul>
9190
<li><a name="method_tensor_data.0"></a> <a href="#tensor_data"><a href="#tensor_data"><code>tensor-data</code></a></a></li>
9291
</ul>
93-
<h2><a name="wasi_nn_errors_0_2_0_rc_2024_06_25"></a>Import interface wasi:nn/[email protected]</h2>
94-
<p>TODO: create function-specific errors (https://github.com/WebAssembly/wasi-nn/issues/42)</p>
95-
<hr />
96-
<h3>Types</h3>
97-
<h4><a name="error_code"></a><code>enum error-code</code></h4>
98-
<h5>Enum Cases</h5>
99-
<ul>
100-
<li>
101-
<p><a name="error_code.invalid_argument"></a><code>invalid-argument</code></p>
102-
<p>Caller module passed an invalid argument.
103-
</li>
104-
<li>
105-
<p><a name="error_code.invalid_encoding"></a><code>invalid-encoding</code></p>
106-
<p>Invalid encoding.
107-
</li>
108-
<li>
109-
<p><a name="error_code.timeout"></a><code>timeout</code></p>
110-
<p>The operation timed out.
111-
</li>
112-
<li>
113-
<p><a name="error_code.runtime_error"></a><code>runtime-error</code></p>
114-
<p>Runtime Error.
115-
</li>
116-
<li>
117-
<p><a name="error_code.unsupported_operation"></a><code>unsupported-operation</code></p>
118-
<p>Unsupported operation.
119-
</li>
120-
<li>
121-
<p><a name="error_code.too_large"></a><code>too-large</code></p>
122-
<p>Graph is too large.
123-
</li>
124-
<li>
125-
<p><a name="error_code.not_found"></a><code>not-found</code></p>
126-
<p>Graph not found.
127-
</li>
128-
<li>
129-
<p><a name="error_code.security"></a><code>security</code></p>
130-
<p>The operation is insecure or has insufficient privilege to be performed.
131-
e.g., cannot access a hardware feature requested
132-
</li>
133-
<li>
134-
<p><a name="error_code.unknown"></a><code>unknown</code></p>
135-
<p>The operation failed for an unspecified reason.
136-
</li>
137-
</ul>
138-
<h4><a name="error"></a><code>resource error</code></h4>
139-
<hr />
140-
<h3>Functions</h3>
141-
<h4><a name="constructor_error"></a><code>[constructor]error: func</code></h4>
142-
<h5>Params</h5>
143-
<ul>
144-
<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>
145-
<li><a name="constructor_error.data"></a><code>data</code>: <code>string</code></li>
146-
</ul>
147-
<h5>Return values</h5>
148-
<ul>
149-
<li><a name="constructor_error.0"></a> own&lt;<a href="#error"><a href="#error"><code>error</code></a></a>&gt;</li>
150-
</ul>
151-
<h4><a name="method_error_code"></a><code>[method]error.code: func</code></h4>
152-
<p>Return the error code.</p>
153-
<h5>Params</h5>
154-
<ul>
155-
<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>
156-
</ul>
157-
<h5>Return values</h5>
158-
<ul>
159-
<li><a name="method_error_code.0"></a> <a href="#error_code"><a href="#error_code"><code>error-code</code></a></a></li>
160-
</ul>
161-
<h4><a name="method_error_data"></a><code>[method]error.data: func</code></h4>
162-
<p>Errors can propagated with backend specific status through a string value.</p>
163-
<h5>Params</h5>
164-
<ul>
165-
<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>
166-
</ul>
167-
<h5>Return values</h5>
168-
<ul>
169-
<li><a name="method_error_data.0"></a> <code>string</code></li>
170-
</ul>
17192
<h2><a name="wasi_nn_inference_0_2_0_rc_2024_06_25"></a>Import interface wasi:nn/[email protected]</h2>
17293
<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
17394
<a href="#graph"><code>graph</code></a> to input tensors before <code>compute</code>-ing an inference:</p>
17495
<hr />
17596
<h3>Types</h3>
176-
<h4><a name="error"></a><code>type error</code></h4>
177-
<p><a href="#error"><a href="#error"><code>error</code></a></a></p>
178-
<p>
179-
#### <a name="tensor"></a>`type tensor`
180-
[`tensor`](#tensor)
97+
<h4><a name="tensor"></a><code>type tensor</code></h4>
98+
<p><a href="#tensor"><a href="#tensor"><code>tensor</code></a></a></p>
18199
<p>
182100
#### <a name="tensor_data"></a>`type tensor-data`
183101
[`tensor-data`](#tensor_data)
@@ -197,7 +115,7 @@ e.g., cannot access a hardware feature requested
197115
</ul>
198116
<h5>Return values</h5>
199117
<ul>
200-
<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>
118+
<li><a name="method_graph_execution_context_set_input.0"></a> result&lt;_, <code>string</code>&gt;</li>
201119
</ul>
202120
<h4><a name="method_graph_execution_context_compute"></a><code>[method]graph-execution-context.compute: func</code></h4>
203121
<p>Compute the inference on the given inputs.</p>
@@ -210,7 +128,7 @@ https://github.com/WebAssembly/wasi-nn/issues/43.</p>
210128
</ul>
211129
<h5>Return values</h5>
212130
<ul>
213-
<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>
131+
<li><a name="method_graph_execution_context_compute.0"></a> result&lt;_, <code>string</code>&gt;</li>
214132
</ul>
215133
<h4><a name="method_graph_execution_context_get_output"></a><code>[method]graph-execution-context.get-output: func</code></h4>
216134
<p>Extract the outputs after inference.</p>
@@ -221,18 +139,15 @@ https://github.com/WebAssembly/wasi-nn/issues/43.</p>
221139
</ul>
222140
<h5>Return values</h5>
223141
<ul>
224-
<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>
142+
<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>
225143
</ul>
226144
<h2><a name="wasi_nn_graph_0_2_0_rc_2024_06_25"></a>Import interface wasi:nn/[email protected]</h2>
227145
<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
228146
framework (e.g., TensorFlow):</p>
229147
<hr />
230148
<h3>Types</h3>
231-
<h4><a name="error"></a><code>type error</code></h4>
232-
<p><a href="#error"><a href="#error"><code>error</code></a></a></p>
233-
<p>
234-
#### <a name="tensor"></a>`type tensor`
235-
[`tensor`](#tensor)
149+
<h4><a name="tensor"></a><code>type tensor</code></h4>
150+
<p><a href="#tensor"><a href="#tensor"><code>tensor</code></a></a></p>
236151
<p>
237152
#### <a name="graph_execution_context"></a>`type graph-execution-context`
238153
[`graph-execution-context`](#graph_execution_context)
@@ -274,7 +189,7 @@ graph IR in parts (e.g., OpenVINO stores its IR and weights separately).</p>
274189
</ul>
275190
<h5>Return values</h5>
276191
<ul>
277-
<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>
192+
<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>
278193
</ul>
279194
<h4><a name="load"></a><code>load: func</code></h4>
280195
<p>Load a <a href="#graph"><code>graph</code></a> from an opaque sequence of bytes to use for inference.</p>
@@ -286,7 +201,7 @@ graph IR in parts (e.g., OpenVINO stores its IR and weights separately).</p>
286201
</ul>
287202
<h5>Return values</h5>
288203
<ul>
289-
<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>
204+
<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>
290205
</ul>
291206
<h4><a name="load_by_name"></a><code>load-by-name: func</code></h4>
292207
<p>Load a <a href="#graph"><code>graph</code></a> by name.</p>
@@ -299,5 +214,5 @@ range from simple to complex (e.g., URLs?) and caching mechanisms of various kin
299214
</ul>
300215
<h5>Return values</h5>
301216
<ul>
302-
<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>
217+
<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>
303218
</ul>

wit/wasi-nn.wit

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ world ml {
1212
import tensor;
1313
import graph;
1414
import inference;
15-
import errors;
1615
}
1716

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

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

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

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

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

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

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

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

130127
/// Extract the outputs after inference.
131-
get-output: func(name: string) -> result<tensor, error>;
132-
}
133-
}
134-
135-
/// TODO: create function-specific errors (https://github.com/WebAssembly/wasi-nn/issues/42)
136-
interface errors {
137-
enum error-code {
138-
// Caller module passed an invalid argument.
139-
invalid-argument,
140-
// Invalid encoding.
141-
invalid-encoding,
142-
// The operation timed out.
143-
timeout,
144-
// Runtime Error.
145-
runtime-error,
146-
// Unsupported operation.
147-
unsupported-operation,
148-
// Graph is too large.
149-
too-large,
150-
// Graph not found.
151-
not-found,
152-
// The operation is insecure or has insufficient privilege to be performed.
153-
// e.g., cannot access a hardware feature requested
154-
security,
155-
// The operation failed for an unspecified reason.
156-
unknown
157-
}
158-
159-
resource error {
160-
constructor(code: error-code, data: string);
161-
162-
/// Return the error code.
163-
code: func() -> error-code;
164-
165-
/// Errors can propagated with backend specific status through a string value.
166-
data: func() -> string;
128+
get-output: func(name: string) -> result<tensor, string>;
167129
}
168130
}

0 commit comments

Comments
 (0)