feat(studio): OpenAPI Converter for TS Capabilities#492
Conversation
Signed-off-by: Sean Teramae <steramae@nvidia.com>
📝 WalkthroughWalkthroughThe SDK adds capability types, lookup and invocation helpers, gateway tools, provider adapters, generated registry output, build-script wiring, and Vitest coverage for invocation, search, gateway, and adapters. ChangesSDK capability pipeline
Suggested reviewers
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 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 `@web/packages/sdk/src/capabilities/adapters/anthropic.ts`:
- Line 4: The import in anthropic.ts is using a relative path that violates the
SDK TypeScript import rule. Replace the `Capability` and `JsonSchema` import
from `../types` with the configured SDK alias path used elsewhere in the
package. Update the top-level import only, keeping the rest of the `anthropic`
adapter logic unchanged.
In `@web/packages/sdk/src/capabilities/adapters/openai.ts`:
- Line 4: The import in the openai adapter uses a relative path that violates
the SDK import rule. Update the import in the openai.ts module to use the
configured SDK absolute alias instead of ../types, keeping the same Capability
and JsonSchema symbols. Make sure all references in this file continue to
resolve through the tsconfig path-mapped alias and no relative TypeScript
imports remain here.
In `@web/packages/sdk/src/capabilities/capabilities.test.ts`:
- Around line 4-10: The test file is using relative imports that violate the
repository’s alias-import rule and will fail linting. Update the imports in
capabilities.test.ts to use the configured absolute alias paths instead of
./adapters/*, ./gateway, ./invoke, ./registry, and ./types, keeping the same
referenced symbols such as toAnthropicTools, callMcpTool, toMcpTools,
toOpenAITools, createGateway, invokeCapability, searchCapabilities, and the type
imports.
In `@web/packages/sdk/src/capabilities/fetchers.ts`:
- Around line 10-15: This module uses relative import paths for the generated
fetchers, which violates the web TypeScript import rule. Update the imports in
fetchers.ts to use the package alias form instead of ../../ paths, keeping the
same symbols such as customFetch as agentsFetch, dataDesignerFetch,
evaluatorFetch, platformFetch, and safeSynthesizerFetch, and ensure all
references continue to resolve through the tsconfig path mapping.
In `@web/packages/sdk/src/capabilities/gateway.ts`:
- Around line 87-91: The searchCapabilities call in gateway.ts currently
forwards args.limit directly, which allows 0 or negative values despite the
schema requiring a minimum of 1. Update the limit handling in the capability
search flow to clamp or validate args.limit as a positive integer before passing
it into searchCapabilities, and fall back to undefined for any non-integer,
zero, or negative input so the helper only receives safe limits.
In `@web/packages/sdk/src/capabilities/registry.ts`:
- Line 4: The import in registry.ts uses a relative path and violates the SDK
import rule enforced by no-relative-import-paths. Update the CapabilityMeta
import in the registry module to use the configured package alias path instead
of ./types, matching the project’s absolute import convention.
- Around line 61-78: The search logic in the capabilities registry accepts a raw
numeric limit and passes it directly to slice, so negative or invalid values can
produce unintended results. Update the search helper in registry.ts to validate
the limit before slicing, either by clamping it to a non-negative integer or by
rejecting invalid inputs. Keep the fix centered around the search_capabilities
flow that builds scored results and returns the sliced summaries.
🪄 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: CHILL
Plan: Enterprise
Run ID: e2d68e96-e2a4-4575-a454-8a5bd7042ee3
⛔ Files ignored due to path filters (1)
web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (15)
web/packages/sdk/generateAll.tsweb/packages/sdk/orval/generate-capabilities.tsweb/packages/sdk/package.jsonweb/packages/sdk/src/capabilities/adapters/anthropic.tsweb/packages/sdk/src/capabilities/adapters/mcp.tsweb/packages/sdk/src/capabilities/adapters/openai.tsweb/packages/sdk/src/capabilities/capabilities.test.tsweb/packages/sdk/src/capabilities/fetchers.tsweb/packages/sdk/src/capabilities/gateway.tsweb/packages/sdk/src/capabilities/index.tsweb/packages/sdk/src/capabilities/invoke.tsweb/packages/sdk/src/capabilities/registry.tsweb/packages/sdk/src/capabilities/types.tsweb/packages/sdk/tsconfig.jsonweb/packages/sdk/vitest.config.ts
| // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import type { Capability, JsonSchema } from '../types'; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Use the SDK alias instead of a relative import.
../types violates the web TypeScript import rule. Replace it with the configured absolute alias path. As per coding guidelines, "Never use relative imports. Always use absolute alias paths ... Use absolute imports via tsconfig path mapping. Never use relative imports in TypeScript."
🤖 Prompt for 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.
In `@web/packages/sdk/src/capabilities/adapters/anthropic.ts` at line 4, The
import in anthropic.ts is using a relative path that violates the SDK TypeScript
import rule. Replace the `Capability` and `JsonSchema` import from `../types`
with the configured SDK alias path used elsewhere in the package. Update the
top-level import only, keeping the rest of the `anthropic` adapter logic
unchanged.
Source: Coding guidelines
| // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import type { Capability, JsonSchema } from '../types'; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Use the SDK alias instead of a relative import.
../types violates the web TypeScript import rule. Replace it with the configured absolute alias path. As per coding guidelines, "Never use relative imports. Always use absolute alias paths ... Use absolute imports via tsconfig path mapping. Never use relative imports in TypeScript."
🤖 Prompt for 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.
In `@web/packages/sdk/src/capabilities/adapters/openai.ts` at line 4, The import
in the openai adapter uses a relative path that violates the SDK import rule.
Update the import in the openai.ts module to use the configured SDK absolute
alias instead of ../types, keeping the same Capability and JsonSchema symbols.
Make sure all references in this file continue to resolve through the tsconfig
path-mapped alias and no relative TypeScript imports remain here.
Source: Coding guidelines
| import { toAnthropicTools } from './adapters/anthropic'; | ||
| import { callMcpTool, toMcpTools } from './adapters/mcp'; | ||
| import { toOpenAITools } from './adapters/openai'; | ||
| import { createGateway } from './gateway'; | ||
| import { invokeCapability } from './invoke'; | ||
| import { searchCapabilities } from './registry'; | ||
| import type { CapabilityContext, CapabilityMeta, FetchRequest } from './types'; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Replace relative imports with configured path aliases.
Line 4-10 violates the repo rule and will trip no-relative-import-paths. Use the package’s absolute alias paths here instead of ./.... As per coding guidelines, "web/**/*.{ts,tsx,js,jsx}: Never use relative imports. Always use absolute alias paths ... ESLint enforces this with no-relative-import-paths."
🤖 Prompt for 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.
In `@web/packages/sdk/src/capabilities/capabilities.test.ts` around lines 4 - 10,
The test file is using relative imports that violate the repository’s
alias-import rule and will fail linting. Update the imports in
capabilities.test.ts to use the configured absolute alias paths instead of
./adapters/*, ./gateway, ./invoke, ./registry, and ./types, keeping the same
referenced symbols such as toAnthropicTools, callMcpTool, toMcpTools,
toOpenAITools, createGateway, invokeCapability, searchCapabilities, and the type
imports.
Source: Coding guidelines
| import { customFetch as agentsFetch } from '../../generated/fetchers/agents'; | ||
| import { customFetch as dataDesignerFetch } from '../../generated/fetchers/data-designer'; | ||
| import { customFetch as evaluatorFetch } from '../../generated/fetchers/evaluator'; | ||
| import { customFetch as platformFetch } from '../../generated/fetchers/platform'; | ||
| import { customFetch as safeSynthesizerFetch } from '../../generated/fetchers/safe-synthesizer'; | ||
| import type { Fetcher } from './types'; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Use absolute alias imports in this module.
These relative imports violate the web TypeScript import rule and will trip no-relative-import-paths. Switch them to the package alias form. As per coding guidelines, "Never use relative imports. Always use absolute alias paths ... Use absolute imports via tsconfig path mapping. Never use relative imports in TypeScript."
🤖 Prompt for 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.
In `@web/packages/sdk/src/capabilities/fetchers.ts` around lines 10 - 15, This
module uses relative import paths for the generated fetchers, which violates the
web TypeScript import rule. Update the imports in fetchers.ts to use the package
alias form instead of ../../ paths, keeping the same symbols such as customFetch
as agentsFetch, dataDesignerFetch, evaluatorFetch, platformFetch, and
safeSynthesizerFetch, and ensure all references continue to resolve through the
tsconfig path mapping.
Source: Coding guidelines
| const results = searchCapabilities(capabilities, query, { | ||
| limit: typeof args.limit === 'number' ? args.limit : undefined, | ||
| service: asString(args.service), | ||
| readOnly: typeof args.readOnly === 'boolean' ? args.readOnly : undefined, | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Clamp limit to a positive integer.
Schema says minimum: 1, but runtime accepts any number. A model passing 0 or a negative value yields empty or surprising slice(0, negative) results.
Proposed guard
- limit: typeof args.limit === 'number' ? args.limit : undefined,
+ limit:
+ typeof args.limit === 'number' && args.limit >= 1
+ ? Math.floor(args.limit)
+ : undefined,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const results = searchCapabilities(capabilities, query, { | |
| limit: typeof args.limit === 'number' ? args.limit : undefined, | |
| service: asString(args.service), | |
| readOnly: typeof args.readOnly === 'boolean' ? args.readOnly : undefined, | |
| }); | |
| const results = searchCapabilities(capabilities, query, { | |
| limit: | |
| typeof args.limit === 'number' && args.limit >= 1 | |
| ? Math.floor(args.limit) | |
| : undefined, | |
| service: asString(args.service), | |
| readOnly: typeof args.readOnly === 'boolean' ? args.readOnly : undefined, | |
| }); |
🤖 Prompt for 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.
In `@web/packages/sdk/src/capabilities/gateway.ts` around lines 87 - 91, The
searchCapabilities call in gateway.ts currently forwards args.limit directly,
which allows 0 or negative values despite the schema requiring a minimum of 1.
Update the limit handling in the capability search flow to clamp or validate
args.limit as a positive integer before passing it into searchCapabilities, and
fall back to undefined for any non-integer, zero, or negative input so the
helper only receives safe limits.
| // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| import type { CapabilityMeta } from './types'; |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Replace the relative import with the package alias.
./types violates the SDK import rule and will trip no-relative-import-paths. Use the configured absolute alias instead. As per coding guidelines, "Never use relative imports. Always use absolute alias paths ... Use absolute imports via tsconfig path mapping. Never use relative imports in TypeScript."
🤖 Prompt for 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.
In `@web/packages/sdk/src/capabilities/registry.ts` at line 4, The import in
registry.ts uses a relative path and violates the SDK import rule enforced by
no-relative-import-paths. Update the CapabilityMeta import in the registry
module to use the configured package alias path instead of ./types, matching the
project’s absolute import convention.
Source: Coding guidelines
| const { limit = 25, service, readOnly } = options; | ||
| const terms = query.toLowerCase().split(/\s+/).filter(Boolean); | ||
|
|
||
| const scored: Array<{ meta: CapabilityMeta; score: number }> = []; | ||
| for (const meta of capabilities) { | ||
| if (service && meta.service !== service) continue; | ||
| if (readOnly !== undefined && meta.readOnly !== readOnly) continue; | ||
|
|
||
| const hay = haystack(meta); | ||
| if (!terms.every((t) => hay.includes(t))) continue; | ||
|
|
||
| const nameOrPath = `${meta.name} ${meta.path}`.toLowerCase(); | ||
| const score = terms.reduce((acc, t) => acc + (nameOrPath.includes(t) ? 1 : 0), 0); | ||
| scored.push({ meta, score }); | ||
| } | ||
|
|
||
| scored.sort((a, b) => b.score - a.score || a.meta.name.localeCompare(b.meta.name)); | ||
| return scored.slice(0, limit).map(({ meta }) => toSummary(meta)); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Validate limit before slicing.
search_capabilities forwards raw numeric limit here, so limit: -1 returns all-but-last because slice(0, -1) is valid JS. Clamp to a non-negative integer or reject invalid values.
Suggested fix
- const { limit = 25, service, readOnly } = options;
+ const { service, readOnly } = options;
+ const limit =
+ options.limit === undefined ? 25 : Math.max(0, Math.trunc(options.limit));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const { limit = 25, service, readOnly } = options; | |
| const terms = query.toLowerCase().split(/\s+/).filter(Boolean); | |
| const scored: Array<{ meta: CapabilityMeta; score: number }> = []; | |
| for (const meta of capabilities) { | |
| if (service && meta.service !== service) continue; | |
| if (readOnly !== undefined && meta.readOnly !== readOnly) continue; | |
| const hay = haystack(meta); | |
| if (!terms.every((t) => hay.includes(t))) continue; | |
| const nameOrPath = `${meta.name} ${meta.path}`.toLowerCase(); | |
| const score = terms.reduce((acc, t) => acc + (nameOrPath.includes(t) ? 1 : 0), 0); | |
| scored.push({ meta, score }); | |
| } | |
| scored.sort((a, b) => b.score - a.score || a.meta.name.localeCompare(b.meta.name)); | |
| return scored.slice(0, limit).map(({ meta }) => toSummary(meta)); | |
| const { service, readOnly } = options; | |
| const limit = | |
| options.limit === undefined ? 25 : Math.max(0, Math.trunc(options.limit)); | |
| const terms = query.toLowerCase().split(/\s+/).filter(Boolean); | |
| const scored: Array<{ meta: CapabilityMeta; score: number }> = []; | |
| for (const meta of capabilities) { | |
| if (service && meta.service !== service) continue; | |
| if (readOnly !== undefined && meta.readOnly !== readOnly) continue; | |
| const hay = haystack(meta); | |
| if (!terms.every((t) => hay.includes(t))) continue; | |
| const nameOrPath = `${meta.name} ${meta.path}`.toLowerCase(); | |
| const score = terms.reduce((acc, t) => acc + (nameOrPath.includes(t) ? 1 : 0), 0); | |
| scored.push({ meta, score }); | |
| } | |
| scored.sort((a, b) => b.score - a.score || a.meta.name.localeCompare(b.meta.name)); | |
| return scored.slice(0, limit).map(({ meta }) => toSummary(meta)); |
🤖 Prompt for 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.
In `@web/packages/sdk/src/capabilities/registry.ts` around lines 61 - 78, The
search logic in the capabilities registry accepts a raw numeric limit and passes
it directly to slice, so negative or invalid values can produce unintended
results. Update the search helper in registry.ts to validate the limit before
slicing, either by clamping it to a non-negative integer or by rejecting invalid
inputs. Keep the fix centered around the search_capabilities flow that builds
scored results and returns the sliced summaries.
|
This PR adds a new script for generating capabilities and some gateway tool definitions (search, describe, read and run) to help with consuming said capabilities.
The idea here, is an agent/model will inveitably want to interact with nemo platform through the API. There already exists nemo skills that describe CLI commands which work well but only for CLI based AIs. In Studio, a web UI based app, we would want an agent to be able to interact with the platform similar to how a user would through publicly available web based APIs. This isn't meant as a replacement for the CLI skills but rather a new tool for web based interaction of agents and nemo platform. Since this is auto generated from the openapi yaml, it is the clearest connection from what is available on the API.
example capability
Summary by CodeRabbit
New Features
Bug Fixes
Tests