Skip to content

Commit 6930b22

Browse files
committed
feat(audio-transform): add LocalVQE backend, bidi gRPC RPC, Studio UI
Introduce a generic "audio transform" capability for any audio-in / audio-out operation (echo cancellation, noise suppression, dereverberation, voice conversion, etc.) and ship LocalVQE as the first backend implementation. Backend protocol: - Two new gRPC RPCs in backend.proto: unary AudioTransform for batch and bidirectional AudioTransformStream for low-latency frame-by-frame use. This is the first bidi stream in the proto; per-frame unary at LocalVQE's 16 ms hop would be RTT-bound. Wire it through pkg/grpc/{client,server, embed,interface,base} with paired-channel ergonomics. LocalVQE backend (backend/go/localvqe/): - Go-Purego wrapper around upstream liblocalvqe.so. CMake builds the upstream shared lib + its libggml-cpu-*.so runtime variants directly — no MODULE wrapper needed because LocalVQE handles CPU feature selection internally via GGML_BACKEND_DL. - Sets GGML_NTHREADS from opts.Threads (or runtime.NumCPU()-1) — without it LocalVQE runs single-threaded at ~1× realtime instead of the documented ~9.6×. - Reference-length policy: zero-pad short refs, truncate long ones (the trailing portion can't have leaked into a mic that wasn't recording). - Ginkgo test suite (9 always-on specs + 2 model-gated). HTTP layer: - POST /audio/transformations (alias /audio/transform): multipart batch endpoint, accepts audio + optional reference + params[*]=v form fields. Persists inputs alongside the output in GeneratedContentDir/audio so the React UI history can replay past (audio, reference, output) triples. - GET /audio/transformations/stream: WebSocket bidi, 16 ms PCM frames (interleaved stereo mic+ref in, mono out). JSON session.update envelope for config; constants hoisted in core/schema/audio_transform.go. - ffmpeg-based input normalisation to 16 kHz mono s16 WAV via the existing utils.AudioToWav (with passthrough fast-path), so the user can upload any format / rate without seeing the model's strict 16 kHz constraint. - BackendTraceAudioTransform integration so /api/backend-traces and the Traces UI light up with audio_snippet base64 and timing. - Routes registered under routes/localai.go (LocalAI extension; OpenAI has no /audio/transformations endpoint), traced via TraceMiddleware. Auth + capability + importer: - FLAG_AUDIO_TRANSFORM (model_config.go), FeatureAudioTransform (default-on, in APIFeatures), three RouteFeatureRegistry rows. - localvqe added to knownPrefOnlyBackends with modality "audio-transform". - Gallery entry localvqe-v1-1.3m (sha256-pinned, hosted on huggingface.co/LocalAI-io/LocalVQE). React UI: - New /app/transform page surfaced via a dedicated "Enhance" sidebar section (sibling of Tools / Biometrics) — the page is enhancement, not generation, so it lives outside Studio. Two AudioInput components (Upload + Record tabs, drag-drop, mic capture). - Echo-test button: records mic while playing the loaded reference through the speakers — the mic naturally picks up speaker bleed, giving a real (mic, ref) pair for AEC testing without leaving the UI. - Reusable WaveformPlayer (canvas peaks + click-to-seek + audio controls) and useAudioPeaks hook (shared module-scoped AudioContext to avoid hitting browser context limits with three players on one page); migrated TTS, Sound, Traces audio blocks to use it. - Past runs saved in localStorage via useMediaHistory('audio-transform') — the history entry stores all three URLs so clicking re-renders the full triple, not just the output. Build + e2e: - 11 matrix entries removed from .github/workflows/backend.yml (CUDA, ROCm, SYCL, Metal, L4T): upstream supports only CPU + Vulkan, so we ship those two and let GPU-class hardware route through Vulkan in the gallery capabilities map. - tests-localvqe-grpc-transform job in test-extra.yml (gated on detect-changes.outputs.localvqe). - New audio_transform capability + 4 specs in tests/e2e-backends. - Playwright spec suite in core/http/react-ui/e2e/audio-transform.spec.js (8 specs covering tabs, file upload, multipart shape, history, errors). Docs: - New docs/content/features/audio-transform.md covering the (audio, reference) mental model, batch + WebSocket wire formats, LocalVQE param keys, and a YAML config example. Cross-links from text-to-audio and audio-to-text feature pages. Assisted-by: Claude:claude-opus-4-7 [Bash Read Edit Write Agent TaskCreate] Signed-off-by: Richard Palethorpe <io@richiejp.com>
1 parent a271c72 commit 6930b22

