From b415db9a2a3fcc34222b6bbcfce850240b800736 Mon Sep 17 00:00:00 2001 From: Adrian Tobiszewski Date: Tue, 15 Sep 2026 11:00:57 +0200 Subject: [PATCH 1/3] Draft --- .github/agents/build-validate.agent.md | 45 ++++ .github/copilot-instructions.md | 208 ++---------------- .../instructions/bazel-build.instructions.md | 32 +++ .../build-workflow.instructions.md | 199 +++++++++++++++++ .../instructions/code-review.instructions.md | 39 ++++ .../instructions/cpp-headers.instructions.md | 25 +++ .../instructions/cpp-sources.instructions.md | 27 +++ .../ovms-auto-validate.instructions.md | 13 ++ AGENTS.md | 51 +++++ 9 files changed, 447 insertions(+), 192 deletions(-) create mode 100644 .github/agents/build-validate.agent.md create mode 100644 .github/instructions/bazel-build.instructions.md create mode 100644 .github/instructions/build-workflow.instructions.md create mode 100644 .github/instructions/code-review.instructions.md create mode 100644 .github/instructions/cpp-headers.instructions.md create mode 100644 .github/instructions/cpp-sources.instructions.md create mode 100644 .github/instructions/ovms-auto-validate.instructions.md create mode 100644 AGENTS.md diff --git a/.github/agents/build-validate.agent.md b/.github/agents/build-validate.agent.md new file mode 100644 index 0000000000..db501e1758 --- /dev/null +++ b/.github/agents/build-validate.agent.md @@ -0,0 +1,45 @@ +--- +description: "Use when: build, test, validate OVMS C++ code changes. Runs bazel build/test inside docker build container. Returns a compact PASS/FAIL report." +tools: [execute, read, search] +user-invocable: true +--- +You are an OVMS build & validation agent. Your job is to build and test code changes and report results. + +## Environment +- Run bazel commands inside the build container: docker exec -i bash -c "cd /ovms && " +- The workspace root is the OVMS repository checkout + +## Workflow +1. Find the running build container that mounts the current workspace (the repo root, `$(pwd)`): + ```bash + for id in $(docker ps -aq); do docker inspect "$id" --format '{{.Names}} {{.State.Status}} {{range .Mounts}}{{.Source}}{{end}}' | grep "$(pwd)"; done + ``` + Pick the container whose mount matches this workspace and extract its name. If it is stopped, start it with `docker start `. If no container matches, report "No build container found" and stop. +2. Run: `bazel build //src:ovms_test` inside the container +3. If build succeeds and a test filter is specified, run: `bazel test --test_summary=detailed --test_output=streamed --test_filter="" //src:ovms_test` inside the container +4. Collect outputs — apply output trimming rules below + +NOTE: Do NOT run clang-format, cpplint, or spell checks. Those are only run manually before commit, not during build validation. + +## Output Trimming (CRITICAL) +Build and test logs can be enormous. You MUST minimize context usage: +- **On success**: report ONLY "PASS" — do not include any log output +- **On failure**: pipe output through `tail -n 30` to get only the final error lines +- **Never** dump full build or test logs into the report +- **Truncate long lines** at 200 characters — test output often contains base64 images or serialized data in strings. Use `cut -c1-200` when piping output +- **For test failures**: report only the test name, the FAILED assertion line, and the expected vs actual values — not the full test output +- **If the user asks** for more detail on a specific failure, THEN read the full output for that specific item only +- Prefer `2>&1 | tail -n 30 | cut -c1-200` suffix on commands to enforce limits + +## Output Format +Return a compact structured report: +- **Build**: PASS/FAIL (if FAIL: last 20 lines of error output, lines truncated at 200 chars) +- **Tests**: PASS/FAIL (if FAIL: list of failing test names + assertion message only, one line each) + +## Constraints +- DO NOT edit any source files +- DO NOT run the full test suite unless explicitly asked — always prefer --test_filter +- ONLY report results, never attempt fixes +- If the build container is not running, report that and stop +- DO NOT dump raw logs — always filter and truncate +- Keep the total report under 50 lines diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ef29ea8a62..66831771ca 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,194 +1,18 @@ # Copilot Instructions for OpenVINO Model Server (OVMS) -## Project Overview - -OpenVINO Model Server (OVMS) is a high-performance inference serving platform built on top of **OpenVINO** and **OpenVINO GenAI**. The codebase is primarily **C++** with **Bazel** as the build system. Supporting infrastructure uses **Makefiles**, **Dockerfiles** (Ubuntu & Red Hat), and **batch files** (Windows). - -**Performance is a top priority** — both **throughput** and **latency** are critical. Code changes should be evaluated for their performance impact. Avoid unnecessary copies, allocations, and blocking operations on the hot path. - -## Repository Structure - -- `src/` — Main C++ source code (server, gRPC/REST handlers, model management, pipelines, mediapipe, LLM, C API) -- `src/test/` — C++ unit tests (gtest-based); this is where most developer tests live -- `src/python/` — Python custom node bindings and related code -- `demos/` — End-user demo applications -- `client/` — Client libraries (C++, Python, Go, Java) -- `docs/` — Documentation -- `third_party/` — Third-party dependency definitions for Bazel -- `Dockerfile.ubuntu` / `Dockerfile.redhat` — Multi-stage Dockerfiles for Linux builds -- `Makefile` — Orchestrates Docker-based builds and test runs -- `*.bat` files — Windows build and setup scripts - -## Code Style - -- C++ style is enforced via `cpplint` and `clang-format` -- Run `make style` to check formatting -- Apache 2.0 license headers are required on all source files - -## Expertise Areas - -1. **OpenVINO Expertise:** - - Proficient with OpenVINO core libraries and `ov::genai` components - - Familiar with OpenVINO performance optimization techniques -2. **C++ Proficiency:** - - Strong C++17 skills - - Familiar with best practices in memory management, concurrency, and template programming -3. **Serving Infrastructure:** - - gRPC and REST API handler design - - Model management, pipeline orchestration, and MediaPipe integration - - C API (`libovms_shared.so`) surface and bindings -4. **Build System Awareness:** - - Bazel build configuration and dependency management - - Minimizing C++ build times: forward declarations, include-what-you-use, avoiding transitive header leakage - - Understanding of Bazel targets, build flags (`--//:distro`), and multi-stage Docker builds - -## Code Review Instructions for PRs - -When analyzing a Pull Request, follow this protocol: - -1. Follow **C++ Core Guidelines** strictly. Include references in review comments. -2. Check for **hidden performance costs**: avoid `dynamic_cast` on the hot path; suggest `static_cast` or redesign if the type is known. -3. **Avoid copies**: ensure large data structures (tensors, buffers) are passed by reference or moved, not copied. -4. **Documentation**: ensure new public APIs have docstrings in C++ headers and Python bindings; update `docs/` as needed. -5. **Test coverage**: ensure that new features or changes have corresponding tests in `src/test/`. -6. **Formatting & safety:** - - No `using namespace std; using namespace ov;`. Prefer explicit using with specific symbols if needed, for readability. - - No `auto` for primitive types where it obscures readability. - - Use `const` and `constexpr` wherever possible. -7. Pass non-fundamental values by `const` reference wherever possible. -8. Prefer member initializer lists over direct assignments in constructor bodies. -9. Verify that the result of every newly introduced function is used in at least one call site (except `void` functions). -10. Use descriptive function and variable names. Avoid duplicate code — extract common functionality into reusable utilities. -11. When initial container values are known upfront, prefer initializer-list / brace-initialization over constructing an empty container and inserting. -12. Unused functions and includes are not allowed. Build times are already long — do not add unnecessary `#include` directives. Prefer forward declarations where possible and follow the include-what-you-use principle. - - **Forward-declare in headers, include in `.cpp`**: if a header only uses pointers or references to a type, use a forward declaration (`class Foo;`) instead of `#include "foo.hpp"`. Move the full `#include` to the `.cpp` file where the type is actually used. - - **Keep headers self-contained but minimal**: each header must compile on its own, but should not pull in transitive dependencies that callers don't need. - - **Prefer opaque types / Pimpl**: for complex implementation details, consider the Pimpl idiom to keep implementation-only types out of the public header entirely. - - **Never include a header solely for a typedef or enum**: forward-declare the enum (`enum class Foo;` in C++17) or relocate the typedef to a lightweight `fwd.hpp`-style header. -13. **No dangling references or temporaries bound to `const T&`**: - - Never use `const T&` parameters with default arguments that construct temporaries (e.g. `const std::string& param = ""`). This binds a reference to a temporary — use a function overload instead, or pass by value. - - When accepting `const T&` in constructors or functions that store the reference, verify that the referenced object's lifetime outlives the usage to avoid dangling references. - - Prefer overloads over default arguments for non-trivial types passed by reference. - -## Build System - -### Bazel (primary build tool) - -Building and testing is done **inside a Docker `-build` container** with the repository mounted. Developers do not run Bazel on the host directly. - -**Important:** Building the `-build` image from scratch is time-expensive, but may be required if dependencies or the build environment change. Before building a new one, check if one already exists: -```bash -docker images | grep -- -build -``` -If a `-build` image exists, start a container from it with the repository mounted: -```bash -docker run -it -v $(pwd):/ovms \ - -e http_proxy=$http_proxy -e https_proxy=$https_proxy -e no_proxy=$no_proxy \ - bash -``` -If a container from a previous session is still available (`docker ps -a`), reuse it with `docker start -i ` or `docker exec -it bash`. - -**Key Bazel targets:** - -| Target | Description | -|--------|-------------| -| `//src:ovms` | Main OVMS server binary | -| `//src:ovms_test` | C++ unit tests (gtest) | -| `//src:ovms_shared` | C API shared library (`libovms_shared.so`) | - -**Build the server:** -```bash -bazel build //src:ovms -``` - -**Build and run unit tests:** -```bash -bazel build //src:ovms_test -bazel test --test_summary=detailed --test_output=streamed //src:ovms_test -``` - -### Red Hat builds — distro flag - -For Red Hat (UBI9) builds, the `--//:distro=redhat` flag must be passed to all Bazel commands: -```bash -bazel build --//:distro=redhat //src:ovms -bazel test --//:distro=redhat //src:ovms_test -``` - -The default distro is `ubuntu`. Inside the `-build` container, the distro is auto-detected from `/etc/redhat-release`. - -### Makefile (Docker-based workflow) - -The Makefile orchestrates full Docker-based builds. Key targets: - -| Target | Description | -|--------|-------------| -| `make docker_build` | Full build: builder image → package → release images (default target) | -| `make ovms_builder_image` | Build the `-build` Docker image (compilation container) | -| `make targz_package` | Extract `.tar.gz` release package | -| `make ovms_release_images` | Build CPU and GPU release Docker images | -| `make run_unit_tests` | Run C++ unit tests in the `-build` container | -| `make test_functional` | Run Python functional tests | -| `make style` / `make cpplint` | Code style checks | - -**Red Hat build via Make:** -```bash -make docker_build BASE_OS=redhat -``` - -**Supported `BASE_OS` values:** `ubuntu24`, `ubuntu22`, `redhat` - -### Dockerfile stages - -Both `Dockerfile.ubuntu` and `Dockerfile.redhat` use multi-stage builds: - -| Stage | Purpose | -|-------|---------| -| `base_build` | System dependencies, Boost, Azure SDK, OpenCV | -| `build` | Bazel + OpenVINO setup, compiles OVMS (the `-build` container) | -| `capi-build` | Builds C API shared library and examples | -| `pkg` | Packages everything into a `.tar.gz` | -| `release` | Minimal runtime image with entrypoint | - -### Windows builds - -Windows builds use batch files in the repository root: -- `windows_install_build_dependencies.bat` — Install build dependencies (MSVC 2022 Build Tools, etc.) -- `windows_build.bat` — Main build script -- `windows_test.bat` — Run tests - -Windows-specific Bazel config: `--config=win_mp_on_py_off` (or `--config=win_mp_on_py_on` for Python support). - -## Testing - -### Test setup - -Before running tests, test models must be prepared: -```bash -make prepare_models -``` - -Models are exported using the `demos/common/export_models/export_model.py` script (used internally by the test setup). - -If LLM test models change (e.g., new model version, OpenVINO version change or config update), you may need to remove and regenerate the LLM test data: -```bash -rm -rf src/test/llm_testing -make prepare_models -``` - -### Running tests - -Running the full `//src:ovms_test` suite is **time-consuming**. During development, always run only the test fixtures relevant to your changes first using `--test_filter`: -```bash -bazel test --test_summary=detailed --test_output=streamed --test_filter="SuiteName.TestName" //src:ovms_test -``` -Run the full test suite only after the targeted tests pass. - -### Test structure - -- **Unit tests** are in `src/test/` — gtest-based C++ tests covering all server components -- Test files follow the `*_test.cpp` naming convention -- Test utilities: `test_utils.hpp`, `light_test_utils.hpp`, `c_api_test_utils.hpp` -- Test models are stored in `src/test/` subdirectories (e.g., `dummy/`, `passthrough/`, `summator/`) -- Specialized test areas: `src/test/llm/`, `src/test/mediapipe/`, `src/test/python/`, `src/test/embeddings/` - +> Canonical guidance for all AI agents lives in **[AGENTS.md](../AGENTS.md)** at the repository +> root. VS Code / Copilot reads that file automatically — start there. + +`AGENTS.md` is a lean index: it holds the always-on principles (performance-first C++17, +include-what-you-use, style via `make` targets, license headers, security) and points to +task-scoped rule files. + +Path-scoped rules auto-attach while you edit matching files and also live under +[.github/instructions/](instructions): + +- [cpp-headers.instructions.md](instructions/cpp-headers.instructions.md) — `*.hpp` rules +- [cpp-sources.instructions.md](instructions/cpp-sources.instructions.md) — `*.cpp` rules +- [bazel-build.instructions.md](instructions/bazel-build.instructions.md) — `BUILD` file rules +- [build-workflow.instructions.md](instructions/build-workflow.instructions.md) — build & test workflow +- [code-review.instructions.md](instructions/code-review.instructions.md) — C++ review standards +- [ovms-auto-validate.instructions.md](instructions/ovms-auto-validate.instructions.md) — post-change validation diff --git a/.github/instructions/bazel-build.instructions.md b/.github/instructions/bazel-build.instructions.md new file mode 100644 index 0000000000..f3b50bc69a --- /dev/null +++ b/.github/instructions/bazel-build.instructions.md @@ -0,0 +1,32 @@ +--- +applyTo: "**/BUILD" +--- +# Bazel BUILD File Guidelines (OVMS) + +## Target Granularity + +- **One logical unit per `ovms_cc_library` target.** Each `.cpp` file should belong to its own target with its associated `.hpp` in `hdrs`. +- **Do not add sources to monolithic targets.** If you're tempted to add a file to an existing large target, create a new focused target instead. + +## Macros + +- Use `ovms_cc_library` for production code (sets standard copts, linkopts, local_defines). +- Use `ovms_cc_test_library` for test utility libraries (adds gtest dep automatically). +- Use `additional_copts` for feature-flag copts (`COPTS_MEDIAPIPE`, `COPTS_PYTHON`). Do not override `copts` directly. + +## Dependencies + +- **Include-what-you-use for deps**: list only direct dependencies. Do not rely on transitive deps. +- **Prefer narrow deps**: depend on interface targets (`servable_metadata_provider`, `model_instance_provider`) over heavy implementation targets (`modelmanager`) when possible. +- **Forward-declaration targets**: for lightweight `_fwd.hpp` headers, create a separate header-only target (e.g., `tensorinfo_fwd`) so consumers can depend on it without pulling the full implementation. +- **Use `select()` for optional deps**: MediaPipe, Python, Drogon features should be behind `select()` on the appropriate config setting. + +## Visibility + +- Default to no `visibility` (package-private). Only add `visibility = ["//visibility:public"]` when the target is genuinely needed outside its package. +- Tight visibility prevents accidental coupling between subsystems. + +## Naming Conventions + +- Target names: lowercase with descriptive names matching the source (e.g., `grpcservermodule`, `kfs_grpc_frontend`). +- Test targets: match the test file name pattern (e.g., `ensemble_tests` for `ensemble_tests.cpp`). diff --git a/.github/instructions/build-workflow.instructions.md b/.github/instructions/build-workflow.instructions.md new file mode 100644 index 0000000000..a7b4f11412 --- /dev/null +++ b/.github/instructions/build-workflow.instructions.md @@ -0,0 +1,199 @@ +--- +description: "Build workflow, Docker setup, Makefile targets, style checks and test execution for OVMS" +--- +# OVMS Build & Test Workflow + +## Docker-Based Development + +Building and testing is done **inside a Docker `-build` container** with the repository mounted. Developers do not run Bazel on the host directly. + +### Checking for existing build images + +Before building a new image (which is time-expensive), check if one exists: +```bash +docker images | grep -- -build +``` + +### Finding a container with the current workspace mounted + +**Do NOT run a `docker ps`/`docker inspect`/mount-path scan as the first +step.** That scan is expensive and must only ever be a last resort. + +**Always start here instead** — check the AI agent's repo memory note (e.g. +`/memories/repo/build-container.md`) for a previously confirmed container +name for this workspace: +1. If a remembered container name exists, verify it's still usable: + ```bash + docker ps -a --filter name= --format '{{.ID}}\t{{.Status}}' + ``` + - If `Up`, use it directly — no further scanning: `docker exec -w /ovms ...`. + - If stopped, restart it (`docker start `) and reuse it. Still no scan needed. +2. Only if the remembered container no longer exists at all, fall back to + scanning by mount path for a running container: + ```bash + docker ps -q | xargs -I{} docker inspect {} --format '{{.ID}} {{range .Mounts}}{{.Source}}{{end}}' | grep "$(pwd)" + ``` + If found, use `docker exec -it bash` to enter it directly. + + If no running container matches, check stopped containers too: + ```bash + docker ps -aq | xargs -I{} docker inspect {} --format '{{.ID}} {{range .Mounts}}{{.Source}}{{end}}' | grep "$(pwd)" + ``` + If found stopped, use `docker start -i `. +3. **Immediately update the repo memory note** with the newly discovered + container name/ID so the scan is never repeated needlessly. + +### Never re-scan once a container is cached + +Once a container name is cached in repo memory, treat it as valid for the +rest of the session **and** future sessions. Do not re-run the mount-path +scan "just to double check" — only re-scan if the cached container is +confirmed gone (not just stopped) via the `docker ps -a --filter name=...` +check above. + +### Starting a build container + +If a `-build` image exists, start a container with the repo mounted: +```bash +docker run -it -v $(pwd):/ovms \ + -e http_proxy=$http_proxy -e https_proxy=$https_proxy -e no_proxy=$no_proxy \ + bash +``` + +If a container from a previous session is available (`docker ps -a`), reuse it: +```bash +docker start -i +# or +docker exec -it bash +``` + +## Bazel Commands (inside build container) + +### Key targets + +| Target | Description | +|--------|-------------| +| `//src:ovms` | Main OVMS server binary | +| `//src:ovms_test` | C++ unit tests (gtest) | +| `//src:ovms_shared` | C API shared library (`libovms_shared.so`) | + +### Build the server +```bash +bazel build //src:ovms +``` + +### Build and run unit tests +```bash +bazel build //src:ovms_test +bazel test --test_summary=detailed --test_output=streamed //src:ovms_test +``` + +### Running specific tests (preferred during development) +```bash +bazel test --test_summary=detailed --test_output=streamed --test_filter="SuiteName.TestName" //src:ovms_test +``` +Always run targeted tests first; run the full suite only after targeted tests pass. + +### Red Hat builds +Pass `--//:distro=redhat` to all Bazel commands: +```bash +bazel build --//:distro=redhat //src:ovms +``` + +### Linux build config +```bash +--config=mp_on_py_on +``` + +### Windows build config +```bash +--config=win_mp_on_py_off +# or with Python: +--config=win_mp_on_py_on +``` + +## Makefile Targets (Docker-based workflow) + +| Target | Description | +|--------|-------------| +| `make docker_build` | Full build: builder image → package → release images | +| `make ovms_builder_image` | Build the `-build` Docker image | +| `make targz_package` | Extract `.tar.gz` release package | +| `make ovms_release_images` | Build CPU and GPU release Docker images | +| `make run_unit_tests` | Run C++ unit tests in the `-build` container | +| `make test_functional` | Run Python functional te| `make style` | All code style checks: spell, clang-format, cpplint, cppclean (see Style Checking section) | + +### Red Hat build via Make +```bash +make docker_build BASE_OS=redhat +``` + +Supported `BASE_OS` values: `ubuntu24`, `ubuntu22`, `redhat` + +## Dockerfile Stages + +Both `Dockerfile.ubuntu` and `Dockerfile.redhat` use multi-stage builds: + +| Stage | Purpose | +|-------|---------| +| `base_build` | System dependencies, Boost, Azure SDK, OpenCV | +| `build` | Bazel + OpenVINO setup, compiles OVMS (the `-build` container) | +| `capi-build` | Builds C API shared library and examples | +| `pkg` | Packages everything into a `.tar.gz` | +| `release` | Minimal runtime image with entrypoint | + +## Style Checking + +**Run style checks on the HOST, not inside the `-build` container.** The Makefile targets set up +their own pinned `venv-style` virtualenv with the correct tool versions; the `-build` container +does not have `clang-format`/`cpplint` installed. Never invoke `clang-format` or `cpplint` by hand +— always go through the `make` targets, or you get the wrong version and spurious diffs. + +**Always run checks individually and sequentially** — never use `make style` (which runs all checks together and wastes time re-running already-passed steps). Fix each step before moving to the next: + +1. **Spelling**: + ```bash + make spell + ``` +2. **clang-format** (formatting): + ```bash + make clang-format-check + ``` +3. **cpplint** (lint rules): + ```bash + make cpplint + ``` +4. **cppclean** (unused includes/code): + ```bash + make cppclean + ``` + +Fix issues from step N before running step N+1 — later steps produce noise if formatting is off. + +## Test Setup + +Before running tests, prepare test models: +```bash +make prepare_models +``` + +If LLM test models need regeneration: +```bash +rm -rf src/test/llm_testing +make prepare_models +``` + +## Windows Builds + +Windows builds use batch files in the repository root: +- `windows_install_build_dependencies.bat` — Install MSVC 2022 Build Tools, etc. +- `windows_build.bat` — Main build script +- `windows_test.bat` — Run tests + +## Test Structure + +- Unit tests are in `src/test/` — gtest-based C++ tests +- Test files: `*_test.cpp` naming convention +- Test utilities: `test_utils.hpp`, `light_test_utils.hpp`, `c_api_test_utils.hpp` +- Test models: `src/test/` subdirectories (`dummy/`, `passthrough/`, `summator/`) +- Specialized: `src/test/llm/`, `src/test/mediapipe/`, `src/test/python/`, `src/test/embeddings/` diff --git a/.github/instructions/code-review.instructions.md b/.github/instructions/code-review.instructions.md new file mode 100644 index 0000000000..6defaff57a --- /dev/null +++ b/.github/instructions/code-review.instructions.md @@ -0,0 +1,39 @@ +--- +description: "C++ review standards for OVMS pull requests and code changes" +applyTo: "src/**/*.{cc,cpp,h,hpp}" +--- +# C++ Review Standards (OVMS) + +Apply these when reviewing a PR or writing C++ to review quality. For include/interface +mechanics see [cpp-headers.instructions.md](cpp-headers.instructions.md) and +[cpp-sources.instructions.md](cpp-sources.instructions.md). + +## Guidelines & performance + +- Follow the **C++ Core Guidelines**; cite the specific guideline in review comments. +- **No hidden performance costs**: avoid `dynamic_cast` on the hot path — use `static_cast` or redesign when the type is known. +- **Avoid copies**: pass large structures (tensors, buffers) by `const&` or move them; never copy. +- Pass non-fundamental values by `const` reference wherever possible. +- Prefer member initializer lists over assignment in constructor bodies. +- When initial container values are known upfront, prefer initializer-list / brace-initialization over building an empty container and inserting. + +## Correctness & safety + +- **No dangling references / temporaries bound to `const T&`**: + - Never give a `const T&` parameter a default that constructs a temporary (e.g. `const std::string& p = ""`). Use an overload or pass by value. + - When a `const T&` is stored, verify the referenced object outlives its use. + - Prefer overloads over default arguments for non-trivial types passed by reference. +- Verify every newly introduced non-`void` function has its result used at least once. +- No unused functions or includes (build times are long — do not add unneeded `#include`s). + +## Style & readability + +- No `using namespace std;` / `using namespace ov;`. Use specific `using` declarations if needed. +- Avoid `auto` for primitive types where it obscures readability. +- Use `const` and `constexpr` wherever possible. +- Descriptive names; extract duplicated logic into reusable utilities. + +## Documentation & tests + +- New public APIs get docstrings in C++ headers and Python bindings; update `docs/` as needed. +- New features or behavior changes must have corresponding tests in `src/test/`. diff --git a/.github/instructions/cpp-headers.instructions.md b/.github/instructions/cpp-headers.instructions.md new file mode 100644 index 0000000000..f3d258802b --- /dev/null +++ b/.github/instructions/cpp-headers.instructions.md @@ -0,0 +1,25 @@ +--- +applyTo: "**/*.hpp" +--- +# C++ Header Guidelines (OVMS) + +## Include Discipline + +- **Ban umbrella includes**: never use `openvino/openvino.hpp`, `rapidjson/document.h`, or similar in headers. Include only the specific subheader you need. +- **Forward-declare in headers**: if a header only uses pointers or references to a type, write `class Foo;` instead of `#include "foo.hpp"`. Move the full include to the `.cpp`. +- **Self-contained but minimal**: every header must compile on its own (`#pragma once`, own includes), but must not pull in transitive dependencies callers don't need. +- **No header solely for a typedef or enum**: forward-declare the enum (`enum class Foo;`) or use a lightweight `_fwd.hpp` header. +- **Third-party headers**: wrap in a port layer (`src/port/_.hpp`) with warning suppression pragmas. Never include third-party headers directly from multiple OVMS headers. + +## Interface Design + +- **Prefer opaque types / Pimpl**: for complex implementation details, keep impl-only types out of the public header. +- **Narrow interfaces over fat classes**: expose only what consumers need. Use abstract base classes (`ModelInstanceProvider`, `ServableMetadataProvider`, `ServableNameChecker`) returning plain data types. +- **Virtual destructors**: any class with virtual methods must have a virtual destructor. + +## Style + +- No `using namespace std;` or `using namespace ov;` in headers — pollutes every includer's namespace. +- Use `const` and `constexpr` wherever possible. +- Prefer `std::string_view` parameters over `const std::string&` for non-owning read-only access where applicable. +- Member variables: prefer member initializer lists in constructors. diff --git a/.github/instructions/cpp-sources.instructions.md b/.github/instructions/cpp-sources.instructions.md new file mode 100644 index 0000000000..8259466cf5 --- /dev/null +++ b/.github/instructions/cpp-sources.instructions.md @@ -0,0 +1,27 @@ +--- +applyTo: "**/*.cpp" +--- +# C++ Source File Guidelines (OVMS) + +## Include Discipline + +- **Include-what-you-use**: include exactly the headers you need. Do not rely on transitive includes from other headers. +- **Own header first**: the first include in `foo.cpp` should be `"foo.hpp"` — this verifies the header is self-contained. +- **Order**: own header → system/STL → third-party → project headers (alphabetical within each group). + +## Build Target Ownership + +- **Every new `.cpp` must belong to its own `ovms_cc_library` target** in a BUILD file. Do not add sources to monolithic targets like `ovms_lib`. +- If a `.cpp` is only used by tests, use `ovms_cc_test_library` instead. + +## Architecture + +- **Cross-module coupling goes through interfaces**, not concrete classes. If you need `ModelManager` functionality, depend on a narrow interface (`ModelInstanceProvider`, `ServableMetadataProvider`) rather than the full class. +- **Template instantiation isolation**: when a template is parametric on frontend types (TFS/KFS/CAPI protos), put the explicit instantiation in a per-frontend `.cpp` (`dag_tfs.cpp`, `dag_kfs.cpp`) so changing one frontend doesn't recompile others. +- **No dead code**: if a function or variable is unused, remove it. Orphan `.cpp` files not referenced by any BUILD target should be deleted. + +## Performance + +- Avoid copies of large structures (tensors, buffers) — pass by `const&` or move. +- On the hot inference path, avoid `dynamic_cast`, unnecessary allocations, and blocking operations. +- Prefer stack allocation and object reuse over repeated heap allocation in request-handling loops. diff --git a/.github/instructions/ovms-auto-validate.instructions.md b/.github/instructions/ovms-auto-validate.instructions.md new file mode 100644 index 0000000000..fae1d01f7e --- /dev/null +++ b/.github/instructions/ovms-auto-validate.instructions.md @@ -0,0 +1,13 @@ +--- +description: "Use when: editing, implementing, fixing, or refactoring C++ source files in the OVMS repository" +applyTo: "src/**/*.{cc,cpp,h,hpp}" +--- +After completing code changes to OVMS C++ files, you MUST run the **build-validate** subagent to verify the build, run relevant tests, and check lint. + +Delegation rules: +- Invoke `build-validate` as a **subagent** (this keeps build logs out of the main conversation context) +- Pass it a short description of what changed and which test fixtures are relevant (e.g. "Changed http_graph_executor_impl.cpp — run test filter HttpGraphExecutor*") +- Do this automatically — do NOT wait for the user to ask +- Do it exactly ONCE per set of changes +- Present the subagent's compact report to the user and let them decide next steps +- If the subagent reports failures, fix them and let the user decide whether to re-validate diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..b3b63fe0bd --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,51 @@ +# AGENTS.md — OpenVINO Model Server (OVMS) + +Canonical, tool-agnostic guidance for AI coding agents (Copilot, Codex, Claude, Cursor, …). +This file is an **index**: it states the few always-on principles and points to detailed, +task-scoped rule files. Read the linked file when your task matches its scope. + +## Project + +OVMS is a high-performance inference serving platform built on **OpenVINO** and +**OpenVINO GenAI**. Primarily **C++17**, built with **Bazel**, orchestrated via **Makefile** +and multi-stage **Dockerfiles** (Ubuntu & Red Hat); Windows via `.bat` scripts. + +**Performance is a top priority** — both throughput and latency. Evaluate every change for its +performance impact. Avoid copies, allocations, and blocking operations on the hot inference path. + +## Repository map + +- `src/` — main C++ source (server, gRPC/REST handlers, model management, DAG pipelines, + MediaPipe, LLM, C API) +- `src/test/` — gtest-based C++ unit tests (`*_test.cpp`); most developer tests live here +- `src/python/` — Python custom node bindings +- `client/` — client libraries (C++, Python, Go, Java) +- `demos/` — end-user demos +- `docs/` — documentation +- `third_party/` — Bazel third-party dependency definitions +- `Dockerfile.ubuntu` / `Dockerfile.redhat` — multi-stage Linux builds +- `Makefile` — Docker-based build/test orchestration +- `*.bat` — Windows build/test scripts + +## Always-on principles + +- **Performance first** on the hot path — no unnecessary copies, allocations, `dynamic_cast`, or blocking calls. +- **Include-what-you-use**; prefer forward declarations in headers, full includes in `.cpp`. Build times are long — do not add unneeded includes. +- **No dead code**: remove unused functions, variables, includes, and orphan files. +- **Style is enforced** via `make` targets (`make clang-format`, `make cpplint`, `make cppclean`, `make spell`) run on the **host**, not inside the build container. Never invoke `clang-format`/`cpplint` by hand. +- **Apache 2.0 license header** is required on all source files. +- **Security**: follow OWASP Top 10; validate at system boundaries; never log secrets (e.g. API keys). + +## Task index — read the matching file for details + +| When you are… | Read | +|---|---| +| Editing a C++ **header** (`*.hpp`) | [.github/instructions/cpp-headers.instructions.md](.github/instructions/cpp-headers.instructions.md) | +| Editing a C++ **source** (`*.cpp`) | [.github/instructions/cpp-sources.instructions.md](.github/instructions/cpp-sources.instructions.md) | +| Editing a **BUILD** file | [.github/instructions/bazel-build.instructions.md](.github/instructions/bazel-build.instructions.md) | +| **Building / testing** OVMS | [.github/instructions/build-workflow.instructions.md](.github/instructions/build-workflow.instructions.md) | +| Reviewing or writing C++ to review standards | [.github/instructions/code-review.instructions.md](.github/instructions/code-review.instructions.md) | +| Finishing a C++ change (validate) | [.github/instructions/ovms-auto-validate.instructions.md](.github/instructions/ovms-auto-validate.instructions.md) | + +In VS Code / Copilot these files auto-attach via their `applyTo` globs. Other agents should +open the relevant file from the table above on demand. From 17c48db0da411f4ff3c2497f7124fdeea83c0aac Mon Sep 17 00:00:00 2001 From: Adrian Tobiszewski Date: Tue, 15 Sep 2026 11:14:42 +0200 Subject: [PATCH 2/3] Self-review --- .github/agents/build-validate.agent.md | 47 +++++++++++++------ .../build-workflow.instructions.md | 6 +++ .../instructions/cpp-headers.instructions.md | 1 + AGENTS.md | 2 +- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/.github/agents/build-validate.agent.md b/.github/agents/build-validate.agent.md index db501e1758..e3ac6465fa 100644 --- a/.github/agents/build-validate.agent.md +++ b/.github/agents/build-validate.agent.md @@ -6,30 +6,49 @@ user-invocable: true You are an OVMS build & validation agent. Your job is to build and test code changes and report results. ## Environment -- Run bazel commands inside the build container: docker exec -i bash -c "cd /ovms && " +- Run bazel commands inside the build container: `docker exec -w /ovms bash -lc ""` - The workspace root is the OVMS repository checkout +- Write full command output to a log file under `logs/`; read only the tail to confirm status, and grep the file for details on failure. Never dump a whole log into the report. ## Workflow -1. Find the running build container that mounts the current workspace (the repo root, `$(pwd)`): +1. Find the build container that mounts the current workspace, deterministically, in **one** command. + It lists all containers (running + stopped) whose mounts include this workspace, puts running + ones first, and prints a single name: ```bash - for id in $(docker ps -aq); do docker inspect "$id" --format '{{.Names}} {{.State.Status}} {{range .Mounts}}{{.Source}}{{end}}' | grep "$(pwd)"; done + for id in $(docker ps -aq); do docker inspect "$id" --format '{{if .State.Running}}0{{else}}1{{end}} {{.Name}} {{range .Mounts}}{{.Source}};{{end}}'; done | grep -F "$(pwd)" | sort | sed -E 's#^[01] /([^ ]+).*#\1#' | head -1 ``` - Pick the container whose mount matches this workspace and extract its name. If it is stopped, start it with `docker start `. If no container matches, report "No build container found" and stop. -2. Run: `bazel build //src:ovms_test` inside the container -3. If build succeeds and a test filter is specified, run: `bazel test --test_summary=detailed --test_output=streamed --test_filter="" //src:ovms_test` inside the container -4. Collect outputs — apply output trimming rules below + - This handles the case of 2–3 matching containers (running one wins) and always yields at most one name. + - If it prints a name, that is your container — use it. If it is stopped, `docker start ` once. + - Run this discovery **exactly once**. Do NOT re-scan or retry it after a later bazel error — a build/test failure is not a "container not found" problem. + - Only if it prints nothing: report "No build container found" and stop. +2. Build, redirecting all output to a log file, then check the tail: + ```bash + mkdir -p logs + docker exec -w /ovms bash -lc "bazel build --curses=no --color=no //src:ovms_test > /ovms/logs/build.log 2>&1; echo EXIT=\$?" + ``` + Read the last lines to confirm status: `tail -n 30 logs/build.log`. +3. If the build succeeds and a test filter is specified, run tests the same way: + ```bash + docker exec -w /ovms bash -lc "bazel test --test_summary=detailed --test_output=errors --curses=no --color=no --test_filter=\"\" //src:ovms_test > /ovms/logs/test.log 2>&1; echo EXIT=\$?" + ``` +4. Determine status from the `EXIT=` code and the log tail. On failure, search the **whole** log + file for the real error (it is often not in the last few lines): + ```bash + grep -nE "^(FAILED|ERROR|\[ FAILED \])" logs/test.log | head -20 + ``` + Then read only the specific matching line ranges. Apply the output trimming rules below to the report. NOTE: Do NOT run clang-format, cpplint, or spell checks. Those are only run manually before commit, not during build validation. ## Output Trimming (CRITICAL) Build and test logs can be enormous. You MUST minimize context usage: -- **On success**: report ONLY "PASS" — do not include any log output -- **On failure**: pipe output through `tail -n 30` to get only the final error lines -- **Never** dump full build or test logs into the report -- **Truncate long lines** at 200 characters — test output often contains base64 images or serialized data in strings. Use `cut -c1-200` when piping output -- **For test failures**: report only the test name, the FAILED assertion line, and the expected vs actual values — not the full test output -- **If the user asks** for more detail on a specific failure, THEN read the full output for that specific item only -- Prefer `2>&1 | tail -n 30 | cut -c1-200` suffix on commands to enforce limits +- **Always redirect full output to `logs/build.log` / `logs/test.log`** (see Workflow). Never pipe raw build/test output into your own context. +- **On success**: report ONLY "PASS" — do not include any log output. +- **On failure**: grep the full log file for the failing lines (`FAILED`, `ERROR`, assertion text) — do not assume the error is in the tail. Report only those lines. +- **Never** dump full build or test logs into the report. +- **Truncate long lines** at 200 characters — test output often contains base64 images or serialized data. Use `cut -c1-200`. +- **For test failures**: report only the test name, the FAILED assertion line, and expected vs actual — not the full test output. +- **If the user asks** for more detail on a specific failure, THEN read the relevant range of the log file for that item only. ## Output Format Return a compact structured report: diff --git a/.github/instructions/build-workflow.instructions.md b/.github/instructions/build-workflow.instructions.md index a7b4f11412..1465c209a3 100644 --- a/.github/instructions/build-workflow.instructions.md +++ b/.github/instructions/build-workflow.instructions.md @@ -3,6 +3,12 @@ description: "Build workflow, Docker setup, Makefile targets, style checks and t --- # OVMS Build & Test Workflow +## Which build do I need? + +- **Most code changes** (`.cpp`/`.hpp`/`BUILD` under `src/`): build & test **inside the existing `-build` container** with Bazel. This is fast and is the default — do NOT rebuild the Docker image. +- **`Dockerfile.*` or `Makefile` changes** (build environment, dependencies, packaging): a **full image build** is required (`make docker_build`). +- **Windows**: use the batch scripts in the repo root (see the Windows Builds section) — no Docker container. + ## Docker-Based Development Building and testing is done **inside a Docker `-build` container** with the repository mounted. Developers do not run Bazel on the host directly. diff --git a/.github/instructions/cpp-headers.instructions.md b/.github/instructions/cpp-headers.instructions.md index f3d258802b..b4735673c8 100644 --- a/.github/instructions/cpp-headers.instructions.md +++ b/.github/instructions/cpp-headers.instructions.md @@ -6,6 +6,7 @@ applyTo: "**/*.hpp" ## Include Discipline - **Ban umbrella includes**: never use `openvino/openvino.hpp`, `rapidjson/document.h`, or similar in headers. Include only the specific subheader you need. +- **Use repo-root-relative include paths across directories**: a same-directory include may be relative (`#include "logging.hpp"`), but never reach across directories with `..` — write `#include "src/foo/bar.hpp"` from the workspace root, never `#include "../foo/bar.hpp"`. - **Forward-declare in headers**: if a header only uses pointers or references to a type, write `class Foo;` instead of `#include "foo.hpp"`. Move the full include to the `.cpp`. - **Self-contained but minimal**: every header must compile on its own (`#pragma once`, own includes), but must not pull in transitive dependencies callers don't need. - **No header solely for a typedef or enum**: forward-declare the enum (`enum class Foo;`) or use a lightweight `_fwd.hpp` header. diff --git a/AGENTS.md b/AGENTS.md index b3b63fe0bd..81d5f4744b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -29,7 +29,7 @@ performance impact. Avoid copies, allocations, and blocking operations on the ho ## Always-on principles -- **Performance first** on the hot path — no unnecessary copies, allocations, `dynamic_cast`, or blocking calls. +- **Performance first** on the hot path — no unnecessary copies, allocations, or blocking calls. - **Include-what-you-use**; prefer forward declarations in headers, full includes in `.cpp`. Build times are long — do not add unneeded includes. - **No dead code**: remove unused functions, variables, includes, and orphan files. - **Style is enforced** via `make` targets (`make clang-format`, `make cpplint`, `make cppclean`, `make spell`) run on the **host**, not inside the build container. Never invoke `clang-format`/`cpplint` by hand. From 28d53322a47c6885f7eb061ef27d1a2d965de1f9 Mon Sep 17 00:00:00 2001 From: Adrian Tobiszewski Date: Wed, 16 Sep 2026 08:27:05 +0200 Subject: [PATCH 3/3] Update --- .github/instructions/code-review.instructions.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/instructions/code-review.instructions.md b/.github/instructions/code-review.instructions.md index 6defaff57a..48e6258c23 100644 --- a/.github/instructions/code-review.instructions.md +++ b/.github/instructions/code-review.instructions.md @@ -33,6 +33,16 @@ mechanics see [cpp-headers.instructions.md](cpp-headers.instructions.md) and - Use `const` and `constexpr` wherever possible. - Descriptive names; extract duplicated logic into reusable utilities. +## Comments + +Comments bloat the code and are a frequent review rejection — keep them minimal. + +- Add a comment only to state what the code cannot show on its own, and keep it to one short line. +- Do not add comments where the code, function name, or test name already conveys the intent. +- No "what happens next" narration before test cases or code blocks — the name should convey intent. +- Only comment genuinely non-obvious things: workarounds, subtle ordering constraints, non-obvious rationale. +- Do not restate what the next line does or explain a change to the reviewer in the code. + ## Documentation & tests - New public APIs get docstrings in C++ headers and Python bindings; update `docs/` as needed.