Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .agents/skills/contribute-adapter/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ the validation matrix, and `prepare-pr` for review handoff.
Follow these repository-specific requirements after applying the public skill:

1. Place the adapter under `adapters/<name>/` with `LICENSE -> ../../LICENSE`,
`README.md`, `fabric-adapter.json`, language-native package and lock files,
a source entry point, and focused tests.
`README.md`, `<name>.fabric-adapter.json`, language-native package and lock
files, a source entry point, and focused tests.
2. Give each Python leaf adapter a small base installation, a `harness` extra
for supported target packages, and a `full` extra for package-installable
integrations. Add a `relay` extra only when the adapter imports NVIDIA NeMo
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ supported harness, install its package extra and set the corresponding adapter
ID. Pass harness-specific options through `HarnessConfig.settings` only when
the selected adapter descriptor declares them in `settings_schema`. Adapters
that expose selectable executables can accept `FabricConfig.workflow`; the
descriptor's `workflow_schema` defines and validates that adapter-owned block.
selected Adapter Target Descriptor defines the workflow entry point and
validates its settings.

For a guided version of this example, refer to the
[`01_quickstart.ipynb` notebook](examples/notebooks/01_quickstart.ipynb). The
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,6 @@
"AdapterConfigInput": {
"description": "Configuration object delivered to an adapter lifecycle host.",
"oneOf": [
{
"const": "fabric_config",
"description": "Deliver the complete northbound `FabricConfig` for legacy adapters.",
"type": "string"
},
{
"const": "agent_config",
"description": "Deliver the resolved southbound `AgentConfig` contract.",
Expand Down Expand Up @@ -105,7 +100,7 @@
},
"input": {
"$ref": "#/$defs/AdapterConfigInput",
"default": "fabric_config",
"default": "agent_config",
"description": "Configuration object delivered to the adapter lifecycle host."
}
},
Expand Down Expand Up @@ -178,6 +173,16 @@
},
"type": "object"
},
"AdapterTargetType": {
"description": "Adapter target categories understood by this Adapter Contract version.",
"oneOf": [
{
"const": "workflow",
"description": "A separately registered custom-agent or workflow target.",
"type": "string"
}
]
},
"AdapterTelemetryProviderSupport": {
"additionalProperties": true,
"description": "Telemetry capabilities for one adapter-supported provider.",
Expand Down Expand Up @@ -278,7 +283,7 @@
"config": {
"$ref": "#/$defs/AdapterConfigSupport",
"default": {
"input": "fabric_config"
"input": "agent_config"
},
"description": "NeMo Fabric config areas this adapter consumes or generates."
},
Expand Down Expand Up @@ -318,12 +323,6 @@
},
"type": "object"
},
"harness": {
"description": "Stable machine-readable harness identifier implemented by this adapter.",
"minLength": 1,
"pattern": "\\S",
"type": "string"
},
"model_schema": {
"additionalProperties": true,
"description": "JSON Schema applied to every normalized `FabricConfig.models` entry.",
Expand All @@ -350,6 +349,13 @@
"null"
]
},
"target_types": {
"description": "Registered target categories this adapter can execute.",
"items": {
"$ref": "#/$defs/AdapterTargetType"
},
"type": "array"
},
"telemetry": {
"$ref": "#/$defs/AdapterTelemetrySupport",
"default": {},
Expand All @@ -362,20 +368,11 @@
"object",
"null"
]
},
"workflow_schema": {
"additionalProperties": true,
"description": "JSON Schema for adapter-owned `FabricConfig.workflow`.",
"type": [
"object",
"null"
]
}
},
"required": [
"contract_version",
"adapter_id",
"harness",
"adapter_kind"
],
"title": "AdapterDescriptor",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"$defs": {
"WorkflowEntrypointConfig": {
"additionalProperties": true,
"description": "Adapter-owned workflow entry point supplied by an Adapter Target Descriptor.",
"properties": {
"kind": {
"description": "Adapter-defined entry-point resolution semantics.",
"minLength": 1,
"pattern": "\\S",
"type": "string"
},
"ref": {
"description": "Adapter-defined workflow reference.",
"minLength": 1,
"pattern": "\\S",
"type": "string"
}
},
"required": [
"kind",
"ref"
],
"type": "object"
},
"WorkflowTargetSpec": {
"additionalProperties": true,
"description": "Workflow-specific Adapter Target Descriptor fields.",
"properties": {
"entrypoint": {
"$ref": "#/$defs/WorkflowEntrypointConfig",
"description": "Entry point projected southbound to the adapter."
},
"settings_schema": {
"additionalProperties": true,
"description": "JSON Schema for `FabricConfig.workflow.settings`.",
"type": [
"object",
"null"
]
}
},
"required": [
"entrypoint"
],
"type": "object"
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "Independently registered target implemented by an adapter.",
"oneOf": [
{
"description": "Registered workflow target.",
"properties": {
"spec": {
"$ref": "#/$defs/WorkflowTargetSpec"
},
"type": {
"const": "workflow",
"type": "string"
}
},
"required": [
"type",
"spec"
],
"type": "object"
}
],
"properties": {
"adapter_id": {
"description": "Adapter implementation selected by this target.",
"minLength": 1,
"pattern": "\\S",
"type": "string"
},
"contract_version": {
"const": "fabric.adapter/v1alpha2",
"description": "Adapter Contract version shared with Adapter Descriptor.",
"minLength": 1,
"type": "string"
},
"id": {
"description": "Unique registered target id.",
"minLength": 1,
"pattern": "\\S",
"type": "string"
}
},
"required": [
"contract_version",
"id",
"adapter_id"
],
"title": "AdapterTargetDescriptor",
"type": "object",
"unevaluatedProperties": true
}
13 changes: 12 additions & 1 deletion adapter-contract/typescript/scripts/check-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ try {
"README.md",
"dist/generated/adapter-descriptor.d.ts",
"dist/generated/adapter-descriptor.js",
"dist/generated/adapter-target-descriptor.d.ts",
"dist/generated/adapter-target-descriptor.js",
"dist/generated/agent-config.d.ts",
"dist/generated/agent-config.js",
"dist/generated/agent-run-request.d.ts",
Expand All @@ -50,6 +52,7 @@ try {
"dist/version.js",
"package.json",
"schemas/adapter-descriptor.schema.json",
"schemas/adapter-target-descriptor.schema.json",
"schemas/agent-config.schema.json",
"schemas/agent-run-request.schema.json",
"schemas/agent-run-result.schema.json",
Expand Down Expand Up @@ -120,20 +123,28 @@ try {
`import { ADAPTER_CONTRACT_VERSION } from "nemo-fabric-adapter-contract";
import agentConfigSchema from "nemo-fabric-adapter-contract/schemas/agent-config" with { type: "json" };
import type { AdapterDescriptor } from "nemo-fabric-adapter-contract";
import type { AdapterTargetDescriptor } from "nemo-fabric-adapter-contract";
import type { AgentRunResult } from "nemo-fabric-adapter-contract";

const descriptor: AdapterDescriptor = {
adapter_id: "example",
adapter_kind: "process",
contract_version: ADAPTER_CONTRACT_VERSION,
harness: "example",
};
const target: AdapterTargetDescriptor = {
adapter_id: "example",
contract_version: ADAPTER_CONTRACT_VERSION,
id: "example.workflow",
spec: { entrypoint: { kind: "factory", ref: "example.agent" } },
type: "workflow",
};
const result: AgentRunResult = {
output: ["ok", null],
status: "succeeded",
};
if (
descriptor.contract_version !== ADAPTER_CONTRACT_VERSION ||
target.adapter_id !== descriptor.adapter_id ||
result.status !== "succeeded" ||
agentConfigSchema.title !== "AgentConfig"
) {
Expand Down
26 changes: 25 additions & 1 deletion adapter-contract/typescript/scripts/generate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ const schemaSpecs = [
output: "adapter-descriptor.ts",
project: projectAdapterDescriptor,
},
{
name: "adapter-target-descriptor",
output: "adapter-target-descriptor.ts",
project: projectAdapterTargetDescriptor,
},
{ name: "agent-config", output: "agent-config.ts" },
{
name: "agent-run-request",
Expand Down Expand Up @@ -233,6 +238,21 @@ function projectAdapterDescriptor(original) {
return schema;
}

function projectAdapterTargetDescriptor(original) {
const schema = deepClone(original);
const contractVersion = requireString(
schema.properties?.contract_version?.const,
"adapter-target-descriptor contract_version.const",
);
schema.properties.contract_version.tsType = JSON.stringify(contractVersion);
// schemars emits `unevaluatedProperties` for the flattened target enum.
// Project the same open extension surface in the form understood by the
// TypeScript generator and its JsonObject guard.
delete schema.unevaluatedProperties;
schema.additionalProperties = true;
return schema;
}

function projectRunRequest(original) {
const schema = deepClone(original);
const input = schema.properties?.input;
Expand Down Expand Up @@ -399,11 +419,15 @@ function replaceGeneratedPropertyType(output, property, projectedType) {
function projectOpenInterfaces(output) {
const openInterface =
/export interface ([A-Za-z][A-Za-z0-9]*) \{\n((?:(?!^export (?:interface|type) ).)*?) \[k: string\]: unknown;\n\}/gms;
const projected = output.replace(
const projectedInterfaces = output.replace(
openInterface,
(_match, name, fields) =>
`export type ${name} = {\n${fields}} & JsonObject;`,
);
const projected = projectedInterfaces.replace(
/ \[k: string\]: unknown;\n\}/g,
"} & JsonObject",
);
if (projected.includes("[k: string]: unknown;")) {
throw new Error(
"Generated output contains an open object that was not projected to JsonObject",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ export type AdapterConfigField =
| "mcp.auth.service_account"
| "mcp.tool_filters"
| "skills";
/**
* Adapter target categories understood by this Adapter Contract version.
*/
export type AdapterTargetType = "workflow";

/**
* Language-neutral adapter descriptor for a harness integration.
Expand All @@ -72,10 +76,6 @@ export type AdapterDescriptor = {
* JSON Schemas for adapter-owned `extensions` at southbound block types.
*/
extension_schemas?: Partial<Record<AdapterExtensionPoint, JsonObject>>;
/**
* Stable machine-readable harness identifier implemented by this adapter.
*/
harness: string;
/**
* JSON Schema applied to every normalized `FabricConfig.models` entry.
*/
Expand All @@ -89,15 +89,15 @@ export type AdapterDescriptor = {
* JSON Schema for adapter-owned `harness.settings`.
*/
settings_schema?: JsonObject | null;
/**
* Registered target categories this adapter can execute.
*/
target_types?: AdapterTargetType[];
telemetry?: AdapterTelemetrySupport;
/**
* JSON Schema applied to every normalized `FabricConfig.tools.definitions` entry.
*/
tool_definition_schema?: JsonObject | null;
/**
* JSON Schema for adapter-owned `FabricConfig.workflow`.
*/
workflow_schema?: JsonObject | null;
} & JsonObject;
/**
* Runtime lifecycle operations supported by this adapter.
Expand Down Expand Up @@ -139,7 +139,7 @@ export type AdapterConfigSupport = {
/**
* Configuration object delivered to the adapter lifecycle host.
*/
input?: "fabric_config" | "agent_config";
input?: "agent_config";
} & JsonObject;
/**
* Runtime requirements.
Expand Down
Loading
Loading