diff --git a/.github/workflows/build-assets.yml b/.github/workflows/build-assets.yml index 2ff173cc8..b47a908e8 100644 --- a/.github/workflows/build-assets.yml +++ b/.github/workflows/build-assets.yml @@ -8,6 +8,7 @@ on: push: branches: - main + pull_request: release: types: [published] @@ -22,7 +23,7 @@ jobs: fail-fast: false matrix: include: - - os: ubuntu-22.04 + - os: ubuntu-22.04 # target: x86_64-unknown-linux-gnu archive: tar.gz binary_name: opsml-server @@ -34,12 +35,6 @@ jobs: binary_name: opsml-server archive_name: opsml-server-aarch64-linux-gnu - - os: macos-latest - target: aarch64-apple-darwin - archive: zip - binary_name: opsml-server - archive_name: opsml-server-aarch64-darwin - - os: macos-13 target: x86_64-apple-darwin archive: zip @@ -78,19 +73,23 @@ jobs: with: python-version: ${{ env.INTERPRETER }} - - name: Install UI dependencies - run: | - make install.ui.deps - make build.ui - - name: Update apt repositories (Linux) if: contains(matrix.os, 'ubuntu') run: | sudo apt-get update -y sudo apt-get install -y build-essential + - name: Install UI dependencies + run: | + # install all deps and build UI + make build.ui + + # remove node_modules and rebuild prod version to reduce size + make install.ui.deps.prod + - name: Build Binaries - run: cargo build -p opsml-server --release --target ${{ matrix.target }} + run: | + cargo build -p opsml-server --release --target ${{ matrix.target }} - name: Prepare binary directory shell: bash @@ -102,6 +101,12 @@ jobs: cp target/${{ matrix.target }}/release/${{ matrix.binary_name }} release-bin/ chmod +x release-bin/${{ matrix.binary_name }} fi + # Copy UI build for Docker builds + if [ "${{ matrix.target }}" == "x86_64-unknown-linux-gnu" ] || [ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]; then + mkdir -p release-bin/ui + cp -r crates/opsml_server/opsml_ui/build release-bin/ui/build || true + cp -r crates/opsml_server/opsml_ui/node_modules release-bin/ui/node_modules || true + fi - name: Create zip archive (Windows/macOS) if: contains(matrix.archive, 'zip') @@ -128,6 +133,28 @@ jobs: path: ${{ matrix.archive_name }}.${{ matrix.archive }} retention-days: 1 + - name: Package UI for Node.js distribution + if: matrix.target == 'x86_64-unknown-linux-gnu' + shell: bash + run: | + mkdir -p packaged-ui + cp -r crates/opsml_server/opsml_ui/build packaged-ui/build + cp -r crates/opsml_server/opsml_ui/node_modules packaged-ui/node_modules + cp crates/opsml_server/opsml_ui/package.json packaged-ui/ + cp crates/opsml_server/opsml_ui/pnpm-lock.yaml packaged-ui/ || true + cd packaged-ui + zip -r ../opsml-ui-node.zip ./* + + # only want to run this once, so only on the first matrix item + # The artifact will be the same on all builds regardless of target + - name: Upload UI Node.js package artifact + if: matrix.target == 'x86_64-unknown-linux-gnu' + uses: actions/upload-artifact@v4 + with: + name: opsml-ui-node + path: opsml-ui-node.zip + retention-days: 1 + publish-docker-images: if: github.event_name == 'release' needs: build @@ -140,14 +167,13 @@ jobs: include: - image: "ubuntu" tag_suffix: "ubuntu" - - image: "alpine" - tag_suffix: "alpine" - - image: "scratch" - tag_suffix: "scratch" + artifact: "opsml-server-x86_64-linux-gnu" - image: "debian" tag_suffix: "debian" - - image: "distroless" - tag_suffix: "distroless" + artifact: "opsml-server-x86_64-linux-gnu" + - image: "rocky" + tag_suffix: "rocky-minimal" + artifact: "opsml-server-x86_64-linux-gnu" steps: - name: Checkout Code @@ -164,12 +190,15 @@ jobs: - name: Extract binary run: | - mkdir -p binary - tar -xzf ./artifacts/opsml-server-x86_64-linux-gnu.tar.gz -C ./binary - - - name: Set up binary permissions - run: | - chmod +x ./binary/opsml-server + archive="./artifacts/${{ matrix.artifact }}.tar.gz" + if [ -f "$archive" ]; then + mkdir -p binary + tar -xzf "$archive" -C ./binary + chmod +x ./binary/opsml-server + else + echo "x86_64 binary $archive not found, skipping this build" + exit 1 + fi - name: Set version tag id: set-version @@ -193,7 +222,7 @@ jobs: file: docker/official/${{ matrix.image }}/Dockerfile push: true build-args: | - OPSML_SERVER_BINARY=./binary/opsml-server + OPSML_SERVER_BINARY=./binary tags: | demml/opsml:${{ matrix.tag_suffix }}-amd64-${{ steps.set-version.outputs.VERSION }} ${{ github.event_name == 'release' && format('demml/opsml:{0}-amd64-latest', matrix.tag_suffix) || '' }} @@ -212,14 +241,13 @@ jobs: include: - image: "ubuntu" tag_suffix: "ubuntu" - - image: "alpine" - tag_suffix: "alpine" - - image: "scratch" - tag_suffix: "scratch" + artifact: "opsml-server-aarch64-linux-gnu" - image: "debian" tag_suffix: "debian" - - image: "distroless" - tag_suffix: "distroless" + artifact: "opsml-server-aarch64-linux-gnu" + - image: "rocky" + tag_suffix: "rocky-minimal" + artifact: "opsml-server-aarch64-linux-gnu" steps: - name: Checkout Code @@ -239,15 +267,15 @@ jobs: - name: Extract binary run: | - if [ -f "./artifacts/opsml-server-aarch64-linux-gnu.tar.gz" ]; then + archive="./artifacts/${{ matrix.artifact }}.tar.gz" + if [ -f "$archive" ]; then mkdir -p binary - tar -xzf ./artifacts/opsml-server-aarch64-linux-gnu.tar.gz -C ./binary + tar -xzf "$archive" -C ./binary chmod +x ./binary/opsml-server else - echo "ARM64 binary not found, skipping this build" + echo "ARM64 binary $archive not found, skipping this build" exit 1 fi - continue-on-error: true - name: Set version tag id: set-version @@ -272,7 +300,7 @@ jobs: push: true platforms: linux/arm64 build-args: | - OPSML_SERVER_BINARY=./binary/opsml-server + OPSML_SERVER_BINARY=./binary tags: | demml/opsml:${{ matrix.tag_suffix }}-arm64-${{ steps.set-version.outputs.VERSION }} ${{ github.event_name == 'release' && format('demml/opsml:{0}-arm64-latest', matrix.tag_suffix) || '' }} diff --git a/Cargo.lock b/Cargo.lock index 3cf7d542a..f4ee9aee2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4412,7 +4412,6 @@ dependencies = [ "password-auth", "rand 0.9.2", "reqwest", - "rust-embed", "rusty-logging", "scouter-client", "semver", @@ -5847,40 +5846,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "rust-embed" -version = "8.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" -dependencies = [ - "rust-embed-impl", - "rust-embed-utils", - "walkdir", -] - -[[package]] -name = "rust-embed-impl" -version = "8.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" -dependencies = [ - "proc-macro2", - "quote", - "rust-embed-utils", - "syn 2.0.106", - "walkdir", -] - -[[package]] -name = "rust-embed-utils" -version = "8.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" -dependencies = [ - "sha2", - "walkdir", -] - [[package]] name = "rustc-demangle" version = "0.1.26" diff --git a/Cargo.toml b/Cargo.toml index 38a0294f9..090ea96a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,6 @@ rand = { version = "0.9.1"} rayon = "1.*" regex = "1.*" reqwest = { version = "0.12.*", features = ["json", "stream", "multipart", "rustls-tls", "rustls-tls-native-roots", "blocking" ], default-features = false } -rust-embed = "8.*" reqwest-middleware = "0.*" rusty-logging = "0.*" # we cant use crates.io because scouter relies on a fork of linfa at the moment diff --git a/Makefile b/Makefile index 87fb5f442..19088cde5 100644 --- a/Makefile +++ b/Makefile @@ -121,11 +121,17 @@ ui.update.deps: install.ui.deps: cd $(UI_DIR) && pnpm install -.PHONY: ui.build +.PHONY: ui.install.deps.prod +install.ui.deps.prod: + # remove existing node_modules + rm -rf $(UI_DIR)/node_modules + # install only production dependencies + cd $(UI_DIR) && pnpm install --prod + +.PHONY: build.ui build.ui: cd $(UI_DIR) && pnpm install cd $(UI_DIR) && pnpm build - touch $(UI_DIR)/site/.gitkeep # to make sure the site folder is not ignored by git ui.dev: cd $(UI_DIR) && pnpm run dev @@ -154,7 +160,7 @@ dev.frontend: .PHONY: build.backend build.backend: - cargo build --release -p opsml-server + cargo build -p opsml-server --target .PHONY: start.backend start.backend: build.backend @@ -176,9 +182,9 @@ start.both: @echo "Starting both servers in production mode..." @echo "Backend API: http://localhost:8080" @echo "Frontend SSR: http://localhost:3000" - @make -j2 start.backend start.frontend + @make -j2 dev.backend start.frontend .PHONY: stop.both stop.both: -lsof -ti:3000 | xargs kill -9 2>/dev/null || true - -lsof -ti:8080 | xargs kill -9 2>/dev/null || true \ No newline at end of file + -lsof -ti:8080 | xargs kill -9 2>/dev/null || true diff --git a/crates/opsml_cli/Cargo.toml b/crates/opsml_cli/Cargo.toml index 9cbe4bee1..7950901d8 100644 --- a/crates/opsml_cli/Cargo.toml +++ b/crates/opsml_cli/Cargo.toml @@ -40,6 +40,8 @@ flate2 = "1.*" tar = "*" + + [dev-dependencies] mockall = { workspace = true } mockito = { workspace = true } diff --git a/crates/opsml_cli/src/actions/ui.rs b/crates/opsml_cli/src/actions/ui.rs index 08ba309b8..1fed84a14 100644 --- a/crates/opsml_cli/src/actions/ui.rs +++ b/crates/opsml_cli/src/actions/ui.rs @@ -10,16 +10,27 @@ use std::{ use sysinfo::{Pid, System}; const GITHUB_REPO: &str = "demml/opsml"; + +// Cache directory const CACHE_DIR: &str = ".opsml-cache"; const PID_FILE: &str = "opsml-server.pid"; -pub fn save_process_id(process: &Child) -> Result<(), UiError> { - let pid_path = get_pid_file_path()?; +// UI archive +const UI_ARCHIVE_NAME: &str = "opsml-ui-node.zip"; +const UI_PID_FILE: &str = "opsml-ui.pid"; + +pub fn save_process_id(process: &Child, is_ui: bool) -> Result<(), UiError> { + let pid_path = get_pid_file_path(is_ui)?; fs::write(&pid_path, process.id().to_string()).map_err(UiError::ProcessIdWriteError) } -fn get_pid_file_path() -> Result { +fn get_pid_file_path(is_ui: bool) -> Result { let cache_dir = get_cache_dir()?; + + if is_ui { + return Ok(cache_dir.join(UI_PID_FILE)); + } + Ok(cache_dir.join(PID_FILE)) } @@ -40,40 +51,101 @@ pub enum Platform { /// /// # Arguments /// * `version` - The version of the OpsML UI to start. If not provided, the latest version will be used. -pub fn start_ui(version: &str, artifact_url: Option) -> Result<(), UiError> { +pub fn start_ui( + version: &str, + server_artifact_url: &Option, + ui_artifact_url: &Option, + dev_mode: &bool, +) -> Result<(), UiError> { + // if dev mode, just start the UI from the local directory + // this assumes the user is running the command from opsml/py-opsml directory + if *dev_mode { + // start the backend server from the local opsml_server director + // always at ./target/debug/opsml-server of the current directory + + // navigate to parent directory (should be opsml root) + let parent_dir = env::current_dir() + .map_err(UiError::CurrentDirError)? + .parent() + .unwrap() + .to_path_buf(); + let server_path = parent_dir.join("target").join("debug").join("opsml-server"); + execute_binary(&server_path)?; + + // start the ui from the local opsml_ui directory + let ui_path = parent_dir + .join("crates") + .join("opsml_server") + .join("opsml_ui"); + execute_ui(&ui_path)?; + return Ok(()); + } + let platform = detect_platform()?; let cache_dir = get_cache_dir()?; let binary_path = cache_dir.join(format!("opsml-server-v{version}")); + let ui_path = cache_dir.join(format!("opsml-ui-v{version}")); if !binary_path.exists() { - download_binary(&platform, version, &cache_dir, artifact_url)?; + download_binary(&platform, version, &cache_dir, server_artifact_url)?; cleanup_old_binaries(&cache_dir, version, &platform)?; } + if !ui_path.exists() { + download_ui_package(version, &cache_dir, ui_artifact_url)?; + cleanup_old_ui_packages(&cache_dir, version)?; + } + + // this will start the binary in a new process execute_binary(&binary_path)?; + + // execute the UI package + execute_ui(&ui_path)?; + Ok(()) } +/// Stop both the backend server and UI pub fn stop_ui() -> Result<(), UiError> { - let pid_path = get_pid_file_path()?; + let cache_dir = get_cache_dir()?; - if !pid_path.exists() { - return Err(UiError::NoRunningServer); + // Stop backend server + let backend_pid_path = cache_dir.join(PID_FILE); + if backend_pid_path.exists() { + let pid_str = fs::read_to_string(&backend_pid_path).map_err(UiError::ProcessIdReadError)?; + let pid: usize = pid_str + .trim() + .parse() + .map_err(UiError::ProcessIdParseError)?; + + let s = System::new_all(); + if let Some(process) = s.process(Pid::from(pid)) { + println!("Stopping OpsML backend server (PID: {pid})"); + process + .kill_and_wait() + .map_err(|_| UiError::ProcessKillError(format!("{pid}")))?; + } + fs::remove_file(&backend_pid_path).ok(); // Clean up PID file } - let pid_str = fs::read_to_string(&pid_path).map_err(UiError::ProcessIdReadError)?; - - let pid: usize = pid_str - .trim() - .parse() - .map_err(UiError::ProcessIdParseError)?; + // Stop UI server - TODO: combine with above loop later + let ui_pid_path = cache_dir.join(UI_PID_FILE); + if ui_pid_path.exists() { + let pid_str = fs::read_to_string(&ui_pid_path).map_err(UiError::ProcessIdReadError)?; + let pid: usize = pid_str + .trim() + .parse() + .map_err(UiError::ProcessIdParseError)?; + + let s = System::new_all(); + if let Some(process) = s.process(Pid::from(pid)) { + println!("Stopping OpsML UI server (PID: {pid})"); + process + .kill_and_wait() + .map_err(|_| UiError::ProcessKillError(format!("{pid}")))?; + } - let s = System::new_all(); - if let Some(process) = s.process(Pid::from(pid)) { - println!("Stopping OpsML UI server (PID: {pid})"); - process - .kill_and_wait() - .map_err(|_| UiError::ProcessKillError(format!("{pid}")))?; + fs::remove_file(&ui_pid_path).ok(); // Clean up PID file } Ok(()) @@ -112,6 +184,7 @@ fn get_cache_dir() -> Result { /// * `platform` - The platform to download the binary for /// * `version` - The version of the binary to download /// * `cache_dir` - The directory to cache the downloaded binary +/// * `artifact_url` - Optional URL to download the binary from /// /// # Returns /// A Result indicating success or failure @@ -119,7 +192,7 @@ fn download_binary( platform: &Platform, version: &str, cache_dir: &Path, - artifact_url: Option, + artifact_url: &Option, ) -> Result<(), UiError> { // standardize naming let archive_name = match platform { @@ -128,9 +201,12 @@ fn download_binary( Platform::Linux(arch) => format!("opsml-server-{arch}-linux-gnu.tar.gz"), }; - let url = artifact_url.unwrap_or(format!( - "https://github.com/{GITHUB_REPO}/releases/download/v{version}/{archive_name}", - )); + let url = match artifact_url { + Some(url) => url.clone(), + None => { + format!("https://github.com/{GITHUB_REPO}/releases/download/v{version}/{archive_name}") + } + }; let response = reqwest::blocking::get(&url).map_err(UiError::DownloadBinaryError)?; let archive_path = cache_dir.join(&archive_name); @@ -192,6 +268,115 @@ fn download_binary( Ok(()) } +/// DOwnload and extract the OpsML UI package +/// +/// # Arguments +/// * `version` - The version of the UI package to download +/// * `cache_dir` - The cache directory to store the UI package +/// * `ui_artifact_url` - Optional URL to download the UI package from +/// # Returns +/// A Result indicating success or failure +fn download_ui_package( + version: &str, + cache_dir: &Path, + ui_artifact_url: &Option, +) -> Result<(), UiError> { + let url = match ui_artifact_url { + Some(url) => url.clone(), + None => format!( + "https://github.com/{GITHUB_REPO}/releases/download/v{version}/{UI_ARCHIVE_NAME}" + ), + }; + + println!("Downloading UI package for version {version}..."); + + let response = reqwest::blocking::get(&url).map_err(UiError::DownloadBinaryError)?; + let archive_path = cache_dir.join(UI_ARCHIVE_NAME); + + let bytes = response.bytes().map_err(UiError::DownloadBinaryError)?; + fs::write(&archive_path, bytes).map_err(UiError::WriteBinaryError)?; + + // Create versioned UI directory + let ui_dir = cache_dir.join(format!("opsml-ui-v{version}")); + fs::create_dir_all(&ui_dir).map_err(UiError::CreateCacheDirError)?; + + // Extract UI package (always zip format) + let reader = fs::File::open(&archive_path).map_err(UiError::ArchiveOpenError)?; + let mut archive = zip::ZipArchive::new(reader).map_err(UiError::ArchiveZipError)?; + + archive + .extract(&ui_dir) + .map_err(UiError::ZipArchiveExtractionError)?; + + // Clean up archive + fs::remove_file(archive_path).map_err(UiError::RemoveArchiveError)?; + + println!("UI package extracted to: {}", ui_dir.display()); + Ok(()) +} + +/// Execute the UI package with Node.js. Sveltekit SSR apps typically require Node.js +/// We also run the server on port 3000 +/// +/// # Arguments +/// * `ui_dir` - The directory containing the extracted UI package +fn execute_ui(ui_dir: &Path) -> Result<(), UiError> { + // Check if Node.js is available + let node_check = Command::new("node").arg("--version").output(); + + if node_check.is_err() { + return Err(UiError::NodeNotFound); + } + + // Look for package.json to determine how to start the UI + let package_json_path = ui_dir.join("package.json"); + if !package_json_path.exists() { + return Err(UiError::PackageJsonNotFound); + } + + println!("Starting UI server with Node.js on port 3000..."); + + // Start the UI server process + // Assuming the UI can be started with "node build/server.js" + let ui_process = Command::new("node") + .current_dir(ui_dir) + .arg("build/index.js") + .spawn() + .or_else(|_| { + // Fallback: try npm start + Command::new("npm").current_dir(ui_dir).arg("start").spawn() + }) + .map_err(UiError::UiSpawnError)?; + + // Save the UI process ID + save_process_id(&ui_process, true)?; + + let ui_id = ui_process.id(); + println!("Started OpsML UI server (PID: {ui_id}) on http://localhost:3000"); + + Ok(()) +} + +/// Cleans up old UI packages from the cache directory +fn cleanup_old_ui_packages(cache_dir: &Path, current_version: &str) -> Result<(), UiError> { + let prefix = "opsml-ui-v"; + let current_ui_dir = format!("{prefix}{current_version}"); + + for entry in fs::read_dir(cache_dir).map_err(UiError::ReadError)? { + let entry = entry.map_err(UiError::ReadError)?; + let path = entry.path(); + + if let Some(dir_name) = path.file_name().and_then(|f| f.to_str()) { + // Check if it's a UI directory and not the current version + if path.is_dir() && dir_name.starts_with(prefix) && dir_name != current_ui_dir { + fs::remove_dir_all(&path).map_err(UiError::RemoveFileError)?; + } + } + } + + Ok(()) +} + ///// Execute the OpsML UI binary /// /// # Arguments @@ -208,7 +393,7 @@ fn execute_binary(binary_path: &Path) -> Result<(), UiError> { .map_err(UiError::BinarySpawnError)?; // Save the process ID - save_process_id(&child_process)?; + save_process_id(&child_process, false)?; let id = child_process.id(); @@ -423,7 +608,7 @@ mod tests { ); // Call download_binary directly for more focused testing - download_binary(&platform, version, cache_path, Some(full_mock_url))?; + download_binary(&platform, version, cache_path, &Some(full_mock_url))?; // Assert that the binary was extracted and renamed correctly let expected_binary_path = cache_path.join(format!("opsml-server-v{version}")); @@ -457,7 +642,7 @@ mod tests { mock_server.url, version, archive_name ); - download_binary(&platform, version, cache_path, Some(full_mock_url))?; + download_binary(&platform, version, cache_path, &Some(full_mock_url))?; // Windows binaries have .exe extension let expected_binary_path = cache_path.join(format!("opsml-server-v{}.exe", version)); @@ -500,7 +685,7 @@ mod tests { ); // download_binary contains the cfg logic for extraction, so we call it directly - download_binary(&platform, version, cache_path, Some(full_mock_url))?; + download_binary(&platform, version, cache_path, &Some(full_mock_url))?; // Linux binaries have no extension in this setup let expected_binary_path = cache_path.join(format!("opsml-server-v{version}")); diff --git a/crates/opsml_cli/src/cli/arg.rs b/crates/opsml_cli/src/cli/arg.rs index 329c0feb8..26ee2c093 100644 --- a/crates/opsml_cli/src/cli/arg.rs +++ b/crates/opsml_cli/src/cli/arg.rs @@ -254,4 +254,29 @@ pub struct UiArgs { /// Version #[arg(long = "version")] pub version: Option, + + /// Server binary url + /// Optional URL to download the opsml-server binary from. This is typically used for testing purposes. + #[arg(long = "server-url")] + pub server_url: Option, + + /// UI binary url + /// Optional URL to download the opsml-ui binary from. This is typically used for testing purposes. + #[arg(long = "ui-url")] + pub ui_url: Option, + + /// Development mode + /// If set, the UI will execute the local debug server build and the ui build located in crates/opsml_server/opsml_ui + /// This is only intended for development purposes. + #[arg(long = "dev-mode", default_value = "false")] + pub dev_mode: bool, +} + +#[derive(Args, Clone)] +pub struct StopUiArgs { + /// Development mode + /// If set, the UI will execute the local debug server build and the ui build located in crates/opsml_server/opsml_ui + /// This is only intended for development purposes. + #[arg(long = "dev-mode", default_value = "false")] + pub dev_mode: bool, } diff --git a/crates/opsml_cli/src/cli/commands.rs b/crates/opsml_cli/src/cli/commands.rs index 840028869..ccdcc0366 100644 --- a/crates/opsml_cli/src/cli/commands.rs +++ b/crates/opsml_cli/src/cli/commands.rs @@ -178,6 +178,7 @@ pub enum GenerateCommands { } #[derive(Subcommand)] +#[command(version = None)] pub enum ScouterCommands { /// Update Scouter Drift Profile Status /// @@ -187,6 +188,7 @@ pub enum ScouterCommands { } #[derive(Subcommand)] +#[command(version = None)] pub enum UiCommands { /// Start a local OpsML UI /// diff --git a/crates/opsml_cli/src/error.rs b/crates/opsml_cli/src/error.rs index 466b106c0..1ed8d86e0 100644 --- a/crates/opsml_cli/src/error.rs +++ b/crates/opsml_cli/src/error.rs @@ -98,6 +98,9 @@ pub enum UiError { #[error("Failed to get create cache directory")] CreateCacheDirError(#[source] std::io::Error), + #[error("Failed to get current directory")] + CurrentDirError(#[source] std::io::Error), + #[error("Unsupported platform - os: {0}, arch: {1}")] UnsupportedPlatformError(&'static str, &'static str), @@ -169,4 +172,16 @@ pub enum UiError { #[error("Script execution failed with status code: {0:?}")] ScriptFailedWithStatus(Option), + + #[error("Failed to start UI server")] + UiStartError, + + #[error("Failed to spawn UI process")] + UiSpawnError(#[source] std::io::Error), + + #[error("Node.js executable not found. Node is required to run the OpsML UI.")] + NodeNotFound, + + #[error("Package JSON not found")] + PackageJsonNotFound, } diff --git a/crates/opsml_cli/src/lib.rs b/crates/opsml_cli/src/lib.rs index ea934fd64..5b6b77af0 100644 --- a/crates/opsml_cli/src/lib.rs +++ b/crates/opsml_cli/src/lib.rs @@ -116,7 +116,8 @@ pub fn run_cli(args: Vec) -> anyhow::Result<()> { println!("Starting opsml-ui..."); let default_version = opsml_version::version(); let version = args.version.as_deref().unwrap_or_else(|| &default_version); - start_ui(version, None).context("Failed to start opsml-ui")?; + start_ui(version, &args.server_url, &args.ui_url, &args.dev_mode) + .context("Failed to start opsml-ui")?; Ok(()) } UiCommands::Stop => { diff --git a/crates/opsml_mocks/src/mock.rs b/crates/opsml_mocks/src/mock.rs index fbb716341..c072d44f5 100644 --- a/crates/opsml_mocks/src/mock.rs +++ b/crates/opsml_mocks/src/mock.rs @@ -212,10 +212,10 @@ impl OpsmlTestServer { println!("Mock Scouter Server stopped"); } - pub fn set_env_vars_for_client(&self) -> PyResult<()> { + pub fn set_env_vars_for_client(&self, _port: u16) -> PyResult<()> { #[cfg(feature = "server")] { - std::env::set_var("OPSML_TRACKING_URI", "http://localhost:3000"); + std::env::set_var("OPSML_TRACKING_URI", format!("http://localhost:{}", _port)); std::env::set_var("APP_ENV", "dev_client"); Ok(()) } @@ -245,7 +245,7 @@ impl OpsmlTestServer { let handle = self.handle.clone(); let runtime = self.runtime.clone(); - let port = match (3000..3010) + let port = match (8000..8010) .find(|port| StdTcpListener::bind(("127.0.0.1", *port)).is_ok()) { Some(p) => p, @@ -280,7 +280,7 @@ impl OpsmlTestServer { .send(); if let Ok(response) = res { if response.status() == 200 { - self.set_env_vars_for_client()?; + self.set_env_vars_for_client(port)?; println!("Opsml Server started successfully"); app_state().reset_app_state().map_err(|e| { TestServerError::CustomError(format!( diff --git a/crates/opsml_server/Cargo.toml b/crates/opsml_server/Cargo.toml index 348ba40bf..84fd32be4 100644 --- a/crates/opsml_server/Cargo.toml +++ b/crates/opsml_server/Cargo.toml @@ -34,7 +34,6 @@ mimalloc = { workspace = true } password-auth = { workspace = true } rand = { workspace = true } reqwest = { workspace = true } -rust-embed = { workspace = true } rusty-logging = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } diff --git a/crates/opsml_server/opsml_ui/site/.gitkeep b/crates/opsml_server/opsml_ui/site/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/crates/opsml_server/opsml_ui/src/hooks.server.ts b/crates/opsml_server/opsml_ui/src/hooks.server.ts index becae2560..6e5abeb4c 100644 --- a/crates/opsml_server/opsml_ui/src/hooks.server.ts +++ b/crates/opsml_server/opsml_ui/src/hooks.server.ts @@ -18,6 +18,7 @@ const PUBLIC_ROUTES = [ ServerPaths.SSO_AUTH, ServerPaths.SSO_CALLBACK, ServerPaths.USER, + ServerPaths.HEALTHCHECK, "/", ]; diff --git a/crates/opsml_server/opsml_ui/src/lib/components/api/routes.ts b/crates/opsml_server/opsml_ui/src/lib/components/api/routes.ts index 2027eb9ed..8e5b81a1d 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/api/routes.ts +++ b/crates/opsml_server/opsml_ui/src/lib/components/api/routes.ts @@ -77,7 +77,23 @@ export enum ServerPaths { SSO_AUTH = "/api/user/sso", SSO_CALLBACK = "/api/user/sso/callback", USER = "/api/user/info", + UPDATE_USER = "/api/user/update", REGISTRY_PAGE = "/api/card/registry/page", REGISTRY_STATS = "/api/card/registry/stats", METADATA = "/api/card/metadata", + CARD_FROM_UID = "/api/card/uid", + VERSION_PAGE = "/api/card/registry/version/page", + MONITORING_METRICS = "/api/card/monitoring/metrics", + MONITORING_PROFILES = "/api/card/monitoring/profiles", + MONITORING_ALERTS = "/api/card/monitoring/alerts", + ACKNOWLEDGE_ALERT = "/api/card/monitoring/alerts/acknowledge", + UPDATE_MONITORING_PROFILE = "/api/card/monitoring/profile/update", + LLM_MONITORING_RECORDS = "/api/card/monitoring/llm/records", + EXPERIMENT_GROUPED_METRICS = "/api/card/experiment/metrics/grouped", + DATA_PROFILE = "/api/card/data/profile", + EXPERIMENT_METRIC_NAMES = "/api/card/experiment/metrics/names", + EXPERIMENT_RECENT = "/api/card/experiment/recent", + EXPERIMENT_HARDWARE = "/api/card/experiment/hardware", + CREATE_SPACE = "/api/space/create", + HEALTHCHECK = "/api/health", } diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/CardReadMe.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/CardReadMe.svelte index 9fe6b133b..9fe1b1234 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/CardReadMe.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/CardReadMe.svelte @@ -22,20 +22,12 @@ readMe: ReadMe; }>(); - - - - let readMeUrl = $state(`/opsml/${getRegistryPath(registryType)}/card/${space}/${name}/${version}/readme`); - - onMount(async () => { - if (readMe.exists) { html = await convertMarkdown(readMe.readme); } - }); diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/CardSearch.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/CardSearch.svelte index a4441949e..84bf5515d 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/CardSearch.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/CardSearch.svelte @@ -9,6 +9,7 @@ import { getRegistryPage, getRegistryStats } from '$lib/components/api/registry'; import { Combobox } from "melt/builders"; import CardPage from '$lib/components/card/CardPage.svelte'; + import MultiComboBoxDropDown from '$lib/components/utils/MultiComboBoxDropDown.svelte'; let { page, selectedName, selectedSpace } = $props<{ page: RegistryPageReturn; @@ -20,6 +21,7 @@ let currentPage = $state(1); let totalPages = $state(1); let artifactSearchQuery = $state(selectedName || ''); + let filteredSpaces: string[] = $state([]); let filteredTags: string[] = $state([]); @@ -32,10 +34,6 @@ let availableSpaces = page.spaces; let availableTags = page.tags; - //@ts-ignore - const spacesCombobox = new Combobox({ multiple: true, onValueChange: onSpaceChange }); - //@ts-ignore - const tagsCombobox = new Combobox({ multiple: true, onValueChange: onTagsChange }); onMount(() => { totalPages = Math.ceil(registryStats.stats.nbr_names / 30); @@ -43,31 +41,25 @@ // if selectedSpace is defined, add it to filteredSpaces and spacesCombobox if (selectedSpace && !filteredSpaces.includes(selectedSpace)) { filteredSpaces = [...filteredSpaces, selectedSpace]; - spacesCombobox.select(selectedSpace); } }); let searchTimeout: ReturnType | null = null; - function onTagsChange() { + + function onInputChange() { if (searchTimeout) clearTimeout(searchTimeout); searchTimeout = setTimeout(async () => { - // set filteredTags based on tagsCombobox.value - //@ts-ignore - filteredTags = [...tagsCombobox.value] as string[]; await searchPage(); }, 100); } - function onSpaceChange() { - if (searchTimeout) clearTimeout(searchTimeout); - searchTimeout = setTimeout(async () => { - // set filteredSpaces based on spacesCombobox.value - //@ts-ignore - filteredSpaces = [...spacesCombobox.value] as string[]; - await searchPage(); - }, 100); - } + // effect to run SearchPage when filteredSpaces changes + $effect(() => { + if (filteredSpaces.length > 0 || filteredTags.length > 0) { + onInputChange(); + } + }); const searchPage = async function () { [registryPage, registryStats] = await Promise.all([ @@ -85,26 +77,6 @@ - @@ -125,77 +97,24 @@
-
-
- - -
-
- {#each availableSpaces as option (option)} -
- {option} - {#if spacesCombobox.isSelected(option)} - ✓ - {/if} -
- {:else} - No results found - {/each} -
- - -
- {#each filteredSpaces as space} - - {/each} -
+ -

-
-
- - -
-
- {#each availableTags as option (option)} -
- {option} - {#if tagsCombobox.isSelected(option)} - ✓ - {/if} -
- {:else} - No results found - {/each} -
- -
- {#each filteredTags as tag} - - {/each} -
-
+ +
+
diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/VersionPage.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/VersionPage.svelte index 8314ae8fb..d61ea36af 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/VersionPage.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/VersionPage.svelte @@ -1,12 +1,13 @@
-
-
{#if data.readme.exists}
@@ -37,11 +33,9 @@
{/if}
- -
-
-
- - - - \ No newline at end of file +
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/data/DataProfileViz.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/data/DataProfileViz.svelte index 22fab458e..031d07341 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/data/DataProfileViz.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/data/DataProfileViz.svelte @@ -1,10 +1,10 @@ -
-
-
-
Data Profile:
-
-
- +
+ +
+
+
+
+
- - +
-
- {#each features as feature} - {@const featureProfile: FeatureProfile = profile.features[feature]} -
-
- - - {#if !featureProfile.numeric_stats} - - {:else} - - {/if} -
- {#if featureProfile.numeric_stats} - - {:else if featureProfile.string_stats} - - {/if} + +
+ {#each filteredFeatures as feature} + {@const featureProfile: FeatureProfile = profile.features[feature]} +
+ +
+ + +
+ + + {#if featureProfile.numeric_stats} + + {:else if featureProfile.string_stats} + + {/if} +
{/each} -
-
03
-
+
+
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/data/NumericStats.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/data/NumericStats.svelte index 9143c6d60..66b54fb6d 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/data/NumericStats.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/data/NumericStats.svelte @@ -2,90 +2,83 @@ import type { NumericStats } from "./types"; import HistChart from "$lib/components/viz/HistChart.svelte"; - -let { - numericData -} = $props<{ - numericData: NumericStats; -}>(); - + let { numericData } = $props<{ numericData: NumericStats }>(); let resetZoom: boolean = $state(false); - - let resetZoomClicked = () => { + + function resetZoomClicked() { resetZoom = !resetZoom; } - -
-
-
- -
-
-
General Stats
-
-
-
Distinct:
-
{numericData.distinct.count} ({numericData.distinct.percent}%)
-
-
-
Mean:
-
{numericData.stddev.toFixed(3)}
-
-
-
Minimum:
-
{numericData.min.toFixed(3)}
-
-
-
Maximum:
-
{numericData.max.toFixed(3)}
-
+
+ +
+ +
+
+
General Stats
- - -
-
-
Quantiles
-
-
-
Q25:
-
{numericData.quantiles.q25.toFixed(3)}
-
-
-
Q50:
-
{numericData.quantiles.q50.toFixed(3)}
-
-
-
Q75:
-
{numericData.quantiles.q75.toFixed(3)}
-
-
-
Q99:
-
{numericData.quantiles.q99.toFixed(3)}
-
+
+
Distinct:
+
{numericData.distinct.count} ({numericData.distinct.percent}%)
+
+
+
Mean:
+
{numericData.stddev.toFixed(3)}
+
+
+
Minimum:
+
{numericData.min.toFixed(3)}
+
+
+
Maximum:
+
{numericData.max.toFixed(3)}
+ +
+
+
Quantiles
+
+
+
Q25:
+
{numericData.quantiles.q25.toFixed(3)}
+
+
+
Q50:
+
{numericData.quantiles.q50.toFixed(3)}
+
+
+
Q75:
+
{numericData.quantiles.q75.toFixed(3)}
+
+
+
Q99:
+
{numericData.quantiles.q99.toFixed(3)}
+
+
-
+ +
-
-
Distribution
-
-
- -
- +
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/data/StringStats.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/data/StringStats.svelte index 08688ab75..85be37f0e 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/data/StringStats.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/data/StringStats.svelte @@ -1,73 +1,64 @@ - -
-
-
- - -
-
-
Stats
-
-
-
Distinct:
-
{stringData.distinct.count} ({stringData.distinct.percent}%)
-
-
-
Min Length:
-
{stringData.char_stats.min_length}
-
-
-
Max Length:
-
{stringData.char_stats.max_length}
-
-
-
Median Length:
-
{stringData.char_stats.median_length}
-
-
-
Mean Length:
-
{stringData.char_stats.mean_length}
-
-
-
- + import type { StringStats } from "./types"; + import WordBarChart from "$lib/components/viz/WordBarChart.svelte"; + + let { stringData } = $props<{ stringData: StringStats }>(); + let resetZoom: boolean = $state(false); + + function resetZoomClicked() { + resetZoom = !resetZoom; + } + + +
+ +
+ +
+
+
Stats
-
- -
-
-
Word Occurrence (Top 10)
-
- -
- -
- -
- +
+
Distinct:
+
{stringData.distinct.count} ({stringData.distinct.percent}%)
-
\ No newline at end of file +
+
Min Length:
+
{stringData.char_stats.min_length}
+
+
+
Max Length:
+
{stringData.char_stats.max_length}
+
+
+
Median Length:
+
{stringData.char_stats.median_length}
+
+
+
Mean Length:
+
{stringData.char_stats.mean_length}
+
+
+
+ + +
+
+
Word Occurrence (Top 10)
+ +
+
+ +
+
+
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/data/getDataProfile.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/data/getDataProfile.ts new file mode 100644 index 000000000..da3d1c938 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/data/getDataProfile.ts @@ -0,0 +1,12 @@ +import { createInternalApiClient } from "$lib/api/internalClient"; +import type { DataCard } from "../card_interfaces/datacard"; +import { ServerPaths } from "$lib/components/api/routes"; + +export async function getDataProfile( + fetch: typeof window.fetch, + card: DataCard +) { + const client = createInternalApiClient(fetch); + const response = await client.post(ServerPaths.DATA_PROFILE, { card }); + return response.json(); +} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/data/utils.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/data/utils.ts index 4f2e4a3de..e2127619b 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/data/utils.ts +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/data/utils.ts @@ -1,9 +1,6 @@ -import { getRawFile } from "$lib/components/files/utils"; -import type { DataCard } from "../card_interfaces/datacard"; import type { DataProfile, WordStats } from "$lib/components/card/data/types"; -import { RegistryType, getRegistryTableName } from "$lib/utils"; -function loadDataProfile(jsonString: string): DataProfile { +export function loadDataProfile(jsonString: string): DataProfile { try { // Parse the JSON string const parsedData = JSON.parse(jsonString); @@ -15,19 +12,6 @@ function loadDataProfile(jsonString: string): DataProfile { } } -export async function getDataProfile(card: DataCard): Promise { - let dataProfileUri = card.metadata.interface_metadata.save_metadata - .data_profile_uri as string; - - let profilePath = `${getRegistryTableName(RegistryType.Data)}/${card.space}/${ - card.name - }/v${card.version}/${dataProfileUri}`; - - let rawFile = await getRawFile(profilePath, card.uid, RegistryType.Data); - - return loadDataProfile(rawFile.content); -} - export function getSortedFeatureNames(dataProfile: DataProfile): string[] { return Object.keys(dataProfile.features).sort(); } diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/card/+page.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/experiment/ExperimentPage.svelte similarity index 93% rename from crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/card/+page.svelte rename to crates/opsml_server/opsml_ui/src/lib/components/card/experiment/ExperimentPage.svelte index d544a739a..cc3929e84 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/card/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/experiment/ExperimentPage.svelte @@ -1,23 +1,18 @@ -
-
-
{#if data.readme.exists}
@@ -40,14 +35,10 @@
{/if}
-
-
- - - \ No newline at end of file +
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/experiment/VizBody.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/experiment/VizBody.svelte index d478b6bab..31fe8155e 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/experiment/VizBody.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/experiment/VizBody.svelte @@ -1,13 +1,9 @@ + + +
+
+ +

+ + +
/
+
{metadata.version}
+

+ + +
+
+ +
+ {@render children()} +
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/ExperimentCardLayout.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/ExperimentCardLayout.svelte new file mode 100644 index 000000000..7b15b291c --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/ExperimentCardLayout.svelte @@ -0,0 +1,143 @@ + + +
+
+

+ + + +
{metadata.version}
+

+ + +
+
+ +
+ {@render children()} +
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/ModelCardLayout.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/ModelCardLayout.svelte new file mode 100644 index 000000000..64473b4ea --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/ModelCardLayout.svelte @@ -0,0 +1,125 @@ + + +
+
+

+ + +
/
+
{metadata.version}
+

+ + +
+
+ +
+ {@render children()} +
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/PromptCardLayout.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/PromptCardLayout.svelte new file mode 100644 index 000000000..b869fd771 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/PromptCardLayout.svelte @@ -0,0 +1,128 @@ + + + +
+
+ +

+ + + +
{metadata.version}
+

+ + + +
+
+ +
+ {@render children()} +
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/ServiceCardLayout.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/ServiceCardLayout.svelte new file mode 100644 index 000000000..bbc9d72c8 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/layouts/ServiceCardLayout.svelte @@ -0,0 +1,123 @@ + + + +
+
+ +

+ + + +
{metadata.version}
+

+ + + +
+
+ +
+ {@render children()} +
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/card/+page.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/model/ModelPage.svelte similarity index 86% rename from crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/card/+page.svelte rename to crates/opsml_server/opsml_ui/src/lib/components/card/model/ModelPage.svelte index 02338397c..21902ec85 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/card/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/model/ModelPage.svelte @@ -1,19 +1,16 @@ + let { data } = $props(); + let card: ModelCard = $state(data.metadata); +
-
-
{#if data.readme.exists}
@@ -36,7 +33,6 @@
{/if}
-
import { DriftType } from "./types"; - import { type DriftConfigType, type DriftProfile, type DriftProfileResponse, type UiProfile } from "./util"; + import { type DriftConfigType, type UiProfile } from "./utils"; import { Clock } from 'lucide-svelte'; import { TimeInterval } from '$lib/components/card/monitoring/types'; - import Dropdown from '$lib/components/utils/Dropdown.svelte'; import { KeySquare } from 'lucide-svelte'; import CustomConfigHeader from "./custom/CustomConfigHeader.svelte"; import PsiConfigHeader from "./psi/PsiConfigHeader.svelte"; import SpcConfigHeader from "./spc/SpcConfigHeader.svelte"; import LLMConfigHeader from "./llm/LLMConfigHeader.svelte"; import type { RegistryType } from "$lib/utils"; + import ComboBoxDropDown from "$lib/components/utils/ComboBoxDropDown.svelte"; // props @@ -45,7 +45,6 @@ let previousName = $state(currentName); let previousTimeInterval = $state(currentTimeInterval); - // Effect for handling a name change from the dropdown $effect(() => { if (currentName && currentName !== previousName) { @@ -66,9 +65,8 @@
- -
-
+
+
Drift Type:
{#each availableDriftTypes as drift_type} {#if drift_type === currentDriftType} @@ -84,31 +82,37 @@ {/each}
-
-
-
- +
+ +
+
Time Interval:
+
+
+ +
+ +
- -
- -
-
- + + +
+
Name:
+
+
+ +
+ +
- -
@@ -151,7 +155,4 @@ /> {/if}
-
- - - +
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/MonitoringDashboard.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/MonitoringDashboard.svelte new file mode 100644 index 000000000..6b6709bfb --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/MonitoringDashboard.svelte @@ -0,0 +1,263 @@ + + +
+
+ + +
+
+
+ + +
+ {#if currentName && latestMetrics} + {#if currentMetricData} + + {:else} +
+ No data available for selected metric +
+ {/if} + {:else} +
+ Select a metric to view data +
+ {/if} +
+ + +
+ +
+ + + {#if currentLLMRecordPage} +
+ +
+ {/if} +
+
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/VizBody.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/VizBody.svelte index f534f6d33..540a8ec33 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/VizBody.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/VizBody.svelte @@ -7,12 +7,12 @@ import type { MetricData, SpcDriftFeature, BinnedPsiMetric, BinnedMetric, BinnedMetricStats } from '$lib/components/card/monitoring/types'; import Pill from '$lib/components/utils/Pill.svelte'; import { TimeInterval } from '$lib/components/card/monitoring/types'; - import { type DriftConfigType } from './util'; + import { type DriftConfigType } from './utils'; import CustomAlertPill from '$lib/components/card/monitoring/custom/CustomAlertPill.svelte'; import LLMAlertPill from '$lib/components/card/monitoring/llm/LLMAlertPill.svelte'; import { getCustomAlertCondition, type CustomMetricDriftConfig } from './custom/custom'; import { getLLMAlertCondition, type LLMDriftConfig } from './llm/llm'; - import { type DriftProfile } from './util'; + import { type DriftProfile } from './utils'; let { metricData, diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/alert/AlertTable.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/alert/AlertTable.svelte index 677535f3a..f1836a840 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/alert/AlertTable.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/alert/AlertTable.svelte @@ -2,8 +2,6 @@ import type { Alert } from "./types"; import CodeModal from "../CodeModal.svelte"; - - let { alerts, // need to make an effect that updates this when the alerts change updateAlert @@ -17,7 +15,7 @@ let {
-
Drift Alerts
+
Drift Alerts
{#if alerts.length === 0}
No alerts to display @@ -25,34 +23,34 @@ let { {:else}
- + - - - - - - {#each alerts as alert, i} - - + - - + + - + {/each} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/alert/utils.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/alert/utils.ts new file mode 100644 index 000000000..90b4e7d99 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/alert/utils.ts @@ -0,0 +1,20 @@ +import { createInternalApiClient } from "$lib/api/internalClient"; +import { ServerPaths } from "$lib/components/api/routes"; + +export async function acknowledgeMonitoringAlert( + fetch: typeof globalThis.fetch, + id: number, + space: string +): Promise { + let response = await createInternalApiClient(fetch).put( + ServerPaths.ACKNOWLEDGE_ALERT, + { + id: id, + active: false, + space: space, + } + ); + + let booleanResponse = (await response.json()) as boolean; + return booleanResponse; +} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/custom/mocks.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/custom/mocks.ts new file mode 100644 index 000000000..395762b3a --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/custom/mocks.ts @@ -0,0 +1,136 @@ +import { DriftType } from "../types"; +import { AlertThreshold, type CustomDriftProfile } from "./custom"; +import type { BinnedMetrics } from "../types"; +/** + * Mock CustomDriftProfile for UI development and testing. + * Includes realistic metric values, config, and alert conditions. + */ +export const mockCustomDriftProfile: CustomDriftProfile = { + metrics: { + custom: 0.94, + f1_score: 0.88, + latency_ms: 250, + }, + config: { + sample_size: 100, + sample: true, + space: "services", + name: "api_monitor", + version: "2.1.0", + drift_type: DriftType.Custom, + alert_config: { + dispatch_config: { + Console: { enabled: true }, + Slack: { channel: "#custom-alerts" }, + }, + schedule: "0 */4 * * *", // Every 4 hours + alert_conditions: { + custom: { + alert_threshold: AlertThreshold.Below, + alert_threshold_value: 0.9, + }, + f1_score: { + alert_threshold: AlertThreshold.Above, + alert_threshold_value: 0.85, + }, + latency_ms: { + alert_threshold: AlertThreshold.Below, + alert_threshold_value: 1000, + }, + }, + }, + }, + scouter_version: "1.0.0", +}; + +export const mockCustomMetrics: BinnedMetrics = { + metrics: { + custom: { + metric: "custom", + created_at: [ + "2025-03-25 00:43:59", + "2025-03-26 10:00:00", + "2025-03-27 11:00:00", + "2025-03-28 12:00:00", + "2025-03-29 12:00:00", + ], + stats: [ + { + avg: 0.95, + lower_bound: 0.92, + upper_bound: 0.98, + }, + { + avg: 0.94, + lower_bound: 0.91, + upper_bound: 0.97, + }, + { + avg: 0.96, + lower_bound: 0.93, + upper_bound: 0.99, + }, + { + avg: 0.9, + lower_bound: 0.93, + upper_bound: 0.99, + }, + { + avg: 0.4, + lower_bound: 0.93, + upper_bound: 0.99, + }, + ], + }, + f1_score: { + metric: "f1_score", + created_at: [ + "2024-03-26T10:00:00", + "2024-03-26T11:00:00", + "2024-03-26T12:00:00", + ], + stats: [ + { + avg: 0.88, + lower_bound: 0.85, + upper_bound: 0.91, + }, + { + avg: 0.87, + lower_bound: 0.84, + upper_bound: 0.9, + }, + { + avg: 0.89, + lower_bound: 0.86, + upper_bound: 0.92, + }, + ], + }, + latency_ms: { + metric: "latency_ms", + created_at: [ + "2024-03-26T10:00:00", + "2024-03-26T11:00:00", + "2024-03-26T12:00:00", + ], + stats: [ + { + avg: 150.5, + lower_bound: 145.0, + upper_bound: 156.0, + }, + { + avg: 148.2, + lower_bound: 143.5, + upper_bound: 153.0, + }, + { + avg: 152.8, + lower_bound: 147.2, + upper_bound: 158.4, + }, + ], + }, + }, +}; diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/example.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/example.ts deleted file mode 100644 index 019b13770..000000000 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/example.ts +++ /dev/null @@ -1,416 +0,0 @@ -import type { - BinnedMetrics, - BinnedPsiFeatureMetrics, - BinnedSpcFeatureMetrics, - LLMDriftServerRecord, - LLMPageResponse, - PaginationCursor, -} from "./types"; -import { Status } from "./types"; -import type { Alert } from "./alert/types"; - -const sampleCustomMetrics: BinnedMetrics = { - metrics: { - custom: { - metric: "custom", - created_at: [ - "2025-03-25 00:43:59", - "2025-03-26 10:00:00", - "2025-03-27 11:00:00", - "2025-03-28 12:00:00", - "2025-03-29 12:00:00", - ], - stats: [ - { - avg: 0.95, - lower_bound: 0.92, - upper_bound: 0.98, - }, - { - avg: 0.94, - lower_bound: 0.91, - upper_bound: 0.97, - }, - { - avg: 0.96, - lower_bound: 0.93, - upper_bound: 0.99, - }, - { - avg: 0.9, - lower_bound: 0.93, - upper_bound: 0.99, - }, - { - avg: 0.4, - lower_bound: 0.93, - upper_bound: 0.99, - }, - ], - }, - f1_score: { - metric: "f1_score", - created_at: [ - "2024-03-26T10:00:00", - "2024-03-26T11:00:00", - "2024-03-26T12:00:00", - ], - stats: [ - { - avg: 0.88, - lower_bound: 0.85, - upper_bound: 0.91, - }, - { - avg: 0.87, - lower_bound: 0.84, - upper_bound: 0.9, - }, - { - avg: 0.89, - lower_bound: 0.86, - upper_bound: 0.92, - }, - ], - }, - latency_ms: { - metric: "latency_ms", - created_at: [ - "2024-03-26T10:00:00", - "2024-03-26T11:00:00", - "2024-03-26T12:00:00", - ], - stats: [ - { - avg: 150.5, - lower_bound: 145.0, - upper_bound: 156.0, - }, - { - avg: 148.2, - lower_bound: 143.5, - upper_bound: 153.0, - }, - { - avg: 152.8, - lower_bound: 147.2, - upper_bound: 158.4, - }, - ], - }, - }, -}; - -const sampleLLMMetrics: BinnedMetrics = { - metrics: { - reformulation_quality: { - metric: "reformulation_quality", - created_at: [ - "2025-03-25 00:43:59", - "2025-03-26 10:00:00", - "2025-03-27 11:00:00", - "2025-03-28 12:00:00", - "2025-03-29 12:00:00", - ], - stats: [ - { - avg: 0.95, - lower_bound: 0.92, - upper_bound: 0.98, - }, - { - avg: 0.94, - lower_bound: 0.91, - upper_bound: 0.97, - }, - { - avg: 0.96, - lower_bound: 0.93, - upper_bound: 0.99, - }, - { - avg: 0.9, - lower_bound: 0.93, - upper_bound: 0.99, - }, - { - avg: 0.4, - lower_bound: 0.93, - upper_bound: 0.99, - }, - ], - }, - coherence: { - metric: "coherence", - created_at: [ - "2024-03-26T10:00:00", - "2024-03-26T11:00:00", - "2024-03-26T12:00:00", - ], - stats: [ - { - avg: 0.88, - lower_bound: 0.85, - upper_bound: 0.91, - }, - { - avg: 0.87, - lower_bound: 0.84, - upper_bound: 0.9, - }, - { - avg: 0.89, - lower_bound: 0.86, - upper_bound: 0.92, - }, - ], - }, - }, -}; - -const sampleSpcMetrics: BinnedSpcFeatureMetrics = { - features: { - col_0: { - created_at: [ - "2025-03-25 00:43:59", - "2025-03-26 10:00:00", - "2025-03-27 11:00:00", - "2025-03-28 12:00:00", - "2025-03-29 12:00:00", - ], - values: [100, 105, 200, 1025, 101], - }, - col_1: { - created_at: [ - "2025-03-25 00:43:59", - "2025-03-26 10:00:00", - "2025-03-27 11:00:00", - "2025-03-28 12:00:00", - "2025-03-29 12:00:00", - ], - values: [100, 105, 200, 1025, 101], - }, - }, -}; - -const samplePsiMetrics: BinnedPsiFeatureMetrics = { - features: { - col_0: { - created_at: [ - "2025-03-25 00:43:59", - "2025-03-26 10:00:00", - "2025-03-27 11:00:00", - "2025-03-28 12:00:00", - "2025-03-29 12:00:00", - ], - psi: [0.05, 0.07, 0.04, 0.1, 0.05], - overall_psi: 0.053, - bins: { - 0: 0.1, - 1: 0.2, - 2: 0.3, - 3: 0.25, - 4: 0.15, - }, - }, - col_1: { - created_at: [ - "2025-03-25 00:43:59", - "2025-03-25 01:43:59", - "2025-03-25 02:43:59", - "2025-03-25 03:43:59", - "2025-03-25 04:43:59", - "2025-03-25 05:43:59", - "2025-03-25 06:43:59", - "2025-03-25 07:43:59", - "2025-03-25 08:43:59", - "2025-03-25 09:43:59", - ], - psi: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5], - overall_psi: 0.053, - bins: { - 0: 0.1, - 1: 0.2, - 2: 0.3, - 3: 0.25, - 4: 0.15, - }, - }, - }, -}; - -export { - sampleSpcMetrics, - samplePsiMetrics, - sampleCustomMetrics, - sampleLLMMetrics, -}; - -export const sampleAlerts: Alert[] = [ - { - created_at: "2024-03-28 10:30:00", - name: "credit_model", - space: "models", - version: "1.0.0", - entity_name: "credit_score", - alert: { type: "drift_detected", message: "PSI value exceeded threshold" }, - id: 1, - drift_type: "psi", - active: true, - }, - { - created_at: "2024-03-28 09:45:00", - name: "fraud_detection", - space: "models", - version: "2.1.0", - entity_name: "transaction_amount", - alert: { type: "spc_violation", message: "Value outside control limits" }, - id: 2, - drift_type: "spc", - active: true, - }, - { - created_at: "2024-03-28 09:00:00", - name: "customer_churn", - space: "ml_models", - version: "1.2.3", - entity_name: "usage_frequency", - alert: { type: "custom_metric", message: "Metric below threshold" }, - id: 3, - drift_type: "custom", - active: true, - }, - { - created_at: "2024-03-27 23:15:00", - name: "recommendation_engine", - space: "recsys", - version: "3.0.1", - entity_name: "user_engagement", - alert: { type: "drift_detected", message: "Distribution shift detected" }, - id: 4, - drift_type: "psi", - active: false, - }, - { - created_at: "2024-03-27 22:30:00", - name: "credit_model", - space: "models", - version: "1.0.0", - entity_name: "debt_ratio", - alert: { type: "spc_violation", message: "Consecutive points above mean" }, - id: 5, - drift_type: "spc", - active: true, - }, - { - created_at: "2024-03-27 21:45:00", - name: "fraud_detection", - space: "models", - version: "2.1.0", - entity_name: "ip_velocity", - alert: { type: "psi_threshold", message: "PSI above 0.2" }, - id: 6, - drift_type: "psi", - active: true, - }, - { - created_at: "2024-03-27 20:00:00", - name: "price_optimization", - space: "pricing", - version: "1.1.0", - entity_name: "demand_forecast", - alert: { type: "custom_metric", message: "Accuracy below target" }, - id: 7, - drift_type: "custom", - active: false, - }, - { - created_at: "2024-03-27 19:15:00", - name: "customer_churn", - space: "ml_models", - version: "1.2.3", - entity_name: "support_tickets", - alert: { type: "drift_detected", message: "Significant feature drift" }, - id: 8, - drift_type: "psi", - active: true, - }, - { - created_at: "2024-03-27 18:30:00", - name: "recommendation_engine", - space: "recsys", - version: "3.0.1", - entity_name: "click_through_rate", - alert: { type: "spc_violation", message: "Point beyond 3 sigma" }, - id: 9, - drift_type: "spc", - active: true, - }, - { - created_at: "2024-03-27 17:45:00", - name: "price_optimization", - space: "pricing", - version: "1.1.0", - entity_name: "competitor_prices", - alert: { type: "custom_metric", message: "Data freshness warning" }, - id: 10, - drift_type: "custom", - active: true, - }, -]; - -function randomStatus(): Status { - const statuses = [ - Status.Pending, - Status.Processing, - Status.Processed, - Status.Failed, - ]; - return statuses[Math.floor(Math.random() * statuses.length)]; -} - -function randomDate(offsetDays: number = 0): string { - const date = new Date(); - date.setDate(date.getDate() - offsetDays); - return date.toISOString(); -} - -export const mockLLMDriftServerRecords: LLMDriftServerRecord[] = Array.from( - { length: 30 }, - (_, i) => ({ - created_at: randomDate(30 - i), - space: `space_${(i % 5) + 1}`, - name: `card_${(i % 10) + 1}`, - version: `v${(i % 3) + 1}.0.${i}`, - prompt: i % 2 === 0 ? `Prompt for card_${(i % 10) + 1}` : undefined, - context: `Context for card_${(i % 10) + 1}`, - status: randomStatus(), - id: i + 1, - uid: `uid_${i + 1}`, - score: { - relevance: { - score: Number(Math.random().toFixed(2)), - reason: - "The prompt is relevant and you should use it!. AHHHHHHHHHHHHHHHHHHHHHHHHH", - }, - coherence: { - score: Number(Math.random().toFixed(2)), - reason: "Sample reason", - }, - }, - updated_at: randomDate(29 - i), - processing_started_at: i % 3 === 0 ? randomDate(30 - i) : undefined, - processing_ended_at: i % 4 === 0 ? randomDate(29 - i) : undefined, - processing_duration: - i % 5 === 0 ? Math.floor(Math.random() * 60) : undefined, - }) -); - -let paginationCursor: PaginationCursor = { - id: mockLLMDriftServerRecords.length, -}; -export const mockLLMDriftPageResponse: LLMPageResponse = { - items: mockLLMDriftServerRecords, - next_cursor: paginationCursor, - has_more: true, -}; diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/getMonitoringDashboardData.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/getMonitoringDashboardData.ts new file mode 100644 index 000000000..c602e2784 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/getMonitoringDashboardData.ts @@ -0,0 +1,236 @@ +import { getMaxDataPoints, RegistryType } from "$lib/utils"; +import { + getLatestMonitoringMetrics, + getLLMMonitoringRecordPage, + getMonitoringAlerts, + getMonitoringDriftProfiles, + getProfileConfig, + getProfileFeatures, + getCurrentMetricData, + type UiProfile, + type DriftProfileResponse, +} from "$lib/components/card/monitoring/utils"; +import { + DriftType, + TimeInterval, + type ServiceInfo, + type BinnedDriftMap, + type MetricData, + type DriftProfileUri, +} from "$lib/components/card/monitoring/types"; +import type { Alert } from "$lib/components/card/monitoring/alert/types"; + +/** + * Parent data interface expected from SvelteKit page hierarchy + * Supports different metadata structures for different registry types + */ +export interface MonitoringParentData { + metadata: { + uid: string; + metadata: { + // For Prompt registries - direct drift profile URI map + drift_profile_uri_map?: Record; + // For Model/Data/Experiment registries - nested structure + interface_metadata?: { + save_metadata?: { + drift_profile_uri_map?: Record; + }; + }; + }; + }; + registryType: RegistryType; +} + +/** + * Extracts the drift profile URI map from metadata based on registry type + * + * Different registry types store drift profile URI maps in different locations: + * - Prompt registries: metadata.metadata.drift_profile_uri_map + * - Model/Data/Experiment registries: metadata.metadata.interface_metadata.save_metadata.drift_profile_uri_map + * + * @param metadata - The metadata object containing drift profile information + * @param registryType - The type of registry (Prompt, Model, Data, or Experiment) + * @returns Record of drift profile URI mappings, empty object if none found + */ +function getDriftProfileUriMap( + metadata: MonitoringParentData["metadata"], + registryType: RegistryType +): Record { + if (registryType === RegistryType.Prompt) { + return metadata.metadata.drift_profile_uri_map ?? {}; + } + + // Model, Data, Experiment registries + return ( + metadata.metadata.interface_metadata?.save_metadata + ?.drift_profile_uri_map ?? {} + ); +} + +/** + * Configuration options for monitoring dashboard data loading + */ +export interface MonitoringDashboardLoadOptions { + /** Initial time interval for metrics, defaults to 6 hours */ + initialTimeInterval?: TimeInterval; + /** Whether to load LLM records for prompt registries, defaults to true */ + loadLLMRecords?: boolean; + /** Whether to load alerts, defaults to true */ + loadAlerts?: boolean; +} + +/** + * Return type for monitoring dashboard data + */ +export interface MonitoringDashboardData { + profiles: DriftProfileResponse; + keys: DriftType[]; + currentName: string; + currentNames: string[]; + currentDriftType: DriftType; + currentProfile: UiProfile; + currentConfig: ReturnType; + latestMetrics: BinnedDriftMap; + currentMetricData: MetricData; + maxDataPoints: number; + currentAlerts: Alert[]; + currentLLMRecords?: any; // Type this more specifically based on your LLM record structure +} + +/** + * Loads monitoring dashboard data for SvelteKit pages + * + * This function encapsulates the common data loading logic for monitoring + * dashboards, providing consistent initialization across different routes. + * + * @param fetch - SvelteKit fetch function for server-side requests + * @param parentData - Data from parent layout loaders + * @param options - Configuration options for data loading + * @returns Promise resolving to monitoring dashboard data + * + * @example + * ```typescript + * // In your +page.ts file + * export const load: PageLoad = async ({ parent, fetch }) => { + * const parentData = await parent(); + * return await loadMonitoringDashboardData(fetch, parentData); + * }; + * ``` + */ +export async function loadMonitoringDashboardData( + fetch: typeof globalThis.fetch, + parentData: MonitoringParentData, + options: MonitoringDashboardLoadOptions = {} +): Promise { + const { + initialTimeInterval = TimeInterval.SixHours, + loadLLMRecords = true, + loadAlerts = true, + } = options; + + const { metadata, registryType } = parentData; + + // Extract drift profile URI map based on registry type + const profileMap = getDriftProfileUriMap(metadata, registryType); + + // Load drift profiles + const profiles = await getMonitoringDriftProfiles( + fetch, + metadata.uid, + profileMap, + registryType + ); + + // Extract and sort available drift types + const keys: DriftType[] = Object.keys(profiles) + .filter((key): key is DriftType => + Object.values(DriftType).includes(key as DriftType) + ) + .sort(); + + // Validate that we have at least one drift type + if (keys.length === 0) { + throw new Error("No valid drift types found in profiles"); + } + + // Initialize current selections with first available drift type + const currentDriftType = keys[0]; + const currentProfile: UiProfile = profiles[currentDriftType]; + const currentNames: string[] = getProfileFeatures( + currentDriftType, + currentProfile.profile + ); + + // Validate that we have at least one feature name + if (currentNames.length === 0) { + throw new Error( + `No feature names found for drift type: ${currentDriftType}` + ); + } + + const currentName: string = currentNames[0]; + const currentConfig = getProfileConfig( + currentDriftType, + currentProfile.profile + ); + const maxDataPoints = getMaxDataPoints(); + + // Load metrics data + const latestMetrics = await getLatestMonitoringMetrics( + fetch, + profiles, + initialTimeInterval, + maxDataPoints + ); + + // Get current metric data for selected drift type and name + const currentMetricData = getCurrentMetricData( + latestMetrics, + currentDriftType, + currentName + ); + + // Load alerts if enabled + const currentAlerts = loadAlerts + ? await getMonitoringAlerts( + fetch, + currentConfig.space, + currentConfig.name, + currentConfig.version, + initialTimeInterval, + true + ) + : []; + + // Load LLM records for prompt registries if enabled + let currentLLMRecords; + if (loadLLMRecords && registryType === RegistryType.Prompt) { + const serviceInfo: ServiceInfo = { + space: currentConfig.space, + name: currentConfig.name, + version: currentConfig.version, + }; + + currentLLMRecords = await getLLMMonitoringRecordPage( + fetch, + serviceInfo, + undefined, + undefined + ); + } + + return { + profiles, + keys, + currentName, + currentNames, + currentDriftType, + currentProfile, + currentConfig, + latestMetrics, + currentMetricData, + maxDataPoints, + currentAlerts, + currentLLMRecords, + }; +} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/llm/LLMRecordTable.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/llm/LLMRecordTable.svelte index 9241e9689..be70fd73e 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/llm/LLMRecordTable.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/llm/LLMRecordTable.svelte @@ -1,7 +1,7 @@
-
LLM Records
+
LLM Records
{#if pageItems.length === 0}
No LLM Drift Records Found
{:else}
-
+ ID + Drift Type + Name + Details + Status + Created At @@ -62,9 +60,9 @@ let {
{alert.id} - {alert.id} + {alert.entity_name}{alert.entity_name}
{alert.created_at}{alert.created_at}
- +
+ - - - - - - - {#each pageItems as record, i} - - - + - + {#if record.prompt} - + {:else} - + {/if} {#if record.context} - + {:else} - + {/if} - - +
+ ID + Status + Score + Prompt + Context + Created At + Processing Duration @@ -116,10 +115,10 @@ let {
{record.id} - + {record.id} + {record.created_at} + {record.created_at} {record.processing_duration !== undefined ? `${record.processing_duration / 1000} seconds` : 'N/A'} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/llm/mocks.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/llm/mocks.ts new file mode 100644 index 000000000..a837c0909 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/llm/mocks.ts @@ -0,0 +1,286 @@ +import { + DriftType, + Status, + type LLMDriftServerRecord, + type LLMPageResponse, + type PaginationCursor, +} from "../types"; +import { AlertThreshold } from "./llm"; +import { type BinnedMetrics } from "../types"; +import { + type Prompt, + Provider, + Role, + ResponseType, +} from "$lib/components/genai/types"; +import type { LLMDriftProfile } from "./llm"; +import type { OpenAIChatSettings } from "$lib/components/genai/settings/openai"; +import type { GeminiSettings } from "$lib/components/genai/settings/gemini"; + +/** + * Mock OpenAI Prompt + */ +export const mockOpenAIPrompt: Prompt = { + message: [ + { + content: { Str: "What is the capital of France?" }, + role: Role.User, + variables: [], + }, + { + content: { Str: "The capital of France is Paris." }, + role: Role.Assistant, + variables: [], + }, + ], + system_instruction: [ + { + content: { Str: "You are a helpful assistant." }, + role: Role.Assistant || "system", + variables: [], + }, + ], + model_settings: { + OpenAIChat: { + max_completion_tokens: 128, + temperature: 0.7, + top_p: 1, + stop_sequences: ["\n"], + audio: { format: "mp3", voice: "en-US-Wavenet-D" }, + tool_choice: { mode: "auto" }, + tools: [], + metadata: { project: "demo" }, + } as OpenAIChatSettings, + }, + model: "gpt-4", + provider: Provider.OpenAI, + version: "1.0.0", + response_json_schema: undefined, + parameters: ["capital", "country"], + response_type: ResponseType.Pydantic, +}; + +/** + * Mock Gemini Prompt + */ +export const mockGeminiPrompt: Prompt = { + message: [ + { + content: { Str: "Summarize the attached document." }, + role: Role.User, + variables: [], + }, + { + content: { Str: "Here is the summary of your document..." }, + role: Role.Assistant, + variables: [], + }, + ], + system_instruction: [ + { + content: { Str: "You are an expert summarizer." }, + role: Role.Assistant || "system", + variables: [], + }, + ], + model_settings: { + GoogleChat: { + labels: { project: "demo-gemini" }, + generation_config: { + temperature: 0.5, + top_p: 0.9, + max_output_tokens: 256, + stop_sequences: [""], + response_modalities: ["Text"], + }, + safety_settings: [ + { + category: "HarmCategoryUnspecified", + threshold: "BlockNone", + }, + ], + } as GeminiSettings, + }, + model: "gemini-pro", + provider: Provider.Gemini, + version: "1.0.0", + response_json_schema: undefined, + parameters: ["summary", "document"], + response_type: ResponseType.Score, +}; + +/** + * Mock LLMDriftProfile for UI development and testing. + * Includes realistic metrics, config, and alert conditions for LLM drift monitoring. + */ +export const mockLLMDriftProfile: LLMDriftProfile = { + metrics: [ + { + name: "toxicity_score", + value: 0.12, + prompt: mockOpenAIPrompt, + alert_condition: { + alert_threshold: AlertThreshold.Above, + alert_threshold_value: 0.1, + }, + }, + { + name: "coherence_score", + value: 0.85, + prompt: mockGeminiPrompt, + alert_condition: { + alert_threshold: AlertThreshold.Below, + alert_threshold_value: 0.8, + }, + }, + ], + metric_names: ["toxicity_score", "coherence_score"], + config: { + sample_rate: 50, + space: "llm-services", + name: "summarizer", + version: "3.0.0", + drift_type: DriftType.LLM, + alert_config: { + dispatch_config: { + Console: { enabled: true }, + Slack: { channel: "#llm-alerts" }, + }, + schedule: "0 */2 * * *", // Every 2 hours + alert_conditions: { + toxicity_score: { + alert_threshold: AlertThreshold.Above, + alert_threshold_value: 0.1, + }, + coherence_score: { + alert_threshold: AlertThreshold.Below, + alert_threshold_value: 0.8, + }, + }, + }, + }, + scouter_version: "2.0.0", +}; + +export const mockLLMMetrics: BinnedMetrics = { + metrics: { + toxicity_score: { + metric: "toxicity_score", + created_at: [ + "2025-03-25 00:43:59", + "2025-03-26 10:00:00", + "2025-03-27 11:00:00", + "2025-03-28 12:00:00", + "2025-03-29 12:00:00", + ], + stats: [ + { + avg: 0.95, + lower_bound: 0.92, + upper_bound: 0.98, + }, + { + avg: 0.94, + lower_bound: 0.91, + upper_bound: 0.97, + }, + { + avg: 0.96, + lower_bound: 0.93, + upper_bound: 0.99, + }, + { + avg: 0.9, + lower_bound: 0.93, + upper_bound: 0.99, + }, + { + avg: 0.4, + lower_bound: 0.93, + upper_bound: 0.99, + }, + ], + }, + coherence_score: { + metric: "coherence_score", + created_at: [ + "2024-03-26T10:00:00", + "2024-03-26T11:00:00", + "2024-03-26T12:00:00", + ], + stats: [ + { + avg: 0.88, + lower_bound: 0.85, + upper_bound: 0.91, + }, + { + avg: 0.87, + lower_bound: 0.84, + upper_bound: 0.9, + }, + { + avg: 0.89, + lower_bound: 0.86, + upper_bound: 0.92, + }, + ], + }, + }, +}; + +function randomStatus(): Status { + const statuses = [ + Status.Pending, + Status.Processing, + Status.Processed, + Status.Failed, + ]; + return statuses[Math.floor(Math.random() * statuses.length)]; +} + +function randomDate(offsetDays: number = 0): string { + const date = new Date(); + date.setDate(date.getDate() - offsetDays); + return date.toISOString(); +} + +export const mockLLMDriftServerRecords: LLMDriftServerRecord[] = Array.from( + { length: 30 }, + (_, i) => ({ + created_at: randomDate(30 - i), + space: `space_${(i % 5) + 1}`, + name: `card_${(i % 10) + 1}`, + version: `v${(i % 3) + 1}.0.${i}`, + prompt: i % 2 === 0 ? `Prompt for card_${(i % 10) + 1}` : undefined, + context: `Context for card_${(i % 10) + 1}`, + status: randomStatus(), + id: i + 1, + uid: `uid_${i + 1}`, + score: { + relevance: { + score: Number(Math.random().toFixed(2)), + reason: + "The prompt is relevant and you should use it!. AHHHHHHHHHHHHHHHHHHHHHHHHH", + }, + coherence: { + score: Number(Math.random().toFixed(2)), + reason: "Sample reason", + }, + }, + updated_at: randomDate(29 - i), + processing_started_at: i % 3 === 0 ? randomDate(30 - i) : undefined, + processing_ended_at: i % 4 === 0 ? randomDate(29 - i) : undefined, + processing_duration: + i % 5 === 0 ? Math.floor(Math.random() * 60) : undefined, + }) +); + +let paginationCursor: PaginationCursor = { + id: mockLLMDriftServerRecords.length, +}; +export const mockLLMDriftPageResponse: LLMPageResponse = { + items: mockLLMDriftServerRecords, + next_cursor: paginationCursor, + has_more: true, +}; diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/mocks.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/mocks.ts new file mode 100644 index 000000000..510a5974b --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/mocks.ts @@ -0,0 +1,163 @@ +import { DriftType } from "./types"; +import { mockPsiDriftProfile } from "./psi/mocks"; +import { mockCustomDriftProfile } from "./custom/mocks"; +import { mockSpcDriftProfile } from "./spc/mocks"; +import { mockLLMDriftProfile } from "./llm/mocks"; +import type { UiProfile } from "./utils"; +import type { Alert } from "./alert/types"; + +/** + * Mock DriftProfileResponse for testing and UI development. + * Each profile uses a realistic mock and a sample URI. + */ +export const mockDriftProfileResponse: Record = { + [DriftType.Spc]: { + profile_uri: "/profiles/spc/mock.json", + profile: { + Spc: mockSpcDriftProfile, + Psi: mockPsiDriftProfile, + Custom: mockCustomDriftProfile, + LLM: mockLLMDriftProfile, + }, + }, + [DriftType.Psi]: { + profile_uri: "/profiles/psi/mock.json", + profile: { + Spc: mockSpcDriftProfile, + Psi: mockPsiDriftProfile, + Custom: mockCustomDriftProfile, + LLM: mockLLMDriftProfile, + }, + }, + [DriftType.Custom]: { + profile_uri: "/profiles/custom/mock.json", + profile: { + Spc: mockSpcDriftProfile, + Psi: mockPsiDriftProfile, + Custom: mockCustomDriftProfile, + LLM: mockLLMDriftProfile, + }, + }, + [DriftType.LLM]: { + profile_uri: "/profiles/llm/mock.json", + profile: { + Spc: mockSpcDriftProfile, + Psi: mockPsiDriftProfile, + Custom: mockCustomDriftProfile, + LLM: mockLLMDriftProfile, + }, + }, +}; + +export const mockAlerts: Alert[] = [ + { + created_at: "2024-03-28 10:30:00", + name: "credit_model", + space: "models", + version: "1.0.0", + entity_name: "credit_score", + alert: { type: "drift_detected", message: "PSI value exceeded threshold" }, + id: 1, + drift_type: "psi", + active: true, + }, + { + created_at: "2024-03-28 09:45:00", + name: "fraud_detection", + space: "models", + version: "2.1.0", + entity_name: "transaction_amount", + alert: { type: "spc_violation", message: "Value outside control limits" }, + id: 2, + drift_type: "spc", + active: true, + }, + { + created_at: "2024-03-28 09:00:00", + name: "customer_churn", + space: "ml_models", + version: "1.2.3", + entity_name: "usage_frequency", + alert: { type: "custom_metric", message: "Metric below threshold" }, + id: 3, + drift_type: "custom", + active: true, + }, + { + created_at: "2024-03-27 23:15:00", + name: "recommendation_engine", + space: "recsys", + version: "3.0.1", + entity_name: "user_engagement", + alert: { type: "drift_detected", message: "Distribution shift detected" }, + id: 4, + drift_type: "psi", + active: false, + }, + { + created_at: "2024-03-27 22:30:00", + name: "credit_model", + space: "models", + version: "1.0.0", + entity_name: "debt_ratio", + alert: { type: "spc_violation", message: "Consecutive points above mean" }, + id: 5, + drift_type: "spc", + active: true, + }, + { + created_at: "2024-03-27 21:45:00", + name: "fraud_detection", + space: "models", + version: "2.1.0", + entity_name: "ip_velocity", + alert: { type: "psi_threshold", message: "PSI above 0.2" }, + id: 6, + drift_type: "psi", + active: true, + }, + { + created_at: "2024-03-27 20:00:00", + name: "price_optimization", + space: "pricing", + version: "1.1.0", + entity_name: "demand_forecast", + alert: { type: "custom_metric", message: "Accuracy below target" }, + id: 7, + drift_type: "custom", + active: false, + }, + { + created_at: "2024-03-27 19:15:00", + name: "customer_churn", + space: "ml_models", + version: "1.2.3", + entity_name: "support_tickets", + alert: { type: "drift_detected", message: "Significant feature drift" }, + id: 8, + drift_type: "psi", + active: true, + }, + { + created_at: "2024-03-27 18:30:00", + name: "recommendation_engine", + space: "recsys", + version: "3.0.1", + entity_name: "click_through_rate", + alert: { type: "spc_violation", message: "Point beyond 3 sigma" }, + id: 9, + drift_type: "spc", + active: true, + }, + { + created_at: "2024-03-27 17:45:00", + name: "price_optimization", + space: "pricing", + version: "1.1.0", + entity_name: "competitor_prices", + alert: { type: "custom_metric", message: "Data freshness warning" }, + id: 10, + drift_type: "custom", + active: true, + }, +]; diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/psi/PsiConfigHeader.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/psi/PsiConfigHeader.svelte index 505f0d074..abeae0b8d 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/psi/PsiConfigHeader.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/psi/PsiConfigHeader.svelte @@ -3,7 +3,7 @@ import type { PsiAlertConfig, PsiDriftConfig } from "./psi"; import Pill from "$lib/components/utils/Pill.svelte"; import UpdateModal from "../update/UpdateModal.svelte"; - import type { UiProfile } from "../util"; + import type { UiProfile } from "../utils"; import { onMount } from "svelte"; import type { RegistryType } from "$lib/utils"; @@ -46,9 +46,7 @@
-
Dispatch:
- {#if hasSlackConfig(alertConfig.dispatch_config)} {/if} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/psi/mocks.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/psi/mocks.ts new file mode 100644 index 000000000..9c01793c4 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/psi/mocks.ts @@ -0,0 +1,105 @@ +import { DriftType } from "../types"; +import { BinType, type PsiDriftProfile } from "./psi"; +import type { BinnedPsiFeatureMetrics } from "../types"; +/** + * Mock PsiDriftProfile for UI development and testing. + * Provides realistic bins, config, and alert settings for PSI drift monitoring. + */ +export const mockPsiDriftProfile: PsiDriftProfile = { + features: { + feature_a: { + id: "feature_a", + bins: [ + { id: 0, lower_limit: 18, upper_limit: 25, proportion: 0.15 }, + { id: 1, lower_limit: 26, upper_limit: 35, proportion: 0.35 }, + { id: 2, lower_limit: 36, upper_limit: 50, proportion: 0.3 }, + { id: 3, lower_limit: 51, upper_limit: null, proportion: 0.2 }, + ], + timestamp: "2025-10-13T08:00:00Z", + bin_type: BinType.Numeric, + }, + feature_b: { + id: "feature_b", + bins: [ + { id: 0, lower_limit: null, upper_limit: null, proportion: 0.55 }, // Male + { id: 1, lower_limit: null, upper_limit: null, proportion: 0.45 }, // Female + ], + timestamp: "2025-10-13T08:00:00Z", + bin_type: BinType.Binary, + }, + }, + config: { + space: "models", + name: "credit_score", + version: "1.0.0", + drift_type: DriftType.Psi, + feature_map: { + features: { + feature_a: { "2025-10-13T08:00:00Z": 30, "2025-10-13T09:00:00Z": 32 }, + feature_b: { + "2025-10-13T08:00:00Z": 0.5, + "2025-10-13T09:00:00Z": 0.52, + }, + }, + }, + targets: ["feature_a", "feature_b"], + alert_config: { + dispatch_config: { + Console: { enabled: true }, + Slack: { channel: "#psi-alerts" }, + }, + schedule: "0 */12 * * *", // Every 12 hours + features_to_monitor: ["feature_a", "feature_b"], + threshold: { + Normal: { alpha: 0.05 }, + }, + }, + }, + scouter_version: "1.2.0", +}; + +export const mockPsiMetrics: BinnedPsiFeatureMetrics = { + features: { + feature_a: { + created_at: [ + "2025-03-25 00:43:59", + "2025-03-26 10:00:00", + "2025-03-27 11:00:00", + "2025-03-28 12:00:00", + "2025-03-29 12:00:00", + ], + psi: [0.05, 0.07, 0.04, 0.1, 0.05], + overall_psi: 0.053, + bins: { + 0: 0.1, + 1: 0.2, + 2: 0.3, + 3: 0.25, + 4: 0.15, + }, + }, + feature_b: { + created_at: [ + "2025-03-25 00:43:59", + "2025-03-25 01:43:59", + "2025-03-25 02:43:59", + "2025-03-25 03:43:59", + "2025-03-25 04:43:59", + "2025-03-25 05:43:59", + "2025-03-25 06:43:59", + "2025-03-25 07:43:59", + "2025-03-25 08:43:59", + "2025-03-25 09:43:59", + ], + psi: [1, 2, 3, 4, 5, 1, 2, 3, 4, 5], + overall_psi: 0.053, + bins: { + 0: 0.1, + 1: 0.2, + 2: 0.3, + 3: 0.25, + 4: 0.15, + }, + }, + }, +}; diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/spc/SpcConfigHeader.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/spc/SpcConfigHeader.svelte index 8091a9c93..66ba1a4dc 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/spc/SpcConfigHeader.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/spc/SpcConfigHeader.svelte @@ -3,7 +3,7 @@ import type { SpcAlertConfig, SpcDriftConfig } from "./spc"; import Pill from "$lib/components/utils/Pill.svelte"; import UpdateModal from "../update/UpdateModal.svelte"; - import type { UiProfile } from "../util"; + import type { UiProfile } from "../utils"; import type { RegistryType } from "$lib/utils"; ; @@ -37,9 +37,8 @@
- -
Dispatch:
- +
Dispatch:
+ {#if hasSlackConfig(alertConfig.dispatch_config)} {/if} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/spc/mocks.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/spc/mocks.ts new file mode 100644 index 000000000..788f417fb --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/spc/mocks.ts @@ -0,0 +1,89 @@ +import { DriftType } from "$lib/components/card/monitoring/types"; +import type { BinnedSpcFeatureMetrics } from "../types"; +import { type SpcDriftProfile } from "./spc"; +import { AlertZone } from "./spc"; + +/** + * Mock SpcDriftProfile for UI development and testing. + * Includes realistic feature drift profiles, config, and alert settings. + */ +export const mockSpcDriftProfile: SpcDriftProfile = { + features: { + feature_a: { + id: "feature_a", + center: 50, + one_ucl: 60, + one_lcl: 40, + two_ucl: 65, + two_lcl: 35, + three_ucl: 70, + three_lcl: 30, + timestamp: "2025-10-13T08:00:00Z", + }, + feature_b: { + id: "feature_b", + center: 100, + one_ucl: 110, + one_lcl: 90, + two_ucl: 115, + two_lcl: 85, + three_ucl: 120, + three_lcl: 80, + timestamp: "2025-10-13T08:00:00Z", + }, + }, + config: { + sample_size: 30, + sample: true, + space: "models", + name: "credit_score", + version: "1.0.0", + drift_type: DriftType.Spc, + feature_map: { + features: { + feature_a: { "2025-10-13T08:00:00Z": 52, "2025-10-13T09:00:00Z": 48 }, + feature_b: { "2025-10-13T08:00:00Z": 102, "2025-10-13T09:00:00Z": 98 }, + }, + }, + targets: ["feature_a", "feature_b"], + alert_config: { + rule: { + rule: "Zone Rule", + zones_to_monitor: [AlertZone.Zone1, AlertZone.Zone2, AlertZone.Zone3], + }, + dispatch_config: { + Console: { enabled: true }, + Slack: { channel: "#alerts" }, + OpsGenie: { team: "ML", priority: "high" }, + }, + schedule: "0 */6 * * *", // Every 6 hours + features_to_monitor: ["feature_a", "feature_b"], + }, + }, + scouter_version: "1.2.0", +}; + +export const mockSpcMetrics: BinnedSpcFeatureMetrics = { + features: { + feature_a: { + created_at: [ + "2025-03-25 00:43:59", + "2025-03-26 10:00:00", + "2025-03-27 11:00:00", + "2025-03-28 12:00:00", + "2025-03-29 12:00:00", + ], + values: [52, 48, 47, 49, 51], + }, + feature_b: { + created_at: [ + "2025-03-25 00:43:59", + "2025-03-26 10:00:00", + "2025-03-27 11:00:00", + "2025-03-28 12:00:00", + "2025-03-29 12:00:00", + ], + values: [100, 105, 200, 300, 101], + }, + }, +}; diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/types.ts b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/types.ts index 521c33c13..a74d2da61 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/types.ts +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/types.ts @@ -1,5 +1,4 @@ import type { RegistryType } from "$lib/utils"; -import { string } from "zod"; export enum AlertDispatchType { Slack = "Slack", @@ -14,6 +13,12 @@ export enum DriftType { LLM = "LLM", } +export interface DriftProfileUri { + root_dir: string; + uri: string; + drift_type: DriftType; +} + export interface FeatureMap { features: Record>; } diff --git a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/update/UpdateModal.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/update/UpdateModal.svelte index d208918fd..d0bd6852b 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/update/UpdateModal.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/monitoring/update/UpdateModal.svelte @@ -1,12 +1,11 @@ @@ -49,6 +47,3 @@
- - - \ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/card/+page.svelte b/crates/opsml_server/opsml_ui/src/lib/components/card/service/ServicePage.svelte similarity index 91% rename from crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/card/+page.svelte rename to crates/opsml_server/opsml_ui/src/lib/components/card/service/ServicePage.svelte index e0a23cfbd..a3384a58d 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/card/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/card/service/ServicePage.svelte @@ -1,16 +1,15 @@ -
+
-
+
{#each splitPath as path, index} {#if index < 3} @@ -68,7 +68,7 @@
-
+
diff --git a/crates/opsml_server/opsml_ui/src/lib/components/files/FileViewer.svelte b/crates/opsml_server/opsml_ui/src/lib/components/files/FileViewer.svelte index 0f09bfc02..e66104062 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/files/FileViewer.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/files/FileViewer.svelte @@ -4,8 +4,6 @@ import ImageViewer from './ImageViewer.svelte'; import MarkdownViewer from './MarkdownViewer.svelte'; import CodeViewer from './CodeViewer.svelte'; - import CodeBlock from "$lib/components/codeblock/CodeBlock.svelte"; - import { Code } from "lucide-svelte"; /** * Main file viewer component that routes to appropriate specialized viewers diff --git a/crates/opsml_server/opsml_ui/src/lib/components/files/fileTreeLoader.ts b/crates/opsml_server/opsml_ui/src/lib/components/files/fileTreeLoader.ts new file mode 100644 index 000000000..a9e68afc9 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/files/fileTreeLoader.ts @@ -0,0 +1,42 @@ +import { getFileTree } from "$lib/server/card/files/utils"; +import { getRegistryPath, getRegistryTableName } from "$lib/utils"; + +/** + * Loads file tree and navigation metadata for a card file route. + * Encapsulates slug parsing, registry path construction, and file tree retrieval. + * @param parent - SvelteKit parent loader function + * @param params - Route parameters + * @param fetch - SvelteKit fetch function + * @returns File tree, previous path, and root indicator + */ +export async function loadFileTree({ + parent, + params, + fetch, +}: { + parent: () => Promise<{ metadata: any; registryType: string }>; + params: { file: string }; + fetch: typeof globalThis.fetch; +}) { + const slug = params.file as string; + const slugs = slug.split("/"); + + const { metadata, registryType } = await parent(); + + // @ts-ignore + const tableName = getRegistryTableName(registryType); + let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; + + // @ts-ignore + const previousPath = `/opsml/${getRegistryPath(registryType)}/card/${ + metadata.space + }/${metadata.name}/${metadata.version}/files/${slugs + .slice(0, slugs.length - 1) + .join("/")}`.replace(/\/$/, ""); + + basePath = `${basePath}/${slugs.join("/")}`; + + const fileTree = await getFileTree(fetch, basePath); + + return { fileTree, previousPath, isRoot: false }; +} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/files/utils.ts b/crates/opsml_server/opsml_ui/src/lib/components/files/utils.ts index b4857394f..e8f4e7b9f 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/files/utils.ts +++ b/crates/opsml_server/opsml_ui/src/lib/components/files/utils.ts @@ -1,22 +1,4 @@ -import { opsmlClient } from "$lib/components/api/client.svelte"; -import { RoutePaths } from "$lib/components/api/routes"; -import type { FileTreeResponse, RawFile, RawFileRequest } from "./types"; import { AcceptableSuffix } from "./types"; -import { userStore } from "../user/user.svelte"; -import type { RegistryType } from "$lib/utils"; - -export async function getFileTree(path: string): Promise { - const params = { - path: path, - }; - - const response = await opsmlClient.get( - RoutePaths.FILE_TREE, - params, - userStore.jwt_token - ); - return (await response.json()) as FileTreeResponse; -} export function timeAgo(timestamp: string): string { let date: Date; @@ -115,25 +97,6 @@ export function isAcceptableSuffix(suffix: string): boolean { ); } -export async function getRawFile( - path: string, - uid: string, - registry_type: RegistryType -): Promise { - const body: RawFileRequest = { - path: path, - uid: uid, - registry_type: registry_type, - }; - - const response = await opsmlClient.post( - RoutePaths.FILE_CONTENT, - body, - userStore.jwt_token - ); - return (await response.json()) as RawFile; -} - function splitViewPath(path: string): string[] { let splitPath = path.split("/"); return splitPath; diff --git a/crates/opsml_server/opsml_ui/src/lib/components/genai/mock.ts b/crates/opsml_server/opsml_ui/src/lib/components/genai/mock.ts new file mode 100644 index 000000000..d5e642175 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/genai/mock.ts @@ -0,0 +1,95 @@ +import { Provider, ResponseType, Role, type Prompt } from "./types"; +import type { OpenAIChatSettings } from "./settings/openai"; +import type { GeminiSettings } from "./settings/gemini"; + +/** + * Mock OpenAI Prompt + */ +export const mockOpenAIPrompt: Prompt = { + message: [ + { + content: { Str: "What is the capital of France?" }, + role: Role.User, + variables: [], + }, + { + content: { Str: "The capital of France is Paris." }, + role: Role.Assistant, + variables: [], + }, + ], + system_instruction: [ + { + content: { Str: "You are a helpful assistant." }, + role: Role.Assistant || "system", + variables: [], + }, + ], + model_settings: { + OpenAIChat: { + max_completion_tokens: 128, + temperature: 0.7, + top_p: 1, + stop_sequences: ["\n"], + audio: { format: "mp3", voice: "en-US-Wavenet-D" }, + tool_choice: { mode: "auto" }, + tools: [], + metadata: { project: "demo" }, + } as OpenAIChatSettings, + }, + model: "gpt-4", + provider: Provider.OpenAI, + version: "1.0.0", + response_json_schema: undefined, + parameters: ["capital", "country"], + response_type: ResponseType.Pydantic, +}; + +/** + * Mock Gemini Prompt + */ +export const mockGeminiPrompt: Prompt = { + message: [ + { + content: { Str: "Summarize the attached document." }, + role: Role.User, + variables: [], + }, + { + content: { Str: "Here is the summary of your document..." }, + role: Role.Assistant, + variables: [], + }, + ], + system_instruction: [ + { + content: { Str: "You are an expert summarizer." }, + role: Role.System || "system", + variables: [], + }, + ], + model_settings: { + GoogleChat: { + labels: { project: "demo-gemini" }, + generation_config: { + temperature: 0.5, + top_p: 0.9, + max_output_tokens: 256, + stop_sequences: [""], + response_modalities: ["Text"], + }, + safety_settings: [ + { + category: "HarmCategoryUnspecified", + threshold: "BlockNone", + }, + ], + } as GeminiSettings, + }, + model: "gemini-pro", + provider: Provider.Gemini, + version: "1.0.0", + response_json_schema: undefined, + parameters: ["summary", "document"], + response_type: ResponseType.Score, +}; diff --git a/crates/opsml_server/opsml_ui/src/lib/components/genai/settings/gemini.ts b/crates/opsml_server/opsml_ui/src/lib/components/genai/settings/gemini.ts new file mode 100644 index 000000000..9741a5d5e --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/genai/settings/gemini.ts @@ -0,0 +1,196 @@ +/** + * Gemini model settings and supporting types. + * Strictly typed for SSR, hydration, and maintainability in SvelteKit. + */ + +export enum SchemaType { + TypeUnspecified = "TypeUnspecified", + String = "String", + Number = "Number", + Integer = "Integer", + Boolean = "Boolean", + Array = "Array", + Object = "Object", + Null = "Null", +} + +export interface Schema { + type?: SchemaType; + format?: string; + title?: string; + description?: string; + nullable?: boolean; + default?: unknown; + items?: Schema; + min_items?: string; + max_items?: string; + enum?: string[]; + properties?: Record; + property_ordering?: string[]; + required?: string[]; + min_properties?: string; + max_properties?: string; + minimum?: number; + maximum?: number; + min_length?: string; + max_length?: string; + pattern?: string; + example?: unknown; + any_of?: Schema[]; + additional_properties?: unknown; + ref_path?: string; + defs?: Record; +} + +export enum HarmCategory { + HarmCategoryUnspecified = "HarmCategoryUnspecified", + HarmCategoryHateSpeech = "HarmCategoryHateSpeech", + HarmCategoryDangerousContent = "HarmCategoryDangerousContent", + HarmCategoryHarassment = "HarmCategoryHarassment", + HarmCategorySexuallyExplicit = "HarmCategorySexuallyExplicit", + HarmCategoryImageHate = "HarmCategoryImageHate", + HarmCategoryImageDangerousContent = "HarmCategoryImageDangerousContent", + HarmCategoryImageHarassment = "HarmCategoryImageHarassment", + HarmCategoryImageSexuallyExplicit = "HarmCategoryImageSexuallyExplicit", +} + +export enum HarmBlockThreshold { + HarmBlockThresholdUnspecified = "HarmBlockThresholdUnspecified", + BlockLowAndAbove = "BlockLowAndAbove", + BlockMediumAndAbove = "BlockMediumAndAbove", + BlockOnlyHigh = "BlockOnlyHigh", + BlockNone = "BlockNone", + Off = "Off", +} + +export enum HarmBlockMethod { + HarmBlockMethodUnspecified = "HarmBlockMethodUnspecified", + Severity = "Severity", + Probability = "Probability", +} + +export interface SafetySetting { + category: HarmCategory; + threshold: HarmBlockThreshold; + method?: HarmBlockMethod; +} + +export enum Modality { + ModalityUnspecified = "ModalityUnspecified", + Text = "Text", + Image = "Image", + Audio = "Audio", +} + +export enum MediaResolution { + MediaResolutionUnspecified = "MediaResolutionUnspecified", + MediaResolutionLow = "MediaResolutionLow", + MediaResolutionMedium = "MediaResolutionMedium", + MediaResolutionHigh = "MediaResolutionHigh", +} + +export enum ModelRoutingPreference { + Unknown = "Unknown", + PrioritizeQuality = "PrioritizeQuality", + Balanced = "Balanced", + PrioritizeCost = "PrioritizeCost", +} + +export interface ThinkingConfig { + include_thoughts?: boolean; + thinking_budget?: number; +} + +export interface AutoRoutingMode { + model_routing_preference?: ModelRoutingPreference; +} + +export interface ManualRoutingMode { + model_name: string; +} + +export type RoutingConfigMode = AutoRoutingMode | ManualRoutingMode; + +export interface RoutingConfig { + routing_config: RoutingConfigMode; +} + +export interface PrebuiltVoiceConfig { + voice_name: string; +} + +export type VoiceConfigMode = PrebuiltVoiceConfig; + +export interface VoiceConfig { + voice_config: VoiceConfigMode; +} + +export interface SpeechConfig { + voice_config?: VoiceConfig; + language_code?: string; +} + +export interface GenerationConfig { + stop_sequences?: string[]; + response_mime_type?: string; + response_modalities?: Modality[]; + thinking_config?: ThinkingConfig; + temperature?: number; + top_p?: number; + top_k?: number; + candidate_count?: number; + max_output_tokens?: number; + response_logprobs?: boolean; + logprobs?: number; + presence_penalty?: number; + frequency_penalty?: number; + seed?: number; + response_schema?: Schema; + response_json_schema?: unknown; + routing_config?: RoutingConfig; + audio_timestamp?: boolean; + media_resolution?: MediaResolution; + speech_config?: SpeechConfig; + enable_affective_dialog?: boolean; +} + +export interface ModelArmorConfig { + prompt_template_name?: string; + response_template_name?: string; +} + +export enum Mode { + ModeUnspecified = "ModeUnspecified", + Any = "Any", + Auto = "Auto", + None = "None", +} + +export interface FunctionCallingConfig { + mode?: Mode; + allowed_function_names?: string[]; +} + +export interface LatLng { + latitude: number; + longitude: number; +} + +export interface RetrievalConfig { + lat_lng: LatLng; + language_code: string; +} + +export interface ToolConfig { + function_calling_config?: FunctionCallingConfig; + retrieval_config?: RetrievalConfig; +} + +export interface GeminiSettings { + labels?: Record; + tool_config?: ToolConfig; + generation_config?: GenerationConfig; + safety_settings?: SafetySetting[]; + model_armor_config?: ModelArmorConfig; + extra_body?: unknown; +} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/genai/settings/openai.ts b/crates/opsml_server/opsml_ui/src/lib/components/genai/settings/openai.ts new file mode 100644 index 000000000..d737559d0 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/genai/settings/openai.ts @@ -0,0 +1,166 @@ +/** + * OpenAIChatSettings and supporting types. + * Strictly typed for SvelteKit SSR and hydration. + */ + +// Audio parameter settings for model +export interface AudioParam { + format: string; + voice: string; +} + +// Content part for multi-part content +export interface ContentPart { + type: string; + text: string; +} + +// Content can be a simple string or an array of parts +export type Content = string | ContentPart[]; + +// Prediction output type +export interface Prediction { + type: string; + content: Content; +} + +// Streaming options for model output +export interface StreamOptions { + include_obfuscation?: boolean; + include_usage?: boolean; +} + +// Tool choice modes +export type ToolChoiceMode = "none" | "auto" | "required"; + +// Function tool choice specification +export interface FunctionChoice { + name: string; +} + +// Function tool choice object +export interface FunctionToolChoice { + type: "function"; + function: FunctionChoice; +} + +// Custom tool choice specification +export interface CustomChoice { + name: string; +} + +// Custom tool choice object +export interface CustomToolChoice { + type: "custom"; + custom: CustomChoice; +} + +// Tool definition for allowed tools +export interface ToolDefinition { + type: "function"; + function: FunctionChoice; +} + +// Allowed tools mode +export type AllowedToolsMode = "auto" | "required"; + +// Inner allowed tools configuration +export interface InnerAllowedTools { + mode: AllowedToolsMode; + tools: ToolDefinition[]; +} + +// Allowed tools configuration +export interface AllowedTools { + type: "allowed_tools"; + allowed_tools: InnerAllowedTools; +} + +// Tool choice union +export type ToolChoice = + | { mode: ToolChoiceMode } + | FunctionToolChoice + | CustomToolChoice + | AllowedTools; + +// Function definition for tool +export interface FunctionDefinition { + name: string; + description?: string; + parameters?: unknown; + strict?: boolean; +} + +// Function tool for model +export interface FunctionTool { + function: FunctionDefinition; + type: "function"; +} + +// Text format for custom tool +export interface TextFormat { + type: string; +} + +// Grammar format for custom tool +export interface Grammar { + definition: string; + syntax: string; +} + +export interface GrammarFormat { + grammar: Grammar; + type: string; +} + +// Custom tool format union +export type CustomToolFormat = TextFormat | GrammarFormat; + +// Custom tool definition +export interface CustomDefinition { + name: string; + description?: string; + format?: CustomToolFormat; +} + +// Custom tool for model +export interface CustomTool { + custom: CustomDefinition; + type: "custom"; +} + +// Tool union +export type Tool = FunctionTool | CustomTool; + +// OpenAIChatSettings main interface +export interface OpenAIChatSettings { + max_completion_tokens?: number; + temperature?: number; + top_p?: number; + top_k?: number; + frequency_penalty?: number; + timeout?: number; + parallel_tool_calls?: boolean; + seed?: number; + logit_bias?: Record; + stop_sequences?: string[]; + logprobs?: boolean; + audio?: AudioParam; + metadata?: Record; + modalities?: string[]; + n?: number; + prediction?: Prediction; + presence_penalty?: number; + prompt_cache_key?: string; + reasoning_effort?: string; + safety_identifier?: string; + service_tier?: string; + store?: boolean; + stream?: boolean; + stream_options?: StreamOptions; + tool_choice?: ToolChoice; + tools?: Tool[]; + top_logprobs?: number; + verbosity?: string; + extra_body?: unknown; +} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/genai/types.ts b/crates/opsml_server/opsml_ui/src/lib/components/genai/types.ts new file mode 100644 index 000000000..493f9f260 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/lib/components/genai/types.ts @@ -0,0 +1,107 @@ +import type { GeminiSettings } from "./settings/gemini"; +import type { OpenAIChatSettings } from "./settings/openai"; + +/** + * Provider enum for model sources. + */ +export enum Provider { + OpenAI = "OpenAI", + Gemini = "Gemini", + Google = "Google", + Vertex = "Vertex", + Undefined = "Undefined", +} + +/** + * ResponseType enum for prompt responses. + */ +export enum ResponseType { + Score = "Score", + Pydantic = "Pydantic", + Null = "Null", +} + +/** + * Audio URL type for audio content. + */ +export interface AudioUrl { + url: string; + kind: "audio-url"; +} + +/** + * Image URL type for image content. + */ +export interface ImageUrl { + url: string; + kind: "image-url"; +} + +/** + * Document URL type for document content. + */ +export interface DocumentUrl { + url: string; + kind: "document-url"; +} + +/** + * Binary content type for arbitrary media. + */ +export interface BinaryContent { + data: Uint8Array; + media_type: string; + kind: "binary"; +} + +/** + * PromptContent union for all supported content types. + */ +export type PromptContent = + | { Str: string } + | { Audio: AudioUrl } + | { Image: ImageUrl } + | { Document: DocumentUrl } + | { Binary: BinaryContent }; + +/** + * Role enum for message sender/receiver. + */ +export enum Role { + User = "user", + Assistant = "assistant", + Developer = "developer", + Tool = "tool", + Model = "model", +} + +/** + * Message structure for prompt conversations. + */ +export interface Message { + content: PromptContent; + role: Role | string; + variables: string[]; +} + +/** + * ModelSettings union for supported providers. + */ +export type ModelSettings = + | { OpenAIChat: OpenAIChatSettings } + | { GoogleChat: GeminiSettings }; + +/** + * Main Prompt interface for LLM requests. + */ +export interface Prompt { + message: Message[]; + system_instruction: Message[]; + model_settings: ModelSettings; + model: string; + provider: Provider; + version: string; + response_json_schema?: unknown; + parameters: string[]; + response_type: ResponseType; +} diff --git a/crates/opsml_server/opsml_ui/src/lib/components/nav/Navbar.svelte b/crates/opsml_server/opsml_ui/src/lib/components/nav/Navbar.svelte index c8640c626..6f9fcdd93 100644 --- a/crates/opsml_server/opsml_ui/src/lib/components/nav/Navbar.svelte +++ b/crates/opsml_server/opsml_ui/src/lib/components/nav/Navbar.svelte @@ -42,22 +42,6 @@ - -{#if isSidebarOpen} - -{/if} - - - \ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/data/profile/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/data/profile/+server.ts new file mode 100644 index 000000000..58be93062 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/data/profile/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { getDataProfile } from "$lib/server/data/utils"; + +/** Get a page of LLM monitoring records + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { card } = await request.json(); + const response = await getDataProfile(fetch, card); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/hardware/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/hardware/+server.ts new file mode 100644 index 000000000..684d8f2bc --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/hardware/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { getHardwareMetrics } from "$lib/server/experiment/utils"; + +/** Get the hardware metrics for a set of experiments + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { uid } = await request.json(); + const response = await getHardwareMetrics(fetch, uid); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/metrics/grouped/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/metrics/grouped/+server.ts new file mode 100644 index 000000000..4fed2cc92 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/metrics/grouped/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { getGroupedMetrics } from "$lib/server/experiment/utils"; + +/** Get the grouped metrics for a set of experiments + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { experiments, selectedMetrics } = await request.json(); + const response = await getGroupedMetrics(fetch, experiments, selectedMetrics); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/metrics/names/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/metrics/names/+server.ts new file mode 100644 index 000000000..84d210382 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/metrics/names/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { getCardMetricNames } from "$lib/server/experiment/utils"; + +/** Get the metric names for a given experiment + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { uid } = await request.json(); + const response = await getCardMetricNames(fetch, uid); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/recent/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/recent/+server.ts new file mode 100644 index 000000000..c93f72f49 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/experiment/recent/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { getRecentExperiments } from "$lib/server/experiment/utils"; + +/** Get recent experiments for a space and name + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { space, name, version } = await request.json(); + const response = await getRecentExperiments(fetch, space, name, version); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/alerts/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/alerts/+server.ts new file mode 100644 index 000000000..de80bde32 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/alerts/+server.ts @@ -0,0 +1,17 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { getDriftAlerts } from "$lib/server/card/monitoring/utils"; + +/** Get a page of recent drift alerts + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { space, name, version, timeInterval, active } = await request.json(); + const response = await getDriftAlerts( + fetch, + space, + name, + version, + timeInterval, + active + ); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/alerts/acknowledge/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/alerts/acknowledge/+server.ts new file mode 100644 index 000000000..bfe0bfb0d --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/alerts/acknowledge/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { acknowledgeAlert } from "$lib/server/card/monitoring/utils"; + +/** Get a page of recent drift alerts + */ +export const PUT: RequestHandler = async ({ request, fetch }) => { + const { id, space } = await request.json(); + const response = await acknowledgeAlert(fetch, id, space); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/llm/record/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/llm/record/+server.ts new file mode 100644 index 000000000..f4301d96f --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/llm/record/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { getLLMRecordPage } from "$lib/server/card/monitoring/utils"; + +/** Get a page of LLM monitoring records + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { service_info, status, cursor } = await request.json(); + const response = await getLLMRecordPage(fetch, service_info, status, cursor); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/metrics/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/metrics/+server.ts new file mode 100644 index 000000000..af53b7c27 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/metrics/+server.ts @@ -0,0 +1,15 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { getLatestMetrics } from "$lib/server/card/monitoring/utils"; + +/** Get a page of latest metrics for drift profiles + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { profiles, time_interval, max_data_points } = await request.json(); + const response = await getLatestMetrics( + fetch, + profiles, + time_interval, + max_data_points + ); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/profile/update/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/profile/update/+server.ts new file mode 100644 index 000000000..8cca1ad34 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/profile/update/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { updateDriftProfile } from "$lib/server/card/monitoring/utils"; + +/** Get a page of recent drift alerts + */ +export const PUT: RequestHandler = async ({ request, fetch }) => { + const { update_request } = await request.json(); + const response = await updateDriftProfile(fetch, update_request); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/profiles/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/profiles/+server.ts new file mode 100644 index 000000000..4a28a60e6 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/monitoring/profiles/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { getDriftProfiles } from "$lib/server/card/monitoring/utils"; + +/** Get a page of latest metrics for drift profiles + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { uid, driftMap, registryType } = await request.json(); + const response = await getDriftProfiles(fetch, uid, driftMap, registryType); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/readme/create/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/readme/create/+server.ts new file mode 100644 index 000000000..fcb90eb20 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/readme/create/+server.ts @@ -0,0 +1,17 @@ +import type { RequestHandler } from "./$types"; +import { json } from "@sveltejs/kit"; +import { createReadMe } from "$lib/server/card/readme/utils"; + +/** Get Card Metadata + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { space, name, registry_type, content } = await request.json(); + const response = await createReadMe( + space, + name, + registry_type, + content, + fetch + ); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/registry/version/page/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/registry/version/page/+server.ts new file mode 100644 index 000000000..433294fa9 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/registry/version/page/+server.ts @@ -0,0 +1,17 @@ +import type { RequestHandler } from "../$types"; +import { getVersionPage } from "$lib/server/card/utils"; +import { json } from "@sveltejs/kit"; + +/** Get a page of versions for a registry item + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { registry_type, space, name, page } = await request.json(); + const response = await getVersionPage( + fetch, + registry_type, + space, + name, + page + ); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/card/uid/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/card/uid/+server.ts new file mode 100644 index 000000000..90cd34484 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/card/uid/+server.ts @@ -0,0 +1,19 @@ +import { json } from "@sveltejs/kit"; +import type { RequestHandler } from "./$types"; +import { getCardfromUid } from "$lib/server/card/utils"; +import { RegistryType } from "$lib/utils"; + +/** + * Server route to fetch card information by UID. + * Client should provide the UID in the request. + */ +export const GET: RequestHandler = async ({ fetch, url }) => { + let registry_type: RegistryType = url.searchParams.get( + "registry_type" + ) as RegistryType; + let uid = url.searchParams.get("uid") || ""; + let response = await getCardfromUid(registry_type, uid, fetch); + + // Return card info to client for further handling + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/health/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/health/+server.ts new file mode 100644 index 000000000..2cb18b091 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/health/+server.ts @@ -0,0 +1,7 @@ +// Health check endpoint + +import type { RequestHandler } from "@sveltejs/kit"; + +export const GET: RequestHandler = async () => { + return new Response("OK", { status: 200 }); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/space/create/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/space/create/+server.ts new file mode 100644 index 000000000..516f40e6a --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/space/create/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { createSpace } from "$lib/server/space/utils"; + +/** Get a page of LLM monitoring records + */ +export const POST: RequestHandler = async ({ request, fetch }) => { + const { space, description } = await request.json(); + const response = await createSpace(fetch, space, description); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/user/info/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/user/info/+server.ts index 47eb3952e..dfee6448e 100644 --- a/crates/opsml_server/opsml_ui/src/routes/api/user/info/+server.ts +++ b/crates/opsml_server/opsml_ui/src/routes/api/user/info/+server.ts @@ -1,6 +1,6 @@ import type { RequestHandler } from "./$types"; import { json } from "@sveltejs/kit"; -import { getUser } from "$lib/server/user/util"; +import { getUser } from "$lib/server/user/utils"; import { logger } from "$lib/server/logger"; /** @@ -14,7 +14,7 @@ export const GET: RequestHandler = async ({ cookies, fetch }) => { if (jwt_token && username) { try { - const user = await getUser(username, fetch, jwt_token); + const user = await getUser(username, fetch); return json({ success: true, user }); } catch (error) { logger.error(`Error fetching user info: ${error}`); diff --git a/crates/opsml_server/opsml_ui/src/routes/api/user/logout/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/user/logout/+server.ts index 7672bda08..8793019f4 100644 --- a/crates/opsml_server/opsml_ui/src/routes/api/user/logout/+server.ts +++ b/crates/opsml_server/opsml_ui/src/routes/api/user/logout/+server.ts @@ -7,14 +7,13 @@ import { createOpsmlClient } from "$lib/server/api/opsmlClient"; /** * Helper function for logging out a user via the api client - * @param jwt_token - JWT token of the user to log out * @returns */ async function logout( fetch: typeof globalThis.fetch, jwt_token?: string ): Promise { - let opsmlClient = createOpsmlClient(fetch, jwt_token); + let opsmlClient = createOpsmlClient(fetch); let path = `${RoutePaths.LOGOUT}`; const response = await opsmlClient.get(path, undefined); return (await response.json()) as LogOutResponse; @@ -25,7 +24,7 @@ async function logout( * Only authentication and cookie logic here; UI navigation is handled client-side. */ export const GET: RequestHandler = async ({ cookies, fetch }) => { - let loggedOut = await logout(fetch, cookies.get("jwt_token")); + let loggedOut = await logout(fetch); if (loggedOut) { // update the user store diff --git a/crates/opsml_server/opsml_ui/src/routes/api/user/register/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/user/register/+server.ts index 01ad6c138..72e0704e2 100644 --- a/crates/opsml_server/opsml_ui/src/routes/api/user/register/+server.ts +++ b/crates/opsml_server/opsml_ui/src/routes/api/user/register/+server.ts @@ -1,5 +1,5 @@ import type { RequestHandler } from "./$types"; -import { registerUser } from "$lib/server/user/util"; +import { registerUser } from "$lib/server/user/utils"; import { json } from "@sveltejs/kit"; import { logger } from "$lib/server/logger"; @@ -10,6 +10,5 @@ import { logger } from "$lib/server/logger"; export const POST: RequestHandler = async ({ request, cookies, fetch }) => { const { username, password, email } = await request.json(); const response = await registerUser(username, password, email, fetch); - logger.debug(`User registration response: ${JSON.stringify(response)}`); return json(response); }; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/user/reset/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/user/reset/+server.ts index dbc272124..3249633ae 100644 --- a/crates/opsml_server/opsml_ui/src/routes/api/user/reset/+server.ts +++ b/crates/opsml_server/opsml_ui/src/routes/api/user/reset/+server.ts @@ -1,20 +1,18 @@ import type { RequestHandler } from "./$types"; -import { resetUserPassword } from "$lib/server/user/util"; +import { resetUserPassword } from "$lib/server/user/utils"; import { json } from "@sveltejs/kit"; /** * Handles login requests and sets JWT cookie. * Only authentication and cookie logic here; UI navigation is handled client-side. */ -export const POST: RequestHandler = async ({ request, cookies, fetch }) => { +export const POST: RequestHandler = async ({ request, fetch }) => { const { username, recovery_code, new_password } = await request.json(); - const jwt_token = cookies.get("jwt_token"); const response = await resetUserPassword( username, recovery_code, new_password, - fetch, - jwt_token + fetch ); return json(response); }; diff --git a/crates/opsml_server/opsml_ui/src/routes/api/user/sso/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/user/sso/+server.ts index 85b8b391e..5353ac9ef 100644 --- a/crates/opsml_server/opsml_ui/src/routes/api/user/sso/+server.ts +++ b/crates/opsml_server/opsml_ui/src/routes/api/user/sso/+server.ts @@ -1,18 +1,15 @@ import { json } from "@sveltejs/kit"; import type { RequestHandler } from "./$types"; -import { getSsoAuthURL } from "$lib/server/user/util"; +import { getSsoAuthURL } from "$lib/server/user/utils"; import { logger } from "$lib/server/logger"; /** * Server route to fetch SSO authentication URL and related state. * Client should store state/code_verifier and perform redirect. */ -export const GET: RequestHandler = async ({ fetch, cookies }) => { - // Optionally, extract JWT from cookies if needed for SSO - const jwt_token = cookies.get("jwt_token"); - +export const GET: RequestHandler = async ({ fetch, url }) => { // Fetch SSO auth URL and related info from backend - const ssoAuthUrl = await getSsoAuthURL(fetch, jwt_token); + const ssoAuthUrl = await getSsoAuthURL(fetch); // Log the SSO URL fetch action logger.debug(`Fetched SSO authentication URL. ${JSON.stringify(ssoAuthUrl)}`); diff --git a/crates/opsml_server/opsml_ui/src/routes/api/user/sso/callback/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/user/sso/callback/+server.ts index 3e387563b..e03cbc1d6 100644 --- a/crates/opsml_server/opsml_ui/src/routes/api/user/sso/callback/+server.ts +++ b/crates/opsml_server/opsml_ui/src/routes/api/user/sso/callback/+server.ts @@ -1,6 +1,6 @@ import { json } from "@sveltejs/kit"; import type { RequestHandler } from "./$types"; -import { exchangeSsoCallbackCode } from "$lib/server/user/util"; +import { exchangeSsoCallbackCode } from "$lib/server/user/utils"; import { setTokenInCookies, setUsernameInCookies, @@ -8,12 +8,10 @@ import { export const POST: RequestHandler = async ({ request, fetch, cookies }) => { const { code, code_verifier } = await request.json(); - const jwt_token = cookies.get("jwt_token"); const loginResponse = await exchangeSsoCallbackCode( code, code_verifier, - fetch, - jwt_token + fetch ); setTokenInCookies(cookies, loginResponse.jwt_token); setUsernameInCookies(cookies, loginResponse.username); diff --git a/crates/opsml_server/opsml_ui/src/routes/api/user/update/+server.ts b/crates/opsml_server/opsml_ui/src/routes/api/user/update/+server.ts new file mode 100644 index 000000000..12c2d39c4 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/api/user/update/+server.ts @@ -0,0 +1,10 @@ +import { type RequestHandler, json } from "@sveltejs/kit"; +import { updateUser } from "$lib/server/user/utils"; + +/** Update user details + */ +export const PUT: RequestHandler = async ({ request, fetch }) => { + const { options, username } = await request.json(); + const response = await updateUser(fetch, options, username); + return json(response); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+layout.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+layout.server.ts new file mode 100644 index 000000000..25b1938d0 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+layout.server.ts @@ -0,0 +1,9 @@ +import type { LayoutServerLoad } from "./$types"; +import { getRegistryFromString } from "$lib/utils"; + +export const load: LayoutServerLoad = async ({ params }) => { + let registryType = getRegistryFromString(params.registry); + return { + registryType, + }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+layout.svelte similarity index 69% rename from crates/opsml_server/opsml_ui/src/routes/opsml/data/+layout.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+layout.svelte index c934f0101..f92199147 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/+layout.svelte +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+layout.svelte @@ -1,10 +1,13 @@
+ {#key page.params.registry} {@render children()} + {/key}
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+page.server.ts new file mode 100644 index 000000000..ea4ee2b16 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+page.server.ts @@ -0,0 +1,34 @@ +import { setupRegistryPage } from "$lib/server/card/utils"; +import { getRegistryFromString, RegistryType } from "$lib/utils"; +import { redirect } from "@sveltejs/kit"; +import type { PageServerLoad, EntryGenerator } from "./$types"; + +export const entries: EntryGenerator = () => { + return [ + { registry: "model" }, + { registry: "data" }, + { registry: "experiment" }, + { registry: "service" }, + ]; +}; + +export const load: PageServerLoad = async ({ parent, fetch }) => { + let { registryType } = await parent(); + + if (!registryType) { + throw redirect(307, "/opsml/home"); + } + + let registryPage = await setupRegistryPage( + registryType, + undefined, + undefined, + fetch + ); + + return { + page: registryPage, + selectedSpace: undefined, + selectedName: undefined, + }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+page.svelte new file mode 100644 index 000000000..ebbc454d9 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/+page.svelte @@ -0,0 +1,15 @@ + + + + + + diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/[uid]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/[uid]/+page.server.ts new file mode 100644 index 000000000..80de0f0d4 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/[uid]/+page.server.ts @@ -0,0 +1,32 @@ +import { redirect } from "@sveltejs/kit"; +import type { PageServerLoad } from "./$types"; +import { getRegistryTypeLowerCase } from "$lib/utils"; +import type { ServiceCard } from "$lib/components/card/card_interfaces/servicecard"; +import { getCardMetadata } from "$lib/server/card/utils"; +import type { BaseCard } from "$lib/components/home/types"; + +export const load: PageServerLoad = async ({ parent, params, fetch }) => { + let { registryType } = await parent(); + + console.log("uid param:", params.uid); + + if (!registryType) { + throw redirect(307, "/opsml/home"); + } + + let metadata = (await getCardMetadata( + undefined, + undefined, + undefined, + params.uid, + registryType, + fetch + )) as BaseCard; + + redirect( + 301, + `/opsml/${getRegistryTypeLowerCase(registryType)}/card/${metadata.space}/${ + metadata.name + }/${metadata.version}/card` + ); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/+page.server.ts new file mode 100644 index 000000000..2d5950bff --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/+page.server.ts @@ -0,0 +1,20 @@ +import { setupRegistryPage } from "$lib/server/card/utils"; +import type { PageServerLoad } from "./$types"; +import { redirect } from "@sveltejs/kit"; + +export const load: PageServerLoad = async ({ parent, params, fetch }) => { + const space = params.space; + const name = undefined; // No name parameter in this route + let { registryType } = await parent(); + + if (!registryType) { + throw redirect(307, "/opsml/home"); + } + + let registryPage = await setupRegistryPage(registryType, space, name, fetch); + return { + page: registryPage, + selectedSpace: space, + selectedName: name, + }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/data/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/+page.server.ts new file mode 100644 index 000000000..4ba153e21 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/+page.server.ts @@ -0,0 +1,16 @@ +import { setupRegistryPage } from "$lib/server/card/utils"; +import type { PageServerLoad } from "./$types"; +import { redirect } from "@sveltejs/kit"; + +export const load: PageServerLoad = async ({ parent, params, fetch }) => { + const space = params.space; + const name = params.name; + let { registryType } = await parent(); + + if (!registryType) { + throw redirect(307, "/opsml/home"); + } + + let registryPage = await setupRegistryPage(registryType, space, name, fetch); + return { page: registryPage, selectedSpace: space, selectedName: name }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/experiment/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/+layout.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/+layout.server.ts new file mode 100644 index 000000000..2a4fc4e99 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/+layout.server.ts @@ -0,0 +1,17 @@ +import type { LayoutServerLoad } from "./$types"; +import { loadCardLayout } from "$lib/server/card/layout"; +import type { RegistryType } from "$lib/utils"; + +// @ts-ignore +export const load: LayoutServerLoad = async ({ params, parent, fetch }) => { + const { registryType } = await parent(); + const { space, name, version } = params; + + return await loadCardLayout( + registryType as RegistryType, + space, + name, + version, + fetch + ); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/+layout.svelte new file mode 100644 index 000000000..1026d2935 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/+layout.svelte @@ -0,0 +1,43 @@ + + + + + {@render children()} + \ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/card/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/card/+page.svelte new file mode 100644 index 000000000..a5877ef79 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/card/+page.svelte @@ -0,0 +1,30 @@ + + +{#if registryType === RegistryType.Data} + +{:else if registryType === RegistryType.Model} + +{:else if registryType === RegistryType.Experiment} + +{:else if registryType === RegistryType.Service} + +{:else} +
+
+

Unknown registry type: {registryType}

+
+
+{/if} + diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/figures/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/figures/+page.server.ts new file mode 100644 index 000000000..ba1b8ec1d --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/figures/+page.server.ts @@ -0,0 +1,17 @@ +import { getExperimentFigures } from "$lib/server/experiment/utils"; +import type { PageServerLoad } from "./$types"; + +export const load: PageServerLoad = async ({ parent, fetch }) => { + const { metadata } = await parent(); + + // get metric names, parameters + let experimentFigures = await getExperimentFigures( + fetch, + metadata.uid, + metadata.space, + metadata.name, + metadata.version + ); + + return { figures: experimentFigures }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/figures/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/figures/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/figures/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/figures/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/+page.server.ts similarity index 50% rename from crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/+page.ts rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/+page.server.ts index 1171d584b..a89a7acea 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/+page.ts +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/+page.server.ts @@ -1,17 +1,14 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { getFileTree } from "$lib/components/files/utils"; +import type { PageServerLoad } from "./$types"; +import { getFileTree } from "$lib/server/card/files/utils"; import { getRegistryTableName } from "$lib/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; -export const load: PageLoad = async ({ parent }) => { +export const load: PageServerLoad = async ({ parent, fetch }) => { const { metadata, registryType } = await parent(); let tableName = getRegistryTableName(registryType); let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; - let fileTree = await getFileTree(basePath); + let fileTree = await getFileTree(fetch, basePath); return { fileTree, previousPath: basePath, isRoot: true }; }; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/[...file]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/[...file]/+page.server.ts new file mode 100644 index 000000000..b530d7468 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/[...file]/+page.server.ts @@ -0,0 +1,6 @@ +import type { PageServerLoad } from "./$types"; +import { loadFileTree } from "$lib/components/files/fileTreeLoader"; + +export const load: PageServerLoad = async (args) => { + return await loadFileTree(args); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/[...file]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/[...file]/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/[...file]/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/[...file]/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/view/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/view/+page.server.ts new file mode 100644 index 000000000..ae92372e6 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/view/+page.server.ts @@ -0,0 +1,13 @@ +import type { PageServerLoad } from "./$types"; +import { getRawFile } from "$lib/server/card/files/utils"; +import { RegistryType } from "$lib/utils"; + +export const load: PageServerLoad = async ({ parent, url, fetch }) => { + const { registryType, metadata } = await parent(); + const viewPath = (url as URL).searchParams.get("path") as string; + + let rawFile = await getRawFile(fetch, viewPath, metadata.uid, registryType); + let splitPath = viewPath.split("/"); + + return { rawFile, viewPath, splitPath }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/view/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/view/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/view/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/files/view/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/hardware/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/hardware/+page.svelte similarity index 91% rename from crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/hardware/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/hardware/+page.svelte index da021dbca..48b4b565b 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/hardware/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/hardware/+page.svelte @@ -14,10 +14,10 @@ -
+
-
+
-
+
-
+
-
+
{ + const { registryType, metadata } = await parent(); + + if (registryType !== RegistryType.Experiment) { + throw goto( + `/opsml/${getRegistryPath(registryType)}/card/${metadata.space}/${ + metadata.name + }/${metadata.version}` + ); + } + + // get metric names, parameters + let resp = await createInternalApiClient(fetch).post( + ServerPaths.EXPERIMENT_HARDWARE, + { + uid: metadata.uid, + } + ); + const hardwareMetrics = (await resp.json()) as UiHardwareMetrics; + + return { hardwareMetrics }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/metrics/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/metrics/+page.svelte similarity index 51% rename from crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/metrics/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/metrics/+page.svelte index 69578a7a7..e1c1b8006 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/metrics/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/metrics/+page.svelte @@ -1,57 +1,42 @@ -
-
+
+
-
+
-
+
-
+
-
Search Metrics
+
Search Metrics
-
+
Plot Type:
-
-
- -
- +
-
- -
-
-
- -
Items
-
-
- - {#each filteredEntities as entity} - - {#if selectedMetrics.includes(entity)} - - {:else} - - {/if} - - {/each} -
-
- -
-
- -
Previous Versions
-
-

Select previous version to compare metrics

-
- {#each recentExperiments as experiment} - {#if selectedExperiments.includes(experiment)} - - {:else} - - {/if} - {/each} -
+
+
@@ -163,7 +120,7 @@ -
+
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/metrics/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/metrics/+page.ts new file mode 100644 index 000000000..d30fb1b71 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/metrics/+page.ts @@ -0,0 +1,38 @@ +export const ssr = false; + +import { getRegistryPath, RegistryType } from "$lib/utils"; +import { goto } from "$app/navigation"; +import type { PageLoad } from "./$types"; +import { ServerPaths } from "$lib/components/api/routes"; +import { createInternalApiClient } from "$lib/api/internalClient"; +import type { Experiment } from "$lib/components/card/experiment/types"; + +export const load: PageLoad = async ({ parent, fetch }) => { + const { registryType, metadata } = await parent(); + + if (registryType !== RegistryType.Experiment) { + throw goto( + `/opsml/${getRegistryPath(registryType)}/card/${metadata.space}/${ + metadata.name + }/${metadata.version}` + ); + } + + const [metricNamesResp, recentExperimentsResp] = await Promise.all([ + createInternalApiClient(fetch).post(ServerPaths.EXPERIMENT_METRIC_NAMES, { + uid: metadata.uid, + }), + createInternalApiClient(fetch).post(ServerPaths.EXPERIMENT_RECENT, { + space: metadata.space, + name: metadata.name, + version: metadata.version, + }), + ]); + + const [metricNames, recentExperiments] = await Promise.all([ + metricNamesResp.json() as Promise, + recentExperimentsResp.json() as Promise, + ]); + + return { metadata, metricNames, recentExperiments }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/monitoring/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/monitoring/+page.svelte new file mode 100644 index 000000000..718aa1508 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/monitoring/+page.svelte @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/monitoring/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/monitoring/+page.ts new file mode 100644 index 000000000..8bfa8b52c --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/monitoring/+page.ts @@ -0,0 +1,27 @@ +// Base data for monitoring page. +// This needs to be client-side because we need to calculate max data points from window size +export const ssr = false; + +import type { PageLoad } from "./$types"; +import { TimeInterval } from "$lib/components/card/monitoring/types"; +import { loadMonitoringDashboardData } from "$lib/components/card/monitoring/getMonitoringDashboardData"; +import { getRegistryPath, RegistryType } from "$lib/utils"; +import { goto } from "$app/navigation"; + +export const load: PageLoad = async ({ parent, fetch }) => { + const { registryType, metadata } = await parent(); + + // redirect to card layout if registryType is not 'model' + if (registryType !== RegistryType.Model) { + throw goto( + `/opsml/${getRegistryPath(registryType)}/card/${metadata.space}/${ + metadata.name + }/${metadata.version}` + ); + } + return loadMonitoringDashboardData(fetch, await parent(), { + initialTimeInterval: TimeInterval.SixHours, + loadLLMRecords: false, + loadAlerts: true, + }); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/profile/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/profile/+page.svelte new file mode 100644 index 000000000..3e1b22f76 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/profile/+page.svelte @@ -0,0 +1,33 @@ + + + +{#if dataProfile} +
+ +
+{:else} +
+
+
+

Data Profile

+

+ No data profile is available for this card. +

+
+
+
+{/if} diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/profile/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/profile/+page.ts new file mode 100644 index 000000000..9f11564cb --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/profile/+page.ts @@ -0,0 +1,34 @@ +export const ssr = false; + +import { redirect } from "@sveltejs/kit"; +import { getSortedFeatureNames } from "$lib/components/card/data/utils"; +import { getRegistryPath, RegistryType } from "$lib/utils"; +import type { PageLoad } from "./$types"; +import { getDataProfile } from "$lib/components/card/data/getDataProfile"; + +export const load: PageLoad = async ({ parent, fetch }) => { + const { registryType, metadata } = await parent(); + + if (registryType !== RegistryType.Data) { + throw redirect( + 302, + `/opsml/${getRegistryPath(registryType)}/card/${metadata.space}/${ + metadata.name + }/${metadata.version}` + ); + } + + let dataProfile = metadata.metadata.interface_metadata.save_metadata + ?.data_profile_uri + ? await getDataProfile(fetch, metadata) + : undefined; + + // get sorted feature names from dataProfile.features + let featureNames: string[] = []; + + if (dataProfile) { + featureNames = getSortedFeatureNames(dataProfile); + } + + return { dataProfile, featureNames }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/readme/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/readme/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/readme/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/readme/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/readme/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/readme/+page.ts new file mode 100644 index 000000000..02003744e --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/readme/+page.ts @@ -0,0 +1,13 @@ +import type { PageLoad } from "./$types"; +import { generateReadmeContent } from "$lib/components/readme/readmeGenerator"; + +export const load: PageLoad = async ({ parent }) => { + const { metadata, registryType, readme } = await parent(); + + const content = generateReadmeContent(registryType, metadata, readme); + + return { + metadata, + content, + }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/versions/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/versions/+page.server.ts new file mode 100644 index 000000000..f295f088b --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/versions/+page.server.ts @@ -0,0 +1,13 @@ +import { getRegistryStats, getVersionPage } from "$lib/server/card/utils"; +import type { PageServerLoad } from "./$types"; + +export const load: PageServerLoad = async ({ parent, fetch }) => { + const { metadata, registryType } = await parent(); + + let [versionPage, versionStats] = await Promise.all([ + getVersionPage(fetch, registryType, metadata.space, metadata.name), + getRegistryStats(fetch, registryType, metadata.name, [metadata.space]), + ]); + + return { versionPage, versionStats }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/versions/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/versions/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/versions/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/[registry]/card/[space]/[name]/[version]/versions/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/+page.server.ts deleted file mode 100644 index 85e0390a1..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/+page.server.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ fetch }) => { - let registryPage = await setupRegistryPage( - RegistryType.Data, - undefined, - undefined, - fetch - ); - - return { - page: registryPage, - selectedSpace: undefined, - selectedName: undefined, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/[uid]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/[uid]/+page.server.ts deleted file mode 100644 index bbb54771a..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/[uid]/+page.server.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { redirect } from "@sveltejs/kit"; -import type { PageLoad } from "./$types"; -import { getRegistryTypeLowerCase, RegistryType } from "$lib/utils"; -import type { ServiceCard } from "$lib/components/card/card_interfaces/servicecard"; -import { getCardMetadata } from "$lib/server/card/utils"; - -export const load: PageLoad = async ({ params, fetch }) => { - let registryType = RegistryType.Data; - - let resp = await getCardMetadata( - undefined, - undefined, - undefined, - params.uid, - registryType, - fetch - ); - - let metadata = (await resp.json()) as ServiceCard; - - redirect( - 301, - `/opsml/${getRegistryTypeLowerCase(registryType)}/card/${metadata.space}/${ - metadata.name - }/${metadata.version}/card` - ); -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/+page.server.ts deleted file mode 100644 index 33d52d2fb..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/+page.server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = undefined; // No name parameter in this route - - let registryPage = await setupRegistryPage( - RegistryType.Data, - space, - name, - fetch - ); - return { - page: registryPage, - selectedSpace: space, - selectedName: name, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/+page.server.ts deleted file mode 100644 index a6553dd7b..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/+page.server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = params.name; - - let registryPage = await setupRegistryPage( - RegistryType.Data, - space, - name, - fetch - ); - return { - page: registryPage, - selectedSpace: space, - selectedName: name, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/+page.svelte deleted file mode 100644 index 06e219501..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/+page.svelte +++ /dev/null @@ -1,19 +0,0 @@ - - - \ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/+layout.svelte deleted file mode 100644 index 76a930728..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/+layout.svelte +++ /dev/null @@ -1,78 +0,0 @@ - - -
- - - - -
- {@render children()} -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/+layout.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/+layout.ts deleted file mode 100644 index b6dcfc044..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/+layout.ts +++ /dev/null @@ -1,29 +0,0 @@ -export const prerender = false; -export const ssr = false; -import { getRegistryTypeLowerCase, RegistryType } from "$lib/utils"; -import { getCardMetadata } from "$lib/components/card/utils"; - -import type { LayoutLoad } from "./$types"; -import type { DataCard } from "$lib/components/card/card_interfaces/datacard"; -import { getCardReadMe } from "$lib/components/readme/util"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -// @ts-ignore -export const load: LayoutLoad = async ({ params, parent }) => { - const { registryType } = await parent(); - const { space, name, version } = params; - - let metadata = (await getCardMetadata( - space, - name, - version, - undefined, - registryType - )) as DataCard; - - let readme = await getCardReadMe(metadata.name, metadata.space, registryType); - - let activeTab = "card"; // Default active tab - - return { metadata, registryType, readme, activeTab }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/card/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/card/+page.ts deleted file mode 100644 index 9958265b6..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/card/+page.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType, readme } = await parent(); - - return { metadata, registryType, readme }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/[...file]/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/[...file]/+page.ts deleted file mode 100644 index 51e2b25cc..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/[...file]/+page.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const ssr = false; -export const prerender = false; - -import type { PageLoad } from "./$types"; -import { getFileTree } from "$lib/components/files/utils"; -import { getRegistryPath, getRegistryTableName } from "$lib/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent, params }) => { - let slug = params.file as string; - - // split slug with '/' - let slugs = slug.split("/"); - - const { metadata, registryType } = await parent(); - - let tableName = getRegistryTableName(registryType); - let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; - - // join all but the last element of the slugs to get the previous path without final "/" - let previousPath = `/opsml/${getRegistryPath(registryType)}/card/${ - metadata.space - }/${metadata.name}/${metadata.version}/files/${slugs - .slice(0, slugs.length - 1) - .join("/")}`.replace(/\/$/, ""); - - // add the rest of the slugs to the basePath - basePath = `${basePath}/${slugs.join("/")}`; - - let fileTree = await getFileTree(basePath); - - return { fileTree, previousPath, isRoot: false }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/view/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/view/+page.ts deleted file mode 100644 index f165ecabc..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/view/+page.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const ssr = false; -export const prerender = false; - -import type { PageLoad } from "./$types"; -import { getRawFile } from "$lib/components/files/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent, url }) => { - const { metadata, registryType } = await parent(); - const viewPath = (url as URL).searchParams.get("path") as string; - - let rawFile = await getRawFile(viewPath, metadata.uid, registryType); - let splitPath = viewPath.split("/"); - - return { rawFile, viewPath, splitPath }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/profile/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/profile/+page.svelte deleted file mode 100644 index ce566c09a..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/profile/+page.svelte +++ /dev/null @@ -1,32 +0,0 @@ - - - -
- {#if dataProfile} -
- -
- - {:else} -
-

No Data Profile Found!

-
-

A data profile was not saved with the current DataCard

-
-
- {/if} -
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/profile/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/profile/+page.ts deleted file mode 100644 index 5da9906d2..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/profile/+page.ts +++ /dev/null @@ -1,26 +0,0 @@ -export const ssr = false; - -import { - getDataProfile, - getSortedFeatureNames, -} from "$lib/components/card/data/utils"; -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata } = await parent(); - - let dataProfile = metadata.metadata.interface_metadata.save_metadata - ?.data_profile_uri - ? await getDataProfile(metadata) - : undefined; - - // get sorted feature names from dataProfile.features - let featureNames: string[] = []; - - if (dataProfile) { - featureNames = getSortedFeatureNames(dataProfile); - } - - return { dataProfile, featureNames }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/readme/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/readme/+page.ts deleted file mode 100644 index 8b9cbc38e..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/readme/+page.ts +++ /dev/null @@ -1,82 +0,0 @@ -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType, readme } = await parent(); - - let content: string = ""; - - if (readme.exists) { - content = readme.readme; - } else { - content = `# DataCard for ${metadata.space}/${metadata.name} - - - -**Summary:** -Provide a concise summary of the data card, its intended use, and any notable features. - ---- - -## Model Details - -- **Space:** ${metadata.space} -- **Name:** ${metadata.name} -- **Version:** ${metadata.version} -- **Registry:** ${registryType} - ---- - -## Description - -Describe the model or dataset in detail. -Include its origin, intended use cases, and any relevant background information. - ---- - -## Contact - -- **Contact Person/Team:** [Add contact information or support email] -- **License:** [Specify license, e.g., MIT, Apache 2.0, etc.] - ---- - -## Data Development - -Explain how the data/model was developed, including sources, methodology, and any preprocessing steps. - ---- - -## Uses - -List and describe common or recommended uses for this data card. - ---- - -## Bias, Risk, and Limitations - - - -Discuss any known biases, risks, or limitations associated with this data/model. -Mention steps taken to mitigate these issues, if any. - ---- - -## Code Examples - - -\`\`\`python -# Example usage in Python -from opsml import CardRegistry -registry = CardRegistry("${registryType.toLowerCase()}") -card = registry.load_card(uid="${metadata.uid}") -\`\`\` - -`; - } - - return { - metadata, - content, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/versions/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/versions/+page.ts deleted file mode 100644 index aaf382229..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/versions/+page.ts +++ /dev/null @@ -1,21 +0,0 @@ -export const ssr = false; - -import { getRegistryStats, getVersionPage } from "$lib/components/card/utils"; -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType } = await parent(); - - // get metric names, parameters - let versionPage = await getVersionPage( - registryType, - metadata.space, - metadata.name - ); - - let space = [metadata.space]; - let versionStats = await getRegistryStats(registryType, metadata.name, space); - - return { versionPage, versionStats }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/+layout.svelte deleted file mode 100644 index c934f0101..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/+layout.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -
- - {@render children()} - -
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/+page.server.ts deleted file mode 100644 index bd7544d6d..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/+page.server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ fetch }) => { - const space = undefined; - const name = undefined; // No name parameter in this route - - let registryPage = await setupRegistryPage( - RegistryType.Experiment, - space, - name, - fetch - ); - return { - page: registryPage, - selectedSpace: space, - selectedName: name, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/[uid]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/[uid]/+page.server.ts deleted file mode 100644 index f2be3b024..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/[uid]/+page.server.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { redirect } from "@sveltejs/kit"; -import type { PageLoad } from "./$types"; -import { getRegistryTypeLowerCase, RegistryType } from "$lib/utils"; -import type { ServiceCard } from "$lib/components/card/card_interfaces/servicecard"; -import { getCardMetadata } from "$lib/server/card/utils"; - -export const load: PageLoad = async ({ params, fetch }) => { - let registryType = RegistryType.Experiment; - - let resp = await getCardMetadata( - undefined, - undefined, - undefined, - params.uid, - registryType, - fetch - ); - - let metadata = (await resp.json()) as ServiceCard; - - redirect( - 301, - `/opsml/${getRegistryTypeLowerCase(registryType)}/card/${metadata.space}/${ - metadata.name - }/${metadata.version}/card` - ); -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/+page.server.ts deleted file mode 100644 index 5c1eaba2f..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/+page.server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = undefined; // No name parameter in this route - - let registryPage = await setupRegistryPage( - RegistryType.Experiment, - space, - name, - fetch - ); - return { - page: registryPage, - selectedSpace: space, - selectedName: name, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/+page.server.ts deleted file mode 100644 index d31972b92..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/+page.server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = params.name; - - let registryPage = await setupRegistryPage( - RegistryType.Experiment, - space, - name, - fetch - ); - return { - page: registryPage, - selectedSpace: space, - selectedName: name, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/+layout.svelte deleted file mode 100644 index f9e635a79..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/+layout.svelte +++ /dev/null @@ -1,92 +0,0 @@ - - -
- - - -
- {@render children()} -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/+layout.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/+layout.ts deleted file mode 100644 index be5b8e1f5..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/+layout.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const prerender = false; -export const ssr = false; -import { getRegistryTypeLowerCase } from "$lib/utils"; -import { getCardMetadata } from "$lib/components/card/utils"; - -import type { LayoutLoad } from "./$types"; -import { getCardReadMe } from "$lib/components/readme/util"; -import type { ExperimentCard } from "$lib/components/card/card_interfaces/experimentcard"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -// @ts-ignore -export const load: LayoutLoad = async ({ params, parent }) => { - const { registryType } = await parent(); - const { space, name, version } = params; - - let metadata = (await getCardMetadata( - space, - name, - version, - undefined, - registryType - )) as ExperimentCard; - - let readme = await getCardReadMe(metadata.name, metadata.space, registryType); - - let registryPath = getRegistryTypeLowerCase(registryType); - let activeTab = "card"; // Default active tab - - return { metadata, registryType, readme, activeTab }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/card/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/card/+page.ts deleted file mode 100644 index ca136902c..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/card/+page.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { getCardParameters } from "$lib/components/card/experiment/util"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType, readme, registryPath } = await parent(); - - let parameters = await getCardParameters(metadata.uid); - - return { metadata, registryType, readme, registryPath, parameters }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/figures/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/figures/+page.ts deleted file mode 100644 index 65a751233..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/figures/+page.ts +++ /dev/null @@ -1,22 +0,0 @@ -export const ssr = false; - -import { - getExperimentFigures, - getHardwareMetrics, -} from "$lib/components/card/experiment/util"; -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata } = await parent(); - - // get metric names, parameters - let experimentFigures = await getExperimentFigures( - metadata.uid, - metadata.space, - metadata.name, - metadata.version - ); - - return { figures: experimentFigures }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/+page.svelte deleted file mode 100644 index 737159d98..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/+page.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -
-
- -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/+page.ts deleted file mode 100644 index 1171d584b..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/+page.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { getFileTree } from "$lib/components/files/utils"; -import { getRegistryTableName } from "$lib/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType } = await parent(); - - let tableName = getRegistryTableName(registryType); - let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; - - let fileTree = await getFileTree(basePath); - - return { fileTree, previousPath: basePath, isRoot: true }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/[...file]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/[...file]/+page.svelte deleted file mode 100644 index f20625802..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/[...file]/+page.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -
-
- -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/[...file]/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/[...file]/+page.ts deleted file mode 100644 index 51e2b25cc..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/[...file]/+page.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const ssr = false; -export const prerender = false; - -import type { PageLoad } from "./$types"; -import { getFileTree } from "$lib/components/files/utils"; -import { getRegistryPath, getRegistryTableName } from "$lib/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent, params }) => { - let slug = params.file as string; - - // split slug with '/' - let slugs = slug.split("/"); - - const { metadata, registryType } = await parent(); - - let tableName = getRegistryTableName(registryType); - let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; - - // join all but the last element of the slugs to get the previous path without final "/" - let previousPath = `/opsml/${getRegistryPath(registryType)}/card/${ - metadata.space - }/${metadata.name}/${metadata.version}/files/${slugs - .slice(0, slugs.length - 1) - .join("/")}`.replace(/\/$/, ""); - - // add the rest of the slugs to the basePath - basePath = `${basePath}/${slugs.join("/")}`; - - let fileTree = await getFileTree(basePath); - - return { fileTree, previousPath, isRoot: false }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/view/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/view/+page.svelte deleted file mode 100644 index aefa8b439..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/view/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - -
-
- -
-
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/view/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/view/+page.ts deleted file mode 100644 index d81fc697b..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/files/view/+page.ts +++ /dev/null @@ -1,20 +0,0 @@ -export const ssr = false; -export const prerender = false; - -import type { PageLoad } from "./$types"; -import { getRawFile } from "$lib/components/files/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent, url }) => { - const { metadata } = await parent(); - const viewPath = (url as URL).searchParams.get("path") as string; - - let rawFile = await getRawFile( - viewPath, - metadata.uid, - metadata.registry_type - ); - let splitPath = viewPath.split("/"); - - return { rawFile, viewPath, splitPath }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/hardware/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/hardware/+page.ts deleted file mode 100644 index 018ac4b78..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/hardware/+page.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const ssr = false; - -import { getHardwareMetrics } from "$lib/components/card/experiment/util"; -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata } = await parent(); - - // get metric names, parameters - let hardwareMetrics = await getHardwareMetrics(metadata.uid); - - return { hardwareMetrics }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/metrics/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/metrics/+page.ts deleted file mode 100644 index 30c8c735d..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/metrics/+page.ts +++ /dev/null @@ -1,23 +0,0 @@ -export const ssr = false; - -import { - getCardMetricNames, - getRecentExperiments, -} from "$lib/components/card/experiment/util"; -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata } = await parent(); - - // get metric names, parameters - let metricNames = await getCardMetricNames(metadata.uid); - - let recentExperiments = await getRecentExperiments( - metadata.space, - metadata.name, - metadata.version - ); - - return { metadata, metricNames, recentExperiments }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/readme/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/readme/+page.ts deleted file mode 100644 index 2040659e7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/readme/+page.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType, readme } = await parent(); - - let content: string = ""; - - if (readme.exists) { - content = readme.readme; - } else { - content = `# ExperimentCard for ${metadata.space}/${metadata.name} - - - -**Summary:** -Provide a concise summary of the experiment card, its intended use, and any notable features. - ---- - -## Experiment Details - -- **Space:** ${metadata.space} -- **Name:** ${metadata.name} -- **Version:** ${metadata.version} -- **Registry:** ${registryType} - ---- - -## Description - -Describe the experiment in detail. -Include its origin, intended use cases, and any relevant background information. - ---- - -## Experiment Development - -Explain how the experiment was developed, including sources, methodology, and any preprocessing steps. - ---- - -## Code Examples - - -\`\`\`python -# Example usage in Python -from opsml import CardRegistry -registry = CardRegistry("${registryType.toLowerCase()}") -card = registry.load_card(uid="${metadata.uid}") -\`\`\` - -`; - } - - return { - metadata, - content, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/versions/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/versions/+page.ts deleted file mode 100644 index de913c0f4..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/versions/+page.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const ssr = false; - -import { getRegistryStats, getVersionPage } from "$lib/components/card/utils"; -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType } = await parent(); - - let [versionPage, versionStats] = await Promise.all([ - getVersionPage(registryType, metadata.space, metadata.name), - getRegistryStats(registryType, metadata.name, [metadata.space]), - ]); - - return { versionPage, versionStats }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/+page.svelte index 375735755..a1b8019b0 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/+page.svelte @@ -1,5 +1,4 @@ diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+layout.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+layout.server.ts new file mode 100644 index 000000000..25b1938d0 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+layout.server.ts @@ -0,0 +1,9 @@ +import type { LayoutServerLoad } from "./$types"; +import { getRegistryFromString } from "$lib/utils"; + +export const load: LayoutServerLoad = async ({ params }) => { + let registryType = getRegistryFromString(params.registry); + return { + registryType, + }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+layout.svelte new file mode 100644 index 000000000..ffae99c18 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+layout.svelte @@ -0,0 +1,10 @@ + + +
+ {#key page.params.registry} + {@render children()} + {/key} +
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+page.server.ts new file mode 100644 index 000000000..ea4ee2b16 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+page.server.ts @@ -0,0 +1,34 @@ +import { setupRegistryPage } from "$lib/server/card/utils"; +import { getRegistryFromString, RegistryType } from "$lib/utils"; +import { redirect } from "@sveltejs/kit"; +import type { PageServerLoad, EntryGenerator } from "./$types"; + +export const entries: EntryGenerator = () => { + return [ + { registry: "model" }, + { registry: "data" }, + { registry: "experiment" }, + { registry: "service" }, + ]; +}; + +export const load: PageServerLoad = async ({ parent, fetch }) => { + let { registryType } = await parent(); + + if (!registryType) { + throw redirect(307, "/opsml/home"); + } + + let registryPage = await setupRegistryPage( + registryType, + undefined, + undefined, + fetch + ); + + return { + page: registryPage, + selectedSpace: undefined, + selectedName: undefined, + }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+page.svelte new file mode 100644 index 000000000..e80b5cd59 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/+page.svelte @@ -0,0 +1,20 @@ + + +{#if data.registryType === RegistryType.Agent} + +{:else} + +{/if} + + + diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/[uid]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/[uid]/+page.server.ts new file mode 100644 index 000000000..80de0f0d4 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/[uid]/+page.server.ts @@ -0,0 +1,32 @@ +import { redirect } from "@sveltejs/kit"; +import type { PageServerLoad } from "./$types"; +import { getRegistryTypeLowerCase } from "$lib/utils"; +import type { ServiceCard } from "$lib/components/card/card_interfaces/servicecard"; +import { getCardMetadata } from "$lib/server/card/utils"; +import type { BaseCard } from "$lib/components/home/types"; + +export const load: PageServerLoad = async ({ parent, params, fetch }) => { + let { registryType } = await parent(); + + console.log("uid param:", params.uid); + + if (!registryType) { + throw redirect(307, "/opsml/home"); + } + + let metadata = (await getCardMetadata( + undefined, + undefined, + undefined, + params.uid, + registryType, + fetch + )) as BaseCard; + + redirect( + 301, + `/opsml/${getRegistryTypeLowerCase(registryType)}/card/${metadata.space}/${ + metadata.name + }/${metadata.version}/card` + ); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/+page.server.ts new file mode 100644 index 000000000..2d5950bff --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/+page.server.ts @@ -0,0 +1,20 @@ +import { setupRegistryPage } from "$lib/server/card/utils"; +import type { PageServerLoad } from "./$types"; +import { redirect } from "@sveltejs/kit"; + +export const load: PageServerLoad = async ({ parent, params, fetch }) => { + const space = params.space; + const name = undefined; // No name parameter in this route + let { registryType } = await parent(); + + if (!registryType) { + throw redirect(307, "/opsml/home"); + } + + let registryPage = await setupRegistryPage(registryType, space, name, fetch); + return { + page: registryPage, + selectedSpace: space, + selectedName: name, + }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/+page.server.ts new file mode 100644 index 000000000..4ba153e21 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/+page.server.ts @@ -0,0 +1,16 @@ +import { setupRegistryPage } from "$lib/server/card/utils"; +import type { PageServerLoad } from "./$types"; +import { redirect } from "@sveltejs/kit"; + +export const load: PageServerLoad = async ({ parent, params, fetch }) => { + const space = params.space; + const name = params.name; + let { registryType } = await parent(); + + if (!registryType) { + throw redirect(307, "/opsml/home"); + } + + let registryPage = await setupRegistryPage(registryType, space, name, fetch); + return { page: registryPage, selectedSpace: space, selectedName: name }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/+layout.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/+layout.server.ts new file mode 100644 index 000000000..2a4fc4e99 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/+layout.server.ts @@ -0,0 +1,17 @@ +import type { LayoutServerLoad } from "./$types"; +import { loadCardLayout } from "$lib/server/card/layout"; +import type { RegistryType } from "$lib/utils"; + +// @ts-ignore +export const load: LayoutServerLoad = async ({ params, parent, fetch }) => { + const { registryType } = await parent(); + const { space, name, version } = params; + + return await loadCardLayout( + registryType as RegistryType, + space, + name, + version, + fetch + ); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/+layout.svelte new file mode 100644 index 000000000..181d46f9d --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/+layout.svelte @@ -0,0 +1,43 @@ + + + + + {@render children()} + \ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/card/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/card/+page.svelte new file mode 100644 index 000000000..5d42bde2b --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/card/+page.svelte @@ -0,0 +1,24 @@ + + +{#if registryType === RegistryType.Prompt} + +{:else if registryType === RegistryType.Mcp} + +{:else} +
+
+

Unknown registry type: {registryType}

+
+
+{/if} + diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/+page.server.ts similarity index 50% rename from crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/+page.ts rename to crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/+page.server.ts index 1171d584b..647de28a6 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/+page.ts +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/+page.server.ts @@ -1,17 +1,13 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { getFileTree } from "$lib/components/files/utils"; +import type { PageServerLoad } from "./$types"; +import { getFileTree } from "$lib/server/card/files/utils"; import { getRegistryTableName } from "$lib/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; -export const load: PageLoad = async ({ parent }) => { +export const load: PageServerLoad = async ({ parent, fetch }) => { const { metadata, registryType } = await parent(); let tableName = getRegistryTableName(registryType); let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; - - let fileTree = await getFileTree(basePath); + let fileTree = await getFileTree(fetch, basePath); return { fileTree, previousPath: basePath, isRoot: true }; }; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/+page.svelte similarity index 97% rename from crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/+page.svelte index 6e0fa7a5d..930e00f35 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/+page.svelte @@ -10,7 +10,7 @@
- { + return await loadFileTree(args); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/[...file]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/[...file]/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/[...file]/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/[...file]/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/view/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/view/+page.server.ts new file mode 100644 index 000000000..ae92372e6 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/view/+page.server.ts @@ -0,0 +1,13 @@ +import type { PageServerLoad } from "./$types"; +import { getRawFile } from "$lib/server/card/files/utils"; +import { RegistryType } from "$lib/utils"; + +export const load: PageServerLoad = async ({ parent, url, fetch }) => { + const { registryType, metadata } = await parent(); + const viewPath = (url as URL).searchParams.get("path") as string; + + let rawFile = await getRawFile(fetch, viewPath, metadata.uid, registryType); + let splitPath = viewPath.split("/"); + + return { rawFile, viewPath, splitPath }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/view/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/view/+page.svelte similarity index 98% rename from crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/view/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/view/+page.svelte index 07d91dfd8..248f0ea31 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/data/card/[space]/[name]/[version]/files/view/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/files/view/+page.svelte @@ -19,4 +19,4 @@ version={data.metadata.version} />
-
\ No newline at end of file +
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/monitoring/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/monitoring/+page.svelte new file mode 100644 index 000000000..e0911b72c --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/monitoring/+page.svelte @@ -0,0 +1,23 @@ + + + \ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/monitoring/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/monitoring/+page.ts new file mode 100644 index 000000000..aeda26b23 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/monitoring/+page.ts @@ -0,0 +1,27 @@ +// Base data for monitoring page. +// This needs to be client-side because we need to calculate max data points from window size +export const ssr = false; + +import type { PageLoad } from "./$types"; +import { TimeInterval } from "$lib/components/card/monitoring/types"; +import { loadMonitoringDashboardData } from "$lib/components/card/monitoring/getMonitoringDashboardData"; +import { getRegistryPath, RegistryType } from "$lib/utils"; +import { goto } from "$app/navigation"; + +export const load: PageLoad = async ({ parent, fetch }) => { + const { registryType, metadata } = await parent(); + + // redirect to card layout if registryType is not 'prompt' + if (registryType !== RegistryType.Prompt) { + throw goto( + `/opsml/${getRegistryPath(registryType)}/card/${metadata.space}/${ + metadata.name + }/${metadata.version}` + ); + } + return loadMonitoringDashboardData(fetch, await parent(), { + initialTimeInterval: TimeInterval.SixHours, + loadLLMRecords: true, + loadAlerts: true, + }); +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/readme/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/readme/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/readme/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/readme/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/readme/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/readme/+page.ts new file mode 100644 index 000000000..02003744e --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/readme/+page.ts @@ -0,0 +1,13 @@ +import type { PageLoad } from "./$types"; +import { generateReadmeContent } from "$lib/components/readme/readmeGenerator"; + +export const load: PageLoad = async ({ parent }) => { + const { metadata, registryType, readme } = await parent(); + + const content = generateReadmeContent(registryType, metadata, readme); + + return { + metadata, + content, + }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/versions/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/versions/+page.server.ts new file mode 100644 index 000000000..f295f088b --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/versions/+page.server.ts @@ -0,0 +1,13 @@ +import { getRegistryStats, getVersionPage } from "$lib/server/card/utils"; +import type { PageServerLoad } from "./$types"; + +export const load: PageServerLoad = async ({ parent, fetch }) => { + const { metadata, registryType } = await parent(); + + let [versionPage, versionStats] = await Promise.all([ + getVersionPage(fetch, registryType, metadata.space, metadata.name), + getRegistryStats(fetch, registryType, metadata.name, [metadata.space]), + ]); + + return { versionPage, versionStats }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/versions/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/versions/+page.svelte similarity index 100% rename from crates/opsml_server/opsml_ui/src/routes/opsml/experiment/card/[space]/[name]/[version]/versions/+page.svelte rename to crates/opsml_server/opsml_ui/src/routes/opsml/genai/[registry]/card/[space]/[name]/[version]/versions/+page.svelte diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/agent/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/agent/+layout.svelte deleted file mode 100644 index 3919da045..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/agent/+layout.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -
- {@render children()} -
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/agent/+layout.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/agent/+layout.ts deleted file mode 100644 index a56b576a3..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/agent/+layout.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const prerender = false; -export const ssr = false; -import { RegistryType } from "$lib/utils"; -import type { LayoutLoad } from "./$types"; - -export const load: LayoutLoad = async ({}) => { - return { - registryType: RegistryType.Prompt, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/+layout.svelte deleted file mode 100644 index d1b71b732..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/+layout.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -
- {@render children()} -
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/+page.server.ts deleted file mode 100644 index bdc80ff03..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/+page.server.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ fetch }) => { - let registryPage = await setupRegistryPage( - RegistryType.Mcp, - undefined, - undefined, - fetch - ); - - return { - page: registryPage, - selectedSpace: undefined, - selectedName: undefined, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/[uid]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/[uid]/+page.server.ts deleted file mode 100644 index 237d24cff..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/[uid]/+page.server.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { redirect } from "@sveltejs/kit"; -import type { PageLoad } from "./$types"; -import { getRegistryTypeLowerCase, RegistryType } from "$lib/utils"; -import type { ServiceCard } from "$lib/components/card/card_interfaces/servicecard"; -import { getCardMetadata } from "$lib/server/card/utils"; - -export const load: PageLoad = async ({ params, fetch }) => { - let registryType = RegistryType.Mcp; - - let resp = await getCardMetadata( - undefined, - undefined, - undefined, - params.uid, - registryType, - fetch - ); - - let metadata = (await resp.json()) as ServiceCard; - - redirect( - 301, - `/opsml/${getRegistryTypeLowerCase(registryType)}/card/${metadata.space}/${ - metadata.name - }/${metadata.version}/card` - ); -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/+page.server.ts deleted file mode 100644 index 4b13367ac..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/+page.server.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = undefined; // No name parameter in this route - let registryPage = await setupRegistryPage( - RegistryType.Mcp, - space, - name, - fetch - ); - return { page: registryPage, selectedSpace: space, selectedName: name }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/+page.server.ts deleted file mode 100644 index 13cfe14ce..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/+page.server.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = params.name; - let registryPage = await setupRegistryPage( - RegistryType.Mcp, - space, - name, - fetch - ); - return { page: registryPage, selectedSpace: space, selectedName: name }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/+layout.svelte deleted file mode 100644 index a919095bb..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/+layout.svelte +++ /dev/null @@ -1,60 +0,0 @@ - - -
-
-
-

- - -
/
-
{data.metadata.version}
-

- - -
-
- - -
- {@render children()} -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/+layout.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/+layout.ts deleted file mode 100644 index 4772e4e15..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/+layout.ts +++ /dev/null @@ -1,29 +0,0 @@ -export const prerender = false; -export const ssr = false; -import { getRegistryPath, getRegistryTypeLowerCase } from "$lib/utils"; -import { getCardMetadata } from "$lib/components/card/utils"; - -import type { LayoutLoad } from "./$types"; -import { getCardReadMe } from "$lib/components/readme/util"; -import type { ServiceCard } from "$lib/components/card/card_interfaces/servicecard"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -// @ts-ignore -export const load: LayoutLoad = async ({ params, parent }) => { - const { registryType } = await parent(); - const { space, name, version } = params; - - let metadata = (await getCardMetadata( - space, - name, - version, - undefined, - registryType - )) as ServiceCard; - - let readme = await getCardReadMe(metadata.name, metadata.space, registryType); - - let activeTab = "card"; // Default active tab - - return { metadata, registryType, readme, activeTab }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/card/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/card/+page.svelte deleted file mode 100644 index a5bf69147..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/card/+page.svelte +++ /dev/null @@ -1,67 +0,0 @@ - - - -
- -
- -
- -
- - {#if service.cards && service.cards.cards.length > 0} -
-
-
- - - Card List - - -
- {#each service.cards.cards as card} - - {/each} -
- - {/if} - - - {#if deploymentConfig && deploymentConfig.length > 0} -
- {#each deploymentConfig as config} -
- -
- {/each} -
- {/if} - - - - - \ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/card/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/card/+page.ts deleted file mode 100644 index 806ba447b..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/card/+page.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -// @ts-ignore -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType, readme } = await parent(); - - return { metadata, registryType, readme }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/versions/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/versions/+page.svelte deleted file mode 100644 index 23a0b60da..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/versions/+page.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/versions/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/versions/+page.ts deleted file mode 100644 index cf66e8859..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/mcp/card/[space]/[name]/[version]/versions/+page.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const ssr = false; - -import { getRegistryStats, getVersionPage } from "$lib/components/card/utils"; -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -// @ts-ignore -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType } = await parent(); - - let [versionPage, versionStats] = await Promise.all([ - getVersionPage(registryType, metadata.space, metadata.name), - getRegistryStats(registryType, metadata.name, [metadata.space]), - ]); - - return { versionPage, versionStats }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/+layout.svelte deleted file mode 100644 index d1b71b732..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/+layout.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -
- {@render children()} -
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/+page.server.ts deleted file mode 100644 index d58a35ffb..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/+page.server.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ fetch }) => { - let registryPage = await setupRegistryPage( - RegistryType.Prompt, - undefined, - undefined, - fetch - ); - - return { - page: registryPage, - selectedSpace: undefined, - selectedName: undefined, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/[uid]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/[uid]/+page.server.ts deleted file mode 100644 index c7221a1b6..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/[uid]/+page.server.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { redirect } from "@sveltejs/kit"; -import type { PageLoad } from "./$types"; -import { getRegistryTypeLowerCase, RegistryType } from "$lib/utils"; -import type { ServiceCard } from "$lib/components/card/card_interfaces/servicecard"; -import { getCardMetadata } from "$lib/server/card/utils"; - -export const load: PageLoad = async ({ params, fetch }) => { - let registryType = RegistryType.Prompt; - - let resp = await getCardMetadata( - undefined, - undefined, - undefined, - params.uid, - registryType, - fetch - ); - - let metadata = (await resp.json()) as ServiceCard; - - redirect( - 301, - `/opsml/${getRegistryTypeLowerCase(registryType)}/card/${metadata.space}/${ - metadata.name - }/${metadata.version}/card` - ); -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/+page.server.ts deleted file mode 100644 index c3392a4a9..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/+page.server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = undefined; // No name parameter in this route - - let registryPage = await setupRegistryPage( - RegistryType.Prompt, - space, - name, - fetch - ); - return { - page: registryPage, - selectedSpace: space, - selectedName: name, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/+page.server.ts deleted file mode 100644 index 3fb18ee1d..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/+page.server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = params.name; - - let registryPage = await setupRegistryPage( - RegistryType.Prompt, - space, - name, - fetch - ); - return { page: registryPage, selectedSpace: space, selectedName: name }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/+layout.svelte deleted file mode 100644 index 017e2fbc8..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/+layout.svelte +++ /dev/null @@ -1,82 +0,0 @@ - - -
-
-
-

- - -
/
-
{data.metadata.version}
-

- -
- - - Card - - - {#if data.metadata.metadata.drift_profile_uri_map && uiSettingsStore.scouterEnabled} - - - Monitoring - - {/if} - - - - Files - - - - - Versions - -
-
-
- - -
- {@render children()} -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/+layout.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/+layout.ts deleted file mode 100644 index 375b5a93b..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/+layout.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const prerender = false; -export const ssr = false; -import { getCardMetadata } from "$lib/components/card/utils"; - -import type { LayoutLoad } from "./$types"; -import { getCardReadMe } from "$lib/components/readme/util"; -import type { PromptCard } from "$lib/components/card/card_interfaces/promptcard"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -// @ts-ignore -export const load: LayoutLoad = async ({ params, parent }) => { - const { registryType } = await parent(); - const { space, name, version } = params; - - let metadata = (await getCardMetadata( - space, - name, - version, - undefined, - registryType - )) as PromptCard; - - console.log("Prompt Card Metadata:", metadata); - - let readme = await getCardReadMe(metadata.name, metadata.space, registryType); - - let activeTab = "card"; // Default active tab - - return { metadata, registryType, readme, activeTab }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/card/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/card/+page.ts deleted file mode 100644 index 9958265b6..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/card/+page.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType, readme } = await parent(); - - return { metadata, registryType, readme }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/+page.svelte deleted file mode 100644 index 737159d98..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/+page.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -
-
- -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/+page.ts deleted file mode 100644 index 1ad2f386e..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/+page.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { getFileTree } from "$lib/components/files/utils"; -import { getRegistryTableName } from "$lib/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryPath, registryType } = await parent(); - - let tableName = getRegistryTableName(registryType); - let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; - - let fileTree = await getFileTree(basePath); - - return { fileTree, previousPath: basePath, isRoot: true, registryPath }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/[...file]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/[...file]/+page.svelte deleted file mode 100644 index f20625802..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/[...file]/+page.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -
-
- -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/[...file]/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/[...file]/+page.ts deleted file mode 100644 index fb5a36de8..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/[...file]/+page.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const ssr = false; -export const prerender = false; - -import type { PageLoad } from "./$types"; -import { getFileTree } from "$lib/components/files/utils"; -import { getRegistryPath, getRegistryTableName } from "$lib/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent, params }) => { - let slug = params.file as string; - - // split slug with '/' - let slugs = slug.split("/"); - - const { metadata, registryType } = await parent(); - - let tableName = getRegistryTableName(registryType); - let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; - - // join all but the last element of the slugs to get the previous path without final "/" - let previousPath = `/opsml/${getRegistryPath(registryType)}/card/${ - metadata.space - }/${metadata.name}/${metadata.version}/files/${slugs - .slice(0, slugs.length - 1) - .join("/")}`.replace(/\/$/, ""); - - // add the rest of the slugs to the basePath - basePath = `${basePath}/${slugs.join("/")}`; - - let fileTree = await getFileTree(basePath); - - return { fileTree, previousPath, isRoot: false, registryType }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/view/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/view/+page.svelte deleted file mode 100644 index ee5caa798..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/view/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - -
-
- -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/view/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/view/+page.ts deleted file mode 100644 index f165ecabc..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/files/view/+page.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const ssr = false; -export const prerender = false; - -import type { PageLoad } from "./$types"; -import { getRawFile } from "$lib/components/files/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent, url }) => { - const { metadata, registryType } = await parent(); - const viewPath = (url as URL).searchParams.get("path") as string; - - let rawFile = await getRawFile(viewPath, metadata.uid, registryType); - let splitPath = viewPath.split("/"); - - return { rawFile, viewPath, splitPath }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/monitoring/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/monitoring/+page.svelte deleted file mode 100644 index 5102ecdb4..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/monitoring/+page.svelte +++ /dev/null @@ -1,211 +0,0 @@ - - -
-
- - - -
-
-
- - -
- - {#if currentName && latestMetrics} - {#if currentMetricData} - - {:else} -
- No data available for selected metric -
- {/if} - {:else} -
- Select a metric to view data -
- {/if} -
- -
- -
- -
- -
- - -
-
- \ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/monitoring/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/monitoring/+page.ts deleted file mode 100644 index 227f6969e..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/monitoring/+page.ts +++ /dev/null @@ -1,97 +0,0 @@ -export const ssr = false; - -import { getMaxDataPoints } from "$lib/utils"; -import type { PageLoad } from "./$types"; -import { - getDriftProfiles, - getProfileConfig, - getProfileFeatures, - type UiProfile, -} from "$lib/components/card/monitoring/utils"; -import { DriftType, TimeInterval } from "$lib/components/card/monitoring/types"; -import { - getLatestMetrics, - getCurrentMetricData, -} from "$lib/components/card/monitoring/utils"; -import { getDriftAlerts } from "$lib/components/card/monitoring/alert/utils"; -import { getLLMRecordPage } from "$lib/components/card/monitoring/utils"; -import type { ServiceInfo } from "$lib/components/card/monitoring/types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType } = await parent(); - - let profiles = await getDriftProfiles( - metadata.uid, - metadata.metadata.drift_profile_uri_map ?? {}, - registryType - ); - - // get all keys which should be of DriftType - const keys: DriftType[] = Object.keys(profiles) - .filter((key): key is DriftType => { - return Object.values(DriftType).includes(key as DriftType); - }) - .sort(); - - let currentDriftType = keys[0]; - let currentProfile: UiProfile = profiles[currentDriftType]; - let currentNames: string[] = getProfileFeatures( - currentDriftType, - currentProfile.profile - ); - let currentName: string = currentNames[0]; - let currentConfig = getProfileConfig( - currentDriftType, - currentProfile.profile - ); - let maxDataPoints = getMaxDataPoints(); - - let latestMetrics = await getLatestMetrics( - profiles, - TimeInterval.SixHours, - maxDataPoints - ); - - // Filter latest metrics to the current drift type - let currentMetricData = getCurrentMetricData( - latestMetrics, - currentDriftType, - currentName - ); - - let currentAlerts = await getDriftAlerts( - currentConfig.space, - currentConfig.name, - currentConfig.version, - TimeInterval.SixHours, - true - ); - - let service_info: ServiceInfo = { - space: currentConfig.space, - name: currentConfig.name, - version: currentConfig.version, - }; - - let currentLLMRecords = await getLLMRecordPage( - service_info, - undefined, - undefined - ); - - return { - profiles, - keys, - currentName, - currentNames, - currentDriftType, - currentProfile, - currentConfig, - latestMetrics, - currentMetricData, - maxDataPoints, - currentAlerts, - currentLLMRecords, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/readme/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/readme/+page.svelte deleted file mode 100644 index 575f90d49..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/readme/+page.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - -
-
-
- -
-
-
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/readme/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/readme/+page.ts deleted file mode 100644 index 5a627a9b4..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/readme/+page.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType, readme, registryPath } = await parent(); - - let content: string = ""; - - if (readme.exists) { - content = readme.readme; - } else { - content = `# Prompt Card for ${metadata.space}/${metadata.name} - -**Summary:** -Briefly describe what this prompt does and its main purpose. - ---- - -## Details - -- **Space:** ${metadata.space} -- **Name:** ${metadata.name} -- **Version:** ${metadata.version} -- **Registry:** ${registryType} - ---- - -## Description - -Describe the prompt in detail, including its intended use, context, and any relevant background. - ---- - -## Development Notes - -Explain how the prompt was created, including any sources, methodology, or special considerations. - ---- - -## Example Usage - -\`\`\`python -from opsml import CardRegistry -registry = CardRegistry("${registryPath}") -card = registry.load_card(uid="${metadata.uid}") -\`\`\` -`; - } - - return { - metadata, - content, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/versions/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/versions/+page.svelte deleted file mode 100644 index 23a0b60da..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/versions/+page.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/versions/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/versions/+page.ts deleted file mode 100644 index de913c0f4..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/genai/prompt/card/[space]/[name]/[version]/versions/+page.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const ssr = false; - -import { getRegistryStats, getVersionPage } from "$lib/components/card/utils"; -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType } = await parent(); - - let [versionPage, versionStats] = await Promise.all([ - getVersionPage(registryType, metadata.space, metadata.name), - getRegistryStats(registryType, metadata.name, [metadata.space]), - ]); - - return { versionPage, versionStats }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/model/+layout.svelte deleted file mode 100644 index c934f0101..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/+layout.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -
- - {@render children()} - -
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/+page.server.ts deleted file mode 100644 index e78075d8b..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/+page.server.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ fetch }) => { - let registryPage = await setupRegistryPage( - RegistryType.Model, - undefined, - undefined, - fetch - ); - - return { - page: registryPage, - selectedSpace: undefined, - selectedName: undefined, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/model/+page.svelte deleted file mode 100644 index 1a760d5aa..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/[uid]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/[uid]/+page.server.ts deleted file mode 100644 index 05959e330..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/[uid]/+page.server.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { redirect } from "@sveltejs/kit"; -import type { PageLoad } from "./$types"; -import { getRegistryTypeLowerCase, RegistryType } from "$lib/utils"; -import type { ServiceCard } from "$lib/components/card/card_interfaces/servicecard"; -import { getCardMetadata } from "$lib/server/card/utils"; - -export const load: PageLoad = async ({ params, fetch }) => { - let registryType = RegistryType.Model; - - let resp = await getCardMetadata( - undefined, - undefined, - undefined, - params.uid, - registryType, - fetch - ); - - let metadata = (await resp.json()) as ServiceCard; - - redirect( - 301, - `/opsml/${getRegistryTypeLowerCase(registryType)}/card/${metadata.space}/${ - metadata.name - }/${metadata.version}/card` - ); -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/+page.server.ts deleted file mode 100644 index a61b14371..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/+page.server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = undefined; // No name parameter in this route - - let registryPage = await setupRegistryPage( - RegistryType.Model, - space, - name, - fetch - ); - return { - page: registryPage, - selectedSpace: space, - selectedName: name, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/+page.svelte deleted file mode 100644 index b802292c5..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/+page.svelte +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/+page.server.ts deleted file mode 100644 index 53e9e9c06..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/+page.server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = params.name; - - let registryPage = await setupRegistryPage( - RegistryType.Model, - space, - name, - fetch - ); - return { page: registryPage, selectedSpace: space, selectedName: name }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/+layout.svelte deleted file mode 100644 index 1f3d56b1b..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/+layout.svelte +++ /dev/null @@ -1,79 +0,0 @@ - - -
-
-
-

- - -
/
-
{data.metadata.version}
-

- -
- - - Card - - - - Files - - {#if data.metadata.metadata.interface_metadata.save_metadata.drift_profile_uri_map && uiSettingsStore.scouterEnabled} - - - Monitoring - - {/if} - - - Versions - -
-
-
- - -
- {@render children()} -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/+layout.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/+layout.ts deleted file mode 100644 index 1ec47a389..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/+layout.ts +++ /dev/null @@ -1,30 +0,0 @@ -export const prerender = false; -export const ssr = false; -import { getRegistryTypeLowerCase } from "$lib/utils"; -import { getCardMetadata } from "$lib/components/card/utils"; - -import type { LayoutLoad } from "./$types"; -import type { ModelCard } from "$lib/components/card/card_interfaces/modelcard"; -import { getCardReadMe } from "$lib/components/readme/util"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -// @ts-ignore -export const load: LayoutLoad = async ({ params, parent }) => { - const { registryType } = await parent(); - const { space, name, version } = params; - - let metadata = (await getCardMetadata( - space, - name, - version, - undefined, - registryType - )) as ModelCard; - - let readme = await getCardReadMe(metadata.name, metadata.space, registryType); - - let registryPath = getRegistryTypeLowerCase(registryType); - let activeTab = "card"; // Default active tab - - return { metadata, registryType, readme, activeTab }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/card/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/card/+page.ts deleted file mode 100644 index 9958265b6..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/card/+page.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType, readme } = await parent(); - - return { metadata, registryType, readme }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/+page.ts deleted file mode 100644 index 1171d584b..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/+page.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { getFileTree } from "$lib/components/files/utils"; -import { getRegistryTableName } from "$lib/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType } = await parent(); - - let tableName = getRegistryTableName(registryType); - let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; - - let fileTree = await getFileTree(basePath); - - return { fileTree, previousPath: basePath, isRoot: true }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/[...file]/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/[...file]/+page.ts deleted file mode 100644 index 51e2b25cc..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/[...file]/+page.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const ssr = false; -export const prerender = false; - -import type { PageLoad } from "./$types"; -import { getFileTree } from "$lib/components/files/utils"; -import { getRegistryPath, getRegistryTableName } from "$lib/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent, params }) => { - let slug = params.file as string; - - // split slug with '/' - let slugs = slug.split("/"); - - const { metadata, registryType } = await parent(); - - let tableName = getRegistryTableName(registryType); - let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; - - // join all but the last element of the slugs to get the previous path without final "/" - let previousPath = `/opsml/${getRegistryPath(registryType)}/card/${ - metadata.space - }/${metadata.name}/${metadata.version}/files/${slugs - .slice(0, slugs.length - 1) - .join("/")}`.replace(/\/$/, ""); - - // add the rest of the slugs to the basePath - basePath = `${basePath}/${slugs.join("/")}`; - - let fileTree = await getFileTree(basePath); - - return { fileTree, previousPath, isRoot: false }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/view/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/view/+page.ts deleted file mode 100644 index ce2345bd8..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/files/view/+page.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const ssr = false; -export const prerender = false; - -import type { PageLoad } from "./$types"; -import { getRawFile } from "$lib/components/files/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; -import { RegistryType } from "$lib/utils"; - -export const load: PageLoad = async ({ parent, url }) => { - const { metadata } = await parent(); - const viewPath = (url as URL).searchParams.get("path") as string; - - let rawFile = await getRawFile(viewPath, metadata.uid, RegistryType.Model); - let splitPath = viewPath.split("/"); - - return { rawFile, viewPath, splitPath }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/monitoring/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/monitoring/+page.svelte deleted file mode 100644 index b53b8f5bc..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/monitoring/+page.svelte +++ /dev/null @@ -1,200 +0,0 @@ - - -
-
- - - -
-
-
- - -
- - {#if currentName && latestMetrics} - {#if currentMetricData} - - {:else} -
- No data available for selected metric -
- {/if} - {:else} -
- Select a metric to view data -
- {/if} -
- - -
- -
- - - - -
-
- \ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/monitoring/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/monitoring/+page.ts deleted file mode 100644 index 3002f5709..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/monitoring/+page.ts +++ /dev/null @@ -1,85 +0,0 @@ -export const ssr = false; - -import { getMaxDataPoints } from "$lib/utils"; -import type { PageLoad } from "./$types"; -import { - getDriftProfiles, - getProfileConfig, - getProfileFeatures, - type UiProfile, -} from "$lib/components/card/monitoring/utils"; -import { DriftType, TimeInterval } from "$lib/components/card/monitoring/types"; -import { - // getLatestMetricsExample, - getLatestMetrics, - getCurrentMetricData, -} from "$lib/components/card/monitoring/utils"; -import { getDriftAlerts } from "$lib/components/card/monitoring/alert/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType } = await parent(); - - let profiles = await getDriftProfiles( - metadata.uid, - metadata.metadata.interface_metadata.save_metadata.drift_profile_uri_map ?? - {}, - registryType - ); - - // get all keys which should be of DriftType - const keys: DriftType[] = Object.keys(profiles) - .filter((key): key is DriftType => { - return Object.values(DriftType).includes(key as DriftType); - }) - .sort(); - - let currentDriftType = keys[0]; - let currentProfile: UiProfile = profiles[currentDriftType]; - let currentNames: string[] = getProfileFeatures( - currentDriftType, - currentProfile.profile - ); - let currentName: string = currentNames[0]; - let currentConfig = getProfileConfig( - currentDriftType, - currentProfile.profile - ); - let maxDataPoints = getMaxDataPoints(); - - // get latest metrics for all available drift profiles - let latestMetrics = await getLatestMetrics( - profiles, - TimeInterval.SixHours, - maxDataPoints - ); - - // Filter latest metrics to the current drift type - let currentMetricData = getCurrentMetricData( - latestMetrics, - currentDriftType, - currentName - ); - - let currentAlerts = await getDriftAlerts( - currentConfig.space, - currentConfig.name, - currentConfig.version, - TimeInterval.SixHours, - true - ); - - return { - profiles, - keys, - currentName, - currentNames, - currentDriftType, - currentProfile, - currentConfig, - latestMetrics, - currentMetricData, - maxDataPoints, - currentAlerts, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/readme/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/readme/+page.svelte deleted file mode 100644 index 575f90d49..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/readme/+page.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - -
-
-
- -
-
-
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/readme/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/readme/+page.ts deleted file mode 100644 index 5d22264f2..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/readme/+page.ts +++ /dev/null @@ -1,80 +0,0 @@ -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType, readme } = await parent(); - - let content: string = ""; - - if (readme.exists) { - content = readme.readme; - } else { - content = `# ModelCard for ${metadata.space}/${metadata.name} - - - -**Summary:** -Provide a concise summary of the model card, its intended use, and any notable features. - ---- - -## Model Details - -- **Space:** ${metadata.space} -- **Name:** ${metadata.name} -- **Version:** ${metadata.version} -- **Registry:** ${registryType} - ---- - -## Description - -Describe the model in detail. -Include its origin, intended use cases, and any relevant background information. - ---- - -## Contact - -- **Contact Person/Team:** [Add contact information or support email] -- **License:** [Specify license, e.g., MIT, Apache 2.0, etc.] - ---- - -## Model Development - -Explain how the model was developed, including sources, methodology, and any preprocessing steps. - ---- - -## Uses - -List and describe common or recommended uses for this model card. - ---- - -## Bias, Risk, and Limitations - -Discuss any known biases, risks, or limitations associated with this model. -Mention steps taken to mitigate these issues, if any. - ---- - -## Code Examples - - -\`\`\`python -# Example usage in Python -from opsml import CardRegistry -registry = CardRegistry("${registryType.toLowerCase()}") -card = registry.load_card(uid="${metadata.uid}") -\`\`\` - -`; - } - - return { - metadata, - content, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/versions/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/versions/+page.svelte deleted file mode 100644 index 23a0b60da..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/versions/+page.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/versions/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/versions/+page.ts deleted file mode 100644 index de913c0f4..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/model/card/[space]/[name]/[version]/versions/+page.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const ssr = false; - -import { getRegistryStats, getVersionPage } from "$lib/components/card/utils"; -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType } = await parent(); - - let [versionPage, versionStats] = await Promise.all([ - getVersionPage(registryType, metadata.space, metadata.name), - getRegistryStats(registryType, metadata.name, [metadata.space]), - ]); - - return { versionPage, versionStats }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/service/+layout.svelte deleted file mode 100644 index c934f0101..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/+layout.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -
- - {@render children()} - -
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/service/+page.server.ts deleted file mode 100644 index f403e4db6..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/+page.server.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ fetch }) => { - let registryPage = await setupRegistryPage( - RegistryType.Service, - undefined, - undefined, - fetch - ); - - return { - page: registryPage, - selectedSpace: undefined, - selectedName: undefined, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/service/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/[uid]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/service/[uid]/+page.server.ts deleted file mode 100644 index 887104d22..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/[uid]/+page.server.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { redirect } from "@sveltejs/kit"; -import type { PageLoad } from "./$types"; -import { getRegistryTypeLowerCase, RegistryType } from "$lib/utils"; -import type { ServiceCard } from "$lib/components/card/card_interfaces/servicecard"; -import { getCardMetadata } from "$lib/server/card/utils"; - -export const load: PageLoad = async ({ params, fetch }) => { - let registryType = RegistryType.Service; - - let resp = await getCardMetadata( - undefined, - undefined, - undefined, - params.uid, - registryType, - fetch - ); - - let metadata = (await resp.json()) as ServiceCard; - - redirect( - 301, - `/opsml/${getRegistryTypeLowerCase(registryType)}/card/${metadata.space}/${ - metadata.name - }/${metadata.version}/card` - ); -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/+page.server.ts deleted file mode 100644 index 6ef0af10b..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/+page.server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = undefined; // No name parameter in this route - - let registryPage = await setupRegistryPage( - RegistryType.Service, - space, - name, - fetch - ); - return { - page: registryPage, - selectedSpace: space, - selectedName: name, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/+page.server.ts deleted file mode 100644 index 9e4470472..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/+page.server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { setupRegistryPage } from "$lib/server/card/utils"; -import type { PageServerLoad } from "./$types"; -import { RegistryType } from "$lib/utils"; - -export const load: PageServerLoad = async ({ params, fetch }) => { - const space = params.space; - const name = params.name; - - let registryPage = await setupRegistryPage( - RegistryType.Service, - space, - name, - fetch - ); - return { - page: registryPage, - selectedSpace: space, - selectedName: name, - }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/+page.svelte deleted file mode 100644 index 512d359b7..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/+page.svelte +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/+layout.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/+layout.svelte deleted file mode 100644 index 50fc73dab..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/+layout.svelte +++ /dev/null @@ -1,69 +0,0 @@ - - -
-
-
-

- - -
/
-
{data.metadata.version}
-

- - -
-
- - -
- {@render children()} -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/+layout.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/+layout.ts deleted file mode 100644 index 4772e4e15..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/+layout.ts +++ /dev/null @@ -1,29 +0,0 @@ -export const prerender = false; -export const ssr = false; -import { getRegistryPath, getRegistryTypeLowerCase } from "$lib/utils"; -import { getCardMetadata } from "$lib/components/card/utils"; - -import type { LayoutLoad } from "./$types"; -import { getCardReadMe } from "$lib/components/readme/util"; -import type { ServiceCard } from "$lib/components/card/card_interfaces/servicecard"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -// @ts-ignore -export const load: LayoutLoad = async ({ params, parent }) => { - const { registryType } = await parent(); - const { space, name, version } = params; - - let metadata = (await getCardMetadata( - space, - name, - version, - undefined, - registryType - )) as ServiceCard; - - let readme = await getCardReadMe(metadata.name, metadata.space, registryType); - - let activeTab = "card"; // Default active tab - - return { metadata, registryType, readme, activeTab }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/card/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/card/+page.ts deleted file mode 100644 index 806ba447b..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/card/+page.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const ssr = false; - -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -// @ts-ignore -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType, readme } = await parent(); - - return { metadata, registryType, readme }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/+page.svelte deleted file mode 100644 index 737159d98..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/+page.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -
-
- -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/[...file]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/[...file]/+page.svelte deleted file mode 100644 index a38cf8d7a..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/[...file]/+page.svelte +++ /dev/null @@ -1,24 +0,0 @@ - - -
-
- -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/[...file]/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/[...file]/+page.ts deleted file mode 100644 index 51e2b25cc..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/[...file]/+page.ts +++ /dev/null @@ -1,33 +0,0 @@ -export const ssr = false; -export const prerender = false; - -import type { PageLoad } from "./$types"; -import { getFileTree } from "$lib/components/files/utils"; -import { getRegistryPath, getRegistryTableName } from "$lib/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent, params }) => { - let slug = params.file as string; - - // split slug with '/' - let slugs = slug.split("/"); - - const { metadata, registryType } = await parent(); - - let tableName = getRegistryTableName(registryType); - let basePath = `${tableName}/${metadata.space}/${metadata.name}/v${metadata.version}`; - - // join all but the last element of the slugs to get the previous path without final "/" - let previousPath = `/opsml/${getRegistryPath(registryType)}/card/${ - metadata.space - }/${metadata.name}/${metadata.version}/files/${slugs - .slice(0, slugs.length - 1) - .join("/")}`.replace(/\/$/, ""); - - // add the rest of the slugs to the basePath - basePath = `${basePath}/${slugs.join("/")}`; - - let fileTree = await getFileTree(basePath); - - return { fileTree, previousPath, isRoot: false }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/view/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/view/+page.svelte deleted file mode 100644 index 30f571e82..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/view/+page.svelte +++ /dev/null @@ -1,23 +0,0 @@ - - -
-
- -
-
diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/view/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/view/+page.ts deleted file mode 100644 index f165ecabc..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/files/view/+page.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const ssr = false; -export const prerender = false; - -import type { PageLoad } from "./$types"; -import { getRawFile } from "$lib/components/files/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent, url }) => { - const { metadata, registryType } = await parent(); - const viewPath = (url as URL).searchParams.get("path") as string; - - let rawFile = await getRawFile(viewPath, metadata.uid, registryType); - let splitPath = viewPath.split("/"); - - return { rawFile, viewPath, splitPath }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/versions/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/versions/+page.svelte deleted file mode 100644 index 23a0b60da..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/versions/+page.svelte +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/versions/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/versions/+page.ts deleted file mode 100644 index de913c0f4..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/service/card/[space]/[name]/[version]/versions/+page.ts +++ /dev/null @@ -1,16 +0,0 @@ -export const ssr = false; - -import { getRegistryStats, getVersionPage } from "$lib/components/card/utils"; -import type { PageLoad } from "./$types"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; - -export const load: PageLoad = async ({ parent }) => { - const { metadata, registryType } = await parent(); - - let [versionPage, versionStats] = await Promise.all([ - getVersionPage(registryType, metadata.space, metadata.name), - getRegistryStats(registryType, metadata.name, [metadata.space]), - ]); - - return { versionPage, versionStats }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/space/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/space/+page.server.ts new file mode 100644 index 000000000..7f2a8a26d --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/space/+page.server.ts @@ -0,0 +1,11 @@ +export const ssr = false; + +import { getAllSpaceStats } from "$lib/server/space/utils"; +import type { PageServerLoad } from "./$types"; + +export const load: PageServerLoad = async ({ fetch }) => { + // get space for url if exists + let spaces = await getAllSpaceStats(fetch); + + return { spaces }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/space/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/space/+page.ts deleted file mode 100644 index 3025d2b34..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/space/+page.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const ssr = false; - -import { getAllSpaceStats } from "$lib/components/space/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; -import type { PageLoad } from "./$types"; - -export const load: PageLoad = async ({}) => { - // get space for url if exists - let spaces = await getAllSpaceStats(); - - return { spaces }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/space/[space]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/space/[space]/+page.server.ts new file mode 100644 index 000000000..5defb6d38 --- /dev/null +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/space/[space]/+page.server.ts @@ -0,0 +1,11 @@ +import { getSpace } from "$lib/server/space/utils"; +import type { PageServerLoad } from "./$types"; +import { getRecentCards } from "$lib/components/home/utils.server"; + +export const load: PageServerLoad = async ({ params, fetch }) => { + // get space for url if exists + let spaceRecord = await getSpace(fetch, params.space); + let recentCards = await getRecentCards(fetch, params.space); + + return { spaceRecord, recentCards }; +}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/space/[space]/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/space/[space]/+page.svelte index 1564df424..558ccd739 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/space/[space]/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/space/[space]/+page.svelte @@ -1,11 +1,13 @@ diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/space/[space]/+page.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/space/[space]/+page.ts deleted file mode 100644 index 20e38b7a2..000000000 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/space/[space]/+page.ts +++ /dev/null @@ -1,14 +0,0 @@ -export const prerender = false; - -import { getSpace } from "$lib/components/space/utils"; -import { validateUserOrRedirect } from "$lib/components/user/user.svelte"; -import type { PageLoad } from "./$types"; -import { getRecentCards } from "$lib/components/home/utils.server"; - -export const load: PageLoad = async ({ params }) => { - // get space for url if exists - let spaceRecord = await getSpace(params.space); - let recentCards = await getRecentCards(params.space); - - return { spaceRecord, recentCards }; -}; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/user/logout/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/user/logout/+page.svelte index 785dbd53a..2d826434d 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/user/logout/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/user/logout/+page.svelte @@ -7,9 +7,6 @@ import Warning from "$lib/components/utils/Warning.svelte"; import type { LogOutResponse } from "$lib/components/user/types"; - - let { fetch }: PageProps = $props(); - let showLogoutError: boolean = $state(false); let errorMessage: string = $state("Failed to logout"); diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/user/profile/[username]/+page.server.ts b/crates/opsml_server/opsml_ui/src/routes/opsml/user/profile/[username]/+page.server.ts index eb4175648..699276518 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/user/profile/[username]/+page.server.ts +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/user/profile/[username]/+page.server.ts @@ -1,8 +1,8 @@ import type { PageServerLoad } from "./$types"; -import { getUser } from "$lib/server/user/util"; +import { getUser } from "$lib/server/user/utils"; export const load: PageServerLoad = async ({ params, cookies, fetch }) => { const username = params.username ?? cookies.get("username") ?? ""; - const userInfo = await getUser(username, fetch, cookies.get("jwt_token")); + const userInfo = await getUser(username, fetch); return { userInfo }; }; diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/user/register/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/user/register/+page.svelte index 1310e4512..8cdf63baf 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/user/register/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/user/register/+page.svelte @@ -10,7 +10,6 @@ import { userStore } from "$lib/components/user/user.svelte"; import type { CreateUserUiResponse } from "$lib/components/user/types"; - let { fetch }: PageProps = $props(); let username: string = $state(''); let password: string = $state(''); diff --git a/crates/opsml_server/opsml_ui/src/routes/opsml/user/reset/+page.svelte b/crates/opsml_server/opsml_ui/src/routes/opsml/user/reset/+page.svelte index 6e134a41c..64a53d7c1 100644 --- a/crates/opsml_server/opsml_ui/src/routes/opsml/user/reset/+page.svelte +++ b/crates/opsml_server/opsml_ui/src/routes/opsml/user/reset/+page.svelte @@ -5,11 +5,9 @@ import { ServerPaths, UiPaths } from "$lib/components/api/routes"; import { goTop } from "$lib/utils"; import { validatePasswordResetSchema, type PasswordResetSchema } from "$lib/components/user/schema"; - import { HelpCircle } from 'lucide-svelte'; + import { HelpCircle, Eye, EyeOff } from 'lucide-svelte'; import { createInternalApiClient } from "$lib/api/internalClient"; - let { fetch }: PageProps = $props(); - let username: string = $state(''); let recoveryCode: string = $state(''); let newPassword: string = $state(''); @@ -19,6 +17,16 @@ let resetMessage: string = $state("Password reset successful! You can now log in with your new password."); let showPasswordHelp: boolean = $state(false); let passwordErrors = $state>>({}); + let passwordVisible: boolean = $state(false); + + function togglePasswordHelp() { + showPasswordHelp = !showPasswordHelp; + } + + function togglePasswordVisibility() { + passwordVisible = !passwordVisible; + + } /** * Handles password reset form submission. @@ -26,9 +34,11 @@ * @param event Form submit event */ async function handleReset(event: Event) { + event.preventDefault(); - const argsValid = validatePasswordResetSchema(username, recoveryCode, newPassword); + + const argsValid = validatePasswordResetSchema(username, recoveryCode, newPassword, confirmPassword); if (!argsValid.success) { showPasswordHelp = true; @@ -69,88 +79,117 @@ async function handleReset(event: Event) { -
+
+ OpsML logo -

Reset your password

+

Reset your password

+ {#if showResetMessage} - +
+ +
{/if} -
-
\ No newline at end of file diff --git a/crates/opsml_server/opsml_ui/svelte.config.js b/crates/opsml_server/opsml_ui/svelte.config.js index 043584b99..2c865299b 100644 --- a/crates/opsml_server/opsml_ui/svelte.config.js +++ b/crates/opsml_server/opsml_ui/svelte.config.js @@ -9,11 +9,7 @@ const config = { adapter: adapter({ out: "build", precompress: false, - envPrefix: "OPSML_", }), - env: { - privatePrefix: "OPSML_", - }, }, extensions: [".svelte"], }; diff --git a/crates/opsml_server/src/core/mod.rs b/crates/opsml_server/src/core/mod.rs index 9266c2ffe..bb7aed51b 100644 --- a/crates/opsml_server/src/core/mod.rs +++ b/crates/opsml_server/src/core/mod.rs @@ -15,5 +15,4 @@ pub mod settings; pub mod setup; pub mod shutdown; pub mod state; -pub mod ui; pub mod user; diff --git a/crates/opsml_server/src/core/router.rs b/crates/opsml_server/src/core/router.rs index 6ef79a507..8310c3b22 100644 --- a/crates/opsml_server/src/core/router.rs +++ b/crates/opsml_server/src/core/router.rs @@ -11,7 +11,6 @@ use crate::core::middleware::metrics::track_metrics; use crate::core::scouter::route::get_scouter_router; use crate::core::settings::route::get_settings_router; use crate::core::state::AppState; -use crate::core::ui::get_ui_router; use crate::core::user::route::get_user_router; use anyhow::Result; use axum::http::{ @@ -48,7 +47,6 @@ pub async fn create_router(app_state: Arc) -> Result { let user_routes = get_user_router(ROUTE_PREFIX).await?; let scouter_routes = get_scouter_router(ROUTE_PREFIX).await?; let genai_routes = get_genai_router(ROUTE_PREFIX).await?; - let ui_routes = get_ui_router().await?; // merge all the routes except the auth routes // All routes except the auth, healthcheck, ui and ui settings routes are protect by the auth middleware @@ -77,7 +75,6 @@ pub async fn create_router(app_state: Arc) -> Result { .merge(health_routes) .merge(settings_routes) .merge(auth_routes) - .merge(ui_routes) .route_layer(middleware::from_fn(track_metrics)) .layer(cors) .with_state(app_state)) diff --git a/crates/opsml_server/src/core/ui/mod.rs b/crates/opsml_server/src/core/ui/mod.rs deleted file mode 100644 index 0d20d3ff2..000000000 --- a/crates/opsml_server/src/core/ui/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod route; -pub mod schema; -pub use route::get_ui_router; diff --git a/crates/opsml_server/src/core/ui/route.rs b/crates/opsml_server/src/core/ui/route.rs deleted file mode 100644 index 230b4ec2b..000000000 --- a/crates/opsml_server/src/core/ui/route.rs +++ /dev/null @@ -1,107 +0,0 @@ -use crate::core::state::AppState; -use anyhow::Result; -use axum::Router; -use axum::{ - http::{header::CONTENT_TYPE, StatusCode, Uri}, - response::{IntoResponse, Response}, - routing::get, -}; -use rust_embed::Embed; -use std::sync::Arc; -use tracing::error; - -#[derive(Embed)] -#[folder = "opsml_ui/site/"] -struct Assets; - -async fn serve_sveltekit_app() -> Response { - match Assets::get("index.html") { - Some(content) => { - let mime = mime_guess::from_path("index.html").first_or_octet_stream(); - ([(CONTENT_TYPE, mime.as_ref())], content.data).into_response() - } - None => not_found().await, - } -} - -async fn get_static_file(path: &str) -> Response { - if path.starts_with("opsml/api/") { - return not_found().await; - } - - if let Some(content) = Assets::get(path) { - let mime = mime_guess::from_path(path).first_or_octet_stream(); - return ([(CONTENT_TYPE, mime.as_ref())], content.data).into_response(); - } - - if is_dynamic_card_route(path) || is_dynamic_genai_card_route(path) { - return serve_sveltekit_app().await; - } - - // Check for root opsml path - if path.starts_with("opsml/") && !path.split('/').next_back().unwrap_or("").contains('.') { - return serve_home_html().await; - } - - if path == "opsml" || path == "opsml/" || path.is_empty() { - return serve_home_html().await; - } - - not_found().await -} - -fn is_dynamic_card_route(path: &str) -> bool { - let parts: Vec<&str> = path.split('/').collect(); - - // /opsml/{registry}/card/{space}/{name}/{version} - parts.len() == 6 - && parts[0] == "opsml" - && parts[2] == "card" - && matches!(parts[1], "data" | "model" | "experiment" | "service") -} - -fn is_dynamic_genai_card_route(path: &str) -> bool { - let parts: Vec<&str> = path.split('/').collect(); - - // /opsml/genai/{subregistry}/card/{space}/{name}/{version} - parts.len() == 7 && parts[0] == "opsml" && parts[1] == "genai" && parts[3] == "card" -} - -async fn static_handler(uri: Uri) -> impl IntoResponse { - let path = uri.path().trim_start_matches('/'); - get_static_file(path).await -} - -async fn serve_home_html() -> Response { - match Assets::get("opsml/home.html") { - Some(content) => { - let mime = mime_guess::from_path("home.html").first_or_octet_stream(); - ([(CONTENT_TYPE, mime.as_ref())], content.data).into_response() - } - None => { - error!("opsml/home.html not found!"); - not_found().await - } - } -} - -async fn not_found() -> Response { - (StatusCode::NOT_FOUND, "404").into_response() -} - -async fn opsml_home() -> impl IntoResponse { - get_static_file("opsml/home.html").await -} - -async fn root_redirect() -> Response { - axum::response::Redirect::permanent("/opsml/home").into_response() -} - -pub async fn get_ui_router() -> Result>> { - Ok(Router::new() - .route("/", get(root_redirect)) - .route("/opsml", get(opsml_home)) - .route("/opsml/home", get(opsml_home)) - .route("/opsml/{*path}", get(static_handler)) - .fallback(static_handler)) -} diff --git a/crates/opsml_server/src/core/ui/schema.rs b/crates/opsml_server/src/core/ui/schema.rs deleted file mode 100644 index f0c4df671..000000000 --- a/crates/opsml_server/src/core/ui/schema.rs +++ /dev/null @@ -1,6 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct CardsRequest { - pub space: Option, -} diff --git a/crates/opsml_storage/src/storage/filesystem.rs b/crates/opsml_storage/src/storage/filesystem.rs index 7e088d4ab..0a58246de 100644 --- a/crates/opsml_storage/src/storage/filesystem.rs +++ b/crates/opsml_storage/src/storage/filesystem.rs @@ -350,7 +350,7 @@ mod tests { } pub fn set_env_vars() { - std::env::set_var("OPSML_TRACKING_URI", "http://0.0.0.0:3000"); + std::env::set_var("OPSML_TRACKING_URI", "http://0.0.0.0:8080"); } pub fn unset_env_vars() { diff --git a/docker/official/alpine/Dockerfile b/docker/official/alpine/Dockerfile deleted file mode 100644 index a63dc1ecf..000000000 --- a/docker/official/alpine/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM alpine:latest - -ARG OPSML_SERVER_BINARY - -COPY ${OPSML_SERVER_BINARY} /opsml-server - -CMD ["/opsml-server"] \ No newline at end of file diff --git a/docker/official/debian/Dockerfile b/docker/official/debian/Dockerfile index ca814a1c6..84f905447 100644 --- a/docker/official/debian/Dockerfile +++ b/docker/official/debian/Dockerfile @@ -3,18 +3,44 @@ FROM debian:stable-slim ENV DEBIAN_FRONTEND=noninteractive ENV LANG=en_US.UTF-8 ENV LANGUAGE=en_US.UTF-8 +ENV PROJECT_HOME=opsml +# Create non-root user and group +RUN groupadd -r opsml && useradd -r -g opsml -s /bin/bash -d /app opsml + +# Install required packages: nginx, nodejs, netcat, curl, etc. RUN apt-get update --no-install-recommends \ && apt-get install --no-install-recommends --yes \ - ca-certificates tzdata curl \ + ca-certificates tzdata curl nginx netcat-openbsd \ + && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install --no-install-recommends --yes nodejs \ && rm -rf /var/lib/apt/lists/* \ && apt-get autoremove \ && apt-get clean +ARG OPSML_PORT=8000 ARG OPSML_SERVER_BINARY -COPY ${OPSML_SERVER_BINARY} /opsml-server +# Copy built UI, server binary, NGINX config, and entrypoint script +COPY --chown=opsml:opsml ${OPSML_SERVER_BINARY}/ui/node_modules /app/ui/node_modules/ +COPY --chown=opsml:opsml ${OPSML_SERVER_BINARY}/ui/build /app/ui/build/ +COPY --chown=opsml:opsml ${OPSML_SERVER_BINARY}/opsml-server /usr/local/bin/opsml-server +COPY docker/official/extras/nginx/nginx.conf.template /etc/nginx/nginx.conf.template +COPY docker/official/extras/entrypoint.sh /entrypoint.sh + +# Set permissions and create directories +RUN mkdir -p /var/log/nginx /var/run \ + && rm -f /etc/nginx/sites-enabled/default \ + && chmod +x /entrypoint.sh \ + && chmod +x /usr/local/bin/opsml-server \ + && chown -R opsml:opsml /var/log/nginx /var/run + +ENV OPSML_SERVER_PORT=${OPSML_SERVER_PORT} + +WORKDIR /app + +USER opsml -RUN chmod +x /opsml-server +EXPOSE ${OPSML_PORT} -CMD ["/opsml-server"] \ No newline at end of file +CMD ["/entrypoint.sh"] \ No newline at end of file diff --git a/docker/official/distroless/Dockerfile b/docker/official/distroless/Dockerfile deleted file mode 100644 index d9fff4492..000000000 --- a/docker/official/distroless/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -FROM debian:bullseye-slim as certs -RUN apt-get update && apt-get install -y ca-certificates && update-ca-certificates - -FROM gcr.io/distroless/cc-debian11 -COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ - -ARG OPSML_SERVER_BINARY - -COPY ${OPSML_SERVER_BINARY} /opsml-server -USER nonroot:nonroot -ENTRYPOINT ["/opsml-server"] \ No newline at end of file diff --git a/docker/official/extras/entrypoint.sh b/docker/official/extras/entrypoint.sh new file mode 100644 index 000000000..deaeb27ea --- /dev/null +++ b/docker/official/extras/entrypoint.sh @@ -0,0 +1,142 @@ +#!/bin/bash +set -e + +# All output goes to stdout/stderr for Kubernetes logging +exec 2>&1 + +# PID tracking +RUST_API_PID="" +SVELTEKIT_PID="" +NGINX_PID="" + +# Cleanup function +cleanup() { + echo "$(date): Received shutdown signal, cleaning up..." + + # Graceful shutdown in reverse order + if [[ -n "$NGINX_PID" ]]; then + echo "$(date): Stopping NGINX..." + kill -QUIT $NGINX_PID 2>/dev/null || true + fi + + if [[ -n "$SVELTEKIT_PID" ]]; then + echo "$(date): Stopping SvelteKit..." + kill -TERM $SVELTEKIT_PID 2>/dev/null || true + fi + + if [[ -n "$RUST_API_PID" ]]; then + echo "$(date): Stopping Rust API..." + kill -TERM $RUST_API_PID 2>/dev/null || true + fi + + # Wait for graceful shutdown + sleep 5 + + # Force kill if still running + for pid in $NGINX_PID $SVELTEKIT_PID $RUST_API_PID; do + if [[ -n "$pid" ]] && kill -0 $pid 2>/dev/null; then + echo "$(date): Force killing process $pid" + kill -KILL $pid 2>/dev/null || true + fi + done + + echo "$(date): Cleanup complete" + exit 0 +} + +# Set up signal handlers for Kubernetes +trap cleanup SIGTERM SIGINT + +# Wait for service function +wait_for_service() { + local port=$1 + local service_name=$2 + local max_attempts=30 + local attempt=1 + + echo "$(date): Waiting for $service_name on port $port..." + + while [ $attempt -le $max_attempts ]; do + if nc -z localhost $port 2>/dev/null; then + echo "$(date): $service_name is ready on port $port" + return 0 + fi + + if [ $((attempt % 5)) -eq 0 ]; then + echo "$(date): Still waiting for $service_name (attempt $attempt/$max_attempts)" + fi + + sleep 2 + attempt=$((attempt + 1)) + done + + echo "$(date): ERROR: $service_name failed to start on port $port after $max_attempts attempts" + return 1 +} + +# update nginx template +envsubst '${OPSML_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf + +# Start Rust API server +echo "$(date): Starting Rust API server on port ${OPSML_SERVER_PORT:-8080}..." +/usr/local/bin/opsml-server & +RUST_API_PID=$! +echo "$(date): Rust API PID: $RUST_API_PID" + +# Wait for Rust API +if ! wait_for_service ${OPSML_SERVER_PORT:-8080} "Rust API"; then + echo "$(date): Failed to start Rust API, exiting..." + exit 1 +fi + +# Start SvelteKit server +echo "$(date): Starting SvelteKit server on port 3000..." +cd /app/ui +PORT=3000 NODE_ENV=production node build/index.js & +SVELTEKIT_PID=$! +echo "$(date): SvelteKit PID: $SVELTEKIT_PID" + +# Wait for SvelteKit +if ! wait_for_service 3000 "SvelteKit"; then + echo "$(date): Failed to start SvelteKit, exiting..." + exit 1 +fi + +# Start NGINX +echo "$(date): Testing NGINX configuration..." +if ! nginx -t; then + echo "$(date): NGINX configuration test failed, exiting..." + exit 1 +fi + +echo "$(date): Starting NGINX on port ${OPSML_PORT:-8000}..." +nginx -g "daemon off;" & +NGINX_PID=$! +echo "$(date): NGINX PID: $NGINX_PID" + +# Wait for NGINX +if ! wait_for_service "${OPSML_PORT:-8000}" "NGINX"; then + echo "$(date): Failed to start NGINX, exiting..." + exit 1 +fi + +echo "$(date): All services started successfully" +echo "$(date): Rust API: $RUST_API_PID, SvelteKit: $SVELTEKIT_PID, NGINX: $NGINX_PID" + +# Process monitoring loop +while true; do + # Check if any process has died + for pid_name in "RUST_API_PID:Rust API" "SVELTEKIT_PID:SvelteKit" "NGINX_PID:NGINX"; do + pid_var=$(echo $pid_name | cut -d: -f1) + service_name=$(echo $pid_name | cut -d: -f2) + pid_value=$(eval echo \$$pid_var) + + if [[ -n "$pid_value" ]] && ! kill -0 $pid_value 2>/dev/null; then + echo "$(date): ERROR: $service_name (PID: $pid_value) has died unexpectedly" + cleanup + exit 1 + fi + done + + sleep 10 +done \ No newline at end of file diff --git a/docker/official/extras/nginx/nginx.conf.template b/docker/official/extras/nginx/nginx.conf.template new file mode 100644 index 000000000..8e5b13c1c --- /dev/null +++ b/docker/official/extras/nginx/nginx.conf.template @@ -0,0 +1,117 @@ +worker_processes auto; +pid /tmp/nginx.pid; +error_log /dev/stderr warn; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + # Logging + access_log off; # only want to see ui and backend logs + error_log /dev/stderr; + + # Use /tmp for non-root user + client_body_temp_path /tmp/client_temp; + proxy_temp_path /tmp/proxy_temp_path; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + # Performance optimizations + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + server_tokens off; + + # Buffer sizes for better performance + client_max_body_size 16M; + client_body_buffer_size 128k; + proxy_buffering on; + proxy_buffer_size 4k; + proxy_buffers 8 4k; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_comp_level 6; + gzip_types + text/plain + text/css + application/json + application/javascript + text/xml + application/xml + application/xml+rss + text/javascript + application/wasm; + + + upstream sveltekit_backend { + server 127.0.0.1:3000 max_fails=3 fail_timeout=30s; + keepalive 32; + } + + upstream rust_api { + server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; + keepalive 32; + } + + server { + listen ${OPSML_PORT}; + server_name _; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + # API routes - route to Rust backend + location /opsml/api/ { + proxy_pass http://rust_api/opsml/api/; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 86400; + proxy_send_timeout 86400; + } + + # Health check for Rust API + location /health { + proxy_pass http://rust_api/opsml/api/healthcheck; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + access_log off; # Don't log health checks + } + + # All other routes - route to SvelteKit SSR + location / { + proxy_pass http://sveltekit_backend; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + proxy_read_timeout 86400; + proxy_send_timeout 86400; + } + } +} \ No newline at end of file diff --git a/docker/official/rocky/Dockerfile b/docker/official/rocky/Dockerfile new file mode 100644 index 000000000..f6b5ad5fb --- /dev/null +++ b/docker/official/rocky/Dockerfile @@ -0,0 +1,40 @@ +FROM rockylinux:9-minimal + +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US.UTF-8 +ENV PROJECT_HOME=opsml + +RUN groupadd -r opsml && useradd -r -g opsml -s /bin/bash -d /app opsml + +RUN microdnf update -y \ + && microdnf install -y \ + ca-certificates tzdata curl nginx nmap-ncat \ + glibc-langpack-en \ + && curl -fsSL https://rpm.nodesource.com/setup_22.x | bash - \ + && microdnf install -y nodejs \ + && microdnf clean all \ + && rm -rf /var/cache/dnf/* + +ARG OPSML_PORT=8000 +ARG OPSML_SERVER_BINARY + +# Copy application files +COPY --chown=opsml:opsml ${OPSML_SERVER_BINARY}/ui/node_modules /app/ui/node_modules/ +COPY --chown=opsml:opsml ${OPSML_SERVER_BINARY}/ui/build /app/ui/build/ +COPY --chown=opsml:opsml ${OPSML_SERVER_BINARY}/opsml-server /usr/local/bin/opsml-server +COPY docker/official/extras/nginx/nginx.conf.template /etc/nginx/nginx.conf.template +COPY docker/official/extras/entrypoint.sh /entrypoint.sh + +RUN mkdir -p /var/log/nginx /var/run /var/lib/nginx \ + && rm -f /etc/nginx/conf.d/default.conf \ + && chmod +x /entrypoint.sh \ + && chmod +x /usr/local/bin/opsml-server \ + && chown -R opsml:opsml /var/log/nginx /var/run /var/lib/nginx + +WORKDIR /app + +USER opsml + +EXPOSE ${OPSML_PORT} + +CMD ["/entrypoint.sh"] \ No newline at end of file diff --git a/docker/official/scratch/Dockerfile b/docker/official/scratch/Dockerfile deleted file mode 100644 index 8de338e38..000000000 --- a/docker/official/scratch/Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -FROM rust:latest AS builder - -RUN apt update && update-ca-certificates - -FROM scratch - -ARG OPSML_SERVER_BINARY - -COPY ${OPSML_SERVER_BINARY} /opsml-server - -COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt - -CMD ["/opsml-server"] \ No newline at end of file diff --git a/docker/official/ubuntu/Dockerfile b/docker/official/ubuntu/Dockerfile index 48b14fce8..57949dc00 100644 --- a/docker/official/ubuntu/Dockerfile +++ b/docker/official/ubuntu/Dockerfile @@ -1,19 +1,41 @@ -FROM ubuntu:latest +FROM ubuntu:24.04 ENV DEBIAN_FRONTEND=noninteractive ENV LANG=en_US.UTF-8 ENV LANGUAGE=en_US.UTF-8 ENV PROJECT_HOME=opsml +RUN groupadd -r opsml && useradd -r -g opsml -s /bin/bash -d /app opsml + RUN apt-get update --no-install-recommends \ && apt-get install --no-install-recommends --yes \ - ca-certificates tzdata curl \ + ca-certificates tzdata curl nginx supervisor netcat-openbsd \ + && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install --no-install-recommends --yes nodejs \ && rm -rf /var/lib/apt/lists/* \ && apt-get autoremove \ && apt-get clean +ARG OPSML_PORT=8000 ARG OPSML_SERVER_BINARY -COPY ${OPSML_SERVER_BINARY} /opsml-server +COPY --chown=opsml:opsml ${OPSML_SERVER_BINARY}/ui/node_modules /app/ui/node_modules/ +COPY --chown=opsml:opsml ${OPSML_SERVER_BINARY}/ui/build /app/ui/build/ +COPY --chown=opsml:opsml ${OPSML_SERVER_BINARY}/opsml-server /usr/local/bin/opsml-server +COPY docker/official/extras/nginx/nginx.conf.template /etc/nginx/nginx.conf.template +COPY docker/official/extras/entrypoint.sh /entrypoint.sh + +# Set permissions and create directories +RUN mkdir -p /var/log/nginx /var/run \ + && rm -f /etc/nginx/sites-enabled/default \ + && chmod +x /entrypoint.sh \ + && chmod +x /usr/local/bin/opsml-server \ + && chown -R opsml:opsml /var/log/nginx /var/run + +WORKDIR /app + +USER opsml + +EXPOSE ${OPSML_PORT} -CMD ["/opsml-server"] +CMD ["/entrypoint.sh"] \ No newline at end of file diff --git a/py-opsml/docs/docs/evaluation/llm.md b/py-opsml/docs/docs/evaluation/llm.md index 09520787d..08e254bf8 100644 --- a/py-opsml/docs/docs/evaluation/llm.md +++ b/py-opsml/docs/docs/evaluation/llm.md @@ -191,7 +191,7 @@ As you can see from the above example, the overall flow for evaluating an LLM us


- llm metric flow + llm metric flow

diff --git a/py-opsml/docs/docs/setup/overview.md b/py-opsml/docs/docs/setup/overview.md index 95c3d4ba2..17a500c74 100644 --- a/py-opsml/docs/docs/setup/overview.md +++ b/py-opsml/docs/docs/setup/overview.md @@ -25,26 +25,31 @@ Depending on your use case there are a few different ways to setup and run the s ### Docker -The recommended way to run the server is to use Docker. With every release of opsml, we build and publish new Docker images that you can use to run the server. You can find the latest images on [Docker Hub](https://hub.docker.com/r/demml/opsml). +The recommended way to run the server is to use Docker or any other container service. With every release of opsml, we build and publish new container images that you can use to run the server and UI. You can find the latest images on [Docker Hub](https://hub.docker.com/r/demml/opsml). In most cases, you can run the server with the following command; however, you may wish to use our docker images as base images to build your own custom images. ```console -$ docker run -p 3000:3000 demml/opsml:ubuntu-armd64-{version} +$ docker run -p 8080:8000 demml/opsml:ubuntu-armd64-{version} ``` +### What's in the container? + +The server container image is home to both the UI ([sveltekit nodejs app](https://svelte.dev/docs/kit/introduction)) and the opsml server backend ([Axum](https://github.com/tokio-rs/axum)). The UI is always exposed on port `3000` and the server port is exposed on `8080`. NGINX is used as a reverse proxy to route requests to the appropriate service (see `docker/extras` folder for the NGINX configuration) and exposes everything on port `8000` (configurable through `OPSML_PORT`). + +**Note:** The container images are run through an entrypoint script that starts the UI, server, and NGINX services and ensures that they are all running properly. See `docker/official/extras/entrypoint.sh` for more details. The entrypoint does not make use of any process managers like `supervisord` or `systemd` as we prefer the container orchestration system (e.g., Kubernetes etc.) to handle process management. + ### Binary In addition to docker images, we also build and publish new binaries with every release of opsml. These can be download via github and executed directly. - ### Development -As mentioned in the installation section, you can also start the server via the CLI; however, this is not recommended for production use cases as it requires a python runtime to be installed.. The *Docker* and *Binary* methods do not require a python runtime to be installed as they are 100% Rust implementations. +As mentioned in the installation section, you can also start the server via the CLI; however, this is not recommended for production use cases as it requires a python runtime to be installed. We recommend using the prebuilt containers for production use cases as they only require node (pre-installed in the container) to run the UI and the opsml server binary. !!! Note - While the opsml CLI is written in Rust, it is exposed via PyO3 and requires a python runtime to be installed. + While the opsml CLI is written in Rust, it is exposed via PyO3 and requires a python runtime to be installed. Node will also be required to run the UI locally. ```console $ opsml start ui @@ -52,7 +57,7 @@ $ opsml start ui ### Running the Server -opsml supports multiple backends for both the database and storage. By default, opsml will use SQLite for the database and local file storage for the storage backend. You can change this by setting the `OPSML_TRACKING_URI` and `OPSML_STORAGE_URI` environment variables. +opsml supports multiple backends for both the database and storage client. By default, opsml will use SQLite for the database and local file storage for the storage backend. You can change this by setting the `OPSML_TRACKING_URI` and `OPSML_STORAGE_URI` environment variables, respectively. To run the server with a different database backend, you can set the `OPSML_TRACKING_URI` environment variable to the desired backend. For example, to use Postgres, you can set the following environment variable: @@ -109,7 +114,7 @@ Apart from the `OPSML_TRACKING_URI` and `OPSML_STORAGE_URI` environment variable #### OpsML Server Environment Variables - `APP_ENV`: The current environment. This can be set to `development`, `staging` or `production` or anything else you'd want. The default is `development`. -- `OPSML_SERVER_PORT`: The port that the server will run on. The default is `3000`. +- `OPSML_PORT`: The port that the container will run on. The default is `8000`. - `OPSML_ENCRYPT_KEY`: The master encryption key used to encrypt the data at rest. If not set, opsml will use a default **deterministic** key. This is not recommended for production use cases. opsml requires a pbdkdf2::HmacSha256 key with a length of 32 bytes. You can generate a key using the following command with the opsml CLI: ```console @@ -120,7 +125,7 @@ The encryption key (aka jwt_key) is one of the most important pieces to opsml's - `OPSML_REFRESH_SECRET`: The secret used to sign the refresh tokens. This is used to verify the integrity of the refresh tokens. If not set, opsml will use a default **deterministic** key. This is not recommended for production use cases. opsml requires a pbdkdf2::HmacSha256 key with a length of 32 bytes. You can generate a key similar to the `OPSML_ENCRYPT_KEY` key. - `OPSML_MAX_POOL_CONNECTIONS`: The maximum number of connections to the database. The default is `10`. -- `LOG_LEVEL`: The log level for the server. This can be set to `error`, `warn`, `info`, `debug` or `trace`. The default is `info`. +- `LOG_LEVEL`: The log level for the server and UI. This can be set to `error`, `warn`, `info`, `debug` or `trace`. The default is `info`. - `LOG_JSON`: Whether to log in JSON format or not. This can be set to `true` or `false`. The default is `false`. #### Scouter Environment Variables