59 files changed

Lines changed: 3891 additions & 86 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/backend.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2686,6 +2686,19 @@ jobs:
26862686
dockerfile: "./backend/Dockerfile.golang"
26872687
context: "./"
26882688
ubuntu-version: '2404'
2689+
- build-type: ''
2690+
cuda-major-version: ""
2691+
cuda-minor-version: ""
2692+
platforms: 'linux/amd64,linux/arm64'
2693+
tag-latest: 'auto'
2694+
tag-suffix: '-cpu-localvqe'
2695+
runs-on: 'ubuntu-latest'
2696+
base-image: "ubuntu:24.04"
2697+
skip-drivers: 'false'
2698+
backend: "localvqe"
2699+
dockerfile: "./backend/Dockerfile.golang"
2700+
context: "./"
2701+
ubuntu-version: '2404'
26892702
- build-type: 'sycl_f32'
26902703
cuda-major-version: ""
26912704
cuda-minor-version: ""
@@ -2725,6 +2738,19 @@ jobs:
27252738
dockerfile: "./backend/Dockerfile.golang"
27262739
context: "./"
27272740
ubuntu-version: '2404'
2741+
- build-type: 'vulkan'
2742+
cuda-major-version: ""
2743+
cuda-minor-version: ""
2744+
platforms: 'linux/amd64,linux/arm64'
2745+
tag-latest: 'auto'
2746+
tag-suffix: '-gpu-vulkan-localvqe'
2747+
runs-on: 'ubuntu-latest'
2748+
base-image: "ubuntu:24.04"
2749+
skip-drivers: 'false'
2750+
backend: "localvqe"
2751+
dockerfile: "./backend/Dockerfile.golang"
2752+
context: "./"
2753+
ubuntu-version: '2404'
27282754
- build-type: 'cublas'
27292755
cuda-major-version: "12"
27302756
cuda-minor-version: "0"

.github/workflows/test-extra.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ jobs:
3737
acestep-cpp: ${{ steps.detect.outputs.acestep-cpp }}
3838
qwen3-tts-cpp: ${{ steps.detect.outputs.qwen3-tts-cpp }}
3939
vibevoice-cpp: ${{ steps.detect.outputs.vibevoice-cpp }}
40+
localvqe: ${{ steps.detect.outputs.localvqe }}
4041
voxtral: ${{ steps.detect.outputs.voxtral }}
4142
kokoros: ${{ steps.detect.outputs.kokoros }}
4243
insightface: ${{ steps.detect.outputs.insightface }}
@@ -884,6 +885,26 @@ jobs:
884885
- name: Build vibevoice-cpp backend image and run ASR gRPC e2e tests
885886
run: |
886887
make test-extra-backend-vibevoice-cpp-transcription
888+
# End-to-end audio transform via the e2e-backends gRPC harness. The
889+
# LocalVQE GGUF is small (~5 MB) and the model is real-time on CPU, so
890+
# the default ubuntu-latest pool is plenty.
891+
tests-localvqe-grpc-transform:
892+
needs: detect-changes
893+
if: needs.detect-changes.outputs.localvqe == 'true' || needs.detect-changes.outputs.run-all == 'true'
894+
runs-on: ubuntu-latest
895+
timeout-minutes: 60
896+
steps:
897+
- name: Clone
898+
uses: actions/checkout@v6
899+
with:
900+
submodules: true
901+
- name: Setup Go
902+
uses: actions/setup-go@v5
903+
with:
904+
go-version: '1.25.4'
905+
- name: Build localvqe backend image and run audio_transform gRPC e2e tests
906+
run: |
907+
make test-extra-backend-localvqe-transform
887908
tests-voxtral:
888909
needs: detect-changes
889910
if: needs.detect-changes.outputs.voxtral == 'true' || needs.detect-changes.outputs.run-all == 'true'

