diff --git a/Cargo.lock b/Cargo.lock index a25ff05871..eaf9ec1864 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1870,6 +1870,37 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "curl" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2 0.5.6", + "windows-sys 0.52.0", +] + +[[package]] +name = "curl-sys" +version = "0.4.72+curl-8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "rustls-ffi", + "vcpkg", + "windows-sys 0.52.0", +] + [[package]] name = "darling" version = "0.14.4" @@ -4076,6 +4107,18 @@ dependencies = [ "threadpool", ] +[[package]] +name = "libz-sys" +version = "1.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -4895,6 +4938,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num_threads" version = "0.1.7" @@ -5207,6 +5271,39 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "openvino" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24bd3a7ef39968e6a4f1b1206c1c876f9bd50cf739ccbcd69f8539bbac5dcc7a" +dependencies = [ + "openvino-finder", + "openvino-sys", + "thiserror", +] + +[[package]] +name = "openvino-finder" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d234d1394a413ea8adaf0c40806b9ad1946be6310b441f688840654a331973" +dependencies = [ + "cfg-if", + "log", +] + +[[package]] +name = "openvino-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c98acf37fc84ad9d7da4dc6c18f0f60ad209b43a6f555be01f9003d0a2a43d" +dependencies = [ + "env_logger", + "libloading", + "once_cell", + "openvino-finder", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -6661,6 +6758,21 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-ffi" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da52707cca59e6eef8a78f3ad8d04024254a168ed1b41eb4dfa9616eace781a" +dependencies = [ + "libc", + "log", + "num_enum", + "rustls 0.20.9", + "rustls-pemfile 0.2.1", + "sct", + "webpki", +] + [[package]] name = "rustls-native-certs" version = "0.7.0" @@ -6674,6 +6786,15 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "rustls-pemfile" version = "0.3.0" @@ -7488,13 +7609,16 @@ dependencies = [ "cap-primitives 3.0.0", "cap-std 3.0.0", "crossbeam-channel", + "curl", "futures", "http 1.1.0", "io-extras", + "openvino", "rustix 0.37.27", "spin-componentize", "spin-telemetry", "system-interface", + "table", "tempfile", "tokio", "tracing", diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index cba62cd50e..0dd1959373 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -20,6 +20,8 @@ tokio = "1.0" bytes = "1.0" spin-telemetry = { path = "../telemetry" } http = "1.0" +openvino = { version = "0.6.0", features = ["runtime-linking",] } +table = { path = "../table" } [target.'cfg(unix)'.dependencies] rustix = "0.37.19" @@ -32,3 +34,7 @@ tempfile = "3" tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] } spin-componentize = { workspace = true } futures = "0.3" + +[build-dependencies] +curl = { version = "^0.4", features = ["rustls"] } +anyhow = "^1" \ No newline at end of file diff --git a/crates/core/build.rs b/crates/core/build.rs new file mode 100644 index 0000000000..9303042027 --- /dev/null +++ b/crates/core/build.rs @@ -0,0 +1,47 @@ +use std::env; +use std::fs; +use std::path::PathBuf; +//extern crate curl; + +use curl::easy::Easy; +use std::io::Write; + +fn main() { + let base_url = "https://raw.githubusercontent.com/blocksense-network/imagenet_openvino/db44329b8e2b3398c9cc34dd56d94f3ce6fd6e21/"; //images/0.jpg + + let imagenet_path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/test-programs/imagenet"); + let images_dir = imagenet_path.join("images"); + fs::create_dir_all(images_dir).unwrap(); + let files = ["model.xml", "model.bin", "images/0.jpg", "images/1.jpg"]; + for file in files { + try_download(&(base_url.to_owned() + file), &imagenet_path.join(file)).unwrap(); + } + + println!("cargo:rerun-if-changed=build.rs"); +} + +fn try_download(url: &str, filename: &PathBuf) -> Result<(), anyhow::Error> { + let mut easy = Easy::new(); + easy.url(url) + .map_err(|e| anyhow::anyhow!("Error {} when downloading {}", e.to_string(), url))?; + + let mut dst = Vec::new(); + { + let mut transfer = easy.transfer(); + transfer + .write_function(|data| { + dst.extend_from_slice(data); + Ok(data.len()) + }) + .unwrap(); + transfer + .perform() + .map_err(|e| anyhow::anyhow!("Error {} when downloading {}", e.to_string(), url))?; + } + { + let mut file = std::fs::File::create(filename)?; + file.write_all(dst.as_slice())?; + } + Ok(()) +} diff --git a/crates/core/tests/core-wasi-test/Cargo.toml b/crates/core/tests/core-wasi-test/Cargo.toml index 1df9c0eb6f..6c590c7d56 100644 --- a/crates/core/tests/core-wasi-test/Cargo.toml +++ b/crates/core/tests/core-wasi-test/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" debug = true [dependencies] -wit-bindgen = "0.13.0" +wit-bindgen = "0.26.0" +image2tensor = "0.3.1" [workspace] diff --git a/crates/core/tests/core-wasi-test/src/imagenet.rs b/crates/core/tests/core-wasi-test/src/imagenet.rs new file mode 100644 index 0000000000..5fe02129f1 --- /dev/null +++ b/crates/core/tests/core-wasi-test/src/imagenet.rs @@ -0,0 +1,207 @@ +use crate::ml::test::test::{graph, inference, tensor}; +use image2tensor::convert_image_to_tensor_bytes; + +use crate::imagenet_classes; +use crate::Path; +use std::time::Duration; + +pub fn elapsed_to_string(fn_name: &str, elapsed: &Duration) -> String { + let elapsed_ns = elapsed.as_nanos(); + if elapsed_ns < 1000 { + format!("`{}` took {} ns", fn_name, elapsed_ns) + } else if elapsed_ns < 1000 * 1000 { + format!("`{}` took {:.2} µs", fn_name, elapsed_ns as f64 / 1000.0) + } else { + format!( + "`{}` took {:.2} ms", + fn_name, + elapsed_ns as f64 / 1000.0 / 1000.0 + ) + } +} + +pub fn bytes_to_string(b: usize) -> String { + if b < 1024 { + format!("{} Bytes", b) + } else if b < 1024 * 1024 { + format!("{:.2} kB", b as f64 / 1024.0) + } else { + format!("{:.2} MB", b as f64 / 1024.0 / 1024.0) + } +} + +/// Return the execution target type from string +fn map_string_to_execution_target(target: &str) -> Result { + match target { + "CPU" => Ok(graph::ExecutionTarget::Cpu), + "GPU" => Ok(graph::ExecutionTarget::Gpu), + "TPU" => Ok(graph::ExecutionTarget::Tpu), + _ => Err(format!("Unknown execution targer = {}", target)), + } +} + +pub fn imagenet_openvino_test( + path_as_string: String, + target_as_string: String, + image_file: String, +) -> std::result::Result<(), Box> { + let path = Path::new(&path_as_string); + let target = map_string_to_execution_target(&target_as_string)?; + let model = { + let start_for_elapsed_macro = std::time::Instant::now(); + let model: Vec = std::fs::read(path.join("model.xml"))?; + let elapsed = start_for_elapsed_macro.elapsed(); + eprintln!( + "Loaded model from xml {} {}", + bytes_to_string(model.len()), + elapsed_to_string("fs::read", &elapsed) + ); + model + }; + let weights = { + let start_for_elapsed_macro = std::time::Instant::now(); + let weights = std::fs::read(path.join("model.bin"))?; + let elapsed = start_for_elapsed_macro.elapsed(); + eprintln!( + "Loaded weigths {} {}", + bytes_to_string(weights.len()), + elapsed_to_string("fs::read", &elapsed) + ); + weights + }; + let imagenet_graph = { + let start_for_elapsed_macro = std::time::Instant::now(); + let imagenet_graph = + graph::load(&[model, weights], graph::GraphEncoding::Openvino, target).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed(); + eprintln!("---- {:?} ----", target); + eprintln!( + "Loaded graph with ID: {:?} {}", + imagenet_graph, + elapsed_to_string("graph::load", &elapsed) + ); + imagenet_graph + }; + let context = { + let start_for_elapsed_macro = std::time::Instant::now(); + let context = graph::Graph::init_execution_context(&imagenet_graph).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed(); + eprintln!( + "Created context with ID: {:?} {}", + context, + elapsed_to_string("Graph::init_execution_context", &elapsed) + ); + context + }; + + let tensor_dimensions: Vec = vec![1, 3, 224, 224]; + let tensor_data = convert_image_to_tensor_bytes( + &image_file, //"images/0.jpg", + tensor_dimensions[2], + tensor_dimensions[3], + image2tensor::TensorType::F32, + image2tensor::ColorOrder::BGR, + ) + .unwrap(); + + let tensor_id = { + let start_for_elapsed_macro = std::time::Instant::now(); + let tensor_type = tensor::TensorType::Fp32; + let tensor_id = tensor::Tensor::new(&tensor_dimensions, tensor_type, &tensor_data); + let elapsed = start_for_elapsed_macro.elapsed(); + eprintln!( + "Created tensor with ID: {:?} {}", + tensor_id, + elapsed_to_string("Tensor::new", &elapsed) + ); + tensor_id + }; + let input_name = "0"; + { + let start_for_elapsed_macro = std::time::Instant::now(); + inference::GraphExecutionContext::set_input(&context, input_name, tensor_id).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed(); + eprintln!( + "Input set {}", + elapsed_to_string("GraphExecutionContext::set_input", &elapsed) + ); + } + { + let start_for_elapsed_macro = std::time::Instant::now(); + inference::GraphExecutionContext::compute(&context).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed(); + eprintln!( + "Executed graph inference. {}", + elapsed_to_string("GraphExecutionContext::compute", &elapsed) + ); + } + let output_result_id = { + let start_for_elapsed_macro = std::time::Instant::now(); + let output_result_id = + inference::GraphExecutionContext::get_output(&context, input_name).unwrap(); + let elapsed = start_for_elapsed_macro.elapsed(); + eprintln!( + "Obtaining output {}", + elapsed_to_string("GraphExecutionContext::get_output", &elapsed) + ); + output_result_id + }; + let (output_data, output_dimensions, output_type) = { + let start_for_elapsed_macro = std::time::Instant::now(); + let output_data = tensor::Tensor::data(&output_result_id); + let output_dimensions = tensor::Tensor::dimensions(&output_result_id); + let output_type = tensor::Tensor::ty(&output_result_id); + let elapsed = start_for_elapsed_macro.elapsed(); + eprintln!( + "Copying data from tensor. {}", + elapsed_to_string("Tensor::data+dimensions+type", &elapsed) + ); + (output_data, output_dimensions, output_type) + }; + if output_dimensions.len() == 2 + && output_dimensions[0] == 1 + && output_dimensions[1] == 1001 + && output_type == tensor::TensorType::Fp32 + { + let output_vec_f32 = + unsafe { std::slice::from_raw_parts(output_data.as_ptr() as *const f32, 1001) }; + let results = sort_results(output_vec_f32); + for res in results.iter().take(3) { + println!( + "{:.2} -> {}", + res.weight, + imagenet_classes::IMAGENET_CLASSES[res.index], + ); + } + } else { + eprintln!( + "Output not as expected, output = {:?} {:?}", + &output_dimensions, &output_type + ); + } + Ok(()) +} + +// Sort the buffer of probabilities. The graph places the match probability for each class at the +// index for that class (e.g. the probability of class 42 is placed at buffer[42]). Here we convert +// to a wrapping InferenceResult and sort the results. +pub fn sort_results(buffer: &[f32]) -> Vec { + let mut results: Vec = buffer + .iter() + .skip(1) + .enumerate() + .map(|(c, p)| InferenceResult { + index: c, + weight: *p, + }) + .collect(); + results.sort_by(|a, b| b.weight.partial_cmp(&a.weight).unwrap()); + results +} + +// A wrapper for class ID and match probabilities. +#[derive(Debug, PartialEq)] +pub struct InferenceResult { + pub index: usize, + pub weight: f32, +} diff --git a/crates/core/tests/core-wasi-test/src/imagenet_classes.rs b/crates/core/tests/core-wasi-test/src/imagenet_classes.rs new file mode 100644 index 0000000000..6aab4910c3 --- /dev/null +++ b/crates/core/tests/core-wasi-test/src/imagenet_classes.rs @@ -0,0 +1,1021 @@ +/** + * @license + * Copyright 2019 Google LLC. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============================================================================= + */ + +/* The code in this file is adapted from https://github.com/tensorflow/tfjs-models/blob/master/mobilenet/src/imagenet_classes.ts */ + +pub const IMAGENET_CLASSES: [&str; 1000] = [ + "tench, Tinca tinca", + "goldfish, Carassius auratus", + "great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias", + "tiger shark, Galeocerdo cuvieri", + "hammerhead, hammerhead shark", + "electric ray, crampfish, numbfish, torpedo", + "stingray", + "cock", + "hen", + "ostrich, Struthio camelus", + "brambling, Fringilla montifringilla", + "goldfinch, Carduelis carduelis", + "house finch, linnet, Carpodacus mexicanus", + "junco, snowbird", + "indigo bunting, indigo finch, indigo bird, Passerina cyanea", + "robin, American robin, Turdus migratorius", + "bulbul", + "jay", + "magpie", + "chickadee", + "water ouzel, dipper", + "kite", + "bald eagle, American eagle, Haliaeetus leucocephalus", + "vulture", + "great grey owl, great gray owl, Strix nebulosa", + "European fire salamander, Salamandra salamandra", + "common newt, Triturus vulgaris", + "eft", + "spotted salamander, Ambystoma maculatum", + "axolotl, mud puppy, Ambystoma mexicanum", + "bullfrog, Rana catesbeiana", + "tree frog, tree-frog", + "tailed frog, bell toad, ribbed toad, tailed toad, Ascaphus trui", + "loggerhead, loggerhead turtle, Caretta caretta", + "leatherback turtle, leatherback, leathery turtle, Dermochelys coriacea", + "mud turtle", + "terrapin", + "box turtle, box tortoise", + "banded gecko", + "common iguana, iguana, Iguana iguana", + "American chameleon, anole, Anolis carolinensis", + "whiptail, whiptail lizard", + "agama", + "frilled lizard, Chlamydosaurus kingi", + "alligator lizard", + "Gila monster, Heloderma suspectum", + "green lizard, Lacerta viridis", + "African chameleon, Chamaeleo chamaeleon", + "Komodo dragon, Komodo lizard, dragon lizard, giant lizard, Varanus komodoensis", + "African crocodile, Nile crocodile, Crocodylus niloticus", + "American alligator, Alligator mississipiensis", + "triceratops", + "thunder snake, worm snake, Carphophis amoenus", + "ringneck snake, ring-necked snake, ring snake", + "hognose snake, puff adder, sand viper", + "green snake, grass snake", + "king snake, kingsnake", + "garter snake, grass snake", + "water snake", + "vine snake", + "night snake, Hypsiglena torquata", + "boa constrictor, Constrictor constrictor", + "rock python, rock snake, Python sebae", + "Indian cobra, Naja naja", + "green mamba", + "sea snake", + "horned viper, cerastes, sand viper, horned asp, Cerastes cornutus", + "diamondback, diamondback rattlesnake, Crotalus adamanteus", + "sidewinder, horned rattlesnake, Crotalus cerastes", + "trilobite", + "harvestman, daddy longlegs, Phalangium opilio", + "scorpion", + "black and gold garden spider, Argiope aurantia", + "barn spider, Araneus cavaticus", + "garden spider, Aranea diademata", + "black widow, Latrodectus mactans", + "tarantula", + "wolf spider, hunting spider", + "tick", + "centipede", + "black grouse", + "ptarmigan", + "ruffed grouse, partridge, Bonasa umbellus", + "prairie chicken, prairie grouse, prairie fowl", + "peacock", + "quail", + "partridge", + "African grey, African gray, Psittacus erithacus", + "macaw", + "sulphur-crested cockatoo, Kakatoe galerita, Cacatua galerita", + "lorikeet", + "coucal", + "bee eater", + "hornbill", + "hummingbird", + "jacamar", + "toucan", + "drake", + "red-breasted merganser, Mergus serrator", + "goose", + "black swan, Cygnus atratus", + "tusker", + "echidna, spiny anteater, anteater", + "platypus, duckbill, duckbilled platypus, duck-billed platypus, Ornithorhynchus anatinus", + "wallaby, brush kangaroo", + "koala, koala bear, kangaroo bear, native bear, Phascolarctos cinereus", + "wombat", + "jelly fish", + "sea anemone, anemone", + "brain coral", + "flatworm, platyhelminth", + "nematode, nematode worm, roundworm", + "conch", + "snail", + "slug", + "sea slug, nudibranch", + "chiton, coat-of-mail shell, sea cradle, polyplacophore", + "chambered nautilus, pearly nautilus, nautilus", + "Dungeness crab, Cancer magister", + "rock crab, Cancer irroratus", + "fiddler crab", + "king crab, Alaska crab, Alaskan king crab, Alaska king crab, Paralithodes camtschatica", + "American lobster, Northern lobster, Maine lobster, Homarus americanus", + "spiny lobster, langouste, rock lobster, crawfish, crayfish, sea crawfish", + "crayfish, crawfish, crawdad, crawdaddy", + "hermit crab", + "isopod", + "white stork, Ciconia ciconia", + "black stork, Ciconia nigra", + "spoonbill", + "flamingo", + "little blue heron, Egretta caerulea", + "American egret, great white heron, Egretta albus", + "bittern", + "crane", + "limpkin, Aramus pictus", + "European gallinule, Porphyrio porphyrio", + "American coot, marsh hen, mud hen, water hen, Fulica americana", + "bustard", + "ruddy turnstone, Arenaria interpres", + "red-backed sandpiper, dunlin, Erolia alpina", + "redshank, Tringa totanus", + "dowitcher", + "oystercatcher, oyster catcher", + "pelican", + "king penguin, Aptenodytes patagonica", + "albatross, mollymawk", + "grey whale, gray whale, devilfish, Eschrichtius gibbosus, Eschrichtius robustus", + "killer whale, killer, orca, grampus, sea wolf, Orcinus orca", + "dugong, Dugong dugon", + "sea lion", + "Chihuahua", + "Japanese spaniel", + "Maltese dog, Maltese terrier, Maltese", + "Pekinese, Pekingese, Peke", + "Shih-Tzu", + "Blenheim spaniel", + "papillon", + "toy terrier", + "Rhodesian ridgeback", + "Afghan hound, Afghan", + "basset, basset hound", + "beagle", + "bloodhound, sleuthhound", + "bluetick", + "black-and-tan coonhound", + "Walker hound, Walker foxhound", + "English foxhound", + "redbone", + "borzoi, Russian wolfhound", + "Irish wolfhound", + "Italian greyhound", + "whippet", + "Ibizan hound, Ibizan Podenco", + "Norwegian elkhound, elkhound", + "otterhound, otter hound", + "Saluki, gazelle hound", + "Scottish deerhound, deerhound", + "Weimaraner", + "Staffordshire bullterrier, Staffordshire bull terrier", + "American Staffordshire terrier, Staffordshire terrier, American pit bull terrier, pit bull terrier", + "Bedlington terrier", + "Border terrier", + "Kerry blue terrier", + "Irish terrier", + "Norfolk terrier", + "Norwich terrier", + "Yorkshire terrier", + "wire-haired fox terrier", + "Lakeland terrier", + "Sealyham terrier, Sealyham", + "Airedale, Airedale terrier", + "cairn, cairn terrier", + "Australian terrier", + "Dandie Dinmont, Dandie Dinmont terrier", + "Boston bull, Boston terrier", + "miniature schnauzer", + "giant schnauzer", + "standard schnauzer", + "Scotch terrier, Scottish terrier, Scottie", + "Tibetan terrier, chrysanthemum dog", + "silky terrier, Sydney silky", + "soft-coated wheaten terrier", + "West Highland white terrier", + "Lhasa, Lhasa apso", + "flat-coated retriever", + "curly-coated retriever", + "golden retriever", + "Labrador retriever", + "Chesapeake Bay retriever", + "German short-haired pointer", + "vizsla, Hungarian pointer", + "English setter", + "Irish setter, red setter", + "Gordon setter", + "Brittany spaniel", + "clumber, clumber spaniel", + "English springer, English springer spaniel", + "Welsh springer spaniel", + "cocker spaniel, English cocker spaniel, cocker", + "Sussex spaniel", + "Irish water spaniel", + "kuvasz", + "schipperke", + "groenendael", + "malinois", + "briard", + "kelpie", + "komondor", + "Old English sheepdog, bobtail", + "Shetland sheepdog, Shetland sheep dog, Shetland", + "collie", + "Border collie", + "Bouvier des Flandres, Bouviers des Flandres", + "Rottweiler", + "German shepherd, German shepherd dog, German police dog, alsatian", + "Doberman, Doberman pinscher", + "miniature pinscher", + "Greater Swiss Mountain dog", + "Bernese mountain dog", + "Appenzeller", + "EntleBucher", + "boxer", + "bull mastiff", + "Tibetan mastiff", + "French bulldog", + "Great Dane", + "Saint Bernard, St Bernard", + "Eskimo dog, husky", + "malamute, malemute, Alaskan malamute", + "Siberian husky", + "dalmatian, coach dog, carriage dog", + "affenpinscher, monkey pinscher, monkey dog", + "basenji", + "pug, pug-dog", + "Leonberg", + "Newfoundland, Newfoundland dog", + "Great Pyrenees", + "Samoyed, Samoyede", + "Pomeranian", + "chow, chow chow", + "keeshond", + "Brabancon griffon", + "Pembroke, Pembroke Welsh corgi", + "Cardigan, Cardigan Welsh corgi", + "toy poodle", + "miniature poodle", + "standard poodle", + "Mexican hairless", + "timber wolf, grey wolf, gray wolf, Canis lupus", + "white wolf, Arctic wolf, Canis lupus tundrarum", + "red wolf, maned wolf, Canis rufus, Canis niger", + "coyote, prairie wolf, brush wolf, Canis latrans", + "dingo, warrigal, warragal, Canis dingo", + "dhole, Cuon alpinus", + "African hunting dog, hyena dog, Cape hunting dog, Lycaon pictus", + "hyena, hyaena", + "red fox, Vulpes vulpes", + "kit fox, Vulpes macrotis", + "Arctic fox, white fox, Alopex lagopus", + "grey fox, gray fox, Urocyon cinereoargenteus", + "tabby, tabby cat", + "tiger cat", + "Persian cat", + "Siamese cat, Siamese", + "Egyptian cat", + "cougar, puma, catamount, mountain lion, painter, panther, Felis concolor", + "lynx, catamount", + "leopard, Panthera pardus", + "snow leopard, ounce, Panthera uncia", + "jaguar, panther, Panthera onca, Felis onca", + "lion, king of beasts, Panthera leo", + "tiger, Panthera tigris", + "cheetah, chetah, Acinonyx jubatus", + "brown bear, bruin, Ursus arctos", + "American black bear, black bear, Ursus americanus, Euarctos americanus", + "ice bear, polar bear, Ursus Maritimus, Thalarctos maritimus", + "sloth bear, Melursus ursinus, Ursus ursinus", + "mongoose", + "meerkat, mierkat", + "tiger beetle", + "ladybug, ladybeetle, lady beetle, ladybird, ladybird beetle", + "ground beetle, carabid beetle", + "long-horned beetle, longicorn, longicorn beetle", + "leaf beetle, chrysomelid", + "dung beetle", + "rhinoceros beetle", + "weevil", + "fly", + "bee", + "ant, emmet, pismire", + "grasshopper, hopper", + "cricket", + "walking stick, walkingstick, stick insect", + "cockroach, roach", + "mantis, mantid", + "cicada, cicala", + "leafhopper", + "lacewing, lacewing fly", + "dragonfly, darning needle, devil\"s darning needle, sewing needle, snake feeder, snake doctor, mosquito hawk, skeeter hawk", + "damselfly", + "admiral", + "ringlet, ringlet butterfly", + "monarch, monarch butterfly, milkweed butterfly, Danaus plexippus", + "cabbage butterfly", + "sulphur butterfly, sulfur butterfly", + "lycaenid, lycaenid butterfly", + "starfish, sea star", + "sea urchin", + "sea cucumber, holothurian", + "wood rabbit, cottontail, cottontail rabbit", + "hare", + "Angora, Angora rabbit", + "hamster", + "porcupine, hedgehog", + "fox squirrel, eastern fox squirrel, Sciurus niger", + "marmot", + "beaver", + "guinea pig, Cavia cobaya", + "sorrel", + "zebra", + "hog, pig, grunter, squealer, Sus scrofa", + "wild boar, boar, Sus scrofa", + "warthog", + "hippopotamus, hippo, river horse, Hippopotamus amphibius", + "ox", + "water buffalo, water ox, Asiatic buffalo, Bubalus bubalis", + "bison", + "ram, tup", + "bighorn, bighorn sheep, cimarron, Rocky Mountain bighorn, Rocky Mountain sheep, Ovis canadensis", + "ibex, Capra ibex", + "hartebeest", + "impala, Aepyceros melampus", + "gazelle", + "Arabian camel, dromedary, Camelus dromedarius", + "llama", + "weasel", + "mink", + "polecat, fitch, foulmart, foumart, Mustela putorius", + "black-footed ferret, ferret, Mustela nigripes", + "otter", + "skunk, polecat, wood pussy", + "badger", + "armadillo", + "three-toed sloth, ai, Bradypus tridactylus", + "orangutan, orang, orangutang, Pongo pygmaeus", + "gorilla, Gorilla gorilla", + "chimpanzee, chimp, Pan troglodytes", + "gibbon, Hylobates lar", + "siamang, Hylobates syndactylus, Symphalangus syndactylus", + "guenon, guenon monkey", + "patas, hussar monkey, Erythrocebus patas", + "baboon", + "macaque", + "langur", + "colobus, colobus monkey", + "proboscis monkey, Nasalis larvatus", + "marmoset", + "capuchin, ringtail, Cebus capucinus", + "howler monkey, howler", + "titi, titi monkey", + "spider monkey, Ateles geoffroyi", + "squirrel monkey, Saimiri sciureus", + "Madagascar cat, ring-tailed lemur, Lemur catta", + "indri, indris, Indri indri, Indri brevicaudatus", + "Indian elephant, Elephas maximus", + "African elephant, Loxodonta africana", + "lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens", + "giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca", + "barracouta, snoek", + "eel", + "coho, cohoe, coho salmon, blue jack, silver salmon, Oncorhynchus kisutch", + "rock beauty, Holocanthus tricolor", + "anemone fish", + "sturgeon", + "gar, garfish, garpike, billfish, Lepisosteus osseus", + "lionfish", + "puffer, pufferfish, blowfish, globefish", + "abacus", + "abaya", + "academic gown, academic robe, judge\"s robe", + "accordion, piano accordion, squeeze box", + "acoustic guitar", + "aircraft carrier, carrier, flattop, attack aircraft carrier", + "airliner", + "airship, dirigible", + "altar", + "ambulance", + "amphibian, amphibious vehicle", + "analog clock", + "apiary, bee house", + "apron", + "ashcan, trash can, garbage can, wastebin, ash bin, ash-bin, ashbin, dustbin, trash barrel, trash bin", + "assault rifle, assault gun", + "backpack, back pack, knapsack, packsack, rucksack, haversack", + "bakery, bakeshop, bakehouse", + "balance beam, beam", + "balloon", + "ballpoint, ballpoint pen, ballpen, Biro", + "Band Aid", + "banjo", + "bannister, banister, balustrade, balusters, handrail", + "barbell", + "barber chair", + "barbershop", + "barn", + "barometer", + "barrel, cask", + "barrow, garden cart, lawn cart, wheelbarrow", + "baseball", + "basketball", + "bassinet", + "bassoon", + "bathing cap, swimming cap", + "bath towel", + "bathtub, bathing tub, bath, tub", + "beach wagon, station wagon, wagon, estate car, beach waggon, station waggon, waggon", + "beacon, lighthouse, beacon light, pharos", + "beaker", + "bearskin, busby, shako", + "beer bottle", + "beer glass", + "bell cote, bell cot", + "bib", + "bicycle-built-for-two, tandem bicycle, tandem", + "bikini, two-piece", + "binder, ring-binder", + "binoculars, field glasses, opera glasses", + "birdhouse", + "boathouse", + "bobsled, bobsleigh, bob", + "bolo tie, bolo, bola tie, bola", + "bonnet, poke bonnet", + "bookcase", + "bookshop, bookstore, bookstall", + "bottlecap", + "bow", + "bow tie, bow-tie, bowtie", + "brass, memorial tablet, plaque", + "brassiere, bra, bandeau", + "breakwater, groin, groyne, mole, bulwark, seawall, jetty", + "breastplate, aegis, egis", + "broom", + "bucket, pail", + "buckle", + "bulletproof vest", + "bullet train, bullet", + "butcher shop, meat market", + "cab, hack, taxi, taxicab", + "caldron, cauldron", + "candle, taper, wax light", + "cannon", + "canoe", + "can opener, tin opener", + "cardigan", + "car mirror", + "carousel, carrousel, merry-go-round, roundabout, whirligig", + "carpenter\"s kit, tool kit", + "carton", + "car wheel", + "cash machine, cash dispenser, automated teller machine, automatic teller machine, automated teller, automatic teller, ATM", + "cassette", + "cassette player", + "castle", + "catamaran", + "CD player", + "cello, violoncello", + "cellular telephone, cellular phone, cellphone, cell, mobile phone", + "chain", + "chainlink fence", + "chain mail, ring mail, mail, chain armor, chain armour, ring armor, ring armour", + "chain saw, chainsaw", + "chest", + "chiffonier, commode", + "chime, bell, gong", + "china cabinet, china closet", + "Christmas stocking", + "church, church building", + "cinema, movie theater, movie theatre, movie house, picture palace", + "cleaver, meat cleaver, chopper", + "cliff dwelling", + "cloak", + "clog, geta, patten, sabot", + "cocktail shaker", + "coffee mug", + "coffeepot", + "coil, spiral, volute, whorl, helix", + "combination lock", + "computer keyboard, keypad", + "confectionery, confectionary, candy store", + "container ship, containership, container vessel", + "convertible", + "corkscrew, bottle screw", + "cornet, horn, trumpet, trump", + "cowboy boot", + "cowboy hat, ten-gallon hat", + "cradle", + "crane", + "crash helmet", + "crate", + "crib, cot", + "Crock Pot", + "croquet ball", + "crutch", + "cuirass", + "dam, dike, dyke", + "desk", + "desktop computer", + "dial telephone, dial phone", + "diaper, nappy, napkin", + "digital clock", + "digital watch", + "dining table, board", + "dishrag, dishcloth", + "dishwasher, dish washer, dishwashing machine", + "disk brake, disc brake", + "dock, dockage, docking facility", + "dogsled, dog sled, dog sleigh", + "dome", + "doormat, welcome mat", + "drilling platform, offshore rig", + "drum, membranophone, tympan", + "drumstick", + "dumbbell", + "Dutch oven", + "electric fan, blower", + "electric guitar", + "electric locomotive", + "entertainment center", + "envelope", + "espresso maker", + "face powder", + "feather boa, boa", + "file, file cabinet, filing cabinet", + "fireboat", + "fire engine, fire truck", + "fire screen, fireguard", + "flagpole, flagstaff", + "flute, transverse flute", + "folding chair", + "football helmet", + "forklift", + "fountain", + "fountain pen", + "four-poster", + "freight car", + "French horn, horn", + "frying pan, frypan, skillet", + "fur coat", + "garbage truck, dustcart", + "gasmask, respirator, gas helmet", + "gas pump, gasoline pump, petrol pump, island dispenser", + "goblet", + "go-kart", + "golf ball", + "golfcart, golf cart", + "gondola", + "gong, tam-tam", + "gown", + "grand piano, grand", + "greenhouse, nursery, glasshouse", + "grille, radiator grille", + "grocery store, grocery, food market, market", + "guillotine", + "hair slide", + "hair spray", + "half track", + "hammer", + "hamper", + "hand blower, blow dryer, blow drier, hair dryer, hair drier", + "hand-held computer, hand-held microcomputer", + "handkerchief, hankie, hanky, hankey", + "hard disc, hard disk, fixed disk", + "harmonica, mouth organ, harp, mouth harp", + "harp", + "harvester, reaper", + "hatchet", + "holster", + "home theater, home theatre", + "honeycomb", + "hook, claw", + "hoopskirt, crinoline", + "horizontal bar, high bar", + "horse cart, horse-cart", + "hourglass", + "iPod", + "iron, smoothing iron", + "jack-o\"-lantern", + "jean, blue jean, denim", + "jeep, landrover", + "jersey, T-shirt, tee shirt", + "jigsaw puzzle", + "jinrikisha, ricksha, rickshaw", + "joystick", + "kimono", + "knee pad", + "knot", + "lab coat, laboratory coat", + "ladle", + "lampshade, lamp shade", + "laptop, laptop computer", + "lawn mower, mower", + "lens cap, lens cover", + "letter opener, paper knife, paperknife", + "library", + "lifeboat", + "lighter, light, igniter, ignitor", + "limousine, limo", + "liner, ocean liner", + "lipstick, lip rouge", + "Loafer", + "lotion", + "loudspeaker, speaker, speaker unit, loudspeaker system, speaker system", + "loupe, jeweler\"s loupe", + "lumbermill, sawmill", + "magnetic compass", + "mailbag, postbag", + "mailbox, letter box", + "maillot", + "maillot, tank suit", + "manhole cover", + "maraca", + "marimba, xylophone", + "mask", + "matchstick", + "maypole", + "maze, labyrinth", + "measuring cup", + "medicine chest, medicine cabinet", + "megalith, megalithic structure", + "microphone, mike", + "microwave, microwave oven", + "military uniform", + "milk can", + "minibus", + "miniskirt, mini", + "minivan", + "missile", + "mitten", + "mixing bowl", + "mobile home, manufactured home", + "Model T", + "modem", + "monastery", + "monitor", + "moped", + "mortar", + "mortarboard", + "mosque", + "mosquito net", + "motor scooter, scooter", + "mountain bike, all-terrain bike, off-roader", + "mountain tent", + "mouse, computer mouse", + "mousetrap", + "moving van", + "muzzle", + "nail", + "neck brace", + "necklace", + "nipple", + "notebook, notebook computer", + "obelisk", + "oboe, hautboy, hautbois", + "ocarina, sweet potato", + "odometer, hodometer, mileometer, milometer", + "oil filter", + "organ, pipe organ", + "oscilloscope, scope, cathode-ray oscilloscope, CRO", + "overskirt", + "oxcart", + "oxygen mask", + "packet", + "paddle, boat paddle", + "paddlewheel, paddle wheel", + "padlock", + "paintbrush", + "pajama, pyjama, pj\"s, jammies", + "palace", + "panpipe, pandean pipe, syrinx", + "paper towel", + "parachute, chute", + "parallel bars, bars", + "park bench", + "parking meter", + "passenger car, coach, carriage", + "patio, terrace", + "pay-phone, pay-station", + "pedestal, plinth, footstall", + "pencil box, pencil case", + "pencil sharpener", + "perfume, essence", + "Petri dish", + "photocopier", + "pick, plectrum, plectron", + "pickelhaube", + "picket fence, paling", + "pickup, pickup truck", + "pier", + "piggy bank, penny bank", + "pill bottle", + "pillow", + "ping-pong ball", + "pinwheel", + "pirate, pirate ship", + "pitcher, ewer", + "plane, carpenter\"s plane, woodworking plane", + "planetarium", + "plastic bag", + "plate rack", + "plow, plough", + "plunger, plumber\"s helper", + "Polaroid camera, Polaroid Land camera", + "pole", + "police van, police wagon, paddy wagon, patrol wagon, wagon, black Maria", + "poncho", + "pool table, billiard table, snooker table", + "pop bottle, soda bottle", + "pot, flowerpot", + "potter\"s wheel", + "power drill", + "prayer rug, prayer mat", + "printer", + "prison, prison house", + "projectile, missile", + "projector", + "puck, hockey puck", + "punching bag, punch bag, punching ball, punchball", + "purse", + "quill, quill pen", + "quilt, comforter, comfort, puff", + "racer, race car, racing car", + "racket, racquet", + "radiator", + "radio, wireless", + "radio telescope, radio reflector", + "rain barrel", + "recreational vehicle, RV, R.V.", + "reel", + "reflex camera", + "refrigerator, icebox", + "remote control, remote", + "restaurant, eating house, eating place, eatery", + "revolver, six-gun, six-shooter", + "rifle", + "rocking chair, rocker", + "rotisserie", + "rubber eraser, rubber, pencil eraser", + "rugby ball", + "rule, ruler", + "running shoe", + "safe", + "safety pin", + "saltshaker, salt shaker", + "sandal", + "sarong", + "sax, saxophone", + "scabbard", + "scale, weighing machine", + "school bus", + "schooner", + "scoreboard", + "screen, CRT screen", + "screw", + "screwdriver", + "seat belt, seatbelt", + "sewing machine", + "shield, buckler", + "shoe shop, shoe-shop, shoe store", + "shoji", + "shopping basket", + "shopping cart", + "shovel", + "shower cap", + "shower curtain", + "ski", + "ski mask", + "sleeping bag", + "slide rule, slipstick", + "sliding door", + "slot, one-armed bandit", + "snorkel", + "snowmobile", + "snowplow, snowplough", + "soap dispenser", + "soccer ball", + "sock", + "solar dish, solar collector, solar furnace", + "sombrero", + "soup bowl", + "space bar", + "space heater", + "space shuttle", + "spatula", + "speedboat", + "spider web, spider\"s web", + "spindle", + "sports car, sport car", + "spotlight, spot", + "stage", + "steam locomotive", + "steel arch bridge", + "steel drum", + "stethoscope", + "stole", + "stone wall", + "stopwatch, stop watch", + "stove", + "strainer", + "streetcar, tram, tramcar, trolley, trolley car", + "stretcher", + "studio couch, day bed", + "stupa, tope", + "submarine, pigboat, sub, U-boat", + "suit, suit of clothes", + "sundial", + "sunglass", + "sunglasses, dark glasses, shades", + "sunscreen, sunblock, sun blocker", + "suspension bridge", + "swab, swob, mop", + "sweatshirt", + "swimming trunks, bathing trunks", + "swing", + "switch, electric switch, electrical switch", + "syringe", + "table lamp", + "tank, army tank, armored combat vehicle, armoured combat vehicle", + "tape player", + "teapot", + "teddy, teddy bear", + "television, television system", + "tennis ball", + "thatch, thatched roof", + "theater curtain, theatre curtain", + "thimble", + "thresher, thrasher, threshing machine", + "throne", + "tile roof", + "toaster", + "tobacco shop, tobacconist shop, tobacconist", + "toilet seat", + "torch", + "totem pole", + "tow truck, tow car, wrecker", + "toyshop", + "tractor", + "trailer truck, tractor trailer, trucking rig, rig, articulated lorry, semi", + "tray", + "trench coat", + "tricycle, trike, velocipede", + "trimaran", + "tripod", + "triumphal arch", + "trolleybus, trolley coach, trackless trolley", + "trombone", + "tub, vat", + "turnstile", + "typewriter keyboard", + "umbrella", + "unicycle, monocycle", + "upright, upright piano", + "vacuum, vacuum cleaner", + "vase", + "vault", + "velvet", + "vending machine", + "vestment", + "viaduct", + "violin, fiddle", + "volleyball", + "waffle iron", + "wall clock", + "wallet, billfold, notecase, pocketbook", + "wardrobe, closet, press", + "warplane, military plane", + "washbasin, handbasin, washbowl, lavabo, wash-hand basin", + "washer, automatic washer, washing machine", + "water bottle", + "water jug", + "water tower", + "whiskey jug", + "whistle", + "wig", + "window screen", + "window shade", + "Windsor tie", + "wine bottle", + "wing", + "wok", + "wooden spoon", + "wool, woolen, woollen", + "worm fence, snake fence, snake-rail fence, Virginia fence", + "wreck", + "yawl", + "yurt", + "web site, website, internet site, site", + "comic book", + "crossword puzzle, crossword", + "street sign", + "traffic light, traffic signal, stoplight", + "book jacket, dust cover, dust jacket, dust wrapper", + "menu", + "plate", + "guacamole", + "consomme", + "hot pot, hotpot", + "trifle", + "ice cream, icecream", + "ice lolly, lolly, lollipop, popsicle", + "French loaf", + "bagel, beigel", + "pretzel", + "cheeseburger", + "hotdog, hot dog, red hot", + "mashed potato", + "head cabbage", + "broccoli", + "cauliflower", + "zucchini, courgette", + "spaghetti squash", + "acorn squash", + "butternut squash", + "cucumber, cuke", + "artichoke, globe artichoke", + "bell pepper", + "cardoon", + "mushroom", + "Granny Smith", + "strawberry", + "orange", + "lemon", + "fig", + "pineapple, ananas", + "banana", + "jackfruit, jak, jack", + "custard apple", + "pomegranate", + "hay", + "carbonara", + "chocolate sauce, chocolate syrup", + "dough", + "meat loaf, meatloaf", + "pizza, pizza pie", + "potpie", + "burrito", + "red wine", + "espresso", + "cup", + "eggnog", + "alp", + "bubble", + "cliff, drop, drop-off", + "coral reef", + "geyser", + "lakeside, lakeshore", + "promontory, headland, head, foreland", + "sandbar, sand bar", + "seashore, coast, seacoast, sea-coast", + "valley, vale", + "volcano", + "ballplayer, baseball player", + "groom, bridegroom", + "scuba diver", + "rapeseed", + "daisy", + "yellow lady\"s slipper, yellow lady-slipper, Cypripedium calceolus, Cypripedium parviflorum", + "corn", + "acorn", + "hip, rose hip, rosehip", + "buckeye, horse chestnut, conker", + "coral fungus", + "agaric", + "gyromitra", + "stinkhorn, carrion fungus", + "earthstar", + "hen-of-the-woods, hen of the woods, Polyporus frondosus, Grifola frondosa", + "bolete", + "ear, spike, capitulum", + "toilet tissue, toilet paper, bathroom tissue" +]; diff --git a/crates/core/tests/core-wasi-test/src/main.rs b/crates/core/tests/core-wasi-test/src/main.rs index a1bc0ed28d..9c7d2be664 100644 --- a/crates/core/tests/core-wasi-test/src/main.rs +++ b/crates/core/tests/core-wasi-test/src/main.rs @@ -4,17 +4,33 @@ //! invalid argument(s). use std::time::Duration; +mod multiplier { + wit_bindgen::generate!({ + world: "multiplier", + path: "wit/multiplier.wit" + }); +} + +mod ml { + wit_bindgen::generate!({ + world: "ml", + path: "wit/ml.wit" + }); +} + +mod imagenet; +mod imagenet_classes; -wit_bindgen::generate!({ - world: "multiplier", - path: "wit/multiplier.wit" -}); +use imagenet::imagenet_openvino_test; + +use std::path::Path; type Result = std::result::Result<(), Box>; fn main() -> Result { let mut args = std::env::args(); let cmd = args.next().expect("cmd"); + match cmd.as_str() { "noop" => (), "echo" => { @@ -47,9 +63,15 @@ fn main() -> Result { "multiply" => { let input: i32 = args.next().expect("input").parse().expect("i32"); eprintln!("multiply {input}"); - let output = imports::multiply(input); + let output = multiplier::imports::multiply(input); println!("{output}"); } + "imagenet" => { + let path_as_string = args.next().expect("path"); + let target_as_string = args.next().expect("target"); + let image_file_as_string = args.next().expect("image_file"); + _ = imagenet_openvino_test(path_as_string, target_as_string, image_file_as_string); + } "sleep" => { let duration = Duration::from_millis(args.next().expect("duration_ms").parse().expect("u64")); diff --git a/crates/core/tests/core-wasi-test/wit/ml.wit b/crates/core/tests/core-wasi-test/wit/ml.wit new file mode 100644 index 0000000000..953e58d09c --- /dev/null +++ b/crates/core/tests/core-wasi-test/wit/ml.wit @@ -0,0 +1,168 @@ +package test:test; + +/// `wasi-nn` is a WASI API for performing machine learning (ML) inference. The API is not (yet) +/// capable of performing ML training. WebAssembly programs that want to use a host's ML +/// capabilities can access these capabilities through `wasi-nn`'s core abstractions: _graphs_ and +/// _tensors_. A user `load`s an ML model -- instantiated as a _graph_ -- to use in an ML _backend_. +/// Then, the user passes _tensor_ inputs to the _graph_, computes the inference, and retrieves the +/// _tensor_ outputs. +/// +/// This example world shows how to use these primitives together. +world ml { + import tensor; + import graph; + import inference; + import errors; +} + +/// All inputs and outputs to an ML inference are represented as `tensor`s. +interface tensor { + /// The dimensions of a tensor. + /// + /// The array length matches the tensor rank and each element in the array describes the size of + /// each dimension + type tensor-dimensions = list; + + /// The type of the elements in a tensor. + enum tensor-type { + FP16, + FP32, + FP64, + BF16, + U8, + I32, + I64 + } + + /// The tensor data. + /// + /// Initially conceived as a sparse representation, each empty cell would be filled with zeros + /// and the array length must match the product of all of the dimensions and the number of bytes + /// in the type (e.g., a 2x2 tensor with 4-byte f32 elements would have a data array of length + /// 16). Naturally, this representation requires some knowledge of how to lay out data in + /// memory--e.g., using row-major ordering--and could perhaps be improved. + type tensor-data = list; + + resource tensor { + constructor(dimensions: tensor-dimensions, ty: tensor-type, data: tensor-data); + + // Describe the size of the tensor (e.g., 2x2x2x2 -> [2, 2, 2, 2]). To represent a tensor + // containing a single value, use `[1]` for the tensor dimensions. + dimensions: func() -> tensor-dimensions; + + // Describe the type of element in the tensor (e.g., `f32`). + ty: func() -> tensor-type; + + // Return the tensor data. + data: func() -> tensor-data; + } +} + +/// 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; + } + + /// Describes the encoding of the graph. This allows the API to be implemented by various + /// backends that encode (i.e., serialize) their graph IR with different formats. + enum graph-encoding { + openvino, + onnx, + tensorflow, + pytorch, + tensorflowlite, + ggml, + autodetect, + } + + /// Define where the graph should be executed. + enum execution-target { + cpu, + gpu, + tpu + } + + /// The graph initialization data. + /// + /// This gets bundled up into an array of buffers because implementing backends may encode their + /// graph IR in parts (e.g., OpenVINO stores its IR and weights separately). + type graph-builder = list; + + /// Load a `graph` from an opaque sequence of bytes to use for inference. + load: func(builder: list, encoding: graph-encoding, target: execution-target) -> result; + + /// 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; +} + +/// 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. + /// + /// TODO: this may no longer be necessary in WIT + /// (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>; + + /// 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>; + + /// Extract the outputs after inference. + get-output: func(name: string) -> result; + } +} + +/// 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; + } +} diff --git a/crates/core/tests/integration_test.rs b/crates/core/tests/integration_test.rs index 726f9aa596..ba66361714 100644 --- a/crates/core/tests/integration_test.rs +++ b/crates/core/tests/integration_test.rs @@ -4,10 +4,12 @@ use std::{ time::{Duration, Instant}, }; +mod test_host_components; +use crate::test_host_components::ml::ml::MLHostComponent; +use crate::test_host_components::multiplier::{Multiplier, MultiplierHostComponent}; + use anyhow::Context; -use spin_core::{ - Component, Config, Engine, HostComponent, I32Exit, Store, StoreBuilder, Trap, WasiVersion, -}; +use spin_core::{Component, Config, Engine, I32Exit, Store, StoreBuilder, Trap, WasiVersion}; use tempfile::TempDir; use tokio::{fs, io::AsyncWrite}; @@ -159,6 +161,56 @@ async fn test_host_component_data_update() { assert_eq!(stdout, "500"); } +#[tokio::test(flavor = "multi_thread")] +async fn test_host_component_imagenet_openvino_cpu() { + let engine = test_engine(); + let _handle = engine + .find_host_component_handle::() + .unwrap(); + let imagenet_path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/test-programs/imagenet"); + println!("imagenet_path = {imagenet_path:?}"); + + let stdout = run_core_wasi_test_engine( + &engine, + ["imagenet", "/", "CPU", "images/0.jpg"], + |store_builder| { + store_builder + .read_only_preopened_dir(&imagenet_path, "/".into()) + .unwrap(); + }, + |_| {}, + ) + .await + .unwrap(); + assert_eq!(stdout, "0.47 -> Eskimo dog, husky\n0.37 -> Siberian husky\n0.01 -> malamute, malemute, Alaskan malamute"); +} + +#[tokio::test(flavor = "multi_thread")] +async fn test_host_component_imagenet_openvino_gpu() { + let engine = test_engine(); + let _handle = engine + .find_host_component_handle::() + .unwrap(); + let imagenet_path = + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../target/test-programs/imagenet"); + println!("imagenet_path = {imagenet_path:?}"); + + let stdout = run_core_wasi_test_engine( + &engine, + ["imagenet", "/", "GPU", "images/1.jpg"], + |store_builder| { + store_builder + .read_only_preopened_dir(&imagenet_path, "/".into()) + .unwrap(); + }, + |_| {}, + ) + .await + .unwrap(); + assert_eq!(stdout, "0.96 -> mountain bike, all-terrain bike, off-roader\n0.01 -> bicycle-built-for-two, tandem bicycle, tandem\n0.00 -> alp"); +} + #[tokio::test(flavor = "multi_thread")] #[cfg(not(tarpaulin))] async fn test_panic() { @@ -178,6 +230,8 @@ fn test_config() -> Config { fn test_engine() -> Engine<()> { let mut builder = Engine::builder(&test_config()).unwrap(); builder.add_host_component(MultiplierHostComponent).unwrap(); + builder.add_host_component(MLHostComponent).unwrap(); + builder .link_import(|l, _| wasmtime_wasi::add_to_linker_async(l)) .unwrap(); @@ -210,6 +264,7 @@ async fn run_core_wasi_test_engine<'a>( let mut store = store_builder.build()?; let module_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("../../target/test-programs/core-wasi-test.wasm"); + println!("module_path = {module_path:?}"); let component = spin_componentize::componentize_command(&fs::read(module_path).await?)?; let component = Component::new(engine.as_ref(), &component)?; let instance_pre = engine.instantiate_pre(&component)?; @@ -235,37 +290,6 @@ async fn run_core_wasi_test_engine<'a>( Ok(stdout) } -// Simple test HostComponent; multiplies the input by the configured factor -#[derive(Clone)] -struct MultiplierHostComponent; - -mod multiplier { - wasmtime::component::bindgen!("multiplier" in "tests/core-wasi-test/wit"); -} - -impl HostComponent for MultiplierHostComponent { - type Data = Multiplier; - - fn add_to_linker( - linker: &mut spin_core::Linker, - get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, - ) -> anyhow::Result<()> { - multiplier::imports::add_to_linker(linker, get) - } - - fn build_data(&self) -> Self::Data { - Multiplier(2) - } -} - -struct Multiplier(i32); - -impl multiplier::imports::Host for Multiplier { - fn multiply(&mut self, a: i32) -> i32 { - self.0 * a - } -} - // Write with `print!`, required for test output capture struct TestWriter(tokio::io::Stdout); diff --git a/crates/core/tests/test_host_components/ml.rs b/crates/core/tests/test_host_components/ml.rs new file mode 100644 index 0000000000..309f56d2f3 --- /dev/null +++ b/crates/core/tests/test_host_components/ml.rs @@ -0,0 +1,490 @@ +pub mod ml { + wasmtime::component::bindgen!("ml" in "tests/core-wasi-test/wit"); + + use spin_core::HostComponent; + + use anyhow::{anyhow, Context}; + use test::test::errors; + use test::test::errors::HostError; + use test::test::graph; + use test::test::inference; + use test::test::tensor; + + use test::test::errors::ErrorCode; + use test::test::graph::{ExecutionTarget, Graph, GraphBuilder, GraphEncoding}; + use test::test::inference::GraphExecutionContext; + use wasmtime::component::Resource; + + use openvino::{Layout, Precision, TensorDesc}; + + #[derive(Clone)] + pub struct MLHostComponent; + + impl HostComponent for MLHostComponent { + type Data = MLHostImpl; + + fn add_to_linker( + linker: &mut spin_core::Linker, + get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, + ) -> anyhow::Result<()> { + Ml::add_to_linker(linker, get) + } + + fn build_data(&self) -> Self::Data { + MLHostImpl { + ..Default::default() + } + } + } + #[derive(Debug)] + pub struct GraphInternalData { + pub xml: Vec, + pub weights: Vec, + pub target: ExecutionTarget, + } + + pub struct GraphExecutionContextInternalData { + pub cnn_network: openvino::CNNNetwork, + //pub executable_network: Mutex, + pub infer_request: openvino::InferRequest, + } + + pub struct TensorInternalData { + tensor_dimensions: tensor::TensorDimensions, + tensor_type: tensor::TensorType, + tensor_data: tensor::TensorData, + } + + pub struct ErrorInternalData { + code: errors::ErrorCode, + message: String, + } + + #[derive(Default)] + pub struct MLHostImpl { + pub openvino: Option, + pub graphs: table::Table, + pub executions: table::Table, + pub tensors: table::Table, + pub errors: table::Table, + } + + impl MLHostImpl { + fn new_error( + errors: &mut table::Table, + code: ErrorCode, + message: String, + ) -> Resource { + errors + .push(ErrorInternalData { code, message }) + .map(Resource::::new_own) + .expect("Can't allocate error") + } + + fn init_execution_context_internal( + graph: &GraphInternalData, + openvino: &mut Option, + executions: &mut table::Table, + ) -> Result, anyhow::Error> { + if openvino.is_none() { + openvino.replace(openvino::Core::new(None)?); + } + if openvino.is_some() { + let mut cnn_network = openvino + .as_mut() + .context("Can't create openvino graph without backend")? + .read_network_from_buffer(&graph.xml, &graph.weights)?; + + // Construct OpenVINO graph structures: `cnn_network` contains the graph + // structure, `exec_network` can perform inference. + //let core = self + // .0 + // .as_mut() + // .expect("openvino::Core was previously constructed"); + //let mut cnn_network = core.read_network_from_buffer(&xml, &weights)?; + + // TODO: this is a temporary workaround. We need a more elegant way to + // specify the layout in the long run. However, without this newer + // versions of OpenVINO will fail due to parameter mismatch. + for i in 0..cnn_network.get_inputs_len().unwrap() { + let name = cnn_network.get_input_name(i)?; + cnn_network.set_input_layout(&name, Layout::NHWC)?; + } + + let mut exec_network = openvino + .as_mut() + .expect("") + .load_network(&cnn_network, map_execution_target_to_string(graph.target))?; + let infer_request = exec_network + .create_infer_request() + .context("Can't create InferRequest")?; + let graph_execution_context = GraphExecutionContextInternalData { + cnn_network, + //executable_network: Mutex::new(exec_network), + infer_request, + }; + return executions + .push(graph_execution_context) + .map(Resource::::new_own) + .map_err(|_| anyhow!("Can't store execution context")); + } + Err(anyhow!("Can't create openvino backend")) + } + + fn get_output_internal( + graph_execution: &mut GraphExecutionContextInternalData, + input_name: String, + ) -> Result { + let index = input_name.parse::().map_err(|err| { + format!( + "Can't parse {} to usize for input_name, err = {err}", + input_name + ) + })?; + let output_name = graph_execution + .cnn_network + .get_output_name(index) + .map_err(|err| format!("Can't find output name for ID = {index}, err = {err}"))?; + + let blob = graph_execution + .infer_request + .get_blob(&output_name) + .map_err(|err| { + format!("Can't get blob for output name = {output_name}, err = {err}") + })?; + let tensor_desc = blob + .tensor_desc() + .map_err(|err| format!("Can't get blob description, err = {err}"))?; + let buffer = blob + .buffer() + .map_err(|err| format!("Can't get blob buffer, error = {err}"))? + .to_vec(); + let tensor_dimensions = tensor_desc + .dims() + .iter() + .map(|&d| d as u32) + .collect::>(); + let tensor = TensorInternalData { + tensor_dimensions, + tensor_type: map_precision_to_tensor_type(tensor_desc.precision()), + tensor_data: buffer, + }; + Ok(tensor) + } + } + + impl graph::HostGraph for MLHostImpl { + fn init_execution_context( + &mut self, + graph: Resource, + ) -> Result, Resource> { + let res = match self.graphs.get(graph.rep()) { + Some(graph) => MLHostImpl::init_execution_context_internal( + graph, + &mut self.openvino, + &mut self.executions, + ) + .map_err(|err| ErrorInternalData { + code: ErrorCode::RuntimeError, + message: err.to_string(), + }), + None => Err(ErrorInternalData { + code: ErrorCode::RuntimeError, + message: "Can't create graph execution context".to_string(), + }), + }; + match res { + Ok(res) => Ok(res), + Err(e) => Err(MLHostImpl::new_error(&mut self.errors, e.code, e.message)), + } + } + + fn drop(&mut self, graph: Resource) -> Result<(), anyhow::Error> { + self.graphs + .remove(graph.rep()) + .context(format!("Can't find graph with ID = {}", graph.rep())) + .map(|_| ()) + } + } + + impl errors::HostError for MLHostImpl { + fn new(&mut self, code: errors::ErrorCode, message: String) -> Resource { + MLHostImpl::new_error(&mut self.errors, code, message) + } + + fn drop( + &mut self, + error: Resource, + ) -> std::result::Result<(), anyhow::Error> { + self.errors + .remove(error.rep()) + .context(format!("Can't find error with ID = {}", error.rep())) + .map(|_| ()) + } + + fn code(&mut self, error: Resource) -> ErrorCode { + if let Some(e) = self.errors.get(error.rep()) { + return e.code; + } + panic!("Can't find error with ID = {}", error.rep()); + } + + fn data(&mut self, error: Resource) -> String { + if let Some(e) = self.errors.get(error.rep()) { + return e.message.clone(); + } + panic!("Can't find error with ID = {}", error.rep()); + } + } + impl tensor::HostTensor for MLHostImpl { + fn new( + &mut self, + tensor_dimensions: tensor::TensorDimensions, + tensor_type: tensor::TensorType, + tensor_data: tensor::TensorData, + ) -> Resource { + let tensor = TensorInternalData { + tensor_dimensions, + tensor_type, + tensor_data, + }; + self.tensors + .push(tensor) + .map(Resource::::new_own) + .expect("Can't allocate tensor") + } + fn dimensions(&mut self, tensor: Resource) -> Vec { + if let Some(t) = self.tensors.get(tensor.rep()) { + return t.tensor_dimensions.clone(); + } + panic!("Can't find tensor with ID = {}", tensor.rep()); + } + + fn ty(&mut self, tensor: Resource) -> tensor::TensorType { + if let Some(t) = self.tensors.get(tensor.rep()) { + return t.tensor_type; + } + panic!("Can't find tensor with ID = {}", tensor.rep()); + } + + fn data(&mut self, tensor: Resource) -> tensor::TensorData { + if let Some(t) = self.tensors.get(tensor.rep()) { + return t.tensor_data.clone(); + } + panic!("Can't find tensor with ID = {}", tensor.rep()); + } + fn drop( + &mut self, + tensor: Resource, + ) -> std::result::Result<(), anyhow::Error> { + self.tensors + .remove(tensor.rep()) + .context(format!("Can't find tensor with ID = {}", tensor.rep())) + .map(|_| ()) + } + } + + impl inference::HostGraphExecutionContext for MLHostImpl { + fn set_input( + &mut self, + graph_execution_context: Resource, + input_name: String, + tensor: Resource, + ) -> Result<(), Resource> { + let index = input_name + .parse() + .expect("Can't parse {} to usize for input_name"); + // Construct the blob structure. TODO: there must be some good way to + // discover the layout here; `desc` should not have to default to NHWC. + let tensor_resource = self + .tensors + .get(tensor.rep()) + .unwrap_or_else(|| panic!("Can't find tensor with ID = {}", tensor.rep())); + let precision = map_tensor_type_to_precision(tensor_resource.tensor_type); + let dimensions = tensor_resource + .tensor_dimensions + .iter() + .map(|&d| d as usize) + .collect::>(); + let desc = TensorDesc::new(Layout::NHWC, &dimensions, precision); + let blob = openvino::Blob::new(&desc, &tensor_resource.tensor_data) + .expect("Error in Blob::new"); + let execution_context: &mut GraphExecutionContextInternalData = self + .executions + .get_mut(graph_execution_context.rep()) + .unwrap_or_else(|| panic!("Can't find tensor with ID = {}", tensor.rep())); + let input_name = execution_context + .cnn_network + .get_input_name(index) + .unwrap_or_else(|_| panic!("Can't find input with name = {}", index)); + match execution_context.infer_request.set_blob(&input_name, &blob) { + Ok(res) => Ok(res), + Err(err) => Err(self.new( + ErrorCode::RuntimeError, + format!("Inference error = {:?}", err.to_string()), + )), + } + } + + fn compute( + &mut self, + graph_execution_context: Resource, + ) -> Result<(), Resource> { + let graph_execution = self + .executions + .get_mut(graph_execution_context.rep()) + .ok_or(MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + format!( + "Can't find graph execution context with ID = {}", + graph_execution_context.rep() + ), + ))?; + match graph_execution.infer_request.infer() { + Ok(..) => Ok(()), + Err(err) => Err(MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + format!("Inference error = {:?}", err.to_string()), + )), + } + } + + fn get_output( + &mut self, + graph_execution_context: Resource, + input_name: String, + ) -> Result, Resource> { + let graph_execution = self + .executions + .get_mut(graph_execution_context.rep()) + .ok_or(format!( + "Can't find graph execution context with ID = {}", + graph_execution_context.rep() + )) + .unwrap(); + + match MLHostImpl::get_output_internal(graph_execution, input_name) { + Ok(tensor) => self + .tensors + .push(tensor) + .map(Resource::::new_own) + .map_err(|_| { + MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + "Can't create tensor for get_output".to_string(), + ) + }), + Err(err) => Err(MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + err, + )), + } + } + + fn drop( + &mut self, + execution: Resource, + ) -> std::result::Result<(), anyhow::Error> { + let id = execution.rep(); + self.executions + .remove(id) + .context("{Can't drow GraphExecutionContext with id = {id}") + .map(|_| ()) + } + } + + impl errors::Host for MLHostImpl {} + impl graph::Host for MLHostImpl { + fn load( + &mut self, + graph: Vec, + graph_encoding: GraphEncoding, + target: ExecutionTarget, + ) -> Result, Resource> { + if graph.len() != 2 { + return Err(MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + "Expected 2 elements in graph builder vector".to_string(), + )); + } + if graph_encoding != GraphEncoding::Openvino { + return Err(MLHostImpl::new_error( + &mut self.errors, + ErrorCode::RuntimeError, + "Only OpenVINO encoding is supported".to_string(), + )); + } + // Read the guest array. + let graph_internal_data = GraphInternalData { + xml: graph[0].clone(), + weights: graph[1].clone(), + target, + }; + match self.graphs.push(graph_internal_data) { + Ok(graph_rep) => Ok(Resource::::new_own(graph_rep)), + Err(err) => { + match self.errors.push(ErrorInternalData { + code: ErrorCode::RuntimeError, + message: format!("{:?}", err), + }) { + Ok(error_rep) => Err(Resource::::new_own(error_rep)), + Err(err) => { + panic!("Can't create internal error for {:?}", err); + } + } + } + } + } + fn load_by_name( + &mut self, + _graph: String, + ) -> Result, Resource> { + panic!("[graph::Host] fn load_by_name -> Not implemented"); + } + } + + impl inference::Host for MLHostImpl {} + impl tensor::Host for MLHostImpl {} + + /// Return the execution target string expected by OpenVINO from the + /// `ExecutionTarget` enum provided by wasi-nn. + fn map_execution_target_to_string(target: ExecutionTarget) -> &'static str { + match target { + ExecutionTarget::Cpu => "CPU", + ExecutionTarget::Gpu => "GPU", + ExecutionTarget::Tpu => { + unimplemented!("OpenVINO does not support TPU execution targets") + } + } + } + + /// Return OpenVINO's precision type for the `TensorType` enum provided by + /// wasi-nn. + fn map_tensor_type_to_precision(tensor_type: tensor::TensorType) -> openvino::Precision { + match tensor_type { + tensor::TensorType::Fp16 => Precision::FP16, + tensor::TensorType::Fp32 => Precision::FP32, + tensor::TensorType::Fp64 => Precision::FP64, + tensor::TensorType::U8 => Precision::U8, + tensor::TensorType::I32 => Precision::I32, + tensor::TensorType::I64 => Precision::I64, + tensor::TensorType::Bf16 => todo!("not yet supported in `openvino` bindings"), + } + } + fn map_precision_to_tensor_type(precision: openvino::Precision) -> tensor::TensorType { + match precision { + Precision::FP16 => tensor::TensorType::Fp16, + Precision::FP32 => tensor::TensorType::Fp32, + Precision::FP64 => tensor::TensorType::Fp64, + Precision::U8 => tensor::TensorType::U8, + Precision::I32 => tensor::TensorType::I32, + Precision::I64 => tensor::TensorType::I64, + _ => todo!("not yet supported in `openvino` bindings"), + } + } +} diff --git a/crates/core/tests/test_host_components/mod.rs b/crates/core/tests/test_host_components/mod.rs new file mode 100644 index 0000000000..62a1ead4cb --- /dev/null +++ b/crates/core/tests/test_host_components/mod.rs @@ -0,0 +1,2 @@ +pub mod ml; +pub mod multiplier; diff --git a/crates/core/tests/test_host_components/multiplier.rs b/crates/core/tests/test_host_components/multiplier.rs new file mode 100644 index 0000000000..b744c74df3 --- /dev/null +++ b/crates/core/tests/test_host_components/multiplier.rs @@ -0,0 +1,32 @@ +use spin_core::HostComponent; + +// Simple test HostComponent; multiplies the input by the configured factor +#[derive(Clone)] +pub struct MultiplierHostComponent; + +mod multiplier { + wasmtime::component::bindgen!("multiplier" in "tests/core-wasi-test/wit"); +} + +impl HostComponent for MultiplierHostComponent { + type Data = Multiplier; + + fn add_to_linker( + linker: &mut spin_core::Linker, + get: impl Fn(&mut spin_core::Data) -> &mut Self::Data + Send + Sync + Copy + 'static, + ) -> anyhow::Result<()> { + multiplier::imports::add_to_linker(linker, get) + } + + fn build_data(&self) -> Self::Data { + Multiplier(2) + } +} + +pub struct Multiplier(pub i32); + +impl multiplier::imports::Host for Multiplier { + fn multiply(&mut self, a: i32) -> i32 { + self.0 * a + } +} diff --git a/crates/doctor/src/test.rs b/crates/doctor/src/test.rs index 544eae1b4d..4fa07da64f 100644 --- a/crates/doctor/src/test.rs +++ b/crates/doctor/src/test.rs @@ -1,7 +1,7 @@ #![cfg(test)] #![allow(clippy::expect_fun_call)] -use std::{fs, io::Write, path::Path}; +use std::{io::Write, path::Path}; use tempfile::{NamedTempFile, TempPath}; use toml::Value; diff --git a/crates/locked-app/src/locked.rs b/crates/locked-app/src/locked.rs index 7dd4c5a2f7..1a1ab226af 100644 --- a/crates/locked-app/src/locked.rs +++ b/crates/locked-app/src/locked.rs @@ -294,8 +294,6 @@ pub struct Variable { mod test { use super::*; - use crate::values::ValuesMapBuilder; - #[test] fn locked_app_with_no_host_reqs_serialises_as_v0_and_v0_deserialises_as_v1() { let locked_app = LockedApp { diff --git a/examples/spin-timer/Cargo.lock b/examples/spin-timer/Cargo.lock index 60dfbe4b4e..0fda9848b6 100644 --- a/examples/spin-timer/Cargo.lock +++ b/examples/spin-timer/Cargo.lock @@ -1373,6 +1373,37 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "curl" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2 0.5.5", + "windows-sys 0.52.0", +] + +[[package]] +name = "curl-sys" +version = "0.4.72+curl-8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29cbdc8314c447d11e8fd156dcdd031d9e02a7a976163e396b548c03153bc9ea" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "rustls-ffi", + "vcpkg", + "windows-sys 0.52.0", +] + [[package]] name = "darling" version = "0.14.4" @@ -1753,6 +1784,19 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1930,7 +1974,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", - "spin", + "spin 0.9.8", ] [[package]] @@ -2444,6 +2488,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.28" @@ -2732,6 +2782,17 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" @@ -3003,6 +3064,18 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "libz-sys" +version = "1.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -3666,6 +3739,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -3942,6 +4036,39 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "openvino" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24bd3a7ef39968e6a4f1b1206c1c876f9bd50cf739ccbcd69f8539bbac5dcc7a" +dependencies = [ + "openvino-finder", + "openvino-sys", + "thiserror", +] + +[[package]] +name = "openvino-finder" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d234d1394a413ea8adaf0c40806b9ad1946be6310b441f688840654a331973" +dependencies = [ + "cfg-if", + "log", +] + +[[package]] +name = "openvino-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c98acf37fc84ad9d7da4dc6c18f0f60ad209b43a6f555be01f9003d0a2a43d" +dependencies = [ + "env_logger", + "libloading", + "once_cell", + "openvino-finder", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -4988,6 +5115,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted 0.7.1", + "web-sys", + "winapi", +] + [[package]] name = "ring" version = "0.17.8" @@ -4998,8 +5140,8 @@ dependencies = [ "cfg-if", "getrandom 0.2.12", "libc", - "spin", - "untrusted", + "spin 0.9.8", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -5126,6 +5268,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring 0.16.20", + "sct", + "webpki", +] + [[package]] name = "rustls" version = "0.21.10" @@ -5133,7 +5287,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" dependencies = [ "log", - "ring", + "ring 0.17.8", "rustls-webpki 0.101.7", "sct", ] @@ -5145,13 +5299,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", - "ring", + "ring 0.17.8", "rustls-pki-types", "rustls-webpki 0.102.2", "subtle", "zeroize", ] +[[package]] +name = "rustls-ffi" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9da52707cca59e6eef8a78f3ad8d04024254a168ed1b41eb4dfa9616eace781a" +dependencies = [ + "libc", + "log", + "num_enum", + "rustls 0.20.9", + "rustls-pemfile 0.2.1", + "sct", + "webpki", +] + [[package]] name = "rustls-native-certs" version = "0.7.0" @@ -5165,6 +5334,15 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-pemfile" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -5196,8 +5374,8 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -5206,9 +5384,9 @@ version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ - "ring", + "ring 0.17.8", "rustls-pki-types", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -5269,8 +5447,8 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring", - "untrusted", + "ring 0.17.8", + "untrusted 0.9.0", ] [[package]] @@ -5689,6 +5867,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spin" version = "0.9.8" @@ -5748,11 +5932,14 @@ dependencies = [ "cap-primitives", "cap-std", "crossbeam-channel", + "curl", "http 1.1.0", "io-extras", + "openvino", "rustix 0.37.27", "spin-telemetry", "system-interface", + "table", "tokio", "tracing", "wasi-common", @@ -6937,6 +7124,12 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -7854,6 +8047,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + [[package]] name = "webpki-roots" version = "0.25.4" diff --git a/flake.lock b/flake.lock index 99aca45bd6..4c26fadcc6 100644 --- a/flake.lock +++ b/flake.lock @@ -2,22 +2,21 @@ "nodes": { "fenix": { "inputs": { - "nixpkgs": [ - "nixpkgs" - ], + "nixpkgs": "nixpkgs", "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1716877613, - "narHash": "sha256-GOCKwLphClUGKw0gFDZZmF2UM3vLLLnWrFbAH2AINCI=", + "lastModified": 1710742993, + "narHash": "sha256-W0PQCe0bW3hKF5lHawXrKynBcdSP18Qa4sb8DcUfOqI=", "owner": "nix-community", "repo": "fenix", - "rev": "08ea8011dd25421c104a5f44d16a713a27d93fde", + "rev": "6f2fec850f569d61562d3a47dc263f19e9c7d825", "type": "github" }, "original": { "owner": "nix-community", "repo": "fenix", + "rev": "6f2fec850f569d61562d3a47dc263f19e9c7d825", "type": "github" } }, @@ -58,6 +57,22 @@ } }, "nixpkgs": { + "locked": { + "lastModified": 1719075281, + "narHash": "sha256-CyyxvOwFf12I91PBWz43iGT1kjsf5oi6ax7CrvaMyAo=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a71e967ef3694799d0c418c98332f7ff4cc5f6af", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { "locked": { "lastModified": 1703013332, "narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=", @@ -73,7 +88,7 @@ "type": "github" } }, - "nixpkgs_2": { + "nixpkgs_3": { "locked": { "lastModified": 1681358109, "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", @@ -93,7 +108,7 @@ "inputs": { "fenix": "fenix", "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", "rust-overlay": "rust-overlay" } }, @@ -117,7 +132,7 @@ "rust-overlay": { "inputs": { "flake-utils": "flake-utils_2", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_3" }, "locked": { "lastModified": 1703124916, diff --git a/flake.nix b/flake.nix index 945e121720..8d2cc8ea26 100644 --- a/flake.nix +++ b/flake.nix @@ -7,8 +7,8 @@ flake-utils.url = "github:numtide/flake-utils"; fenix = { - url = "github:nix-community/fenix"; - inputs.nixpkgs.follows = "nixpkgs"; + url = "github:nix-community/fenix?rev=6f2fec850f569d61562d3a47dc263f19e9c7d825"; + #inputs.nixpkgs.follows = "nixpkgs"; }; };