diff --git a/README.md b/README.md new file mode 100644 index 000000000..773be7431 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ + Here is the complete set of instructions: + +1. Clone the Nexus repository + +```bash +git clone https://github.com/nexus +``` + +2. Enter the Nexus flox environment + +```bash +cd nexus +flox activate +``` + +This will activate the flox environment defined in the nexus/flox.nix file. This environment has all the necessary dependencies installed to build and run your project. + +3. Build and run with cosmo + +Now you can use cosmo to build and launch your project: + +```bash +cosmo build +cosmo launch +``` + +**If this is your first run of cosmo**, it will automatically start the tutorial. + +**If not your first run**, you can start the tutorial with: + +```bash +cosmo tutorial hello +``` + +4. Explaining the components + +- flox is a tool for managing declarative Nix-based environments. The nexus/flox.nix file defines an environment with all the dependencies for your project. +- cosmo is a tool for building and deploying WebAssembly actors. You use it to build and launch your actor from within the flox environment. +- Nix is a purely functional package manager that is used by flox to define environments. + +5. Installation (if not already completed) + +Follow the instructions to install flox and configure your system to use it. This will install the necessary tools to get started with the Nexus project. + +- Install Nix (if not already installed) +- Install flox + +You now have all the necessary components installed and configured to build and run the Nexus project! Let me know if you have any other questions. diff --git a/cosmo-overlay.nix b/cosmo-overlay.nix new file mode 100644 index 000000000..52b2ad897 --- /dev/null +++ b/cosmo-overlay.nix @@ -0,0 +1,37 @@ +self: super: + +{ + cosmo = super.stdenv.mkDerivation rec { + pname = "cosmo"; + version = "1.0.0"; + + src = super.fetchurl { + url = "https://cosmonic.sh/install.sh"; + sha256 = "1961f948b184b31a820a68c01388d7c8e2e21c47285b63407de07a774b60b7f8"; + }; + + buildInputs = [ super.curl super.cacert super.which ]; + + phases = [ "installPhase" ]; + + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/cosmo + chmod +x $out/bin/cosmo + + # Replace 'curl' with the absolute path to the curl binary from the Nix store + sed -i 's|curl|${super.curl}/bin/curl|g' $out/bin/cosmo + # Replace 'which' with the absolute path to the which binary from the Nix store + sed -i 's|which|${super.which}/bin/which|g' $out/bin/cosmo + + # Set the installation directory to $out/bin + export COSMO_INSTALL_DIR=$out/bin + + # Replace the original installation directory with $out/bin + sed -i 's|~/.cosmo/bin|$out/bin|g' $out/bin/cosmo + + # Run the modified installation script + bash $out/bin/cosmo + ''; + }; +} diff --git a/myapp/.gitignore b/myapp/.gitignore new file mode 100644 index 000000000..1fab5d88e --- /dev/null +++ b/myapp/.gitignore @@ -0,0 +1,3 @@ +application.zip +src/main/application/security/ +.idea/ diff --git a/myapp/README.md b/myapp/README.md new file mode 100644 index 000000000..093189d73 --- /dev/null +++ b/myapp/README.md @@ -0,0 +1,13 @@ + + + +![Vespa logo](https://vespa.ai/assets/vespa-logo-color.png) + +# Vespa sample applications - Album Recommendations + +A simple Vespa application which can be deployed on one node, +and does search and recommendation in music data. + +Follow +[vespa quick start guide](https://docs.vespa.ai/en/vespa-quick-start.html) +to deploy this. diff --git a/myapp/ext/A-Head-Full-of-Dreams.json b/myapp/ext/A-Head-Full-of-Dreams.json new file mode 100644 index 000000000..c67b90664 --- /dev/null +++ b/myapp/ext/A-Head-Full-of-Dreams.json @@ -0,0 +1,15 @@ +{ + "put": "id:mynamespace:music::a-head-full-of-dreams", + "fields": { + "album": "A Head Full of Dreams", + "artist": "Coldplay", + "year": 2015, + "category_scores": { + "cells": [ + { "address" : { "cat" : "pop" }, "value": 1 }, + { "address" : { "cat" : "rock" }, "value": 0.2 }, + { "address" : { "cat" : "jazz" }, "value": 0 } + ] + } + } +} diff --git a/myapp/ext/Hardwired...To-Self-Destruct.json b/myapp/ext/Hardwired...To-Self-Destruct.json new file mode 100644 index 000000000..319025e7f --- /dev/null +++ b/myapp/ext/Hardwired...To-Self-Destruct.json @@ -0,0 +1,16 @@ +{ + "put": "id:mynamespace:music::hardwired-to-self-destruct", + "fields": { + "album": "Hardwired...To Self-Destruct", + "artist": "Metallica", + "year": 2016, + "category_scores": { + "cells": [ + { "address" : { "cat" : "pop" }, "value": 0 }, + { "address" : { "cat" : "rock" }, "value": 1 }, + { "address" : { "cat" : "jazz" }, "value": 0 } + ] + } + } +} + diff --git a/myapp/ext/Liebe-ist-fur-alle-da.json b/myapp/ext/Liebe-ist-fur-alle-da.json new file mode 100644 index 000000000..208ce8a01 --- /dev/null +++ b/myapp/ext/Liebe-ist-fur-alle-da.json @@ -0,0 +1,15 @@ +{ + "put": "id:mynamespace:music::liebe-ist-für-alle-da", + "fields": { + "album": "Liebe ist für alle da", + "artist": "Rammstein", + "year": 2009, + "category_scores": { + "cells": [ + { "address" : { "cat" : "pop" }, "value": 0.1 }, + { "address" : { "cat" : "rock" }, "value": 1.0 }, + { "address" : { "cat" : "jazz" }, "value": 0 } + ] + } + } +} diff --git a/myapp/ext/Love-Is-Here-To-Stay.json b/myapp/ext/Love-Is-Here-To-Stay.json new file mode 100644 index 000000000..bb1c5a4c3 --- /dev/null +++ b/myapp/ext/Love-Is-Here-To-Stay.json @@ -0,0 +1,16 @@ +{ + "put": "id:mynamespace:music::love-is-here-to-stay", + "fields": { + "album": "Love Is Here To Stay", + "artist": "Diana Krall", + "year": 2018, + "category_scores": { + "cells": [ + { "address" : { "cat" : "pop" }, "value": 0.4 }, + { "address" : { "cat" : "rock" }, "value": 0 }, + { "address" : { "cat" : "jazz" }, "value": 0.8 } + ] + } + } +} + diff --git a/myapp/ext/When-We-All-Fall-Asleep-Where-Do-We-Go.json b/myapp/ext/When-We-All-Fall-Asleep-Where-Do-We-Go.json new file mode 100644 index 000000000..7d01fb271 --- /dev/null +++ b/myapp/ext/When-We-All-Fall-Asleep-Where-Do-We-Go.json @@ -0,0 +1,15 @@ +{ + "put": "id:mynamespace:music::when-we-all-fall-asleep-where-do-we-go", + "fields": { + "album": "When We All Fall Asleep, Where Do We Go?", + "artist": "Billie Eilish", + "year": 2019, + "category_scores": { + "cells": [ + { "address" : { "cat" : "pop" }, "value": 1.0 }, + { "address" : { "cat" : "rock" }, "value": 0 }, + { "address" : { "cat" : "jazz" }, "value": 0.1 } + ] + } + } +} diff --git a/myapp/ext/documents.jsonl b/myapp/ext/documents.jsonl new file mode 100644 index 000000000..779ce399f --- /dev/null +++ b/myapp/ext/documents.jsonl @@ -0,0 +1,5 @@ +{ "put": "id:mynamespace:music::a-head-full-of-dreams", "fields": { "album": "A Head Full of Dreams", "artist": "Coldplay", "year": 2015, "category_scores": { "cells": [ { "address" : { "cat" : "pop" }, "value": 1 }, { "address" : { "cat" : "rock" }, "value": 0.2 }, { "address" : { "cat" : "jazz" }, "value": 0 } ] } }} +{ "put": "id:mynamespace:music::hardwired-to-self-destruct", "fields": { "album": "Hardwired...To Self-Destruct", "artist": "Metallica", "year": 2016, "category_scores": { "cells": [ { "address" : { "cat" : "pop" }, "value": 0 }, { "address" : { "cat" : "rock" }, "value": 1 }, { "address" : { "cat" : "jazz" }, "value": 0 } ] } }} +{ "put": "id:mynamespace:music::liebe-ist-für-alle-da", "fields": { "album": "Liebe ist für alle da", "artist": "Rammstein", "year": 2009, "category_scores": { "cells": [ { "address" : { "cat" : "pop" }, "value": 0.1 }, { "address" : { "cat" : "rock" }, "value": 1.0 }, { "address" : { "cat" : "jazz" }, "value": 0 } ] } }} +{ "put": "id:mynamespace:music::love-is-here-to-stay", "fields": { "album": "Love Is Here To Stay", "artist": "Diana Krall", "year": 2018, "category_scores": { "cells": [ { "address" : { "cat" : "pop" }, "value": 0.4 }, { "address" : { "cat" : "rock" }, "value": 0 }, { "address" : { "cat" : "jazz" }, "value": 0.8 } ] } }} +{ "put": "id:mynamespace:music::when-we-all-fall-asleep-where-do-we-go", "fields": { "album": "When We All Fall Asleep, Where Do We Go?", "artist": "Billie Eilish", "year": 2019, "category_scores": { "cells": [ { "address" : { "cat" : "pop" }, "value": 1.0 }, { "address" : { "cat" : "rock" }, "value": 0 }, { "address" : { "cat" : "jazz" }, "value": 0.1 } ] } }} diff --git a/myapp/myapp/.gitignore b/myapp/myapp/.gitignore new file mode 100644 index 000000000..1fab5d88e --- /dev/null +++ b/myapp/myapp/.gitignore @@ -0,0 +1,3 @@ +application.zip +src/main/application/security/ +.idea/ diff --git a/myapp/myapp/README.md b/myapp/myapp/README.md new file mode 100644 index 000000000..093189d73 --- /dev/null +++ b/myapp/myapp/README.md @@ -0,0 +1,13 @@ + + + +![Vespa logo](https://vespa.ai/assets/vespa-logo-color.png) + +# Vespa sample applications - Album Recommendations + +A simple Vespa application which can be deployed on one node, +and does search and recommendation in music data. + +Follow +[vespa quick start guide](https://docs.vespa.ai/en/vespa-quick-start.html) +to deploy this. diff --git a/myapp/myapp/ext/A-Head-Full-of-Dreams.json b/myapp/myapp/ext/A-Head-Full-of-Dreams.json new file mode 100644 index 000000000..c67b90664 --- /dev/null +++ b/myapp/myapp/ext/A-Head-Full-of-Dreams.json @@ -0,0 +1,15 @@ +{ + "put": "id:mynamespace:music::a-head-full-of-dreams", + "fields": { + "album": "A Head Full of Dreams", + "artist": "Coldplay", + "year": 2015, + "category_scores": { + "cells": [ + { "address" : { "cat" : "pop" }, "value": 1 }, + { "address" : { "cat" : "rock" }, "value": 0.2 }, + { "address" : { "cat" : "jazz" }, "value": 0 } + ] + } + } +} diff --git a/myapp/myapp/ext/Hardwired...To-Self-Destruct.json b/myapp/myapp/ext/Hardwired...To-Self-Destruct.json new file mode 100644 index 000000000..319025e7f --- /dev/null +++ b/myapp/myapp/ext/Hardwired...To-Self-Destruct.json @@ -0,0 +1,16 @@ +{ + "put": "id:mynamespace:music::hardwired-to-self-destruct", + "fields": { + "album": "Hardwired...To Self-Destruct", + "artist": "Metallica", + "year": 2016, + "category_scores": { + "cells": [ + { "address" : { "cat" : "pop" }, "value": 0 }, + { "address" : { "cat" : "rock" }, "value": 1 }, + { "address" : { "cat" : "jazz" }, "value": 0 } + ] + } + } +} + diff --git a/myapp/myapp/ext/Liebe-ist-fur-alle-da.json b/myapp/myapp/ext/Liebe-ist-fur-alle-da.json new file mode 100644 index 000000000..208ce8a01 --- /dev/null +++ b/myapp/myapp/ext/Liebe-ist-fur-alle-da.json @@ -0,0 +1,15 @@ +{ + "put": "id:mynamespace:music::liebe-ist-für-alle-da", + "fields": { + "album": "Liebe ist für alle da", + "artist": "Rammstein", + "year": 2009, + "category_scores": { + "cells": [ + { "address" : { "cat" : "pop" }, "value": 0.1 }, + { "address" : { "cat" : "rock" }, "value": 1.0 }, + { "address" : { "cat" : "jazz" }, "value": 0 } + ] + } + } +} diff --git a/myapp/myapp/ext/Love-Is-Here-To-Stay.json b/myapp/myapp/ext/Love-Is-Here-To-Stay.json new file mode 100644 index 000000000..bb1c5a4c3 --- /dev/null +++ b/myapp/myapp/ext/Love-Is-Here-To-Stay.json @@ -0,0 +1,16 @@ +{ + "put": "id:mynamespace:music::love-is-here-to-stay", + "fields": { + "album": "Love Is Here To Stay", + "artist": "Diana Krall", + "year": 2018, + "category_scores": { + "cells": [ + { "address" : { "cat" : "pop" }, "value": 0.4 }, + { "address" : { "cat" : "rock" }, "value": 0 }, + { "address" : { "cat" : "jazz" }, "value": 0.8 } + ] + } + } +} + diff --git a/myapp/myapp/ext/When-We-All-Fall-Asleep-Where-Do-We-Go.json b/myapp/myapp/ext/When-We-All-Fall-Asleep-Where-Do-We-Go.json new file mode 100644 index 000000000..7d01fb271 --- /dev/null +++ b/myapp/myapp/ext/When-We-All-Fall-Asleep-Where-Do-We-Go.json @@ -0,0 +1,15 @@ +{ + "put": "id:mynamespace:music::when-we-all-fall-asleep-where-do-we-go", + "fields": { + "album": "When We All Fall Asleep, Where Do We Go?", + "artist": "Billie Eilish", + "year": 2019, + "category_scores": { + "cells": [ + { "address" : { "cat" : "pop" }, "value": 1.0 }, + { "address" : { "cat" : "rock" }, "value": 0 }, + { "address" : { "cat" : "jazz" }, "value": 0.1 } + ] + } + } +} diff --git a/myapp/myapp/ext/documents.jsonl b/myapp/myapp/ext/documents.jsonl new file mode 100644 index 000000000..779ce399f --- /dev/null +++ b/myapp/myapp/ext/documents.jsonl @@ -0,0 +1,5 @@ +{ "put": "id:mynamespace:music::a-head-full-of-dreams", "fields": { "album": "A Head Full of Dreams", "artist": "Coldplay", "year": 2015, "category_scores": { "cells": [ { "address" : { "cat" : "pop" }, "value": 1 }, { "address" : { "cat" : "rock" }, "value": 0.2 }, { "address" : { "cat" : "jazz" }, "value": 0 } ] } }} +{ "put": "id:mynamespace:music::hardwired-to-self-destruct", "fields": { "album": "Hardwired...To Self-Destruct", "artist": "Metallica", "year": 2016, "category_scores": { "cells": [ { "address" : { "cat" : "pop" }, "value": 0 }, { "address" : { "cat" : "rock" }, "value": 1 }, { "address" : { "cat" : "jazz" }, "value": 0 } ] } }} +{ "put": "id:mynamespace:music::liebe-ist-für-alle-da", "fields": { "album": "Liebe ist für alle da", "artist": "Rammstein", "year": 2009, "category_scores": { "cells": [ { "address" : { "cat" : "pop" }, "value": 0.1 }, { "address" : { "cat" : "rock" }, "value": 1.0 }, { "address" : { "cat" : "jazz" }, "value": 0 } ] } }} +{ "put": "id:mynamespace:music::love-is-here-to-stay", "fields": { "album": "Love Is Here To Stay", "artist": "Diana Krall", "year": 2018, "category_scores": { "cells": [ { "address" : { "cat" : "pop" }, "value": 0.4 }, { "address" : { "cat" : "rock" }, "value": 0 }, { "address" : { "cat" : "jazz" }, "value": 0.8 } ] } }} +{ "put": "id:mynamespace:music::when-we-all-fall-asleep-where-do-we-go", "fields": { "album": "When We All Fall Asleep, Where Do We Go?", "artist": "Billie Eilish", "year": 2019, "category_scores": { "cells": [ { "address" : { "cat" : "pop" }, "value": 1.0 }, { "address" : { "cat" : "rock" }, "value": 0 }, { "address" : { "cat" : "jazz" }, "value": 0.1 } ] } }} diff --git a/myapp/myapp/schemas/music.sd b/myapp/myapp/schemas/music.sd new file mode 100644 index 000000000..fdca5fcfb --- /dev/null +++ b/myapp/myapp/schemas/music.sd @@ -0,0 +1,49 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +# A description of a type of data, how to store and index it, and what to compute over the data elements +# +# See: +# - https://docs.vespa.ai/en/schemas.html +schema music { + + document music { + + field artist type string { + indexing: summary | index + } + + field album type string { + indexing: summary | index + index: enable-bm25 + } + + field year type int { + indexing: summary | attribute + } + + field category_scores type tensor(cat{}) { + indexing: summary | attribute + } + + } + + fieldset default { + fields: artist, album + } + + # Rank profiles defines what to compute over the data, and how to use the computation result to order them + # They can be selected at query time (ranking.profile=[name]), and can be everything from simple handwritten + # expressions as below to references to large machine-learned models. + # + # See + # - https://docs.vespa.ai/en/ranking.html + rank-profile rank_albums inherits default { + inputs { + query(user_profile) tensor(cat{}) + } + first-phase { + expression: bm25(album) + 0.25 * sum(query(user_profile) * attribute(category_scores)) + } + } + +} diff --git a/myapp/myapp/services.xml b/myapp/myapp/services.xml new file mode 100644 index 000000000..20126161d --- /dev/null +++ b/myapp/myapp/services.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + 2 + + + + + + + + + diff --git a/myapp/schemas/music.sd b/myapp/schemas/music.sd new file mode 100644 index 000000000..fdca5fcfb --- /dev/null +++ b/myapp/schemas/music.sd @@ -0,0 +1,49 @@ +# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +# A description of a type of data, how to store and index it, and what to compute over the data elements +# +# See: +# - https://docs.vespa.ai/en/schemas.html +schema music { + + document music { + + field artist type string { + indexing: summary | index + } + + field album type string { + indexing: summary | index + index: enable-bm25 + } + + field year type int { + indexing: summary | attribute + } + + field category_scores type tensor(cat{}) { + indexing: summary | attribute + } + + } + + fieldset default { + fields: artist, album + } + + # Rank profiles defines what to compute over the data, and how to use the computation result to order them + # They can be selected at query time (ranking.profile=[name]), and can be everything from simple handwritten + # expressions as below to references to large machine-learned models. + # + # See + # - https://docs.vespa.ai/en/ranking.html + rank-profile rank_albums inherits default { + inputs { + query(user_profile) tensor(cat{}) + } + first-phase { + expression: bm25(album) + 0.25 * sum(query(user_profile) * attribute(category_scores)) + } + } + +} diff --git a/myapp/services.xml b/myapp/services.xml new file mode 100644 index 000000000..20126161d --- /dev/null +++ b/myapp/services.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + 2 + + + + + + + + + diff --git a/shell.nix b/shell.nix index d9b6f5adf..cafff23e2 100644 --- a/shell.nix +++ b/shell.nix @@ -1,4 +1,4 @@ -{ pkgs ? import {} }: +{ pkgs ? import { overlays = [ (import ./vespa-cli-overlay.nix) ]; } }: pkgs.mkShell { buildInputs = with pkgs; [ @@ -6,20 +6,105 @@ pkgs.mkShell { cargo tree poetry + openssl_1_1 + vespa-cli ]; shellHook = '' + # Check if Rust is installed, if not install it + if ! command -v rustc &> /dev/null; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + fi + + # Check if wasm32-unknown-unknown target is installed, if not install it + if ! rustup target list --installed | grep -q "wasm32-unknown-unknown"; then + rustup target add wasm32-unknown-unknown + fi + + + # Check if OpenSSL 1.1 is installed + if ! (command -v openssl &> /dev/null && openssl version | grep -q "OpenSSL 1.1"); then + + # Check the architecture and install OpenSSL 1.1 if needed + if [[ $(uname -m) == "arm64" ]]; then + if [[ "$OSTYPE" == "darwin"* ]]; then + # MacOS M1 installation + if ! command -v brew &> /dev/null; then + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + fi + + export PATH="/opt/homebrew/bin:$PATH" + + brew install openssl@1.1 + + elif [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Check for Debian-based system + if grep -qi 'debian' /etc/os-release; then + # Ubuntu ARM installation + + apt update && apt install curl -y + + curl -s https://packagecloud.io/install/repositories/wasmcloud/core/script.deb.sh | bash + + apt install wash + + curl -fLO http://ftp.us.debian.org/debian/pool/main/o/openssl/libssl1.1_1.1.1n-0+deb11u4_arm64.deb + + dpkg -i libssl1.1_1.1.1n-0+deb11u4_arm64.deb + + else + echo "This script is designed for Debian-based systems only." + exit 1 + fi + + else + echo "Unsupported system type." + exit 1 + fi + + else + echo "This script is designed for arm64 systems only." + exit 1 + fi + + fi + + # Check if cosmo is installed, if not install it + if ! command -v cosmo &> /dev/null; then + bash -c "$(curl -fsSL https://cosmonic.sh/install.sh)" + + # Get the current shell name + current_shell="$(basename ${builtins.getEnv "SHELL"})" + + # Update the corresponding configuration file based on the current shell + if [[ "$current_shell" == "bash" ]]; then + cat >> "${builtins.getEnv "HOME"}/.bashrc" <> "${builtins.getEnv "HOME"}/.zshrc" <