Makefile

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Disable parallel execution for backend builds
2-
.NOTPARALLEL: backends/diffusers backends/llama-cpp backends/turboquant backends/outetts backends/piper backends/stablediffusion-ggml backends/whisper backends/faster-whisper backends/silero-vad backends/local-store backends/huggingface backends/rfdetr backends/insightface backends/speaker-recognition backends/kitten-tts backends/kokoro backends/chatterbox backends/llama-cpp-darwin backends/neutts build-darwin-python-backend build-darwin-go-backend backends/mlx backends/diffuser-darwin backends/mlx-vlm backends/mlx-audio backends/mlx-distributed backends/stablediffusion-ggml-darwin backends/vllm backends/vllm-omni backends/sglang backends/moonshine backends/pocket-tts backends/qwen-tts backends/faster-qwen3-tts backends/qwen-asr backends/nemo backends/voxcpm backends/whisperx backends/ace-step backends/acestep-cpp backends/fish-speech backends/voxtral backends/opus backends/trl backends/llama-cpp-quantization backends/kokoros backends/sam3-cpp backends/qwen3-tts-cpp backends/vibevoice-cpp backends/tinygrad backends/sherpa-onnx
2+
.NOTPARALLEL: backends/diffusers backends/llama-cpp backends/turboquant backends/outetts backends/piper backends/stablediffusion-ggml backends/whisper backends/faster-whisper backends/silero-vad backends/local-store backends/huggingface backends/rfdetr backends/insightface backends/speaker-recognition backends/kitten-tts backends/kokoro backends/chatterbox backends/llama-cpp-darwin backends/neutts build-darwin-python-backend build-darwin-go-backend backends/mlx backends/diffuser-darwin backends/mlx-vlm backends/mlx-audio backends/mlx-distributed backends/stablediffusion-ggml-darwin backends/vllm backends/vllm-omni backends/sglang backends/moonshine backends/pocket-tts backends/qwen-tts backends/faster-qwen3-tts backends/qwen-asr backends/nemo backends/voxcpm backends/whisperx backends/ace-step backends/acestep-cpp backends/fish-speech backends/voxtral backends/opus backends/trl backends/llama-cpp-quantization backends/kokoros backends/sam3-cpp backends/qwen3-tts-cpp backends/vibevoice-cpp backends/localvqe backends/tinygrad backends/sherpa-onnx
33

44
GOCMD=go
55
GOTEST=$(GOCMD) test
@@ -874,6 +874,16 @@ test-extra-backend-vibevoice-cpp-transcription: docker-build-vibevoice-cpp
874874
BACKEND_TEST_CAPS=health,load,transcription \
875875
$(MAKE) test-extra-backend
876876

877+
## LocalVQE audio transform (joint AEC + noise suppression + dereverb).
878+
## Exercises the audio_transform capability end-to-end: batch transform
879+
## of a real WAV fixture and bidi streaming of synthetic silent frames.
880+
test-extra-backend-localvqe-transform: docker-build-localvqe
881+
BACKEND_IMAGE=local-ai-backend:localvqe \
882+
BACKEND_TEST_MODEL_URL='https://huggingface.co/LocalAI-io/LocalVQE/resolve/main/localvqe-v1-1.3M-f32.gguf#localvqe-v1-1.3M-f32.gguf' \
883+
BACKEND_TEST_AUDIO_URL=https://github.com/ggml-org/whisper.cpp/raw/master/samples/jfk.wav \
884+
BACKEND_TEST_CAPS=health,load,audio_transform \
885+
$(MAKE) test-extra-backend
886+
877887
## sglang mirrors the vllm setup: HuggingFace model id, same tiny Qwen,
878888
## tool-call extraction via sglang's native qwen parser. CPU builds use
879889
## sglang's upstream pyproject_cpu.toml recipe (see backend/python/sglang/install.sh).
@@ -1017,6 +1027,7 @@ BACKEND_VOXTRAL = voxtral|golang|.|false|true
10171027
BACKEND_ACESTEP_CPP = acestep-cpp|golang|.|false|true
10181028
BACKEND_QWEN3_TTS_CPP = qwen3-tts-cpp|golang|.|false|true
10191029
BACKEND_VIBEVOICE_CPP = vibevoice-cpp|golang|.|false|true
1030+
BACKEND_LOCALVQE = localvqe|golang|.|false|true
10201031
BACKEND_OPUS = opus|golang|.|false|true
10211032
BACKEND_SHERPA_ONNX = sherpa-onnx|golang|.|false|true
10221033

