Motivation
hev-shop is the demo for "hevlayer coordinates ingest → embed → review search →
tag rollups." Today both the indexer and the web pod talk to layer-gateway via
hand-rolled HTTP:
indexer/app/layer_client.py — 551-line httpx wrapper, ~30 methods
web/lib/layer.ts — one POST to /v2/namespaces/{ns}/warm
That's the wrong story for a flagship demo. A customer adopting hevlayer would
reach for a published SDK, not reverse-engineer the gateway's REST shape. We
should use the official hevlayer Python client so the storefront is an
honest example of what integration actually looks like.
This issue tracks hev-shop adoption. The SDK itself lives in
hev/layer — see the companion issue there for
SDK design (codegen vs hand-written) and packaging.
Current surface to cover
The SDK needs to support everything LayerClient does today. Grouped by area:
Pipelines
create_pipeline(id, target_namespace, distance_metric) — POST /v2/pipelines, 409-safe
pipeline_status(id) — GET /v2/pipelines/{id}/status
claim_pipeline_documents(...) with document_id_prefix, lease, worker_id
heartbeat_pipeline_documents(...)
set_pipeline_documents_stage(...) with from_stage, create_missing
release / fail / complete convenience wrappers
stage_pipeline_document(...) — PUT .../documents/{id} with chunks
get_chunks(pipeline_id, document_id)
write_vector(...) — single-vector PUT
Namespaces — read
fetch_document(ns, id, include_attributes=) + _with_perf variant
fetch_many_documents(ns, ids, include_attributes=)
query_namespace(ns, vector, top_k, include_attributes=, filters=) + _with_perf
fetch_namespace_metadata(ns) + _with_perf
Namespaces — write
upsert_vectors(ns, vectors)
patch_attributes(ns, patches) — attribute-only PATCH (does not clobber vectors)
Scans
create_scan / get_scan / list_scans / get_scan_results
wait_for_scan(...) — exponential backoff polling
scan(...) — create + wait convenience
warm_namespace(ns) — POST /v2/namespaces/{ns}/warm
Observability
LayerPerf(latency_ms, cache_status) returned alongside reads — surfaces the
x-layer-cache header verbatim (hit / miss / miss-on-error / None for
query). Product detail and search endpoints expose this to the UI; the SDK
needs an equivalent (per-call timing + cache header).
Auth
- Bearer token via
Authorization header. Defensively strips whitespace from
the key (1Password / kubectl --from-literal / YAML block scalars sometimes
add a trailing newline; httpx rejects header CR/LF with an error that
embeds the key, which would leak it into /detail/ surfaces — see
48a6c26).
Hev-shop migration plan
- Add
hevlayer to indexer/requirements.txt; drop the direct httpx
import for gateway calls (httpx stays for the OpenRouter classifier).
- Replace
indexer/app/layer_client.py with a thin shim that re-exports
the SDK client + LayerPerf type (or delete it and import directly from
hevlayer at call sites in main.py, pipeline.py, extraction.py,
worker.py).
- Update
tests/_fakes.py:FakeLayerClient to satisfy the SDK's
client Protocol / ABC (the SDK should expose one for stub-friendliness;
if it doesn't, raise it in the upstream issue).
- Run
python3 -m pytest tests/ --tb=short in indexer/ — the pipeline
driver, per-stage processors, and HTTP handlers should all pass against
the new fake.
- Web side: replace the single
fetch in web/lib/layer.ts:warmOne with
the SDK's TS client if/when one ships. Until then, leave as-is — it's 30
lines and isolated. (Out of scope for this issue if no TS SDK lands.)
- Smoke check against prod:
curl -s https://api.hev-shop.com/healthz
curl -s https://api.hev-shop.com/meta | jq .
curl -s -X POST -H 'content-type: application/json' \
-d '{"query":"wireless headphones","top_k":3}' \
https://api.hev-shop.com/search | jq .
Out of scope
- Building the SDK itself — tracked in hev/layer#22.
- Migrating the Go CLI off its hand-rolled clients (
go run . status/health);
separate decision since the Go ecosystem story is different.
- Web TS client — depends on whether a TS SDK ships.
Acceptance
indexer/app/layer_client.py is deleted or reduced to a re-export shim.
indexer/requirements.txt lists hevlayer (or equivalent).
- All existing tests pass;
FakeLayerClient satisfies the SDK's stub contract.
/product/{asin}, /search, /search/reviews, /meta return the same
shape as before (including layer_perf blocks).
- Cache-hit signals on product pages still render with latency + cache_status.
Dependencies
- hev/layer#22: define + ship
hevlayer Python SDK (build approach TBD).
Motivation
hev-shopis the demo for "hevlayer coordinates ingest → embed → review search →tag rollups." Today both the indexer and the web pod talk to
layer-gatewayviahand-rolled HTTP:
indexer/app/layer_client.py— 551-line httpx wrapper, ~30 methodsweb/lib/layer.ts— one POST to/v2/namespaces/{ns}/warmThat's the wrong story for a flagship demo. A customer adopting hevlayer would
reach for a published SDK, not reverse-engineer the gateway's REST shape. We
should use the official
hevlayerPython client so the storefront is anhonest example of what integration actually looks like.
This issue tracks hev-shop adoption. The SDK itself lives in
hev/layer — see the companion issue there for
SDK design (codegen vs hand-written) and packaging.
Current surface to cover
The SDK needs to support everything
LayerClientdoes today. Grouped by area:Pipelines
create_pipeline(id, target_namespace, distance_metric)—POST /v2/pipelines, 409-safepipeline_status(id)—GET /v2/pipelines/{id}/statusclaim_pipeline_documents(...)withdocument_id_prefix, lease, worker_idheartbeat_pipeline_documents(...)set_pipeline_documents_stage(...)withfrom_stage,create_missingrelease/fail/completeconvenience wrappersstage_pipeline_document(...)—PUT .../documents/{id}with chunksget_chunks(pipeline_id, document_id)write_vector(...)— single-vector PUTNamespaces — read
fetch_document(ns, id, include_attributes=)+_with_perfvariantfetch_many_documents(ns, ids, include_attributes=)query_namespace(ns, vector, top_k, include_attributes=, filters=)+_with_perffetch_namespace_metadata(ns)+_with_perfNamespaces — write
upsert_vectors(ns, vectors)patch_attributes(ns, patches)— attribute-only PATCH (does not clobber vectors)Scans
create_scan/get_scan/list_scans/get_scan_resultswait_for_scan(...)— exponential backoff pollingscan(...)— create + wait conveniencewarm_namespace(ns)— POST/v2/namespaces/{ns}/warmObservability
LayerPerf(latency_ms, cache_status)returned alongside reads — surfaces thex-layer-cacheheader verbatim (hit/miss/miss-on-error/Noneforquery). Product detail and search endpoints expose this to the UI; the SDK
needs an equivalent (per-call timing + cache header).
Auth
Authorizationheader. Defensively strips whitespace fromthe key (1Password /
kubectl --from-literal/ YAML block scalars sometimesadd a trailing newline; httpx rejects header CR/LF with an error that
embeds the key, which would leak it into
/detail/surfaces — see48a6c26).Hev-shop migration plan
hevlayertoindexer/requirements.txt; drop the directhttpximport for gateway calls (
httpxstays for the OpenRouter classifier).indexer/app/layer_client.pywith a thin shim that re-exportsthe SDK client +
LayerPerftype (or delete it and import directly fromhevlayerat call sites inmain.py,pipeline.py,extraction.py,worker.py).tests/_fakes.py:FakeLayerClientto satisfy the SDK'sclient
Protocol/ ABC (the SDK should expose one for stub-friendliness;if it doesn't, raise it in the upstream issue).
python3 -m pytest tests/ --tb=shortinindexer/— the pipelinedriver, per-stage processors, and HTTP handlers should all pass against
the new fake.
fetchinweb/lib/layer.ts:warmOnewiththe SDK's TS client if/when one ships. Until then, leave as-is — it's 30
lines and isolated. (Out of scope for this issue if no TS SDK lands.)
Out of scope
go run . status/health);separate decision since the Go ecosystem story is different.
Acceptance
indexer/app/layer_client.pyis deleted or reduced to a re-export shim.indexer/requirements.txtlistshevlayer(or equivalent).FakeLayerClientsatisfies the SDK's stub contract./product/{asin},/search,/search/reviews,/metareturn the sameshape as before (including
layer_perfblocks).Dependencies
hevlayerPython SDK (build approach TBD).