feat(runtime): FlowMesh-backed text EmbeddingOp with per-row artifact output#48
Merged
Conversation
Signed-off-by: Zhengyuan Su <su.zhengyuan@u.nus.edu>
timzsu
force-pushed
the
feat/embedding-op-vllm
branch
from
July 4, 2026 13:40
d25afa9 to
3c683b0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
Make the text-embedding path work end-to-end: an
EmbeddingOpjob now dispatches a FlowMesh vLLM embedding task, returns one artifact ref per input doc, and the downloadedembeddings.safetensorscontains real, per-doc vectors. This unblocks routing the skill-integration system's embeddings through Lumilake→FlowMesh instead of a local sentence-transformers model. Also brings the CI workflows onto GitHub-hosted runners with current tool/action versions.Changes
Embedding op
src/lumilake_server/runtime/runtime_manager/flowmesh.py— fix the artifact fetch: the download path wasf"artifacts/{path}", but the FlowMesh results route already prependsartifacts/, so it requestedartifacts/artifacts/embeddings.safetensors→ 404. Pass the barepath. Per-row archive now dedups by path (one download per shared safetensors, replicated per row) and surfaces per-row errors explicitly.src/lumilake_server/ops/embedding_ops.py— EmbeddingOp → FlowMeshembeddingtask (model.vllm.convert: embed), per-rowvectorsoutput.src/lumilake_server/routes/jobs.py—job artifactnow JSON-decodes per-row embedding output refs before the artifact-path membership check (was a raw substring check, so--path <uri>always failed for embedding outputs).src/lumilake_server/runtime/server.py—_relocate_artifacts_for_requestnow actually relocates the nestedoutputuri inside a JSON-encoded row output (was a blind textexec-…/artifacts/→req-…/artifacts/string-replace that rewrote the URI without copying the bytes, leaving the file only under theexec--scoped key).tests/runtime/test_embedding_op.py— fetch-locally proof (loads the archived artifact, asserts[N, dim]real floats).tests/runtime/server/test_run_batch.py— downstream-consumption proof (3 docs → 3 per-row outputs reach a consumer via the real aggregate path).Optimizer
src/lumilake_server/runtime/optimizer/schedule/model_size.py— registerBAAI/bge-small-en-v1.5(0.033B). It has no size suffix, so Halo's_model_suffix_patterncan't infer it and refuses to schedule; suffixed models (e.g.Qwen3-Embedding-0.6B) still infer automatically and need no entry.Docs
docs/OPS.md— EmbeddingOp contract: thevectorsoutput is a per-row list (one entry per input doc, each with itsrowindex + a ref to the sharedembeddings.safetensors, tensor keyembeddings, shape[count, dim]float32). The concrete workflow YAML is intentionally not committed — the contract is here.CI (GitHub-hosted runners)
.github/workflows/*.yml(10 files) —self-hosted→ubuntu-latest; bumpuv0.11.8→0.11.26,zizmor1.24.1→1.26.1,pip-audit2.9.0→2.10.1, docker actions +gh-action-pypi-publishto latest (SHA-pinned);enable-cache: falseon the 3 release/publishsetup-uvsteps (zizmor 1.26.1 cache-poisoning hardening; non-release jobs keep caching).Design
The failure only appeared under a live job — unit tests used in-memory fakes and never exercised the real FlowMesh artifact fetch. Root cause was a doubled
artifacts/path segment; confirmed by reading the worker container'smanifest.json(the file sits at a single-nestedartifacts/embeddings.safetensors). A native FlowMeshEmbeddingTaskfor the same model already succeeds on the box, so the fix aligns our fetch with the server's existing route contract rather than changing the executor. Two red herrings (destinationtype: httpvslocal; a hardcodedtimeoutSecdeep-merge bug) were investigated and reverted — the double-prefix was the sole, sufficient cause of the fetch 404. Verifying the fetch through Lumilake's product surface (lumilake job artifact) rather than the FlowMesh CLI then surfaced two further bugs the CLI shortcut had masked (the JSON-encoded per-row ref was neither collected nor relocated correctly) — both fixed here, so the artifact is fetchable the way a real consumer fetches it.Test Plan
End-to-end command chain (local stack), submitting a real 3-doc job and verifying the fetched vectors:
The fetch goes through Lumilake (job artifact / lumid-data-app), never by probing FlowMesh directly — the artifact is surfaced from the job's s3 output.
What is verified:
vectorsis a per-row list of 3 refs (one per doc, each with itsrow), and none carry a 404 (artifact not found) — the fetch bug is gone.embeddings.safetensorsloads as shape[3, 384]float32, all finite, unit-norm, and the 3 rows are distinct (pairwise cosine < 0.999) → one real embedding per doc.test_run_batch.pyasserts 3 docs → 3 per-row outputs reach a consumer through the real_aggregate_output_nodepath.Test Result
Pre-submission Checklist
CONTRIBUTING.md.uv run pre-commit run --all-filesand fixed any issues.uv run pytest tests/passes locally (739 passed, 1 skipped).[BREAKING]— n/a.