@@ -1127,6 +1138,7 @@ $(eval $(call generate-docker-build-target,$(BACKEND_ACE_STEP)))
11271138
$(eval $(call generate-docker-build-target,$(BACKEND_ACESTEP_CPP)))
11281139
$(eval $(call generate-docker-build-target,$(BACKEND_QWEN3_TTS_CPP)))
11291140
$(eval $(call generate-docker-build-target,$(BACKEND_VIBEVOICE_CPP)))
1141+
$(eval $(call generate-docker-build-target,$(BACKEND_LOCALVQE)))
11301142
$(eval $(call generate-docker-build-target,$(BACKEND_MLX)))
11311143
$(eval $(call generate-docker-build-target,$(BACKEND_MLX_VLM)))
11321144
$(eval $(call generate-docker-build-target,$(BACKEND_MLX_DISTRIBUTED)))
@@ -1141,7 +1153,7 @@ $(eval $(call generate-docker-build-target,$(BACKEND_SHERPA_ONNX)))
11411153
docker-save-%: backend-images
11421154
docker save local-ai-backend:$* -o backend-images/$*.tar
11431155

1144-
docker-build-backends: docker-build-llama-cpp docker-build-ik-llama-cpp docker-build-turboquant docker-build-rerankers docker-build-vllm docker-build-vllm-omni docker-build-sglang docker-build-transformers docker-build-outetts docker-build-diffusers docker-build-kokoro docker-build-faster-whisper docker-build-coqui docker-build-chatterbox docker-build-vibevoice docker-build-moonshine docker-build-pocket-tts docker-build-qwen-tts docker-build-fish-speech docker-build-faster-qwen3-tts docker-build-qwen-asr docker-build-nemo docker-build-voxcpm docker-build-whisperx docker-build-ace-step docker-build-acestep-cpp docker-build-voxtral docker-build-mlx-distributed docker-build-trl docker-build-llama-cpp-quantization docker-build-tinygrad docker-build-kokoros docker-build-sam3-cpp docker-build-qwen3-tts-cpp docker-build-vibevoice-cpp docker-build-insightface docker-build-speaker-recognition docker-build-sherpa-onnx
1156+
docker-build-backends: docker-build-llama-cpp docker-build-ik-llama-cpp docker-build-turboquant docker-build-rerankers docker-build-vllm docker-build-vllm-omni docker-build-sglang docker-build-transformers docker-build-outetts docker-build-diffusers docker-build-kokoro docker-build-faster-whisper docker-build-coqui docker-build-chatterbox docker-build-vibevoice docker-build-moonshine docker-build-pocket-tts docker-build-qwen-tts docker-build-fish-speech docker-build-faster-qwen3-tts docker-build-qwen-asr docker-build-nemo docker-build-voxcpm docker-build-whisperx docker-build-ace-step docker-build-acestep-cpp docker-build-voxtral docker-build-mlx-distributed docker-build-trl docker-build-llama-cpp-quantization docker-build-tinygrad docker-build-kokoros docker-build-sam3-cpp docker-build-qwen3-tts-cpp docker-build-vibevoice-cpp docker-build-localvqe docker-build-insightface docker-build-speaker-recognition docker-build-sherpa-onnx
11451157

11461158
########################################################
11471159
### Mock Backend for E2E Tests

