fix(openshell): upgrade certified stack to 0.0.88 - #377
Conversation
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…VIDIA-AI-Blueprints#346) ## Summary - Adds `sources/nimble_web_search`, a NAT data source that wraps `langchain-nimble`'s `NimbleSearchRetriever`, mirroring the existing `exa_web_search` and `tavily_web_search` packages (typed config, stub-on-missing-key, retries, content truncation, XML-tagged output). - Exposes `lite` / `fast` / `deep` `search_depth`, typed as a `Literal` so invalid values fail at config-parse time. `lite` is the default — metadata-only, token-cheap, works on any account. `fast` is enterprise-tier and surfaces a clear 403 entitlement message on non-enterprise keys. - Adds a typed `focus` mode (default `general`, validated `Literal`), `country` / `locale` regional controls, and an optional `max_content_length` per-result cap. - Wires the plugin into the workspace, `deploy/Dockerfile`, and `scripts/setup.sh` so it installs in dev, Docker, and container builds. The Docker layer also installs Nimble's runtime deps (`langchain-nimble`, `nimble-python`, lockfile-pinned) so `_type: nimble_web_search` resolves in built images, where the `--no-dev` sync would otherwise omit them. - Documentation across the config reference, extending guides, installation, quick-start, deployment (docker-build, docker-compose, kubernetes), FAQ, and troubleshooting (with 401 and 403-enterprise rows). #### DCO sign-off for the squash commit Signed-off-by: Kobi Kadosh <kobi.kadosh@gmail.com> ## Motivation AI-Q ships Tavily- and Exa-backed web search today. [Nimble](https://nimbleway.com/) provides web search and content extraction for AI agents; this adds it as a first-class alternative with the same ergonomics and config surface — handy for users who already have a Nimble subscription, prefer its regional coverage, or want to test across multiple search backends. The default provider is unchanged (Tavily stays the documented default). It wraps the official `langchain-nimble` package (maintained by Nimble) rather than calling the HTTP API directly, so retry, auth, and response normalization come from the upstream integration — the same rationale as the Exa source (NVIDIA-AI-Blueprints#181). ## Configuration ```yaml functions: web_search_tool: _type: nimble_web_search max_results: 5 search_depth: lite # lite (default) | fast (enterprise) | deep focus: general # general (default) | news | location | shopping | geo | social country: US locale: en ``` ```bash NIMBLE_API_KEY=... # or set api_key: in the YAML ``` ## How it works A real `lite` query with `NIMBLE_API_KEY` set, trimmed: ```text <Document href="https://docs.nvidia.com/aiq-blueprint/1.2.1/index.html"> <title> NVIDIA AI-Q Blueprint </title> AI-Q combines intelligent query routing, multi-agent research pipelines, and pluggable knowledge retrieval to deliver comprehensive, citation-backed answers. </Document> --- <Document href="https://build.nvidia.com/nvidia/aiq"> <title> NVIDIA AI-Q Blueprint for intelligent agents </title> The NVIDIA AI-Q Blueprint enables developers to build fully customizable AI agents that they own, inspect and control. Built on LangChain… </Document> ``` Each result renders as an XML `<Document>` block — the same shape the Tavily and Exa sources produce — so existing AI-Q agents consume it with no changes. To try it, point any existing web-search config at `_type: nimble_web_search` and run `nat run` (swap `advanced_search: true` → `search_depth: deep`). ## How this was tested - [x] `uv run pytest sources/nimble_web_search` — **32 passed**, credential-free (the SDK is mocked; no live network in CI). - [x] `uv run pytest sources/exa_web_search sources/nimble_web_search` — **46 passed**, confirming the new package co-runs cleanly with a sibling source. The test module has a unique name and no `tests/__init__.py`, so there's no pytest collection collision when sources are collected together. - [x] `ruff check` and `ruff format --check` — clean (whole repo). `uv lock --check` — no drift. - [x] Repo pre-commit hooks pass on the changed files: `detect-secrets`, `markdown-link-check` (all README/docs links resolve), `end-of-file-fixer`, `trailing-whitespace`, `check-added-large-files`, and `uv-lock` — matching the `AIQ CI` lint job. - [x] `nat info components --types function` lists `nimble_web_search` (1.0.0) next to `exa_web_search` and `tavily_web_search`, so `_type: nimble_web_search` resolves in a workflow. - [x] Container runtime: `langchain-nimble==3.0.0` + `nimble-python==0.18.0` install and import cleanly in a fresh environment the same way `deploy/Dockerfile` installs them, so `_type: nimble_web_search` resolves in built images — not only in editable dev installs. - [x] Live smoke with a real `NIMBLE_API_KEY` across `lite` and `deep`, plus the non-enterprise `fast` path (returns the friendly 403 entitlement message). Output is redacted; no key is logged by construction. Coverage: config defaults / all fields / invalid-enum rejection (incl. `focus`) / out-of-range numeric fields rejected / `focus` defaults to `general` and reaches the SDK / non-default `focus` passthrough / `include_answer` absent from config and kwargs / `FunctionBaseConfig` inheritance, the missing-key stub + warn-once, key-from-config env hydration, result rendering + description fallback, markup escaping of untrusted fields, `search_depth` and `country`/`locale` passthrough, query and content truncation (incl. small-limit hard-cut), empty-result handling, retry-then-succeed, non-transient (401/403) errors short-circuiting without retry, final-retry failure, and the 401 / 403 branches. ## How this was reviewed - Diffed against the merged Exa source to keep structure, retry loop, truncation, and output format at parity; the deviations below are deliberate. - Confirmed credential-free CI behavior and co-run safety with a sibling source. - Scanned the package for secrets and for hardcoded search-endpoint names — none. ## Deviations from the Exa source (all deliberate) 1. `search_depth` (3-value enum), a typed `focus` mode (default `general`), plus `country` / `locale`, mirroring `langchain-nimble`'s surface, where Exa exposes `search_type` / `full_text` / `highlights`. `focus` is a workflow-config setting, not an agent parameter, so general research queries cannot drift to `news`. 2. Falls back to the result's `description` when `page_content` is empty — Nimble's `lite` mode returns metadata only. 3. A 403 branch that turns Nimble's enterprise-tier gating into a clear, actionable message. Exa has no tier gating, so no equivalent. 4. `include_answer` (answer generation) is intentionally not exposed in this initial integration. It can be added in a follow-up. 5. Untrusted result fields (`url`, `title`, body) are HTML-escaped before rendering into the `<Document>` markup, so a result can't break the block or inject into downstream parsers. 6. Numeric config fields are bounded: `max_results` `1-100` (matching `langchain-nimble`'s own `ge=1, le=100`), `max_retries` `ge=1`, `max_content_length` `ge=1` (use `None` to disable truncation). Invalid values fail at config-parse time, and content truncation hard-cuts safely for very small limits. ## Known limitations - `max_results` is a soft cap — Nimble may return up to N+2 documents for N. The provider returns them all; downstream consumers can slice. - `lite` mode returns empty `page_content`; the provider renders the `description` (~150 chars, organic-result quality). - The non-enterprise `fast` path is characterized via its 403 message; the enterprise `fast` behavior itself isn't exercised here. ## Scope **In:** the `nimble_web_search` provider, config/docs/deploy wiring, 32 unit tests, README, troubleshooting rows. **Not in (easy follow-ups):** Nimble Extract / Map / Crawl / Agents; `include_answer`; framework integrations beyond AI-Q's data-source mechanism; any change to the default provider. ## Security - No secrets committed — `deploy/.env.example` carries a commented `NIMBLE_API_KEY=` placeholder only. - Key read from env or a `SecretStr` config field; never logged. - Unit tests need no credentials; the live smoke uses an inline env var and redacted output. ## Summary by CodeRabbit * **New Features** * Added Nimble Web Search as a supported web search option with configurable depth, focus, country/locale targeting, result limits, retries, and optional content truncation. * Provides structured `<Document>` formatted output with safer escaping and improved result URL handling. * **Documentation** * Added Nimble Web Search tool documentation and examples. * Updated installation, quick start, deployment key tables, FAQ, and troubleshooting for `NIMBLE_API_KEY`. * **Deployment/Setup** * Updated Docker/build, compose, Kubernetes, setup scripts, and the `.env.example` template for Nimble support. * **Tests** * Added credential-free recorded replay and opt-in live integration coverage. * **Chores** * Refreshed the secrets baseline metadata. Signed-off-by: Kobi Kadosh <kobi.kadosh@gmail.com> Authors: - Kobi Kadosh (https://github.com/wildcard) - Kyle Zheng (https://github.com/KyleZheng1284) - Ajay Thorve (https://github.com/AjayThorve) Approvers: - Kyle Zheng (https://github.com/KyleZheng1284) - Ajay Thorve (https://github.com/AjayThorve) URL: NVIDIA-AI-Blueprints#261 Co-authored-by: Kobi Kadosh <kobi.kadosh@gmail.com>
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
…/release/2.2 Forward-merge release/2.2 into develop
Signed-off-by: Kyle Zheng <126034466+KyleZheng1284@users.noreply.github.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Enterprise Run ID: 📒 Files selected for processing (7)
📜 Recent review details🧰 Additional context used📓 Path-based instructions (2)**/*.py📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*test*.py📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
🔇 Additional comments (7)
WalkthroughOpenShell support is pinned to version 0.0.88, and workspace selection is threaded through sandbox configuration, provider RPCs, readiness checks, gateway scripts, live tests, documentation, and deployment examples. ChangesOpenShell version contract
Workspace propagation
Readiness and gateway wiring
Live acceptance coverage
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/openshell/check_openshell_readiness.py`:
- Line 286: Use a consistent empty-workspace fallback in both Python entry
points: update the workspace default in
scripts/openshell/check_openshell_readiness.py at lines 286-286 and the
corresponding workspace handling in
scripts/openshell/smoke_openshell_isolation.py at lines 32-36 to use the
environment value only when truthy, otherwise falling back to "default".
- Around line 225-230: Extend the request-labels capability validation around
the existing readiness check to verify that every lifecycle
method—client.create, client.list, client.wait_ready, client.get, client.delete,
and client.wait_deleted—accepts the workspace keyword. Preserve the existing
ReadinessError("request_labels_unsupported") outcome so unsupported methods are
reported consistently instead of being handled as probe or cleanup failures.
In `@scripts/openshell/setup_openshell.sh`:
- Line 793: Update print_next_steps() so generated commands export the currently
selected AIQ_OPENSHELL_WORKSPACE value instead of hardcoding "default"; preserve
the configured workspace in all printed lifecycle commands.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Enterprise
Run ID: 879058d0-b395-4f52-80f1-4734690a57d5
📒 Files selected for processing (22)
configs/config_openshell.ymlconfigs/openshell/aiq-research-policy.yamldocs/source/architecture/agents/sandbox.mddocs/source/deployment/openshell.mdpyproject.tomlscripts/README.mdscripts/openshell/check_openshell_readiness.pyscripts/openshell/setup_openshell.shscripts/openshell/smoke_openshell_isolation.pyscripts/openshell/start_openshell_gateway.shsrc/aiq_agent/agents/deep_researcher/deepagents_runtime.pysrc/aiq_agent/agents/deep_researcher/sandbox/README.mdsrc/aiq_agent/agents/deep_researcher/sandbox/config.pysrc/aiq_agent/agents/deep_researcher/sandbox/providers/openshell.pytests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_live.pytests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_provider.pytests/aiq_agent/agents/deep_researcher/test_deepagents_runtime.pytests/scripts/test_openshell_gateway_installer.pytests/scripts/test_openshell_lifecycle_scripts.pytests/scripts/test_openshell_readiness_checker.pytests/scripts/test_openshell_smoke_wrapper.pytests/scripts/test_openshell_version_tools.py
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
- GitHub Check: Pytest and Coverage
- GitHub Check: Lint and Hooks
🧰 Additional context used
📓 Path-based instructions (9)
{.github/**,ci/**,.pre-commit-config.yaml,pyproject.toml,uv.lock,mcp/pyproject.toml,mcp/uv.lock}
⚙️ CodeRabbit configuration file
{.github/**,ci/**,.pre-commit-config.yaml,pyproject.toml,uv.lock,mcp/pyproject.toml,mcp/uv.lock}: Review automation and packaging changes for least-privilege permissions, pinned versions where appropriate,
copy-pr-bot pull-request/ branch behavior, reproducible uv/npm setup, secret handling, and consistency with
the documented validation matrix.
Files:
pyproject.toml
{deploy/**,configs/**}
⚙️ CodeRabbit configuration file
{deploy/**,configs/**}: Review deployment and config changes for secret separation, safe defaults, local-vs-production behavior, Helm and
Docker portability, and documentation parity. Flag committed credentials, environment-specific NVIDIA internals in
public defaults, and changes that make examples diverge from CI-tested paths.
Files:
configs/openshell/aiq-research-policy.yamlconfigs/config_openshell.yml
src/aiq_agent/agents/**/*
⚙️ CodeRabbit configuration file
src/aiq_agent/agents/**/*: Review agent changes for research workflow correctness, graph state transitions, prompt/tool contracts,
HITL behavior, and failure handling. Flag changes that weaken source attribution, report generation,
async cancellation, checkpointing, or data-source selection without focused tests and docs.
Files:
src/aiq_agent/agents/deep_researcher/sandbox/README.mdsrc/aiq_agent/agents/deep_researcher/deepagents_runtime.pysrc/aiq_agent/agents/deep_researcher/sandbox/config.pysrc/aiq_agent/agents/deep_researcher/sandbox/providers/openshell.py
**/*.py
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Run ruff check and ruff format validation for Python code changes
**/*.py: Python code must be linted and formatted with Ruff using line length 120, target Python 3.11, rule sets E,F,W,I,PL,UP, and isort force-single-line configuration
Never commit secrets, tokens, or environment-specific hostnames; use environment variables and SecretStr instead, resolving API keys at runtime
Never print or log secret values, including in tool output or error messages
Missing-secret paths must degrade gracefully (stub/skip), not crash or leak
Do not hand-reformat unrelated code when making changes; match the existing import and formatting style
Files:
scripts/openshell/smoke_openshell_isolation.pysrc/aiq_agent/agents/deep_researcher/deepagents_runtime.pytests/scripts/test_openshell_lifecycle_scripts.pysrc/aiq_agent/agents/deep_researcher/sandbox/config.pytests/aiq_agent/agents/deep_researcher/test_deepagents_runtime.pysrc/aiq_agent/agents/deep_researcher/sandbox/providers/openshell.pytests/scripts/test_openshell_gateway_installer.pytests/scripts/test_openshell_smoke_wrapper.pytests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_provider.pytests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_live.pytests/scripts/test_openshell_version_tools.pytests/scripts/test_openshell_readiness_checker.pyscripts/openshell/check_openshell_readiness.py
docs/source/**/*
📄 CodeRabbit inference engine (AGENTS.md)
Update the docs under docs/source/ when behavior, configuration, or workflows change
Files:
docs/source/architecture/agents/sandbox.mddocs/source/deployment/openshell.md
{docs/**,README.md,CONTRIBUTING.md,SECURITY.md,CODE-OF-CONDUCT.md}
⚙️ CodeRabbit configuration file
{docs/**,README.md,CONTRIBUTING.md,SECURITY.md,CODE-OF-CONDUCT.md}: Review documentation for command accuracy, branch-name consistency, current CI and copy-pr-bot behavior, public
vs internal boundary clarity, stale examples, and links that no longer match the repository layout.
Files:
docs/source/architecture/agents/sandbox.mddocs/source/deployment/openshell.md
src/aiq_agent/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/aiq_agent/**/*.py: Respect authenticated data sources by honoring requires_auth, per-user token pass-through, and backend token validators; apply owner guardrails before loading protected report or artifact context into an agent
Do not weaken or bypass AuthMiddleware, validators, or auth gating without a prior design discussion
Files:
src/aiq_agent/agents/deep_researcher/deepagents_runtime.pysrc/aiq_agent/agents/deep_researcher/sandbox/config.pysrc/aiq_agent/agents/deep_researcher/sandbox/providers/openshell.py
**/*test*.py
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Run pytest for all behavior changes in Python code
Files:
tests/scripts/test_openshell_lifecycle_scripts.pytests/aiq_agent/agents/deep_researcher/test_deepagents_runtime.pytests/scripts/test_openshell_gateway_installer.pytests/scripts/test_openshell_smoke_wrapper.pytests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_provider.pytests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_live.pytests/scripts/test_openshell_version_tools.pytests/scripts/test_openshell_readiness_checker.py
**/*config*.py
📄 CodeRabbit inference engine (AGENTS.md)
Config schemas must inherit from FunctionBaseConfig and YAML _type names must come from the registered config class
Files:
src/aiq_agent/agents/deep_researcher/sandbox/config.py
🔇 Additional comments (47)
docs/source/architecture/agents/sandbox.md (1)
12-13: LGTM!src/aiq_agent/agents/deep_researcher/sandbox/providers/openshell.py (4)
111-111: LGTM!
439-439: LGTM!
586-590: LGTM!
608-608: LGTM!tests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_provider.py (6)
143-143: LGTM!
162-162: LGTM!
410-410: LGTM!
425-425: LGTM!
440-445: LGTM!
585-585: LGTM!configs/config_openshell.yml (1)
131-131: LGTM!src/aiq_agent/agents/deep_researcher/deepagents_runtime.py (2)
113-117: LGTM!
581-581: LGTM!src/aiq_agent/agents/deep_researcher/sandbox/config.py (1)
94-98: LGTM!src/aiq_agent/agents/deep_researcher/sandbox/README.md (1)
156-156: LGTM!tests/aiq_agent/agents/deep_researcher/test_deepagents_runtime.py (2)
339-339: LGTM!
357-357: LGTM!tests/scripts/test_openshell_readiness_checker.py (6)
65-65: LGTM!
115-135: LGTM!
146-162: LGTM!
169-170: LGTM!
181-191: LGTM!
229-229: LGTM!tests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_live.py (10)
45-45: LGTM!
81-81: LGTM!
139-139: LGTM!
191-198: LGTM!
271-271: LGTM!
281-281: LGTM!
306-313: LGTM!
363-363: LGTM!
380-396: LGTM!
464-467: LGTM!configs/openshell/aiq-research-policy.yaml (2)
10-11: LGTM!
25-26: LGTM!docs/source/deployment/openshell.md (1)
120-138: LGTM!Also applies to: 190-190, 208-208, 235-235, 259-259, 278-278, 304-312, 339-342, 353-353, 374-378, 441-441
pyproject.toml (1)
89-89: LGTM!scripts/README.md (1)
59-67: LGTM!scripts/openshell/setup_openshell.sh (1)
83-83: LGTM!Also applies to: 111-111
tests/scripts/test_openshell_gateway_installer.py (1)
67-67: LGTM!Also applies to: 111-111, 182-182
tests/scripts/test_openshell_lifecycle_scripts.py (1)
134-146: LGTM!Also applies to: 260-264
tests/scripts/test_openshell_version_tools.py (1)
48-49: LGTM!Also applies to: 62-65, 75-81, 102-102, 113-113, 157-170, 197-197, 206-212
scripts/openshell/check_openshell_readiness.py (1)
10-10: LGTM!Also applies to: 46-46, 91-100, 111-111, 120-124, 133-133, 232-250, 267-275, 299-299
scripts/openshell/start_openshell_gateway.sh (1)
14-15: LGTM!Also applies to: 44-44, 65-69, 237-237
scripts/openshell/smoke_openshell_isolation.py (1)
70-70: 🗄️ Data Integrity & IntegrationVerify that live provider configuration consumes the workspace.
The wrapper now exports
AIQ_OPENSHELL_WORKSPACE, but the suppliedtests/aiq_agent/agents/deep_researcher/sandbox/test_openshell_live.pyprovider setup does not show aworkspaceentry in itsopenshell_config. Unless the provider reads this environment variable directly, non-default live runs may still create per-job sandboxes indefault.tests/scripts/test_openshell_smoke_wrapper.py (1)
48-55: LGTM!Also applies to: 69-72
Signed-off-by: Kyle Zheng <kyzheng@nvidia.com>
|
/ok to test 7fa5da3 |
1 similar comment
|
/ok to test 7fa5da3 |
|
/merge |
746124d
into
NVIDIA-AI-Blueprints:release/2.2
Overview
This PR updates AI-Q’s certified OpenShell stack from
0.0.80to0.0.88and adds explicit workspace scoping throughout the sandbox lifecycle.OpenShell
0.0.88includes the upstream Landlock correction needed for AI-Q’s shipped/dev/urandomand/dev/nullrules to work on Linux withhard_requirement. The existing policy remains unchanged and fail-closed. Thebest_effortmode remains available only as an explicit local-development option, primarily for macOS and Docker Desktop environments where hard Landlock is unavailable.The change also updates readiness checks, gateway startup, provider lifecycle operations, documentation, and tests to use the same certified version and workspace.
DCO sign-off for the squash commit
Signed-off-by: Kyle Zheng 126034466+KyleZheng1284@users.noreply.github.com
Validation
0.0.88.hard_requirement.git commit -sor an equivalent sign-off.Where should reviewers start?
Review the certified version update and workspace propagation through the OpenShell provider, readiness probe, gateway launcher, configuration, and live tests. The deployment guide describes the Linux fail-closed behavior and the macOS local-development fallback.
Related Issues
Summary by CodeRabbit
AIQ_OPENSHELL_WORKSPACE, defaulting todefault) across sandbox lifecycle, readiness checks, gateway startup, and live/smoke testing.--workspaceCLI overrides to align gateway and checks with the selected workspace.0.0.80to certified0.0.88, including provisioning and environment contracts.0.0.88.