Add recipe: BYO Vector Store — LlamaCloud Index V2 to Azure AI Search - #79
Conversation
Runs the official index-v2-data-sinks Azure AI Search exporter end-to-end (keyless via Entra ID), proves server-side tenant isolation, and layers LlamaIndex Retrieval Harness primitives (List Files, File Grep, File Read, Hybrid Retrieve) on the exported index. Adds the notebook, its architecture image, the registry entry, the new tags (llamaindex, byo-vector-store, entra-id, multi-tenant, foundry-iq), and openai + beautifulsoup4 to requirements. Registry validator passes (18 notebooks). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
3985e97 to
8f0c9ad
Compare
nicholasdbrady
left a comment
There was a problem hiding this comment.
Hypothesis: This is an intermediate tutorial for developers who want LlamaCloud parsing while retaining Azure AI Search as the governed retrieval store.
Success criterion: A reader can export parsed content keylessly, verify retrieval behavior safely, and reuse the resulting index without mistaking a query filter for an authorization boundary.
Confidence: High from the full notebook, pinned exporter source, registry/site validation, and current Azure AI Search security guidance. I did not repeat the author's live Azure execution.
Verdict
Revise and resubmit — 72/100. The exporter walkthrough and Retrieval Harness are useful, but the current notebook overstates tenant isolation and can delete a pre-existing index with a predictable name. Those need correction before publication.
Required before publishing
-
Critical — Cell 23/24 and related prose/tags: The notebook calls a caller-supplied
tenant_idOData filter “server-side tenant isolation” and says one tenant can never see another tenant's chunks. Azure AI Search executes the filter server-side, but it does not authorize the filter value; a caller with index query access can omit it or request another value. Reframe this as server-side scoped retrieval, not a security boundary, and remove themulti-tenanttag unless the recipe implements enforced security trimming/ACLs or an authorization layer that derives tenant context and injects the filter. Azure's documented security-filter pattern ties stored principal identifiers to the authenticated caller: https://learn.microsoft.com/azure/search/search-security-trimming-for-azure-search -
Critical — Cell 18 and teardown: The notebook deletes
INDEX_NAMEbefore export, whileINDEX_PREFIXis a predictable shared default. A reader can destroy an existing index from an earlier run or another user. Generate a unique demo index name for each run (or require explicit confirmation before replacing an existing index), track whether this run created it, and delete only that resource during teardown. -
Required — Cell 2: Remove the slug/tags/author/last-verified table from the notebook. Forgebook publishing metadata belongs in
registry.yaml; notebooks remain source lessons without site-specific metadata. -
Required — Prerequisites/setup: The pinned package declares
requires-python = ">=3.11,<3.14", not Python 3.13+. Correct the prerequisite and troubleshooting text. If the notebook intentionally requires 3.13 for another concrete dependency, name and verify that dependency explicitly. -
Required — Cell 6: Setup checks only whether
index_v2_data_sinksimports. If another revision is already installed, the pinned commit and compatible Azure SDK versions are not applied, so later behavior can drift from the reviewed source. Install the exact pinned revision unconditionally or verify installed package/dependency versions and fail with a corrective command. -
Required — Cells 12, 21, 24, 29/32/35/38, and 41: Add assertions for the claims that make this a recipe rather than a demo: expected embedding dimension; exported document count; scoped query has results while the alternate scope has none; Retrieval Harness returns expected file/chunk shapes; re-export leaves the count exactly unchanged; snapshots contain the expected file IDs. The current idempotency loop exits after eight seconds and always prints “no duplicates,” even if the final count increased.
-
Required — Opener, architecture image, and production-path section: Qualify “no keys.” The Azure demo defaults to Entra ID, but the production
export_pipelinepath requiresLLAMA_CLOUD_API_KEYand embedding-provider authentication. Say “keyless for Azure services in this demo” so readers do not infer that the complete LlamaCloud workflow is credential-free.
Recommended improvements
-
Optional —
tags.yaml: Keep the genuinely newllamaindexintegration tag, but prefer existing taxonomy where possible:iqoverfoundry-iq,identityoverentra-id, andindexes/azure-ai-searchoverbyo-vector-store. Add new global tags only when they will classify multiple recipes rather than restating this recipe's title. -
Optional —
registry.yaml: Shorten the 241-character description to 200 characters or fewer so it does not overflow the card grid.
What is working
- Praise: The notebook has a clear thesis, one editable configuration cell, explicit runtime/cost guidance, and a useful progression from export to inspectable Retrieval Harness primitives.
- Praise: Pinning the unreleased exporter to an exact commit and grounding the walkthrough in the official sink implementation are strong reproducibility choices.
- Praise: Registry validation, notebook health checks, site preview, and CodeQL all pass.
Re-review gate
After these updates, please include a clean top-to-bottom run with assertion-backed evidence and sanitized outputs. I will re-review only the revised claims/cells plus the registry/tag changes.
…tions Applies all 8 required and 3 optional items from @nicholasdbrady's review. Critical - Reframe Step 4 from "tenant isolation" to server-side *scoped retrieval*. An OData filter is a query scope, not an authorization boundary: nothing verifies the caller is entitled to the tenant_id it passes. The step now states that plainly, demonstrates it (the same credential unfiltered reads the whole index), points to security trimming for enforceable per-user entitlement, and RBAC is named as what actually restricts access. Drops the multi-tenant tag. - Remove the destructive pre-delete of a predictably named index. INDEX_PREFIX now carries a random per-run id, setup asserts the name is free, the export records CREATED_INDEXES, and teardown deletes only that, then verifies it. Required - Drop the publishing-metadata table from cell 2 (it lives in registry.yaml); replace with difficulty/time/cost. - Correct the Python requirement to 3.11-3.13; the pinned commit declares requires-python ">=3.11,<3.14", not 3.13+. - Install the pinned revision unconditionally instead of behind a try/except import guard, and assert the resolved commit from direct_url.json plus the Azure SDK versions, failing with the exact corrective command. - Add assertions to every step: embedding dim, 1:1 embeddings-to-chunks, export count, index existence, provisioned fields, vector dimension, scoped vs alternate scope vs unscoped, harness result shapes and ranking, grep containment with a negative control, file_read ordering against the List Files count, and snapshot ids. Fixes a real bug: the idempotency cell printed "(no duplicates)" unconditionally, even if the count had grown. - Qualify the keyless claim as keyless *for the Azure services used here*; the production export_pipeline still needs LLAMA_CLOUD_API_KEY and embedding-provider credentials. Optional - Reuse the existing tag taxonomy (iq, identity, indexes) instead of adding near-duplicates; llamaindex is now the only new tag. - Shorten the registry description to 170 chars. Verified by a clean keyless top-to-bottom run against a live Azure AI Search service: 0 errors, 60 chunks exported, scope 5 / alternate 0 / unscoped 5, re-export 60 -> 60, teardown removed only this run's index. Outputs cleared per repo convention. Registry validation and site build pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Thanks @nicholasdbrady — this was a genuinely useful review. I accepted all 8 required items and all 3 optional ones; nothing rejected. Pushed as a separate commit ( Re-verified with a clean keyless top-to-bottom run against a live Azure AI Search service: 0 errors, every assertion passing. Outputs cleared afterward per repo convention; the evidence is quoted below. Critical1. The
Rather than just soften the prose, the cell now demonstrates the limitation — it runs the same query a third time with no filter at all: The remaining value is stated honestly (the service never ranks or bills for out-of-scope chunks, so they can't reach the model's context window), security trimming is linked as the path to enforceable per-user entitlement and added to Next steps, and the 2. Destructive delete of a predictably named index. Also right —
"Make it yours" notes that a stable name means dropping the teardown cell too. Required3. Metadata table. Removed — it duplicated 4. Python version. You were right and I'd propagated the error to three places. The pinned commit declares 5. Conditional install. Fixed — the I deliberately did not use 6. Assertions. Added throughout, and this caught a real bug you'd flagged: the idempotency cell printed Coverage: embedding dim vs. configured model · 1:1 embeddings-to-chunks per file · export total vs. expected · index exists after export · all seven provisioned fields present · vector field dimension matches the model · scoped/alternate/unscoped hit counts and Evidence from the live run: 7. "No keys". Qualified everywhere (opener, Prerequisites, image alt text, "Make it yours"). The keyless claim is now explicitly scoped to the Azure services used here — AI Search and Azure OpenAI — and the production section states plainly that Optional — took all three8. Tags. Fair point that I was minting near-duplicates of tags that already exist. Now using Final tags: 9. Description. 241 → 170 chars. 10. Re-review gate. Clean run, assertions passing, sanitized outputs — covered above. Validation
One note on local testing: |
nicholasdbrady
left a comment
There was a problem hiding this comment.
Hypothesis: This is an intermediate tutorial for developers who want LlamaCloud parsing while retaining Azure AI Search as the governed retrieval store.
Success criterion: A reader can export parsed content keylessly for Azure services, verify scoped retrieval safely, and reuse the resulting index through inspectable retrieval primitives.
Confidence: High from the revised notebook, the exact pinned upstream sources, local Forgebook gates, and current Azure AI Search/OpenAI client behavior. I did not repeat the author's live Azure execution.
Verdict
Publish after fixes — 88/100. The earlier security-framing and destructive-lifecycle blockers are resolved. Three smaller issues still need correction before publication because they affect the reusable harness and product accuracy.
| Axis | Score |
|---|---|
| Thesis | 14/15 |
| Opinionated defaults | 9/10 |
| When / What / How | 8/10 |
| Code cell discipline | 9/10 |
| Before/after evidence | 13/15 |
| Runnability | 12/15 |
| Scope | 9/10 |
| Dev-to-dev voice | 5/5 |
| Failure modes | 5/5 |
| Takeaway artifact | 4/5 |
Required before publishing
-
Required — Cell 26 (
list_files/file_read) and scale guidance: Both helpers passtop=1000, which is a total-result ceiling, not complete-corpus pagination. Above 1,000 chunks,list_filessilently returns incomplete counts;file_readcan also truncate a large file, and its assertion can still pass because the expected count came from the same capped listing. This conflicts with “every file,” “whole file,” and the thousands-of-files guidance. Page until exhaustion (with a stable order/key), or detect the ceiling and fail explicitly; then add a test/assertion that crosses one page. -
Required — Prerequisites, Cell 3: The notebook says it calls the direct
/openai/v1endpoint, but the pinnedAzureOpenAI(azure_endpoint=..., api_version=...)client routes embeddings through Azure's deployment-scoped/openai/deployments/{model}/embeddings?api-version=...path. Keep the RBAC distinction, which is useful, but describe this as the Azure OpenAI data-plane deployment endpoint rather than/openai/v1. -
Required —
requirements.txt: The new comment still says the notebook requires “Python 3.13+,” while the revised notebook and pinned package correctly require>=3.11,<3.14. Change it to Python 3.11–3.13 so repository setup guidance does not contradict the recipe.
Recommended improvement
- Optional — Cells 7/14 and prerequisites: The clean run also depends on outbound access to three mutable
paulgraham.compages, but prerequisites name only Azure services. State that dependency and its failure mode, or commit a small sample undernotebooks/data/<slug>/so the teaching corpus remains reproducible if the pages change or reject automated requests.
What is working
- Praise: Step 4 now distinguishes server-side query scope from authorization and demonstrates the limitation with the same credential's unfiltered query. Keep that counterexample.
- Praise: The random per-run index name, create tracking, and teardown assertions remove the destructive shared-name behavior from the first revision.
- Praise: Exact source pinning, version verification, assertion-backed export checks, and the troubleshooting table make the lesson substantially more reproducible.
Mechanical checks
| Check | Result | Evidence |
|---|---|---|
| Registry/authors/tags | Pass | npx --no-install tsx validate-registry.ts validated 18 notebooks |
| Media/data paths | Pass | Recipe health check; slug-scoped architecture image |
| Notebook structure | Pass | 48 cells, 15 non-empty code cells, no leading H1 or site metadata |
| Site build | Pass | npm run build; 19 pages built |
| PR checks | Pass | Preview and CodeQL checks are green |
| Live execution | Not repeated | Author reports a clean assertion-backed Azure run; committed outputs are cleared |
Fix order
- Make corpus/file reads complete or explicitly fail on truncation.
- Correct the Azure OpenAI route description and Python comment.
- Document or stabilize the external sample corpus.
Live Azure execution follow-upI repeated the notebook against live Azure resources in
Result: top-to-bottom execution passed. All 15 code cells executed, there were 0 error outputs, and every assertion passed. Evidence from the run: I also queried the Search service independently after execution: 0 indexes remain. This resolves my earlier static-only execution caveat and confirms the current notebook is runnable with the small embedding model. The three requested publication fixes in my re-review still stand because they concern pagination at scale and documentation accuracy rather than this 60-chunk happy-path run. |
Description
Adds a runnable cookbook: BYO Vector Store — LlamaCloud Index V2 → Azure AI Search.
LlamaCloud parses your documents; Azure AI Search governs and grounds the result. This recipe runs the official
index-v2-data-sinksAzure AI Search exporter end-to-end against your own service — keyless via Entra ID — proves server-side tenant isolation, and layers LlamaIndex Retrieval Harness primitives (List Files, File Grep, File Read, Hybrid Retrieve) on the exported index. The payoff for LlamaIndex customers: land parsed content in their tenant with zero indexing code, then graduate the same index into Foundry IQ agentic retrieval.Included (5 files): the notebook, its architecture image, the
registry.yamlentry, new tags (llamaindex,byo-vector-store,entra-id,multi-tenant,foundry-iq), andopenai+beautifulsoup4inrequirements.txt.Validated live before outputs were cleared: executed top-to-bottom, fully keyless, against a live Azure AI Search + Azure OpenAI
text-embedding-3-large—Auth: Entra ID (keyless), 60 chunks exported via the officialAzureSearchExporter, tenant isolation (forgebook-demo→ 5 hits vsanother-tenant→ 0), all four harness primitives, idempotent re-export (60 → 60, no duplicates), andlist_snapshots()returning one record per file. Outputs contain no secrets. Registry validator passes (18 notebooks). REST api-version pinned to2026-04-01(SDK 12.0.0 default); install pinned to a specificindex-v2-data-sinkscommit (Python 3.13+, GitHub, not PyPI).Closes # — N/A (no linked issue)
Review focus
Cognitive Services OpenAI Userfor the direct/openai/v1embedding path,Foundry Useronly for Foundry project endpoints (verified against Microsoft Learn).retrieve.index-v2-data-sinksfrom a GitHub commit because it has no PyPI release; requires Python 3.13+.Checklist
Notebook contributions
notebooks/directoryregistry.yamlwith all required fields (slug,path,title,authors)authors.yaml(with at leastnameandtitle)tags.yamlcd scripts && npx tsx validate-registry.tsnotebooks/media/<slug>/with descriptive alt textnotebooks/data/<slug>/when applicable — N/A (no data files)Site changes
N/A — this PR only adds a notebook plus its
registry.yaml/tags.yaml/requirements.txtmetadata. The registry drives the site, sopreview.ymlCI will runnpm run build+astro checkon the PR.cd site && npm run build— runs in CIcd site && npx astro check— runs in CI