backend/Dockerfile.golang

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,15 @@ RUN --mount=type=bind,source=.docker/apt-mirror.sh,target=/usr/local/sbin/apt-mi
2626
apt-get update && \
2727
apt-get install -y --no-install-recommends \
2828
build-essential \
29+
gcc-14 g++-14 \
2930
git ccache \
3031
ca-certificates \
3132
make cmake wget libopenblas-dev \
3233
curl unzip \
3334
libssl-dev && \
35+
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100 \
36+
--slave /usr/bin/g++ g++ /usr/bin/g++-14 \
37+
--slave /usr/bin/gcov gcov /usr/bin/gcov-14 && \
3438
apt-get clean && \
3539
rm -rf /var/lib/apt/lists/*
3640

backend/backend.proto

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ service Backend {
4444
rpc AudioEncode(AudioEncodeRequest) returns (AudioEncodeResult) {}
4545
rpc AudioDecode(AudioDecodeRequest) returns (AudioDecodeResult) {}
4646

47+
rpc AudioTransform(AudioTransformRequest) returns (AudioTransformResult) {}
48+
rpc AudioTransformStream(stream AudioTransformFrameRequest) returns (stream AudioTransformFrameResponse) {}
49+
4750
rpc ModelMetadata(ModelOptions) returns (ModelMetadataResponse) {}
4851

4952
// Fine-tuning RPCs
@@ -669,6 +672,56 @@ message AudioDecodeResult {
669672
int32 samples_per_frame = 3;
670673
}
671674

675+
// Generic audio transform: an audio-in, audio-out operation, optionally
676+
// conditioned on a second reference signal. Concrete transforms include
677+
// AEC + noise suppression + dereverberation (LocalVQE), voice conversion
678+
// (reference = target speaker), pitch shifting, etc.
679+
message AudioTransformRequest {
680+
string audio_path = 1; // required, primary input file path
681+
string reference_path = 2; // optional auxiliary; empty => zero-fill
682+
string dst = 3; // required, output file path
683+
map<string, string> params = 4; // backend-specific tuning
684+
}
685+
686+
message AudioTransformResult {
687+
string dst = 1;
688+
int32 sample_rate = 2;
689+
int32 samples = 3;
690+
bool reference_provided = 4;
691+
}
692+
693+
// Bidirectional streaming audio transform. The first message MUST carry a
694+
// Config; subsequent messages carry Frames. A second Config mid-stream
695+
// resets streaming state before the next frame.
696+
message AudioTransformFrameRequest {
697+
oneof payload {
698+
AudioTransformStreamConfig config = 1;
699+
AudioTransformFrame frame = 2;
700+
}
701+
}
702+
703+
message AudioTransformStreamConfig {
704+
enum SampleFormat {
705+
F32_LE = 0;
706+
S16_LE = 1;
707+
}
708+
SampleFormat sample_format = 1;
709+
int32 sample_rate = 2; // 0 => backend default
710+
int32 frame_samples = 3; // 0 => backend default
711+
map<string, string> params = 4;
712+
bool reset = 5; // reset streaming state before next frame
713+
}
714+
715+
message AudioTransformFrame {
716+
bytes audio_pcm = 1; // frame_samples samples in stream's format
717+
bytes reference_pcm = 2; // empty => zero-fill (silent reference)
718+
}
719+
720+
message AudioTransformFrameResponse {
721+
bytes pcm = 1;
722+
int64 frame_index = 2;
723+
}
724+
672725
message ModelMetadataResponse {
673726
bool supports_thinking = 1;
674727
string rendered_template = 2; // The rendered chat template with enable_thinking=true (empty if not applicable)

backend/go/localvqe/.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
sources/
2+
build/
3+
package/
4+
liblocalvqe.so*
5+
libggml*.so*
6+
localvqe
7+
.localvqe-build.stamp

backend/go/localvqe/Makefile

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
CMAKE_ARGS?=
2+
BUILD_TYPE?=
3+
NATIVE?=false
4+
5+
GOCMD?=go
6+
GO_TAGS?=
7+
JOBS?=$(shell nproc --ignore=1)
8+
9+
# LocalVQE upstream version pin. Bump to a specific commit when picking up
10+
# a new release; `main` works for development but is not reproducible.
11+
LOCALVQE_REPO?=https://github.com/localai-org/LocalVQE
12+
LOCALVQE_VERSION?=72bfb4c6
13+
14+
# LocalVQE handles CPU feature selection internally (it ships the multiple
15+
# libggml-cpu-*.so variants and its loader picks the best one at runtime
16+
# via GGML_BACKEND_DL), so we build a single liblocalvqe.so + the per-CPU
17+
# ggml shared libs and let it sort itself out. No need for a wrapper
18+
# MODULE library or per-AVX backend variants here.
19+
20+
CMAKE_ARGS+=-DLOCALVQE_BUILD_SHARED=ON
21+
CMAKE_ARGS+=-DGGML_BUILD_TESTS=OFF
22+
CMAKE_ARGS+=-DGGML_BUILD_EXAMPLES=OFF
23+
24+
ifeq ($(NATIVE),false)
25+
CMAKE_ARGS+=-DGGML_NATIVE=OFF
26+
endif
27+
28+
# LocalVQE upstream supports CPU + Vulkan only. Other BUILD_TYPE values
29+
# fall through to the default CPU build — Vulkan is already as fast as the
30+
# specialised GPU paths would be on this 1.3 M-parameter model.
31+
ifeq ($(BUILD_TYPE),vulkan)
32+
CMAKE_ARGS+=-DGGML_VULKAN=ON -DLOCALVQE_VULKAN=ON
33+
else ifeq ($(OS),Darwin)
34+
CMAKE_ARGS+=-DGGML_METAL=OFF
35+
endif
36+
37+
# --- Sources ---
38+
39+
sources/LocalVQE:
40+
mkdir -p sources/LocalVQE
41+
cd sources/LocalVQE && \
42+
git init && \
43+
git remote add origin $(LOCALVQE_REPO) && \
44+
git fetch origin && \
45+
git checkout $(LOCALVQE_VERSION) && \
46+
git submodule update --init --recursive --depth 1 --single-branch
47+
48+
# --- Native build ---
49+
#
50+
# Drives cmake directly against the upstream LocalVQE/ggml CMakeLists.
51+
# Produces liblocalvqe.so plus the per-CPU libggml-cpu-*.so variants in
52+
# build/bin/, all of which we copy into the backend directory so package.sh
53+
# can pick them up. The `liblocalvqe.so` rule deliberately uses a sentinel
54+
# stamp file because Make's wildcard tracking would otherwise mis-decide
55+
# about freshness when SOVERSION symlinks are involved.
56+
57+
LIB_SENTINEL=.localvqe-build.stamp
58+
59+
$(LIB_SENTINEL): sources/LocalVQE
60+
mkdir -p build && \
61+
cd build && \
62+
cmake ../sources/LocalVQE/ggml $(CMAKE_ARGS) -DCMAKE_BUILD_TYPE=Release && \
63+
cmake --build . --config Release -j$(JOBS)
64+
# Upstream's CPU build sets GGML_BACKEND_DL=ON + GGML_CPU_ALL_VARIANTS=ON,
65+
# which produces multiple libggml-cpu-*.so files (SSE4.2 / AVX2 / AVX-512)
66+
# that the loader picks at runtime. We must build every target — the
67+
# default `--target localvqe_shared` drops these. CMAKE_LIBRARY_OUTPUT_DIRECTORY
68+
# routes all of them into build/bin; copy them out next to the binary.
69+
cp -P build/bin/liblocalvqe.so* . 2>/dev/null || cp -P build/liblocalvqe.so* .
70+
cp -P build/bin/libggml*.so* . 2>/dev/null || true
71+
touch $(LIB_SENTINEL)
72+
73+
liblocalvqe.so: $(LIB_SENTINEL)
74+
75+
# --- Go binary + packaging ---
76+
77+
localvqe: main.go golocalvqe.go $(LIB_SENTINEL)
78+
CGO_ENABLED=0 $(GOCMD) build -tags "$(GO_TAGS)" -o localvqe ./
79+
80+
package: localvqe
81+
bash package.sh
82+
83+
build: package
84+
85+
clean: purge
86+
rm -rf liblocalvqe.so* libggml*.so* package sources/LocalVQE localvqe $(LIB_SENTINEL)
87+
88+
purge:
89+
rm -rf build
90+
91+
test: localvqe
92+
@echo "Running localvqe tests..."
93+
bash test.sh
94+
@echo "localvqe tests completed."
95+
96+
all: localvqe package
97+
98+
.PHONY: build package clean purge test all

0 commit comments

Comments
 (0)