From 2553b440940ab2cb0ae2e961bf66f41ed64c5b4c Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Thu, 16 Jul 2026 15:13:21 -0700 Subject: [PATCH 01/20] FABRIC-71: experiment with typed SDK-backed CLI Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- ATTRIBUTIONS-Rust.md | 6446 +++-------------- CLI.md | 97 + Cargo.lock | 270 - Cargo.toml | 5 - README.md | 11 +- .../nemo_fabric_adapters/claude/adapter.py | 8 +- .../src/nemo_fabric_adapters/codex/adapter.py | 10 +- .../common/relay_gateway.py | 4 +- .../src/nemo_fabric_adapters/common/utils.py | 6 +- .../deepagents/adapter.py | 8 +- .../nemo_fabric_adapters/hermes/adapter.py | 2 +- crates/fabric-cli/Cargo.toml | 24 - crates/fabric-cli/src/main.rs | 489 -- crates/fabric-core/Cargo.toml | 1 - .../fabric-core/examples/generate-schemas.rs | 14 + crates/fabric-core/src/config.rs | 1473 +--- crates/fabric-core/src/doctor.rs | 161 +- crates/fabric-core/src/error.rs | 43 - crates/fabric-core/src/lib.rs | 14 +- crates/fabric-core/src/runtime.rs | 1111 +-- crates/fabric-core/src/schema.rs | 12 +- crates/fabric-python/src/lib.rs | 166 +- .../api/python-library-reference/index.md | 6 +- .../nemo_fabric.client.md | 62 +- .../nemo_fabric.errors.md | 2 +- .../nemo_fabric.models.md | 120 - .../nemo_fabric.types.md | 10 +- .../config/enum-capabilitykind.mdx | 2 +- .../config/enum-capabilitytarget.mdx | 2 +- .../config/enum-fabricdocument.mdx | 110 - .../config/enum-mcpexposure.mdx | 2 +- .../config/enum-relayatifstorageconfig.mdx | 2 +- .../enum-relayatofendpointfieldnamepolicy.mdx | 2 +- .../enum-relayatofendpointtransport.mdx | 2 +- .../config/enum-relayatofmode.mdx | 2 +- .../config/enum-relayotlptransport.mdx | 2 +- .../config/enum-relayunsupportedbehavior.mdx | 2 +- .../config/enum-resolutionstrategy.mdx | 2 +- .../config/enum-telemetryprovider.mdx | 2 +- .../config/fn-load-adapter-descriptor.mdx | 2 +- .../config/fn-load-fabric-document.mdx | 14 - ...n-resolve-effective-config-from-config.mdx | 8 +- ...resolve-effective-config-with-profiles.mdx | 14 - .../config/fn-resolve-effective-config.mdx | 14 - .../fn-resolve-run-plan-from-config.mdx | 10 +- ...resolve-run-plan-from-effective-config.mdx | 2 +- .../fn-resolve-run-plan-with-profiles.mdx | 14 - .../config/fn-resolve-run-plan.mdx | 14 - .../config/fn-validate-agent-directory.mdx | 14 - .../nemo-fabric-core/config/index.mdx | 17 +- .../config/struct-effectiveconfig.mdx | 24 +- .../config/struct-fabricconfig.mdx | 6 +- .../config/struct-harnessconfig.mdx | 2 +- .../config/struct-mcpconfig.mdx | 2 +- .../config/struct-mcpserverplan.mdx | 2 +- .../config/struct-metadataconfig.mdx | 2 +- .../config/struct-modelconfig.mdx | 2 +- .../config/struct-profileconfig.mdx | 114 - .../config/struct-profileregistryconfig.mdx | 106 - .../config/struct-relayatifconfig.mdx | 2 +- .../config/struct-relayatofconfig.mdx | 2 +- .../config/struct-relayatofendpointconfig.mdx | 2 +- .../config/struct-relaycomponentconfig.mdx | 2 +- .../config/struct-relayconfig.mdx | 2 +- .../config/struct-relayconfigpolicy.mdx | 2 +- .../struct-relayobservabilityconfig.mdx | 2 +- .../config/struct-relayotlpconfig.mdx | 2 +- .../config/struct-resolvecontext.mdx | 28 +- .../struct-resolvedadapterdescriptor.mdx | 2 +- .../config/struct-runplan.mdx | 24 +- .../config/struct-runtimecapabilities.mdx | 2 +- .../config/struct-runtimeconfig.mdx | 2 +- .../config/struct-skillconfig.mdx | 2 +- .../config/struct-telemetryconfig.mdx | 2 +- .../config/struct-telemetryplan.mdx | 2 +- .../config/struct-telemetryproviderconfig.mdx | 2 +- .../config/struct-toolsconfig.mdx | 2 +- .../config/struct-toolsplan.mdx | 2 +- .../doctor/enum-doctorstatus.mdx | 2 +- .../doctor/fn-doctor-plan.mdx | 2 +- .../nemo-fabric-core/doctor/index.mdx | 2 +- .../doctor/struct-doctorcheck.mdx | 2 +- .../doctor/struct-doctorreport.mdx | 8 +- .../error/enum-fabricerror.mdx | 90 +- .../nemo-fabric-core/error/index.mdx | 2 +- .../nemo-fabric-core/error/type-result.mdx | 2 +- .../nemo-fabric-core/fn-version.mdx | 2 +- .../nemo-fabric-core/index.mdx | 8 - .../runtime/enum-errorstage.mdx | 2 +- .../nemo-fabric-core/runtime/index.mdx | 2 +- .../runtime/struct-runrequest.mdx | 2 +- .../runtime/struct-runresult.mdx | 6 +- .../schema/enum-schemaname.mdx | 13 +- .../nemo-fabric-core/schema/index.mdx | 2 +- .../fabric/adapters/scripted/run.py | 1 + python/pyproject.toml | 3 + python/src/nemo_fabric/__init__.py | 4 - .../adapters/claude/fabric-adapter.json | 9 + .../adapters/codex/fabric-adapter.json | 9 + .../adapters/deepagents/fabric-adapter.json | 10 + .../adapters/hermes/fabric-adapter.json | 10 + .../code_review_agent/repo/calculator.py | 8 + .../code_review_agent/skills/code-review.md | 8 + python/src/nemo_fabric/_config_sources.py | 90 - python/src/nemo_fabric/_native.pyi | 16 - python/src/nemo_fabric/cli.py | 229 + python/src/nemo_fabric/client.py | 291 +- python/src/nemo_fabric/errors.py | 2 +- python/src/nemo_fabric/factories.py | 36 + python/src/nemo_fabric/models.py | 24 - python/src/nemo_fabric/presets.py | 128 + python/src/nemo_fabric/runtime.py | 20 +- python/src/nemo_fabric/types.py | 59 +- schemas/SCHEMA.md | 20 +- schemas/adapter-descriptor.schema.json | 2 +- schemas/adapter-invocation.schema.json | 48 +- schemas/agent.schema.json | 18 - schemas/effective-config.schema.json | 46 +- schemas/error-info.schema.json | 2 +- schemas/profile.schema.json | 120 - schemas/run-plan.schema.json | 76 +- schemas/run-request.schema.json | 2 +- schemas/run-result.schema.json | 10 +- tests/_utils/configs.py | 90 + tests/_utils/utils.py | 27 +- tests/adapters/test_adapaters_common_utils.py | 8 +- tests/adapters/test_claude_adapter.py | 2 +- tests/adapters/test_codex_adapter.py | 10 +- tests/adapters/test_deepagents.py | 2 +- tests/adapters/test_hermes_adapter.py | 4 +- tests/conftest.py | 16 - tests/e2e/test_cli.py | 270 +- tests/e2e/test_harbor_swebench_task.py | 11 +- tests/e2e/test_hermes_config_mapping.py | 2 +- tests/e2e/test_hermes_e2e.py | 2 +- tests/e2e/test_local_env_e2e.py | 12 +- tests/e2e/test_swebench_style.py | 5 +- tests/fixtures/file-config-agent/agent.yaml | 48 - .../file-config-agent/profiles/codex.yaml | 35 - .../profiles/deepagents.yaml | 36 - .../file-config-agent/profiles/env-local.yaml | 13 - .../profiles/env-opensandbox.yaml | 26 - .../file-config-agent/profiles/hermes.yaml | 33 - .../profiles/mcp-github.yaml | 30 - .../profiles/native-otel.yaml | 23 - .../profiles/relay-openinference.yaml | 29 - .../profiles/relay-otel.yaml | 25 - .../file-config-agent/profiles/relay.yaml | 25 - tests/fixtures/hermes-shim-agent/agent.yaml | 47 - .../hermes-shim-agent/profiles/env-local.yaml | 13 - .../harbor-swebench-django-13741.yaml | 31 - .../profiles/mcp-github.yaml | 20 - .../profiles/swebench-shim.yaml | 30 - tests/integrations/test_harbor_runner.py | 2 - tests/integrations/test_harbor_telemetry.py | 1 - tests/python/test_code_review_example.py | 5 +- tests/python/test_harbor_integration.py | 2 - tests/python/test_native_sdk.py | 52 +- tests/python/test_runtime.py | 62 +- tests/python/test_sdk_concurrency.py | 5 +- tests/python/test_sdk_contract.py | 120 +- tests/python/test_sdk_runtimes.py | 7 +- tests/python/test_typed_config.py | 95 +- uv.lock | 2 +- 164 files changed, 2171 insertions(+), 11731 deletions(-) create mode 100644 CLI.md delete mode 100644 crates/fabric-cli/Cargo.toml delete mode 100644 crates/fabric-cli/src/main.rs create mode 100644 crates/fabric-core/examples/generate-schemas.rs delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-fabricdocument.mdx delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-fabric-document.mdx delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-with-profiles.mdx delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config.mdx delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-with-profiles.mdx delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan.mdx delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-validate-agent-directory.mdx delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-profileconfig.mdx delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-profileregistryconfig.mdx create mode 100644 python/src/nemo_fabric/_bundled/adapters/claude/fabric-adapter.json create mode 100644 python/src/nemo_fabric/_bundled/adapters/codex/fabric-adapter.json create mode 100644 python/src/nemo_fabric/_bundled/adapters/deepagents/fabric-adapter.json create mode 100644 python/src/nemo_fabric/_bundled/adapters/hermes/fabric-adapter.json create mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/repo/calculator.py create mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/skills/code-review.md delete mode 100644 python/src/nemo_fabric/_config_sources.py create mode 100644 python/src/nemo_fabric/cli.py create mode 100644 python/src/nemo_fabric/factories.py create mode 100644 python/src/nemo_fabric/presets.py delete mode 100644 schemas/profile.schema.json create mode 100644 tests/_utils/configs.py delete mode 100644 tests/fixtures/file-config-agent/agent.yaml delete mode 100644 tests/fixtures/file-config-agent/profiles/codex.yaml delete mode 100644 tests/fixtures/file-config-agent/profiles/deepagents.yaml delete mode 100644 tests/fixtures/file-config-agent/profiles/env-local.yaml delete mode 100644 tests/fixtures/file-config-agent/profiles/env-opensandbox.yaml delete mode 100644 tests/fixtures/file-config-agent/profiles/hermes.yaml delete mode 100644 tests/fixtures/file-config-agent/profiles/mcp-github.yaml delete mode 100644 tests/fixtures/file-config-agent/profiles/native-otel.yaml delete mode 100644 tests/fixtures/file-config-agent/profiles/relay-openinference.yaml delete mode 100644 tests/fixtures/file-config-agent/profiles/relay-otel.yaml delete mode 100644 tests/fixtures/file-config-agent/profiles/relay.yaml delete mode 100644 tests/fixtures/hermes-shim-agent/agent.yaml delete mode 100644 tests/fixtures/hermes-shim-agent/profiles/env-local.yaml delete mode 100644 tests/fixtures/hermes-shim-agent/profiles/harbor-swebench-django-13741.yaml delete mode 100644 tests/fixtures/hermes-shim-agent/profiles/mcp-github.yaml delete mode 100644 tests/fixtures/hermes-shim-agent/profiles/swebench-shim.yaml diff --git a/ATTRIBUTIONS-Rust.md b/ATTRIBUTIONS-Rust.md index 921e0d77b..9e2f597d6 100644 --- a/ATTRIBUTIONS-Rust.md +++ b/ATTRIBUTIONS-Rust.md @@ -9,5063 +9,279 @@ This project uses the following third-party libraries. Each library is open-sour This file is automatically generated. Please do not edit it directly. Regenerate with `./scripts/generate_attributions.sh rust`. -## anstream - 1.0.0 -**Repository URL**: https://github.com/rust-cli/anstyle.git +## dyn-clone - 1.0.20 +**Repository URL**: https://github.com/dtolnay/dyn-clone **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ - 1. Definitions. +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +1. Definitions. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - END OF TERMS AND CONDITIONS +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - APPENDIX: How to apply the Apache License to your work. +END OF TERMS AND CONDITIONS - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. +APPENDIX: How to apply the Apache License to your work. - Copyright {yyyy} {name of copyright owner} +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Copyright [yyyy] [name of copyright owner] - http://www.apache.org/licenses/LICENSE-2.0 +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. ``` -## anstyle - 1.0.14 -**Repository URL**: https://github.com/rust-cli/anstyle.git +## heck - 0.5.0 +**Repository URL**: https://github.com/withoutboats/heck **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - 1. Definitions. +1. Definitions. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## anstyle-parse - 1.0.0 -**Repository URL**: https://github.com/rust-cli/anstyle.git -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## anstyle-query - 1.1.5 -**Repository URL**: https://github.com/rust-cli/anstyle.git -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## anstyle-wincon - 3.0.11 -**Repository URL**: https://github.com/rust-cli/anstyle.git -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## bitflags - 2.13.0 -**Repository URL**: https://github.com/bitflags/bitflags -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## block2 - 0.6.2 -**Repository URL**: https://github.com/madsmtm/objc2 -**License Type(s)**: MIT -### License: https://spdx.org/licenses/MIT.html -``` -MIT License - -Copyright (c) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the -following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO -EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -USE OR OTHER DEALINGS IN THE SOFTWARE. - -``` - -## cfg-if - 1.0.4 -**Repository URL**: https://github.com/rust-lang/cfg-if -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## cfg_aliases - 0.2.1 -**Repository URL**: https://github.com/katharostech/cfg_aliases -**License Type(s)**: MIT -### License: https://spdx.org/licenses/MIT.html -``` -MIT License - -Copyright (c) 2020 Katharos Technology - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -``` - -## clap - 4.6.1 -**Repository URL**: https://github.com/clap-rs/clap -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## clap_builder - 4.6.0 -**Repository URL**: https://github.com/clap-rs/clap -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## clap_derive - 4.6.1 -**Repository URL**: https://github.com/clap-rs/clap -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## clap_lex - 1.1.0 -**Repository URL**: https://github.com/clap-rs/clap -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## colorchoice - 1.0.5 -**Repository URL**: https://github.com/rust-cli/anstyle.git -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## ctrlc - 3.5.2 -**Repository URL**: https://github.com/Detegr/rust-ctrlc.git -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright 2017 CtrlC developers - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## dispatch2 - 0.3.1 -**Repository URL**: https://github.com/madsmtm/objc2 -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## dyn-clone - 1.0.20 -**Repository URL**: https://github.com/dtolnay/dyn-clone -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## equivalent - 1.0.2 -**Repository URL**: https://github.com/indexmap-rs/equivalent -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## hashbrown - 0.17.1 -**Repository URL**: https://github.com/rust-lang/hashbrown -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## heck - 0.5.0 -**Repository URL**: https://github.com/withoutboats/heck -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## indexmap - 2.14.0 -**Repository URL**: https://github.com/indexmap-rs/indexmap -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## is_terminal_polyfill - 1.70.2 -**Repository URL**: https://github.com/polyfill-rs/is_terminal_polyfill -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## itoa - 1.0.18 -**Repository URL**: https://github.com/dtolnay/itoa -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## libc - 0.2.186 -**Repository URL**: https://github.com/rust-lang/libc -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## memchr - 2.8.1 -**Repository URL**: https://github.com/BurntSushi/memchr -**License Type(s)**: MIT -### License: https://spdx.org/licenses/MIT.html -``` -The MIT License (MIT) - -Copyright (c) 2015 Andrew Gallant - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -``` - -## nix - 0.31.3 -**Repository URL**: https://github.com/nix-rust/nix -**License Type(s)**: MIT -### License: https://spdx.org/licenses/MIT.html -``` -The MIT License (MIT) - -Copyright (c) 2015 Carl Lerche + nix-rust Authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -``` - -## objc2 - 0.6.4 -**Repository URL**: https://github.com/madsmtm/objc2 -**License Type(s)**: MIT -### License: https://spdx.org/licenses/MIT.html -``` -MIT License - -Copyright (c) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the -following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO -EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -USE OR OTHER DEALINGS IN THE SOFTWARE. - -``` - -## objc2-encode - 4.1.0 -**Repository URL**: https://github.com/madsmtm/objc2 -**License Type(s)**: MIT -### License: https://spdx.org/licenses/MIT.html -``` -MIT License - -Copyright (c) - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and -associated documentation files (the "Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the -following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial -portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT -LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO -EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -USE OR OTHER DEALINGS IN THE SOFTWARE. - -``` - -## once_cell - 1.21.4 -**Repository URL**: https://github.com/matklad/once_cell -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## once_cell_polyfill - 1.70.2 -**Repository URL**: https://github.com/polyfill-rs/once_cell_polyfill -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - -``` - -## portable-atomic - 1.13.1 -**Repository URL**: https://github.com/taiki-e/portable-atomic -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## proc-macro2 - 1.0.106 -**Repository URL**: https://github.com/dtolnay/proc-macro2 -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## pyo3 - 0.28.3 -**Repository URL**: https://github.com/pyo3/pyo3 -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## pyo3-build-config - 0.28.3 -**Repository URL**: https://github.com/pyo3/pyo3 -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - -``` - -## pyo3-ffi - 0.28.3 -**Repository URL**: https://github.com/pyo3/pyo3 -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. Copyright [yyyy] [name of copyright owner] @@ -5073,7 +289,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -5083,8 +299,8 @@ limitations under the License. ``` -## pyo3-macros - 0.28.3 -**Repository URL**: https://github.com/pyo3/pyo3 +## itoa - 1.0.18 +**Repository URL**: https://github.com/dtolnay/itoa **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -5164,8 +380,8 @@ limitations under the License. ``` -## pyo3-macros-backend - 0.28.3 -**Repository URL**: https://github.com/pyo3/pyo3 +## libc - 0.2.186 +**Repository URL**: https://github.com/rust-lang/libc **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -5245,70 +461,227 @@ limitations under the License. ``` -## quote - 1.0.45 -**Repository URL**: https://github.com/dtolnay/quote +## memchr - 2.8.1 +**Repository URL**: https://github.com/BurntSushi/memchr +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +The MIT License (MIT) + +Copyright (c) 2015 Andrew Gallant + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +``` + +## once_cell - 1.21.4 +**Repository URL**: https://github.com/matklad/once_cell **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. -"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. -"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. -"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. -"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. -"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. -"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. -"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). -"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. -"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. -2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. -3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. -4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: - (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and - (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and - (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and - (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. - You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. -5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. -6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. -7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. -8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. -9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. -To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. Copyright [yyyy] [name of copyright owner] @@ -5316,7 +689,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at -http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -5326,8 +699,8 @@ limitations under the License. ``` -## ref-cast - 1.0.25 -**Repository URL**: https://github.com/dtolnay/ref-cast +## portable-atomic - 1.13.1 +**Repository URL**: https://github.com/taiki-e/portable-atomic **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -5407,8 +780,8 @@ limitations under the License. ``` -## ref-cast-impl - 1.0.25 -**Repository URL**: https://github.com/dtolnay/ref-cast +## proc-macro2 - 1.0.106 +**Repository URL**: https://github.com/dtolnay/proc-macro2 **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -5488,8 +861,8 @@ limitations under the License. ``` -## ryu - 1.0.23 -**Repository URL**: https://github.com/dtolnay/ryu +## pyo3 - 0.28.3 +**Repository URL**: https://github.com/pyo3/pyo3 **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -5569,66 +942,8 @@ limitations under the License. ``` -## schemars - 1.2.1 -**Repository URL**: https://github.com/GREsau/schemars -**License Type(s)**: MIT -### License: https://spdx.org/licenses/MIT.html -``` -MIT License - -Copyright (c) 2019 Graham Esau - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -``` - -## schemars_derive - 1.2.1 -**Repository URL**: https://github.com/GREsau/schemars -**License Type(s)**: MIT -### License: https://spdx.org/licenses/MIT.html -``` -MIT License - -Copyright (c) 2019 Graham Esau - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -``` - -## serde - 1.0.228 -**Repository URL**: https://github.com/serde-rs/serde +## pyo3-build-config - 0.28.3 +**Repository URL**: https://github.com/pyo3/pyo3 **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -5708,8 +1023,8 @@ limitations under the License. ``` -## serde_core - 1.0.228 -**Repository URL**: https://github.com/serde-rs/serde +## pyo3-ffi - 0.28.3 +**Repository URL**: https://github.com/pyo3/pyo3 **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -5789,8 +1104,8 @@ limitations under the License. ``` -## serde_derive - 1.0.228 -**Repository URL**: https://github.com/serde-rs/serde +## pyo3-macros - 0.28.3 +**Repository URL**: https://github.com/pyo3/pyo3 **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -5870,8 +1185,8 @@ limitations under the License. ``` -## serde_derive_internals - 0.29.1 -**Repository URL**: https://github.com/serde-rs/serde +## pyo3-macros-backend - 0.28.3 +**Repository URL**: https://github.com/pyo3/pyo3 **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -5951,8 +1266,8 @@ limitations under the License. ``` -## serde_json - 1.0.150 -**Repository URL**: https://github.com/serde-rs/json +## quote - 1.0.45 +**Repository URL**: https://github.com/dtolnay/quote **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -6032,8 +1347,8 @@ limitations under the License. ``` -## serde_yaml - 0.9.34+deprecated -**Repository URL**: https://github.com/dtolnay/serde-yaml +## ref-cast - 1.0.25 +**Repository URL**: https://github.com/dtolnay/ref-cast **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -6113,39 +1428,8 @@ limitations under the License. ``` -## strsim - 0.11.1 -**Repository URL**: https://github.com/rapidfuzz/strsim-rs -**License Type(s)**: MIT -### License: https://spdx.org/licenses/MIT.html -``` -The MIT License (MIT) - -Copyright (c) 2015 Danny Guo -Copyright (c) 2016 Titus Wormer -Copyright (c) 2018 Akash Kurdekar - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -``` - -## syn - 2.0.117 -**Repository URL**: https://github.com/dtolnay/syn +## ref-cast-impl - 1.0.25 +**Repository URL**: https://github.com/dtolnay/ref-cast **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -6225,236 +1509,147 @@ limitations under the License. ``` -## target-lexicon - 0.13.5 -**Repository URL**: https://github.com/bytecodealliance/target-lexicon -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html +## schemars - 1.2.1 +**Repository URL**: https://github.com/GREsau/schemars +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html ``` +MIT License - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Copyright (c) 2019 Graham Esau - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - 1. Definitions. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. +``` - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. +## schemars_derive - 1.2.1 +**Repository URL**: https://github.com/GREsau/schemars +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +MIT License - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. +Copyright (c) 2019 Graham Esau - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. +``` - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." +## serde - 1.0.228 +**Repository URL**: https://github.com/serde-rs/serde +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html +``` +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - END OF TERMS AND CONDITIONS + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - APPENDIX: How to apply the Apache License to your work. + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - Copyright [yyyy] [name of copyright owner] +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - http://www.apache.org/licenses/LICENSE-2.0 +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. +END OF TERMS AND CONDITIONS ---- LLVM Exceptions to the Apache 2.0 License ---- +APPENDIX: How to apply the Apache License to your work. -As an exception, if, as a result of your compiling your source code, portions -of this Software are embedded into an Object form of such source code, you -may redistribute such embedded portions in such Object form without complying -with the conditions of Sections 4(a), 4(b) and 4(d) of the License. +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. -In addition, if you combine or link compiled forms of this Software with -software that is licensed under the GPLv2 ("Combined Software") and if a -court of competent jurisdiction determines that the patent provision (Section -3), the indemnity provision (Section 9) or other Section of the License -conflicts with the conditions of the GPLv2, you may retroactively and -prospectively choose to deem waived or otherwise exclude such Section(s) of -the License, but only in their entirety and only with respect to the Combined -Software. +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. ``` -## thiserror - 2.0.18 -**Repository URL**: https://github.com/dtolnay/thiserror +## serde_core - 1.0.228 +**Repository URL**: https://github.com/serde-rs/serde **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -6534,8 +1729,8 @@ limitations under the License. ``` -## thiserror-impl - 2.0.18 -**Repository URL**: https://github.com/dtolnay/thiserror +## serde_derive - 1.0.228 +**Repository URL**: https://github.com/serde-rs/serde **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -6615,8 +1810,8 @@ limitations under the License. ``` -## unicode-ident - 1.0.24 -**Repository URL**: https://github.com/dtolnay/unicode-ident +## serde_derive_internals - 0.29.1 +**Repository URL**: https://github.com/serde-rs/serde **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -6696,86 +1891,89 @@ limitations under the License. ``` -## unicode-ident - 1.0.24 -**Repository URL**: https://github.com/dtolnay/unicode-ident -**License Type(s)**: Unicode-3.0 -### License: https://spdx.org/licenses/Unicode-3.0.html +## serde_json - 1.0.150 +**Repository URL**: https://github.com/serde-rs/json +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html ``` -UNICODE LICENSE V3 +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ -COPYRIGHT AND PERMISSION NOTICE +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -Copyright © 1991-2023 Unicode, Inc. +1. Definitions. -NOTICE TO USER: Carefully read the following legal agreement. BY -DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR -SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE -TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT -DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. -Permission is hereby granted, free of charge, to any person obtaining a -copy of data files and any associated documentation (the "Data Files") or -software and any associated documentation (the "Software") to deal in the -Data Files or Software without restriction, including without limitation -the rights to use, copy, modify, merge, publish, distribute, and/or sell -copies of the Data Files or Software, and to permit persons to whom the -Data Files or Software are furnished to do so, provided that either (a) -this copyright and permission notice appear with all copies of the Data -Files or Software, or (b) this copyright and permission notice appear in -associated Documentation. +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. -THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF -THIRD PARTY RIGHTS. +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE -BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, -OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA -FILES OR SOFTWARE. +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS -Except as contained in this notice, the name of a copyright holder shall -not be used in advertising or otherwise to promote the sale, use or other -dealings in these Data Files or Software without prior written -authorization of the copyright holder. +APPENDIX: How to apply the Apache License to your work. -``` +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. -## unsafe-libyaml - 0.2.11 -**Repository URL**: https://github.com/dtolnay/unsafe-libyaml -**License Type(s)**: MIT -### License: https://spdx.org/licenses/MIT.html -``` -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: +Copyright [yyyy] [name of copyright owner] -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. ``` -## utf8parse - 0.2.2 -**Repository URL**: https://github.com/alacritty/vte +## syn - 2.0.117 +**Repository URL**: https://github.com/dtolnay/syn **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` @@ -6855,11 +2053,12 @@ limitations under the License. ``` -## windows-link - 0.2.1 -**Repository URL**: https://github.com/microsoft/windows-rs +## target-lexicon - 0.13.5 +**Repository URL**: https://github.com/bytecodealliance/target-lexicon **License Type(s)**: Apache-2.0 ### License: https://spdx.org/licenses/Apache-2.0.html ``` + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -7048,228 +2247,327 @@ limitations under the License. same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright (c) Microsoft Corporation. + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +--- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + + +``` + +## thiserror - 2.0.18 +**Repository URL**: https://github.com/dtolnay/thiserror +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html +``` +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +``` + +## thiserror-impl - 2.0.18 +**Repository URL**: https://github.com/dtolnay/thiserror +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html +``` +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +``` + +## unicode-ident - 1.0.24 +**Repository URL**: https://github.com/dtolnay/unicode-ident +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html +``` +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. - http://www.apache.org/licenses/LICENSE-2.0 +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. -``` +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. -## windows-sys - 0.61.2 -**Repository URL**: https://github.com/microsoft/windows-rs -**License Type(s)**: Apache-2.0 -### License: https://spdx.org/licenses/Apache-2.0.html -``` - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). - 1. Definitions. +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and +END OF TERMS AND CONDITIONS - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. +APPENDIX: How to apply the Apache License to your work. - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. +Copyright [yyyy] [name of copyright owner] - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. +http://www.apache.org/licenses/LICENSE-2.0 - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. +``` - END OF TERMS AND CONDITIONS +## unicode-ident - 1.0.24 +**Repository URL**: https://github.com/dtolnay/unicode-ident +**License Type(s)**: Unicode-3.0 +### License: https://spdx.org/licenses/Unicode-3.0.html +``` +UNICODE LICENSE V3 - APPENDIX: How to apply the Apache License to your work. +COPYRIGHT AND PERMISSION NOTICE - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. +Copyright © 1991-2023 Unicode, Inc. - Copyright (c) Microsoft Corporation. +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. - http://www.apache.org/licenses/LICENSE-2.0 +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. ``` diff --git a/CLI.md b/CLI.md new file mode 100644 index 000000000..d8cbe7d79 --- /dev/null +++ b/CLI.md @@ -0,0 +1,97 @@ + + +# NeMo Fabric CLI Experiments + +This is a working note for experimentation and team discussion. It is not a +committed public interface or release contract. + +## Intent + +Keep `nemo-fabric` as a thin SDK-backed runner: + +- every run starts from a complete, typed `FabricConfig`; +- the CLI only selects where that config comes from; +- planning and execution go through the public Python SDK; +- `base_dir` only resolves relative resource paths; and +- Fabric does not load a persisted YAML, TOML, or JSON configuration. + +Profiles, overlays, config discovery, and compatibility loaders are out of +scope. Fabric has not been released, so this experiment makes a clean break. + +## Current Variation + +### 1. Preset + +```bash +nemo-fabric run --preset hermes --input "Say hello" +``` + +A small, complete config maintained with the CLI. Useful for smoke tests and +quick harness probes. Presets do not inherit or merge. + +### 2. Example + +```bash +nemo-fabric run \ + --example examples.code_review_agent \ + --variant hermes \ + --input "Review this workspace" +``` + +A runnable SDK example with complete variants. Examples are Python source and +assets that people can read, copy, and edit. A variant is a factory, not a +profile. + +### 3. User factory + +```bash +nemo-fabric run \ + --factory my_agent.config:build_config \ + --base-dir . \ + --input "Review this workspace" +``` + +The unrestricted customization path. The callable takes no arguments and +returns a complete `FabricConfig`. + +`--factory` is the current spelling of the earlier `--custom` idea because it +makes the Python contract explicit. + +All three selectors feed the same commands: + +```text +plan doctor run chat +``` + +## Variations to Discuss + +| Question | Lean option | Other reasonable experiments | +| --- | --- | --- | +| Selector grammar | `--preset`, `--example`, `--factory` | source subcommands; `preset:name` references | +| Editable starting points | copy an example's Python source | `example init`; `preset eject`; repository-only samples | +| Example distribution | bundle a small runnable example in the wheel | repository examples only; plugin/entry-point discovery | +| Preset customization | keep presets complete and intentionally small | a few typed flags for model/workspace; no arbitrary `--set` | +| Custom source name | `--factory module:callable` | `--custom module:callable`; `--agent module:callable` | +| Interactive surface | retain `chat` for harness probing | ship only `plan`, `doctor`, and `run` | +| Output | JSON as the automation contract | explicit human-readable output mode | + +## Boundaries + +- No `agent.yaml` or profile directories. +- No Fabric config loader for YAML, TOML, or JSON. +- No preset inheritance or core merge semantics. +- No implicit user/project/system config discovery. +- JSON request input is still valid; it describes an invocation, not an agent. +- Adapter-generated harness files remain valid implementation details. +- Private serialization across Python/Rust or Harbor process boundaries is not + a public authoring format. + +## Current Bias + +Start with the three explicit selectors. Keep presets tiny, make examples the +editable learning surface, and use Python factories for real customization. +Add scaffolding or plugin discovery only after the basic run path proves +useful. diff --git a/Cargo.lock b/Cargo.lock index 1aeec1992..01634ab24 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,164 +2,12 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys", -] - -[[package]] -name = "bitflags" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" - -[[package]] -name = "block2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" -dependencies = [ - "objc2", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "clap" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "clap_lex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - -[[package]] -name = "ctrlc" -version = "3.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" -dependencies = [ - "dispatch2", - "nix", - "windows-sys", -] - -[[package]] -name = "dispatch2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" -dependencies = [ - "bitflags", - "block2", - "libc", - "objc2", -] - [[package]] name = "dyn-clone" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - [[package]] name = "fabric-python" version = "0.1.0" @@ -170,34 +18,12 @@ dependencies = [ "serde_json", ] -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - [[package]] name = "itoa" version = "1.0.18" @@ -216,16 +42,6 @@ version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" -[[package]] -name = "nemo-fabric-cli" -version = "0.1.0" -dependencies = [ - "clap", - "ctrlc", - "nemo-fabric-core", - "serde_json", -] - [[package]] name = "nemo-fabric-core" version = "0.1.0" @@ -233,49 +49,15 @@ dependencies = [ "schemars", "serde", "serde_json", - "serde_yaml", "thiserror", ] -[[package]] -name = "nix" -version = "0.31.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "objc2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - [[package]] name = "once_cell" version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - [[package]] name = "portable-atomic" version = "1.13.1" @@ -378,12 +160,6 @@ dependencies = [ "syn", ] -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - [[package]] name = "schemars" version = "1.2.1" @@ -463,25 +239,6 @@ dependencies = [ "zmij", ] -[[package]] -name = "serde_yaml" -version = "0.9.34+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "syn" version = "2.0.117" @@ -525,33 +282,6 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unsafe-libyaml" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index 05332e3c5..126d28a37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,10 @@ [workspace] members = [ "crates/fabric-core", - "crates/fabric-cli", "crates/fabric-python", ] default-members = [ "crates/fabric-core", - "crates/fabric-cli", ] resolver = "2" @@ -22,12 +20,9 @@ repository = "https://github.com/NVIDIA/nemo-fabric" [workspace.dependencies] nemo-fabric-core = { path = "crates/fabric-core", version = "0.1.0" } -clap = { version = "4", features = ["derive"] } -ctrlc = "3" schemars = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] } serde_json = "1" -serde_yaml = "0.9" thiserror = "2" pyo3 = "0.28.2" diff --git a/README.md b/README.md index b498cd675..971356ac3 100644 --- a/README.md +++ b/README.md @@ -166,13 +166,12 @@ authentication, and execution details. - **Artifacts:** normalized output, logs, patches, and telemetry references returned through an `ArtifactManifest`. -Fabric applies profiles in caller order and validates the final effective config -before planning or running. +For legacy path sources, Fabric applies profiles in caller order and validates +the final effective config before planning or running. -Path sources select profiles by name. Typed `FabricConfig` sources usually -compose the final config in Python; `FabricProfileConfig` values are available -for callers that need ordered file-style overlays. The SDK rejects raw profile -mappings and mixed profile stacks. See the +Path sources temporarily select legacy profiles by name. Typed `FabricConfig` +sources must contain the complete configuration and do not accept profile +overlays; compose variants in Python before calling the SDK. See the [Python SDK guide](docs/sdk/python.mdx) for the complete public API, type definitions, lifecycle semantics, and error behavior. diff --git a/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py b/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py index 2a04a5b6f..16815969d 100644 --- a/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py +++ b/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py @@ -217,14 +217,14 @@ def _selected_model_config(payload: dict[str, Any]) -> dict[str, Any]: def _resolve_path(payload: dict[str, Any], value: str | Path) -> Path: path = Path(value) if not path.is_absolute(): - path = Path(common_utils.config_root(payload)) / path + path = Path(common_utils.base_dir(payload)) / path return path def resolve_cwd(payload: dict[str, Any]) -> Path: environment = common_utils.environment_payload(payload) workspace = environment.get("workspace") - return _resolve_path(payload, workspace or common_utils.config_root(payload)) + return _resolve_path(payload, workspace or common_utils.base_dir(payload)) def selected_model(payload: dict[str, Any]) -> str | None: @@ -388,7 +388,7 @@ def prepare_claude_relay(payload: dict[str, Any]) -> ClaudeRelaySettings | None: ) try: executable = relay_gateway.resolve_relay_command( - Path(common_utils.config_root(payload)).resolve(), + Path(common_utils.base_dir(payload)).resolve(), command, ) except FileNotFoundError as error: @@ -530,7 +530,7 @@ def _artifact_root(payload: dict[str, Any]) -> Path: root = artifacts.get("root") if isinstance(artifacts, dict) else None if root: return Path(root) - return Path(common_utils.config_root(payload)) / "artifacts" / "claude" + return Path(common_utils.base_dir(payload)) / "artifacts" / "claude" def runtime_state_path(payload: dict[str, Any], fabric_runtime_id: str) -> Path: diff --git a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py index 17959350c..73b960b30 100644 --- a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py +++ b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py @@ -177,10 +177,10 @@ def resolve_cwd(payload: dict[str, Any]) -> Path: environment = _mapping( common_utils.environment_payload(payload), name="runtime environment" ) - value = environment.get("workspace") or common_utils.config_root(payload) + value = environment.get("workspace") or common_utils.base_dir(payload) path = Path(str(value)) if not path.is_absolute(): - path = Path(common_utils.config_root(payload)) / path + path = Path(common_utils.base_dir(payload)) / path return path.resolve() @@ -311,7 +311,7 @@ def _artifact_root(payload: dict[str, Any]) -> Path: root = artifacts.get("root") if isinstance(artifacts, dict) else None if root: return Path(str(root)) - return Path(common_utils.config_root(payload)) / "artifacts" / "codex" + return Path(common_utils.base_dir(payload)) / "artifacts" / "codex" def state_dir(payload: dict[str, Any]) -> Path: @@ -471,7 +471,7 @@ def prepare_codex_relay(payload: dict[str, Any]) -> CodexRelaySettings | None: ) try: executable = relay_gateway.resolve_relay_command( - Path(common_utils.config_root(payload)).resolve(), command + Path(common_utils.base_dir(payload)).resolve(), command ) except FileNotFoundError as error: raise AdapterRelayError( @@ -559,7 +559,7 @@ def sdk_config( if codex_bin is not None: path = Path(codex_bin) if not path.is_absolute(): - path = (Path(common_utils.config_root(payload)) / path).resolve() + path = (Path(common_utils.base_dir(payload)) / path).resolve() codex_bin = str(path) return CodexConfig( codex_bin=codex_bin, diff --git a/adapters/common/src/nemo_fabric_adapters/common/relay_gateway.py b/adapters/common/src/nemo_fabric_adapters/common/relay_gateway.py index 0dc1ff585..3d7d1f71d 100644 --- a/adapters/common/src/nemo_fabric_adapters/common/relay_gateway.py +++ b/adapters/common/src/nemo_fabric_adapters/common/relay_gateway.py @@ -47,14 +47,14 @@ class RelayCliContract: observability_version: int -def resolve_relay_command(config_root: Path, value: str | Path) -> Path: +def resolve_relay_command(base_dir: Path, value: str | Path) -> Path: """Resolve the configured Relay CLI to one absolute executable path.""" command = Path(value) if len(command.parts) == 1: resolved = shutil.which(str(command)) else: - candidate = command if command.is_absolute() else config_root / command + candidate = command if command.is_absolute() else base_dir / command resolved = shutil.which(str(candidate.resolve())) if resolved is None: raise FileNotFoundError("NeMo Relay CLI executable was not found") diff --git a/adapters/common/src/nemo_fabric_adapters/common/utils.py b/adapters/common/src/nemo_fabric_adapters/common/utils.py index f815bfe53..09d363540 100644 --- a/adapters/common/src/nemo_fabric_adapters/common/utils.py +++ b/adapters/common/src/nemo_fabric_adapters/common/utils.py @@ -62,8 +62,8 @@ def fabric_config(payload: dict[str, Any]) -> dict[str, Any]: return effective_config(payload).get("config") or {} -def config_root(payload: dict[str, Any]) -> str: - return effective_config(payload).get("config_root") or payload.get("config_root") or "." +def base_dir(payload: dict[str, Any]) -> str: + return effective_config(payload).get("base_dir") or payload.get("base_dir") or "." def agent_name(payload: dict[str, Any]) -> str: @@ -234,7 +234,7 @@ def load_relay_plugin_config(payload: dict[str, Any]) -> dict[str, Any]: def normalize_relay_output_dirs(plugin_config: dict[str, Any], payload: dict[str, Any]) -> None: - base = Path(config_root(payload)).resolve() + base = Path(base_dir(payload)).resolve() runtime_id = runtime_context(payload)["runtime_id"] for component in plugin_config.get("components", []): if component.get("kind") != "observability": diff --git a/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py b/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py index fcbe78966..28138615c 100644 --- a/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py +++ b/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py @@ -225,7 +225,7 @@ def resolve_backend(payload: dict[str, Any]) -> Any: return None root = Path(str(workspace)) if not root.is_absolute(): - root = Path(common_utils.config_root(payload)) / root + root = Path(common_utils.base_dir(payload)) / root from deepagents.backends import FilesystemBackend # virtual_mode=True confines the agent to root_dir; absolute paths and ``..`` @@ -309,17 +309,17 @@ def _mcp_connection(name: str, spec: dict[str, Any]) -> dict[str, Any]: def state_dir(payload: dict[str, Any]) -> Path: - config_root = Path(common_utils.config_root(payload)).resolve() + base_dir = Path(common_utils.base_dir(payload)).resolve() settings = common_utils.settings_payload(payload) configured = settings.get("state_dir") if configured: path = Path(str(configured)) - return path if path.is_absolute() else config_root / path + return path if path.is_absolute() else base_dir / path artifacts = common_utils.runtime_context(payload).get("artifacts") or {} root = artifacts.get("root") or os.environ.get("FABRIC_ARTIFACTS") if root: return Path(str(root)).resolve() / ".fabric" / "deepagents" - return config_root / "artifacts" / "deepagents" / ".fabric" + return base_dir / "artifacts" / "deepagents" / ".fabric" def runtime_state_paths(payload: dict[str, Any], runtime_id: str) -> tuple[Path, Path]: diff --git a/adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py b/adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py index e3c21a82c..1faf92eaa 100755 --- a/adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py +++ b/adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py @@ -192,7 +192,7 @@ async def run_hermes(payload: dict[str, Any]) -> dict[str, Any]: settings = common_utils.settings_payload(payload) request = common_utils.request_payload(payload) model_config = common_utils.selected_model_config(payload) - hermes_home_base = Path(common_utils.config_root(payload)).joinpath( + hermes_home_base = Path(common_utils.base_dir(payload)).joinpath( settings.get("hermes_home", "./artifacts/hermes-home") ) hermes_home = common_utils.runtime_state_directory(hermes_home_base, payload) diff --git a/crates/fabric-cli/Cargo.toml b/crates/fabric-cli/Cargo.toml deleted file mode 100644 index b347ff0b8..000000000 --- a/crates/fabric-cli/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -[package] -name = "nemo-fabric-cli" -version.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true -description = "Command-line interface for NeMo Fabric." -readme = "README.md" - -[[bin]] -name = "fabric" -path = "src/main.rs" - -[lints] -workspace = true - -[dependencies] -clap.workspace = true -ctrlc.workspace = true -nemo-fabric-core.workspace = true -serde_json.workspace = true diff --git a/crates/fabric-cli/src/main.rs b/crates/fabric-cli/src/main.rs deleted file mode 100644 index 4ee4bab9c..000000000 --- a/crates/fabric-cli/src/main.rs +++ /dev/null @@ -1,489 +0,0 @@ -// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -//! NeMo Fabric command-line interface. - -use std::io::{self, BufRead, IsTerminal, Write}; -use std::path::PathBuf; -use std::process::ExitCode; -use std::sync::{ - Arc, - atomic::{AtomicBool, Ordering}, - mpsc::{self, Receiver}, -}; -use std::thread; -use std::time::Duration; - -use clap::{Parser, Subcommand}; -use nemo_fabric_core::{ - AdapterKind, RunPlan, RunRequest, RunResult, RunStatus, RuntimeHandle, SchemaName, doctor_plan, - generate_all_schemas, generate_schema_json, invoke_runtime, - resolve_effective_config_with_profiles, resolve_run_plan_with_profiles, run_plan, - start_runtime, stop_runtime, validate_agent_directory, write_schema_snapshots, -}; -use serde_json::Value; - -#[derive(Debug, Parser)] -#[command(name = "fabric")] -#[command(about = "Harness-management CLI for NeMo Fabric")] -#[command(version)] -struct Cli { - #[command(subcommand)] - command: Option, -} - -#[derive(Debug, Subcommand)] -enum Command { - /// Validate an agent directory or YAML config. - Validate { - /// Path to an agent directory or YAML config. - path: PathBuf, - }, - /// Resolve and print the effective Fabric config as JSON. - Inspect { - /// Path to an agent directory or YAML config. - path: PathBuf, - /// Profile name from configured profile directories, or a YAML profile path. - #[arg(long = "profile")] - profile: Vec, - }, - /// Resolve an agent/profile into a runnable plan. - Plan { - /// Path to an agent directory or YAML config. - path: PathBuf, - /// Profile name from configured profile directories, or a YAML profile path. - #[arg(long = "profile")] - profile: Vec, - }, - /// Diagnose a resolved agent/profile without installing or running it. - Doctor { - /// Path to an agent directory or YAML config. - path: PathBuf, - /// Profile name from configured profile directories, or a YAML profile path. - #[arg(long = "profile")] - profile: Vec, - }, - /// Start an interactive multi-turn runtime. - Chat { - /// Path to an agent directory or YAML config. - path: PathBuf, - /// Profile name from configured profile directories, or a YAML profile path. - #[arg(long = "profile")] - profile: Vec, - /// Show per-turn runtime, invocation, artifact, and telemetry details. - #[arg(long)] - verbose: bool, - }, - /// Run an agent/profile through its Fabric adapter. - Run { - /// Path to an agent directory or YAML config. - path: PathBuf, - /// Profile name from configured profile directories, or a YAML profile path. - #[arg(long = "profile")] - profile: Vec, - /// Inline request payload. - #[arg(long, default_value = "")] - input: String, - /// Request payload file. - #[arg(long)] - input_file: Option, - /// Full Fabric RunRequest JSON payload. - #[arg(long)] - request_json: Option, - /// Full Fabric RunRequest JSON file. - #[arg(long)] - request_file: Option, - /// Show adapter output. - #[arg(long = "show-output")] - show_output: bool, - }, - /// Generate JSON Schema for Fabric config and runtime types. - Schema { - /// Schema name to print or write. Omit to include all schemas. - #[arg(long)] - name: Option, - /// Directory to write schema snapshot files to. Omit to print JSON. - #[arg(long)] - output_dir: Option, - }, - /// Print the Fabric core version. - Version, -} - -fn main() -> ExitCode { - match run() { - Ok(()) => ExitCode::SUCCESS, - Err(error) => { - eprintln!("{error}"); - ExitCode::FAILURE - } - } -} - -fn run() -> Result<(), Box> { - let cli = Cli::parse(); - match cli.command { - Some(Command::Validate { path }) => { - validate_agent_directory(&path)?; - println!("validated {}", path.display()); - } - Some(Command::Inspect { path, profile }) => { - let effective_config = resolve_effective_config_with_profiles(path, &profile)?; - println!("{}", serde_json::to_string_pretty(&effective_config)?); - } - Some(Command::Plan { path, profile }) => { - let plan = resolve_run_plan_with_profiles(path, &profile)?; - println!("{}", serde_json::to_string_pretty(&plan)?); - } - Some(Command::Doctor { path, profile }) => { - let plan = resolve_run_plan_with_profiles(path, &profile)?; - let report = doctor_plan(&plan); - println!("{}", serde_json::to_string_pretty(&report)?); - } - Some(Command::Chat { - path, - profile, - verbose, - }) => { - run_chat(path, &profile, verbose)?; - } - Some(Command::Run { - path, - profile, - input, - input_file, - request_json, - request_file, - show_output, - }) => { - let plan = resolve_run_plan_with_profiles(path, &profile)?; - let request = match (request_file, request_json, input_file) { - (Some(path), None, None) => { - serde_json::from_str::(&std::fs::read_to_string(path)?)? - } - (None, Some(json), None) => serde_json::from_str::(&json)?, - (None, None, Some(path)) => RunRequest::text(std::fs::read_to_string(path)?), - (None, None, None) => RunRequest::text(input), - _ => { - return Err( - "--request-file, --request-json, and --input-file are mutually exclusive" - .into(), - ); - } - }; - let result = run_plan(&plan, request)?; - println!("{}", serde_json::to_string_pretty(&result)?); - if show_output { - if let Some(response) = result.output.get("response") { - println!("\nResponse:"); - if let Some(response) = response.as_str() { - println!("{response}"); - } else { - println!("{}", serde_json::to_string_pretty(response)?); - } - } - } - - let _exit_code = result - .metadata - .get("exit_code") - .and_then(serde_json::Value::as_i64) - .unwrap_or(0); - if _exit_code != 0 { - let message = result - .error - .as_ref() - .map(|error| error.message.clone()) - .unwrap_or_else(|| format!("harness exited with an exit code of {_exit_code}")); - return Err(message.into()); - } - } - Some(Command::Schema { name, output_dir }) => { - if let Some(output_dir) = output_dir { - if let Some(name) = name { - let schema = SchemaName::parse(&name)?; - let path = output_dir.join(schema.filename()); - std::fs::create_dir_all(&output_dir)?; - std::fs::write(&path, generate_schema_json(schema)?)?; - println!("wrote {}", path.display()); - } else { - for path in write_schema_snapshots(&output_dir)? { - println!("wrote {}", path.display()); - } - } - } else if let Some(name) = name { - println!("{}", generate_schema_json(SchemaName::parse(&name)?)?); - } else { - println!( - "{}", - serde_json::to_string_pretty(&generate_all_schemas()?)? - ); - } - } - Some(Command::Version) | None => { - println!("fabric {}", nemo_fabric_core::version()); - } - } - Ok(()) -} - -fn run_chat( - path: PathBuf, - profile: &[String], - verbose: bool, -) -> Result<(), Box> { - let plan = resolve_run_plan_with_profiles(path, profile)?; - let runtime = start_runtime(&plan)?; - let chat_result = chat_loop(&plan, &runtime, verbose); - let stop_result = stop_runtime(&plan, &runtime); - if let Err(error) = chat_result { - return Err(error); - } - stop_result?; - Ok(()) -} - -fn chat_loop( - plan: &RunPlan, - runtime: &RuntimeHandle, - mut verbose: bool, -) -> Result<(), Box> { - let prompt = chat_prompt(plan, &runtime.runtime_id); - let interrupted = Arc::new(AtomicBool::new(false)); - { - let interrupted = Arc::clone(&interrupted); - ctrlc::set_handler(move || { - interrupted.store(true, Ordering::SeqCst); - })?; - } - let input_is_terminal = io::stdin().is_terminal(); - let lines = stdin_lines(); - print_chat_info(plan, runtime); - eprintln!(); - let mut turn_count = 0_u64; - - loop { - eprint!("{prompt}> "); - io::stderr().flush()?; - - let Some(line) = next_chat_line(&lines, &interrupted)? else { - break; - }; - let input = line; - let command = input.trim(); - match command { - "/exit" | "/quit" => break, - "/help" => { - print_chat_help(); - continue; - } - "/info" => { - print_chat_info(plan, runtime); - continue; - } - "/clear" => { - eprint!("\x1b[2J\x1b[H"); - continue; - } - "/verbose" => { - verbose = !verbose; - eprintln!("verbose: {}", if verbose { "on" } else { "off" }); - continue; - } - "" => continue, - _ => {} - } - if let Some(value) = command.strip_prefix("/verbose ") { - match value.trim() { - "on" => { - verbose = true; - eprintln!("verbose: on"); - continue; - } - "off" => { - verbose = false; - eprintln!("verbose: off"); - continue; - } - _ => { - eprintln!("usage: /verbose on|off"); - continue; - } - } - } - if command.starts_with('/') { - eprintln!("unknown command: {command}"); - eprintln!("type /help for available commands"); - continue; - } - - let request = RunRequest::text(input); - let result = invoke_runtime(plan, runtime, request)?; - turn_count += 1; - if !input_is_terminal { - eprintln!(); - } - print_chat_response(&result.output)?; - if verbose { - eprintln!(); - print_turn_verbose(turn_count, &result); - } - - let exit_code = result - .metadata - .get("exit_code") - .and_then(Value::as_i64) - .unwrap_or(0); - if exit_code != 0 { - let message = result - .error - .as_ref() - .map(|error| error.message.clone()) - .unwrap_or_else(|| format!("harness exited with an exit code of {exit_code}")); - return Err(message.into()); - } - } - Ok(()) -} - -fn print_chat_info(plan: &RunPlan, runtime: &RuntimeHandle) { - eprintln!("+================================================================+"); - eprintln!("| NEMO FABRIC |"); - eprintln!("| interactive runtime |"); - eprintln!("+----------------------------------------------------------------+"); - eprintln!("| agent: {}", plan.agent_name); - eprintln!("| profile: {}", profile_label(plan)); - eprintln!("| harness: {}", runtime.harness); - eprintln!("| adapter: {}", adapter_kind_label(runtime.adapter_kind)); - eprintln!("| runtime_id: {}", runtime.runtime_id); - eprintln!("| commands: /help, /info, /verbose on|off, /clear, /exit, /quit"); - eprintln!("+----------------------------------------------------------------"); -} - -fn print_chat_help() { - eprintln!("Commands:"); - eprintln!(" /help show this help"); - eprintln!(" /info show runtime info"); - eprintln!(" /verbose on|off toggle per-turn metadata"); - eprintln!(" /clear clear the terminal"); - eprintln!(" /exit, /quit stop the runtime and exit"); - eprintln!("Type a non-empty message to invoke the same runtime."); -} - -fn print_turn_verbose(turn: u64, result: &RunResult) { - eprintln!("+-- turn {turn} metadata ---------------------------------------------"); - eprintln!("| status: {}", status_label(result.status)); - eprintln!("| request_id: {}", result.request_id); - eprintln!("| invocation_id: {}", result.invocation_id); - eprintln!("| runtime_id: {}", result.runtime_id); - eprintln!("| artifact_count: {}", result.artifacts.artifacts.len()); - if let Some(telemetry) = result.telemetry.as_ref() { - eprintln!("| telemetry: relay_enabled={}", telemetry.relay_enabled); - if let Some(path) = telemetry - .metadata - .get("relay_config_path") - .and_then(Value::as_str) - { - eprintln!("| telemetry_config: {path}"); - } - } - if let Some(error) = result.error.as_ref() { - eprintln!("| error: {} {}", error.code, error.message); - } - eprintln!("+----------------------------------------------------------------"); -} - -fn chat_prompt(plan: &RunPlan, runtime_id: &str) -> String { - format!( - "you[{}:{}]", - profile_label(plan), - short_prompt_label(runtime_id) - ) -} - -fn short_prompt_label(value: &str) -> String { - let mut chars = value.chars(); - let short: String = chars.by_ref().take(24).collect(); - if chars.next().is_none() { - short - } else { - format!("{short}...") - } -} - -fn profile_label(plan: &RunPlan) -> String { - if !plan.profiles.is_empty() { - return plan.profiles.join(", "); - } - "default".to_string() -} - -fn adapter_kind_label(adapter_kind: AdapterKind) -> &'static str { - match adapter_kind { - AdapterKind::Process => "process", - AdapterKind::Http => "http", - AdapterKind::Python => "python", - AdapterKind::NativePlugin => "native_plugin", - } -} - -fn status_label(status: RunStatus) -> &'static str { - match status { - RunStatus::Succeeded => "succeeded", - RunStatus::Failed => "failed", - RunStatus::Cancelled => "cancelled", - } -} - -fn stdin_lines() -> Receiver> { - let (sender, receiver) = mpsc::channel(); - thread::spawn(move || { - let stdin = io::stdin(); - for line in stdin.lock().lines() { - if sender.send(line).is_err() { - break; - } - } - }); - receiver -} - -fn next_chat_line( - lines: &Receiver>, - interrupted: &AtomicBool, -) -> Result, Box> { - loop { - if interrupted.load(Ordering::SeqCst) { - eprintln!(); - return Ok(None); - } - match lines.recv_timeout(Duration::from_millis(100)) { - Ok(line) => return Ok(Some(line?)), - Err(mpsc::RecvTimeoutError::Timeout) => {} - Err(mpsc::RecvTimeoutError::Disconnected) => return Ok(None), - } - } -} - -fn print_chat_response(output: &Value) -> Result<(), Box> { - let response = output.get("response").unwrap_or(output); - if let Some(response) = response.as_str() { - print_chat_text("agent", response); - } else { - let response = serde_json::to_string_pretty(response)?; - print_chat_text("agent", &response); - } - Ok(()) -} - -fn print_chat_text(role: &str, text: &str) { - let mut lines = text.lines(); - if let Some(first) = lines.next() { - eprintln!("{role}> {first}"); - for line in lines { - eprintln!("{:width$}{line}", "", width = role.len() + 2); - } - } else { - eprintln!("{role}>"); - } -} diff --git a/crates/fabric-core/Cargo.toml b/crates/fabric-core/Cargo.toml index ede4e25b6..41cc29ae6 100644 --- a/crates/fabric-core/Cargo.toml +++ b/crates/fabric-core/Cargo.toml @@ -17,5 +17,4 @@ workspace = true schemars.workspace = true serde.workspace = true serde_json.workspace = true -serde_yaml.workspace = true thiserror.workspace = true diff --git a/crates/fabric-core/examples/generate-schemas.rs b/crates/fabric-core/examples/generate-schemas.rs new file mode 100644 index 000000000..ec18b8ac5 --- /dev/null +++ b/crates/fabric-core/examples/generate-schemas.rs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Regenerate committed NeMo Fabric JSON Schema snapshots. + +use std::path::PathBuf; + +fn main() { + let output = std::env::args_os() + .nth(1) + .map(PathBuf::from) + .unwrap_or_else(|| PathBuf::from("schemas")); + nemo_fabric_core::write_schema_snapshots(output).expect("write schema snapshots"); +} diff --git a/crates/fabric-core/src/config.rs b/crates/fabric-core/src/config.rs index 37d83d06c..038d82d90 100644 --- a/crates/fabric-core/src/config.rs +++ b/crates/fabric-core/src/config.rs @@ -3,36 +3,19 @@ //! Fabric config models and loading helpers. -use std::borrow::Cow; use std::collections::{BTreeMap, BTreeSet}; use std::fs; use std::path::{Path, PathBuf}; -use schemars::{JsonSchema, Schema, SchemaGenerator}; +use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_json::Value; use crate::error::{FabricError, Result}; -const AGENT_YAML: &str = "agent.yaml"; /// Adapter descriptor contract version supported by this core. pub const ADAPTER_CONTRACT_VERSION: &str = "fabric.adapter/v1alpha1"; -/// A loaded Fabric document with resolved source path and agent root. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] -#[serde(tag = "kind", rename_all = "snake_case")] -pub enum FabricDocument { - /// Unified Fabric agent config. - FabricConfig { - /// Path to the config file. - path: PathBuf, - /// Root used for resolving relative paths. - root: PathBuf, - /// Parsed config. - config: FabricConfig, - }, -} - /// Versioned Fabric agent config. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct FabricConfig { @@ -65,31 +48,11 @@ pub struct FabricConfig { /// First-class NeMo Relay integration configuration. #[serde(default, skip_serializing_if = "Option::is_none")] pub relay: Option, - /// Optional profile discovery config. - #[serde(default, skip_serializing_if = "ProfileRegistryConfig::is_empty")] - pub profiles: ProfileRegistryConfig, /// Additive fields not yet recognized by this core version. #[serde(default, flatten)] pub extensions: BTreeMap, } -/// Profile discovery config for curated package profiles. -#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] -pub struct ProfileRegistryConfig { - /// Directories searched when a caller selects a profile by name. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub directories: Vec, - /// Additive profile-discovery fields. - #[serde(default, flatten)] - pub extensions: BTreeMap, -} - -impl ProfileRegistryConfig { - fn is_empty(&self) -> bool { - self.directories.is_empty() && self.extensions.is_empty() - } -} - /// Harness-neutral tool capability configuration. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct ToolsConfig { @@ -201,10 +164,10 @@ struct AdapterRegistry { } impl AdapterRegistry { - fn from_config(_config: &FabricConfig, config_root: &Path) -> Result { + fn from_config(_config: &FabricConfig, base_dir: &Path) -> Result { let mut registry = Self::default(); registry.register_repository_directory(&repository_adapter_dir())?; - registry.register_local_directory(&config_root.join("adapters"))?; + registry.register_local_directory(&base_dir.join("adapters"))?; Ok(registry) } @@ -394,94 +357,18 @@ pub struct AdapterTelemetryProviderSupport { pub extensions: BTreeMap, } -/// Profile config applied on top of a Fabric config. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] -pub struct ProfileConfig { - /// Optional profile schema version. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub schema_version: Option, - /// Optional profile name used for directory discovery. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub name: Option, - /// Optional profile description. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, - /// Raw config fields recursively merged over the base config. - #[serde(default, flatten)] - pub overlay: BTreeMap, -} - -#[derive(JsonSchema)] -#[allow(dead_code)] -struct ProfileConfigSchema { - /// Optional profile schema version. - schema_version: Option, - /// Optional profile name used for directory discovery. - name: Option, - /// Optional profile description. - description: Option, - /// Partial harness overlay. - harness: Option>, - /// Partial model overlays by alias. - models: Option>, - /// Partial runtime overlay. - runtime: Option>, - /// Partial environment overlay. - environment: Option>, - /// Tool capability overlay. - tools: Option, - /// Partial skill overlay. - skills: Option>, - /// Partial MCP overlay. - mcp: Option>, - /// Partial telemetry overlay. - telemetry: Option>, - /// Partial Relay integration overlay. - relay: Option>, - /// Additive config overlays. - #[serde(flatten)] - extensions: BTreeMap, -} - -impl JsonSchema for ProfileConfig { - fn schema_name() -> Cow<'static, str> { - "ProfileConfig".into() - } - - fn json_schema(generator: &mut SchemaGenerator) -> Schema { - ProfileConfigSchema::json_schema(generator) - } -} - /// Source context used when resolving an in-memory Fabric config. #[derive(Debug, Clone, PartialEq)] pub struct ResolveContext { - /// Root used to resolve agent package paths. - pub agent_root: PathBuf, - /// Path recorded as config provenance in the run plan. - pub config_path: PathBuf, - /// Root used to resolve config-local paths. - pub config_root: PathBuf, + /// Base directory used to resolve relative config paths. + pub base_dir: PathBuf, } impl ResolveContext { - /// Build a context for an agent package root. - pub fn from_agent_root(root: impl Into) -> Self { - let root = root.into(); + /// Build a context with an explicit base directory. + pub fn new(base_dir: impl Into) -> Self { Self { - agent_root: root.clone(), - config_path: root.join(AGENT_YAML), - config_root: root, - } - } - - /// Build a context for a config file and its config root. - pub fn from_config_path(path: impl Into, root: impl Into) -> Self { - let root = root.into(); - Self { - agent_root: root.clone(), - config_path: path.into(), - config_root: root, + base_dir: base_dir.into(), } } } @@ -1077,29 +964,6 @@ fn default_relay_unsupported_value_behavior() -> RelayUnsupportedBehavior { RelayUnsupportedBehavior::Error } -/// Load a Fabric document from an agent directory or single agent config. -pub fn load_fabric_document(path: impl AsRef) -> Result { - let path = path.as_ref(); - if !path.exists() { - return Err(FabricError::PathNotFound(path.to_path_buf())); - } - if path.is_dir() { - return load_directory(path); - } - load_file(path) -} - -/// Validate an agent directory or config, including discoverable profile YAMLs. -pub fn validate_agent_directory(path: impl AsRef) -> Result<()> { - match load_fabric_document(path)? { - FabricDocument::FabricConfig { root, config, .. } => { - discover_profiles(&config, &root)?; - } - } - - Ok(()) -} - /// Load an adapter descriptor from JSON package metadata. pub fn load_adapter_descriptor(path: impl AsRef) -> Result { let path = path.as_ref(); @@ -1108,127 +972,38 @@ pub fn load_adapter_descriptor(path: impl AsRef) -> Result, - profile: Option<&str>, -) -> Result { - let profiles: Vec = profile.into_iter().map(str::to_string).collect(); - resolve_effective_config_with_profiles(path, &profiles) -} - -/// Resolve an agent directory or single agent config with ordered profiles into -/// merged effective config. -pub fn resolve_effective_config_with_profiles( - path: impl AsRef, - profiles: &[String], -) -> Result { - match load_fabric_document(path)? { - FabricDocument::FabricConfig { path, root, config } => { - let agent_name = config.metadata.name.clone(); - let (profile_configs, selected_profiles) = - load_profile_configs(&agent_name, &config, &root, profiles)?; - resolve_effective_config_from_config_with_profile_names( - config, - &profile_configs, - selected_profiles, - ResolveContext::from_config_path(path, root), - ) - } - } -} - -/// Resolve typed config/profile overlays into merged effective config. +/// Resolve a typed Fabric config into an effective config. pub fn resolve_effective_config_from_config( config: FabricConfig, - profiles: &[ProfileConfig], context: ResolveContext, ) -> Result { - let selected_profiles = profiles - .iter() - .enumerate() - .map(|(index, profile)| { - profile - .name - .clone() - .unwrap_or_else(|| format!("profile_{}", index + 1)) - }) - .collect(); - resolve_effective_config_from_config_with_profile_names( + validate_config(&config)?; + Ok(EffectiveConfig { + agent_name: config.metadata.name.clone(), + base_dir: context.base_dir, config, - profiles, - selected_profiles, - context, - ) -} - -fn load_directory(path: &Path) -> Result { - let agent = path.join(AGENT_YAML); - if agent.exists() { - return load_fabric_config(&agent); - } - Err(FabricError::MissingEntrypoint(path.to_path_buf())) + }) } -fn load_file(path: &Path) -> Result { - match path.file_name().and_then(|name| name.to_str()) { - Some(AGENT_YAML) => load_fabric_config(path), - Some(name) if name.ends_with(".yaml") || name.ends_with(".yml") => load_fabric_config(path), - _ => Err(FabricError::UnsupportedExtension(path.to_path_buf())), +fn validate_config(config: &FabricConfig) -> Result<()> { + if config.harness.adapter_id.trim().is_empty() { + return Err(FabricError::UnknownAdapter { + adapter_id: config.harness.adapter_id.clone(), + available: Vec::new(), + }); } + Ok(()) } -/// Resolve an agent directory or single agent config into a runnable plan. -pub fn resolve_run_plan(path: impl AsRef, profile: Option<&str>) -> Result { - let profiles: Vec = profile.into_iter().map(str::to_string).collect(); - resolve_run_plan_with_profiles(path, &profiles) -} - -/// Resolve an agent directory or single agent config with ordered profile application. -pub fn resolve_run_plan_with_profiles( - path: impl AsRef, - profiles: &[String], -) -> Result { - resolve_run_plan_from_effective_config(resolve_effective_config_with_profiles(path, profiles)?) -} - -/// Resolve a typed Fabric config and typed profile overlays into a runnable plan. +/// Resolve a typed Fabric config into a runnable plan. /// -/// This is the SDK-facing entrypoint. File and YAML loading stays outside this -/// function; callers provide already-typed configs and the path context used for -/// resolving relative paths. +/// Callers provide an already-composed typed config and the explicit base +/// directory used for resolving relative paths. pub fn resolve_run_plan_from_config( config: FabricConfig, - profiles: &[ProfileConfig], context: ResolveContext, ) -> Result { - resolve_run_plan_from_effective_config(resolve_effective_config_from_config( - config, profiles, context, - )?) -} - -fn load_fabric_config(path: &Path) -> Result { - let config = read_yaml::(path)?; - let root = parent_or_current(path); - Ok(FabricDocument::FabricConfig { - path: path.to_path_buf(), - root, - config, - }) -} - -fn read_yaml(path: &Path) -> Result -where - T: for<'de> Deserialize<'de>, -{ - let raw = std::fs::read_to_string(path).map_err(|source| FabricError::Read { - path: path.to_path_buf(), - source, - })?; - serde_yaml::from_str(&raw).map_err(|source| FabricError::ParseYaml { - path: path.to_path_buf(), - source, - }) + resolve_run_plan_from_effective_config(resolve_effective_config_from_config(config, context)?) } fn read_json(path: &Path) -> Result @@ -1245,201 +1020,42 @@ where }) } -fn parent_or_current(path: &Path) -> PathBuf { - path.parent() - .map(Path::to_path_buf) - .unwrap_or_else(|| PathBuf::from(".")) -} - -fn load_profile_configs( - agent_name: &str, - config: &FabricConfig, - config_root: &Path, - profiles: &[String], -) -> Result<(Vec, Vec)> { - if profiles.is_empty() { - return Ok((Vec::new(), Vec::new())); - } - let discovered = discover_profiles(&config, config_root)?; - let mut profile_configs = Vec::new(); - let mut selected_profiles = Vec::new(); - for profile_name in profiles { - let profile_path = - if let Some(path) = resolve_profile_file_reference(config_root, &profile_name) { - path - } else if let Some(path) = discovered.get(profile_name.as_str()) { - path.clone() - } else { - return Err(FabricError::UnknownProfile { - profile: profile_name.clone(), - agent: agent_name.to_string(), - available: discovered.keys().cloned().collect(), - }); - }; - let profile_config = read_yaml::(&profile_path)?; - profile_configs.push(profile_config); - selected_profiles.push(profile_name.clone()); - } - Ok((profile_configs, selected_profiles)) -} - -fn resolve_effective_config_from_config_with_profile_names( - config: FabricConfig, - profiles: &[ProfileConfig], - selected_profiles: Vec, - context: ResolveContext, -) -> Result { - let mut effective = config; - for profile in profiles { - apply_profile_config(&mut effective, profile)?; - } - let config = into_effective_config(effective); - Ok(EffectiveConfig { - agent_name: config.metadata.name.clone(), - profiles: selected_profiles, - agent_root: context.agent_root, - config_path: context.config_path, - config_root: context.config_root, - config, - }) -} - /// Resolve execution planning metadata from merged effective config. pub fn resolve_run_plan_from_effective_config( effective_config: EffectiveConfig, ) -> Result { let config = effective_config.config.clone(); - let config_root = effective_config.config_root.clone(); - let adapter_descriptor = resolve_adapter_descriptor(&config, &config_root)?; + let base_dir = effective_config.base_dir.clone(); + let adapter_descriptor = resolve_adapter_descriptor(&config, &base_dir)?; let descriptor = adapter_descriptor .as_ref() .map(|adapter| &adapter.descriptor); let resolution = resolve_resolution(&config, descriptor)?; - let environment_plan = resolve_environment_plan(&config, &config_root); + let environment_plan = resolve_environment_plan(&config, &base_dir); validate_control_location(descriptor, environment_plan.as_ref())?; - let capability_plan = - resolve_capability_plan(&config, &config_root, adapter_descriptor.as_ref()); + let capability_plan = resolve_capability_plan(&config, &base_dir, adapter_descriptor.as_ref()); let capabilities = resolve_runtime_capabilities(&config, descriptor); let telemetry_plan = resolve_telemetry_plan(&config, descriptor)?; Ok(RunPlan { agent_name: effective_config.agent_name.clone(), - profiles: effective_config.profiles.clone(), adapter_descriptor, resolution, environment_plan, capability_plan, capabilities, telemetry_plan, - agent_root: effective_config.agent_root.clone(), - config_path: effective_config.config_path.clone(), - config_root, + base_dir, config, effective_config, }) } -fn into_effective_config(mut config: FabricConfig) -> FabricConfig { - config.profiles = ProfileRegistryConfig::default(); - config -} - -fn resolve_profile_file_reference(config_root: &Path, profile: &str) -> Option { - let path = Path::new(profile); - let cwd_relative = normalize_path(path.to_path_buf()); - if cwd_relative.is_file() { - return Some(cwd_relative); - } - let config_relative = resolve_path(config_root, path); - if config_relative.is_file() { - return Some(config_relative); - } - None -} - -fn discover_profiles( - config: &FabricConfig, - config_root: &Path, -) -> Result> { - let mut profiles: BTreeMap = BTreeMap::new(); - for directory in &config.profiles.directories { - let directory = resolve_path(config_root, directory); - if !directory.exists() { - println!( - "warning: profile directory does not exist: {}", - directory.display() - ); - continue; - } - let entries = std::fs::read_dir(&directory).map_err(|source| FabricError::Read { - path: directory.clone(), - source, - })?; - for entry in entries { - let path = entry - .map_err(|source| FabricError::Read { - path: directory.clone(), - source, - })? - .path(); - if !is_yaml_file(&path) { - continue; - } - let profile_config = read_yaml::(&path)?; - let Some(name) = profile_config.name.clone().or_else(|| { - path.file_stem() - .and_then(|stem| stem.to_str()) - .map(str::to_string) - }) else { - continue; - }; - let profile_path = normalize_path(path); - if profiles.contains_key(&name) { - return Err(FabricError::ProfileError { - path: profile_path, - message: format!("duplicate profile `{name}`"), - }); - } - profiles.insert(name, profile_path); - } - } - Ok(profiles) -} - -fn is_yaml_file(path: &Path) -> bool { - path.extension() - .and_then(|extension| extension.to_str()) - .is_some_and(|extension| matches!(extension, "yaml" | "yml")) -} - -fn apply_profile_config(config: &mut FabricConfig, profile: &ProfileConfig) -> Result<()> { - let mut merged = serde_json::to_value(&*config).map_err(FabricError::SerializeJson)?; - let overlay = Value::Object(profile.overlay.clone().into_iter().collect()); - merge_json(&mut merged, overlay); - *config = serde_json::from_value(merged).map_err(FabricError::SerializeJson)?; - Ok(()) -} - -fn merge_json(base: &mut Value, overlay: Value) { - match (base, overlay) { - (Value::Object(base), Value::Object(overlay)) => { - for (key, value) in overlay { - if let Some(current) = base.get_mut(&key) { - merge_json(current, value); - } else { - base.insert(key, value); - } - } - } - (base, overlay) => *base = overlay, - } -} - fn resolve_adapter_descriptor( config: &FabricConfig, - config_root: &Path, + base_dir: &Path, ) -> Result> { let adapter_id = &config.harness.adapter_id; - let registry = AdapterRegistry::from_config(config, config_root)?; + let registry = AdapterRegistry::from_config(config, base_dir)?; let Some(entry) = registry.get(adapter_id) else { return Err(FabricError::UnknownAdapter { adapter_id: adapter_id.clone(), @@ -1535,7 +1151,7 @@ fn resolve_resolution( Ok(config.harness.resolution) } -fn resolve_environment_plan(config: &FabricConfig, config_root: &Path) -> Option { +fn resolve_environment_plan(config: &FabricConfig, base_dir: &Path) -> Option { let environment = config.environment.as_ref()?; Some(EnvironmentPlan { provider: environment.provider.clone(), @@ -1544,12 +1160,12 @@ fn resolve_environment_plan(config: &FabricConfig, config_root: &Path) -> Option workspace: environment .workspace .as_ref() - .map(|workspace| resolve_path(config_root, workspace)), + .map(|workspace| resolve_path(base_dir, workspace)), artifacts: environment .artifacts .as_ref() .or(config.runtime.artifacts.as_ref()) - .map(|artifacts| resolve_path(config_root, artifacts)), + .map(|artifacts| resolve_path(base_dir, artifacts)), connection: environment.connection.clone(), metadata: environment.metadata.clone(), settings: environment.settings.clone(), @@ -1558,7 +1174,7 @@ fn resolve_environment_plan(config: &FabricConfig, config_root: &Path) -> Option fn resolve_capability_plan( config: &FabricConfig, - config_root: &Path, + base_dir: &Path, adapter_descriptor: Option<&ResolvedAdapterDescriptor>, ) -> CapabilityPlan { let accepts = |area: &str| { @@ -1580,7 +1196,7 @@ fn resolve_capability_plan( skills .paths .iter() - .map(|path| resolve_path(config_root, path)) + .map(|path| resolve_path(base_dir, path)) .collect() }) .unwrap_or_default(); @@ -1815,32 +1431,24 @@ fn normalize_path(path: PathBuf) -> PathBuf { .collect() } -/// Merged Fabric config after applying selected profiles. +/// Resolved Fabric config with explicit path context. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct EffectiveConfig { /// Stable agent name. pub agent_name: String, - /// Ordered selected profiles. - pub profiles: Vec, - /// Root used to resolve agent package paths. - pub agent_root: PathBuf, - /// Resolved Fabric config path. - pub config_path: PathBuf, - /// Root used to resolve config-local paths. - pub config_root: PathBuf, - /// Merged Fabric config with authoring-time profile discovery removed. + /// Base directory used to resolve relative config paths. + pub base_dir: PathBuf, + /// Complete typed Fabric config. pub config: FabricConfig, } /// Resolved Fabric run plan. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct RunPlan { - /// Merged config and provenance used as the base for this plan. + /// Resolved config used as the base for this plan. pub effective_config: EffectiveConfig, /// Stable agent name. pub agent_name: String, - /// Ordered selected profiles. - pub profiles: Vec, /// Adapter descriptor resolved for this plan, when configured. #[serde(default, skip_serializing_if = "Option::is_none")] pub adapter_descriptor: Option, @@ -1858,13 +1466,9 @@ pub struct RunPlan { /// Resolved telemetry pass-through plan. #[serde(default, skip_serializing_if = "Option::is_none")] pub telemetry_plan: Option, - /// Root used to resolve agent package paths. - pub agent_root: PathBuf, - /// Resolved Fabric config path. - pub config_path: PathBuf, - /// Root used to resolve config-local paths. - pub config_root: PathBuf, - /// Resolved Fabric profile config. + /// Base directory used to resolve relative config paths. + pub base_dir: PathBuf, + /// Complete typed Fabric config. pub config: FabricConfig, } @@ -2041,853 +1645,44 @@ pub struct TelemetryPlan { mod tests { use super::*; - fn file_config_agent_dir() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../tests/fixtures/file-config-agent") - } - - fn example_adapter_descriptor_path() -> PathBuf { - repository_adapter_dir().join("hermes/fabric-adapter.json") - } - - #[test] - fn typed_profiles_merge_partial_objects_and_preserve_extensions() { - let config: FabricConfig = serde_yaml::from_str( - r#" -schema_version: fabric.agent/v1alpha1 -metadata: - name: demo -harness: - adapter_id: nvidia.fabric.hermes - settings: - workspace: ./workspace -runtime: - input_schema: chat - output_schema: message -tools: - blocked: [base] - future: - value: true -future_top_level: - base: true - nested: - first: 1 -"#, - ) - .expect("base config"); - let profile: ProfileConfig = serde_yaml::from_str( - r#" -schema_version: fabric.profile/v1alpha1 -name: overlay -harness: - settings: - timeout_seconds: 30 -runtime: - input_schema: prompt -tools: - blocked: [profile] - future: null -future_top_level: - profile: true - nested: - second: 2 -"#, - ) - .expect("partial profile"); - - let effective = resolve_effective_config_from_config( - config, - &[profile], - ResolveContext::from_agent_root("."), - ) - .expect("effective config"); - let value = serde_json::to_value(&effective.config).expect("config json"); - - assert_eq!(effective.profiles, ["overlay"]); - assert_eq!(value["runtime"]["input_schema"], "prompt"); - assert_eq!(value["harness"]["settings"]["workspace"], "./workspace"); - assert_eq!(value["harness"]["settings"]["timeout_seconds"], 30); - assert_eq!(value["tools"]["blocked"], serde_json::json!(["profile"])); - assert!(value["tools"]["future"].is_null()); - assert_eq!(value["future_top_level"]["base"], true); - assert_eq!(value["future_top_level"]["profile"], true); - assert_eq!(value["future_top_level"]["nested"]["first"], 1); - assert_eq!(value["future_top_level"]["nested"]["second"], 2); - } - - #[test] - fn runtime_uses_stable_defaults_for_omitted_optional_fields() { - let config: FabricConfig = serde_yaml::from_str( - r#" -schema_version: fabric.agent/v1alpha1 -metadata: - name: demo -harness: - adapter_id: nvidia.fabric.hermes -runtime: -"#, - ) - .expect("minimal config"); - - assert_eq!(config.runtime.input_schema, "text"); - assert_eq!(config.runtime.output_schema, "text"); - } - - #[test] - fn relay_telemetry_provider_is_enabled_by_presence() { - let config: FabricConfig = serde_yaml::from_str( - r#" -schema_version: fabric.agent/v1alpha1 -metadata: - name: demo -harness: - adapter_id: nvidia.fabric.hermes -runtime: -telemetry: - providers: - relay: {} -"#, - ) - .expect("config with relay telemetry provider"); - - let telemetry = config.telemetry.as_ref().expect("telemetry config"); - let plan = resolve_telemetry_plan(&config, None) - .expect("resolve telemetry plan") - .expect("telemetry plan"); - - assert!(telemetry.providers.contains_key(&TelemetryProvider::Relay)); - assert_eq!(plan.providers, vec![TelemetryProvider::Relay]); - assert!(plan.relay_enabled); - assert_eq!(plan.relay_config, None); - } - - #[test] - fn native_telemetry_provider_skips_relay_config() { - let config: FabricConfig = serde_yaml::from_str( - r#" -schema_version: fabric.agent/v1alpha1 -metadata: - name: demo -harness: - adapter_id: nvidia.fabric.hermes -runtime: -telemetry: - providers: - native: - config: - exporter: test -relay: - project: relay-project - output_dir: ./relay-output -"#, - ) - .expect("config with native telemetry provider"); - - let plan = resolve_telemetry_plan(&config, None) - .expect("resolve telemetry plan") - .expect("telemetry plan"); - - assert_eq!(plan.providers, vec![TelemetryProvider::Native]); - assert!(!plan.relay_enabled); - assert_eq!(plan.relay_project, None); - assert_eq!(plan.relay_output_dir, None); - assert_eq!(plan.relay_config, None); - assert_eq!( - plan.native_config, - Some(serde_json::json!({"exporter": "test"})) - ); - } - - #[test] - fn relay_telemetry_config_generates_relay_plugin_config() { - let config: FabricConfig = serde_yaml::from_str( - r#" -schema_version: fabric.agent/v1alpha1 -metadata: - name: demo -harness: - adapter_id: nvidia.fabric.hermes -runtime: -telemetry: - providers: - relay: {} -relay: - project: typed-project - output_dir: ./typed-relay - observability: - atof: - enabled: true - output_directory: ./typed-relay - filename: events.atof.jsonl - mode: overwrite - atif: - enabled: true - output_directory: ./typed-relay - filename_template: trajectory-{session_id}.atif.json - agent_name: code-review-agent - opentelemetry: - enabled: true - endpoint: http://localhost:4318/v1/traces - components: - - kind: switchyard - enabled: true - config: - route: canary - policy: - unknown_component: error -"#, - ) - .expect("config with typed relay telemetry"); - - let plan = resolve_telemetry_plan(&config, None) - .expect("resolve telemetry plan") - .expect("telemetry plan"); - let relay_config = plan.relay_config.expect("relay plugin config"); - - assert_eq!(plan.relay_project.as_deref(), Some("typed-project")); - assert_eq!(plan.relay_output_dir, Some(PathBuf::from("./typed-relay"))); - assert_eq!(relay_config["version"], serde_json::json!(1)); - assert_eq!( - relay_config["components"][0]["kind"], - serde_json::json!("observability") - ); - assert_eq!( - relay_config["components"][0]["config"]["atof"]["mode"], - serde_json::json!("overwrite") - ); - assert_eq!( - relay_config["components"][0]["config"]["atif"]["agent_name"], - serde_json::json!("code-review-agent") - ); - assert_eq!( - relay_config["components"][0]["config"]["opentelemetry"]["endpoint"], - serde_json::json!("http://localhost:4318/v1/traces") - ); - assert_eq!( - relay_config["components"][1], - serde_json::json!({ - "kind": "switchyard", - "enabled": true, - "config": {"route": "canary"} - }) - ); - assert_eq!( - relay_config["policy"]["unknown_component"], - serde_json::json!("error") - ); - } - - #[test] - fn telemetry_provider_rejects_unknown_provider_keys() { - let result = serde_yaml::from_str::( - r#" -providers: - unsupported: {} -"#, - ); - - assert!(result.is_err()); - } - - #[test] - fn telemetry_plan_rejects_provider_not_declared_by_adapter() { - let config: FabricConfig = serde_yaml::from_str( - r#" -schema_version: fabric.agent/v1alpha1 -metadata: - name: demo -harness: - adapter_id: nvidia.fabric.claude -runtime: -telemetry: - providers: - relay: {} -"#, - ) - .expect("config with relay telemetry provider"); - let descriptor: AdapterDescriptor = serde_json::from_value(serde_json::json!({ - "contract_version": ADAPTER_CONTRACT_VERSION, - "adapter_id": "nvidia.fabric.claude", - "harness": "claude", - "adapter_kind": "python" + fn typed_config(adapter_id: &str) -> FabricConfig { + serde_json::from_value(serde_json::json!({ + "schema_version": "fabric.agent/v1alpha1", + "metadata": {"name": "typed-agent"}, + "harness": { + "adapter_id": adapter_id, + "resolution": "preinstalled" + }, + "runtime": {}, + "environment": { + "provider": "local", + "workspace": "workspace" + }, + "skills": {"paths": ["skills/review"]} })) - .expect("adapter descriptor"); - - let error = resolve_telemetry_plan(&config, Some(&descriptor)) - .expect_err("unsupported relay provider"); - - assert!(matches!( - error, - FabricError::AdapterDescriptorUnsupported { - adapter_id, - field: "telemetry.providers", - value, - } if adapter_id == "nvidia.fabric.claude" && value == "relay" - )); + .expect("typed config") } - #[test] - fn telemetry_plan_uses_outputs_for_selected_adapter_providers() { - let config: FabricConfig = serde_yaml::from_str( - r#" -schema_version: fabric.agent/v1alpha1 -metadata: - name: demo -harness: - adapter_id: nvidia.fabric.codex -runtime: -telemetry: - providers: - relay: {} - native: {} -"#, - ) - .expect("config with relay and native telemetry providers"); - let descriptor: AdapterDescriptor = serde_json::from_value(serde_json::json!({ - "contract_version": ADAPTER_CONTRACT_VERSION, - "adapter_id": "nvidia.fabric.codex", - "harness": "codex", - "adapter_kind": "python", - "telemetry": { - "providers": { - "relay": { - "outputs": ["atif", "otel"], - "integration_modes": ["hooks", "gateway"] - }, - "native": { - "outputs": ["otel"] - } - } - } - })) - .expect("adapter descriptor"); - - let plan = resolve_telemetry_plan(&config, Some(&descriptor)) - .expect("resolve telemetry plan") - .expect("telemetry plan"); - - assert_eq!( - plan.providers, - vec![TelemetryProvider::Relay, TelemetryProvider::Native] - ); - assert_eq!(plan.adapter_outputs, vec!["atif", "otel"]); + fn repository_root() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../..") } #[test] - fn loads_adapter_descriptor() { - let descriptor = - load_adapter_descriptor(example_adapter_descriptor_path()).expect("adapter descriptor"); - - assert_eq!(descriptor.contract_version, ADAPTER_CONTRACT_VERSION); - assert_eq!(descriptor.adapter_id, "nvidia.fabric.hermes"); - assert_eq!(descriptor.harness, "hermes"); - assert_eq!(descriptor.adapter_kind, AdapterKind::Python); - let descriptor_json = serde_json::to_value(&descriptor).expect("descriptor json"); - assert_eq!( - descriptor_json.get("harness").and_then(Value::as_str), - Some("hermes") - ); - assert!(descriptor_json.get("harness_type").is_none()); - assert_eq!( - descriptor.runner.get("module").and_then(Value::as_str), - Some("nemo_fabric_adapters.hermes.adapter") - ); - assert_eq!( - descriptor.runner.get("callable").and_then(Value::as_str), - Some("run") - ); - assert!(descriptor.config.accepts.contains(&"telemetry".to_string())); - let relay = descriptor - .telemetry - .providers - .get(&TelemetryProvider::Relay) - .expect("relay telemetry support"); - assert!(relay.outputs.contains(&"atif".to_string())); - } - - #[test] - fn resolves_base_config_from_agent_directory() { - let plan = resolve_run_plan(file_config_agent_dir(), None).expect("run plan"); - - assert_eq!(plan.agent_name, "code-review-agent"); - assert!(plan.profiles.is_empty()); - let plan_json = serde_json::to_value(&plan).expect("plan json"); - assert_eq!(plan_json["profiles"], serde_json::json!([])); - assert_eq!( - plan_json["capabilities"], - serde_json::json!({ - "service": false, - "streaming": false, - "updates": false, - "cancellation": false - }) - ); - assert_eq!(plan.config.harness.adapter_id, "nvidia.fabric.hermes"); - assert_eq!( - plan.adapter_descriptor - .as_ref() - .map(|adapter| adapter.descriptor.adapter_id.as_str()), - Some("nvidia.fabric.hermes") - ); - assert_eq!( - plan.adapter_descriptor - .as_ref() - .map(|adapter| adapter.descriptor.adapter_kind), - Some(AdapterKind::Python) - ); - assert_eq!( - plan.adapter_descriptor - .as_ref() - .map(|adapter| adapter.source), - Some(AdapterDescriptorSource::Repository) - ); - assert_eq!(plan.resolution, Some(ResolutionStrategy::Preinstalled)); - assert_eq!( - plan.environment_plan - .as_ref() - .map(|environment| environment.provider.as_str()), - Some("local") - ); - assert_eq!(plan.capability_plan.skill_paths.len(), 1); - assert!(plan.capability_plan.mcp_servers.contains_key("github")); - assert_eq!(plan.capability_plan.native.skill_paths.len(), 1); - assert!( - plan.capability_plan - .native - .mcp_servers - .contains_key("github") - ); - assert!(plan.capability_plan.managed.skill_paths.is_empty()); - assert!(plan.capability_plan.managed.mcp_servers.is_empty()); - assert!(plan.config.profiles.directories.is_empty()); - assert_eq!( - plan.config - .telemetry - .as_ref() - .map(|telemetry| telemetry.providers.is_empty()), - None - ); - } - - #[test] - fn resolves_hermes_adapter_descriptor() { - let plan = resolve_run_plan(file_config_agent_dir(), Some("hermes")).expect("run plan"); - let adapter = plan - .adapter_descriptor - .as_ref() - .expect("configured adapter"); - - assert_eq!(adapter.source, AdapterDescriptorSource::Repository); - assert_eq!(adapter.descriptor.adapter_id, "nvidia.fabric.hermes"); - assert_eq!(adapter.descriptor.adapter_kind, AdapterKind::Python); - assert!(adapter.root.ends_with("adapters/hermes")); - } - - #[test] - fn resolves_package_local_custom_adapter_descriptor() { - let root = - std::env::temp_dir().join(format!("fabric-local-adapter-test-{}", std::process::id())); - let _ = std::fs::remove_dir_all(&root); - std::fs::create_dir_all(root.join("adapters/reviewer-process")).expect("create adapters"); - std::fs::write( - root.join("agent.yaml"), - r#"schema_version: fabric.agent/v1alpha1 -metadata: - name: reviewer-agent -harness: - adapter_id: acme.fabric.reviewer.process -models: - default: - provider: test - model: test-model -runtime: - input_schema: text - output_schema: message -environment: - provider: local -"#, - ) - .expect("write agent config"); - std::fs::write( - root.join("adapters/reviewer-process/fabric-adapter.json"), - r#"{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "acme.fabric.reviewer.process", - "harness": "reviewer", - "adapter_kind": "process" -}"#, - ) - .expect("write adapter descriptor"); - - let plan = resolve_run_plan(&root, None).expect("run plan"); - let adapter = plan - .adapter_descriptor - .as_ref() - .expect("configured adapter"); - - assert_eq!(adapter.source, AdapterDescriptorSource::Local); - assert_eq!( - adapter.descriptor.adapter_id, - "acme.fabric.reviewer.process" - ); - assert_eq!(adapter.descriptor.adapter_kind, AdapterKind::Process); - - let _ = std::fs::remove_dir_all(root); - } - - #[test] - fn resolves_env_profile_from_agent_directory() { - let plan = - resolve_run_plan(file_config_agent_dir(), Some("env_opensandbox")).expect("run plan"); - - assert_eq!(plan.profiles, vec!["env_opensandbox"]); - assert!(plan.config_path.ends_with("agent.yaml")); - assert_eq!( - plan.config - .environment - .as_ref() - .map(|environment| environment.provider.as_str()), - Some("opensandbox") - ); - } - - #[test] - fn resolves_mcp_profile_from_agent_directory() { - let plan = resolve_run_plan(file_config_agent_dir(), Some("mcp_github")).expect("run plan"); - - assert_eq!(plan.profiles, vec!["mcp_github"]); - let plan_json = serde_json::to_value(&plan).expect("plan json"); - assert!(plan_json.get("profile").is_none()); - assert_eq!( - plan.config.mcp.as_ref().map(|mcp| mcp.servers.len()), - Some(1) - ); - assert!(plan.capability_plan.native.mcp_servers.is_empty()); - assert!(plan.capability_plan.managed.mcp_servers.is_empty()); - assert!( - plan.capability_plan.routes.iter().any( - |route| route.name == "github" && route.target == CapabilityTarget::Unsupported - ) - ); - assert!( - plan.capability_plan - .unsupported - .mcp_servers - .contains_key("github") - ); - } - - #[test] - fn resolves_ordered_profiles_from_agent_directory() { - let profiles = vec!["env_local".to_string(), "mcp_github".to_string()]; - let plan = - resolve_run_plan_with_profiles(file_config_agent_dir(), &profiles).expect("run plan"); - - assert_eq!(plan.profiles, profiles); - assert_eq!( - plan.environment_plan - .as_ref() - .map(|environment| environment.provider.as_str()), - Some("local") - ); - assert_eq!( - plan.telemetry_plan - .as_ref() - .map(|telemetry| telemetry.relay_enabled), - Some(true) - ); - assert!(plan.capability_plan.managed.mcp_servers.is_empty()); - assert!( - plan.capability_plan - .unsupported - .mcp_servers - .contains_key("github") - ); - } - - #[test] - fn resolves_in_memory_config_with_typed_profiles() { - let FabricDocument::FabricConfig { config, root, .. } = - load_fabric_document(file_config_agent_dir()).expect("agent config"); - let profile = read_yaml::(&root.join("profiles/mcp-github.yaml")) - .expect("profile config"); - + fn resolves_complete_typed_config_with_explicit_base_dir() { + let base_dir = repository_root(); let plan = resolve_run_plan_from_config( - config, - &[profile], - ResolveContext::from_agent_root(root.clone()), - ) - .expect("run plan"); - - assert_eq!(plan.profiles, vec!["mcp_github"]); - assert!(plan.config_path.ends_with("agent.yaml")); - assert!(plan.config.profiles.directories.is_empty()); - assert!(plan.capability_plan.managed.mcp_servers.is_empty()); - assert!( - plan.capability_plan - .unsupported - .mcp_servers - .contains_key("github") - ); - assert_eq!(plan.config_root, root); - } - - #[test] - fn unsupported_capabilities_do_not_claim_fabric_managed_execution() { - let root = std::env::temp_dir().join(format!( - "fabric-unsupported-capability-test-{}", - std::process::id() - )); - let _ = std::fs::remove_dir_all(&root); - std::fs::create_dir_all(root.join("adapters/minimal")).expect("create adapters"); - std::fs::create_dir_all(root.join("skills/review")).expect("create skills"); - std::fs::write( - root.join("agent.yaml"), - r#"schema_version: fabric.agent/v1alpha1 -metadata: - name: unsupported-capability-agent -harness: - adapter_id: acme.fabric.minimal -models: - default: - provider: test - model: test-model -runtime: - input_schema: text - output_schema: text -tools: - blocked: - - shell -skills: - paths: - - ./skills/review -mcp: - servers: - github: - transport: streamable-http - url: http://example.invalid/mcp - exposure: fabric_managed -"#, - ) - .expect("write agent config"); - std::fs::write( - root.join("adapters/minimal/fabric-adapter.json"), - r#"{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "acme.fabric.minimal", - "harness": "minimal", - "adapter_kind": "process" -}"#, - ) - .expect("write adapter descriptor"); - - let plan = resolve_run_plan(&root, None).expect("run plan"); - - assert!(!plan.capability_plan.managed.tools_configured); - assert!(plan.capability_plan.managed.skill_paths.is_empty()); - assert!(plan.capability_plan.managed.mcp_servers.is_empty()); - assert_eq!(plan.capability_plan.tools.blocked, vec!["shell"]); - assert!(plan.capability_plan.unsupported.tools_configured); - assert_eq!(plan.capability_plan.unsupported.skill_paths.len(), 1); - assert!( - plan.capability_plan - .unsupported - .mcp_servers - .contains_key("github") - ); - assert!( - plan.capability_plan - .routes - .iter() - .all(|route| route.target == CapabilityTarget::Unsupported), - "{:?}", - plan.capability_plan.routes - ); - - let _ = std::fs::remove_dir_all(root); - } - - #[test] - fn blocked_tools_require_policy_specific_adapter_support() { - let root = std::env::temp_dir().join(format!( - "fabric-blocked-tools-capability-test-{}", - std::process::id() - )); - let _ = std::fs::remove_dir_all(&root); - std::fs::create_dir_all(root.join("adapters/tools")).expect("create adapters"); - std::fs::write( - root.join("adapters/tools/fabric-adapter.json"), - r#"{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "acme.fabric.tools", - "harness": "tools", - "adapter_kind": "process", - "config": {"accepts": ["tools"]} -}"#, - ) - .expect("write adapter descriptor"); - std::fs::write( - root.join("agent.yaml"), - r#"schema_version: fabric.agent/v1alpha1 -metadata: - name: blocked-tools-agent -harness: - adapter_id: acme.fabric.tools -runtime: -tools: - blocked: - - browser -"#, - ) - .expect("write agent config"); - - let plan = resolve_run_plan(&root, None).expect("run plan"); - - assert_eq!(plan.capability_plan.tools.blocked, vec!["browser"]); - assert!(plan.capability_plan.tools_configured); - assert!(!plan.capability_plan.native.tools_configured); - assert!(plan.capability_plan.unsupported.tools_configured); - assert!(plan.capability_plan.routes.iter().any(|route| { - route.name == "tools.blocked" && route.target == CapabilityTarget::Unsupported - })); - - std::fs::write( - root.join("adapters/tools/fabric-adapter.json"), - r#"{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "acme.fabric.tools", - "harness": "tools", - "adapter_kind": "process", - "config": {"accepts": ["tools", "tools.blocked"]} -}"#, + typed_config("nvidia.fabric.hermes"), + ResolveContext::new(&base_dir), ) - .expect("write policy-aware adapter descriptor"); - - let plan = resolve_run_plan(&root, None).expect("policy-aware run plan"); - - assert!(plan.capability_plan.native.tools_configured); - assert!(!plan.capability_plan.unsupported.tools_configured); - assert!(plan.capability_plan.routes.iter().any(|route| { - route.name == "tools.blocked" && route.target == CapabilityTarget::HarnessNative - })); - - std::fs::write( - root.join("agent.yaml"), - r#"schema_version: fabric.agent/v1alpha1 -metadata: - name: blocked-tools-agent -harness: - adapter_id: acme.fabric.tools -runtime: -tools: - blocked: [] -"#, - ) - .expect("write empty tools config"); - - let plan = resolve_run_plan(&root, None).expect("run plan"); - - assert!(plan.capability_plan.tools.blocked.is_empty()); - assert!(!plan.capability_plan.tools_configured); - assert!(!plan.capability_plan.native.tools_configured); - assert!(plan.capability_plan.routes.is_empty()); - - let _ = std::fs::remove_dir_all(root); - } - - #[test] - fn later_profiles_override_earlier_profiles() { - let profiles = vec!["env_opensandbox".to_string(), "env_local".to_string()]; - let plan = - resolve_run_plan_with_profiles(file_config_agent_dir(), &profiles).expect("run plan"); - - assert_eq!(plan.profiles, profiles); - assert_eq!( - plan.environment_plan - .as_ref() - .map(|environment| environment.provider.as_str()), - Some("local") - ); - assert_eq!( - plan.environment_plan - .as_ref() - .and_then(|environment| environment.workspace.as_ref()) - .map(|path| path.ends_with("repos/my-service")), - Some(true) - ); - assert_eq!( - plan.telemetry_plan - .as_ref() - .map(|telemetry| telemetry.relay_enabled), - None - ); - - let profiles = vec!["env_local".to_string(), "env_opensandbox".to_string()]; - let plan = - resolve_run_plan_with_profiles(file_config_agent_dir(), &profiles).expect("run plan"); - - assert_eq!( - plan.environment_plan - .as_ref() - .map(|environment| environment.provider.as_str()), - Some("opensandbox") - ); - assert_eq!( - plan.telemetry_plan - .as_ref() - .map(|telemetry| telemetry.relay_enabled), - Some(true) - ); - } - - #[test] - fn resolves_hermes_profile_from_agent_directory() { - let plan = resolve_run_plan(file_config_agent_dir(), Some("hermes")).expect("run plan"); + .expect("typed plan"); - assert_eq!(plan.profiles, vec!["hermes"]); - assert_eq!(plan.config.harness.adapter_id, "nvidia.fabric.hermes"); - assert_eq!( - plan.adapter_descriptor - .as_ref() - .map(|adapter| adapter.descriptor.adapter_id.as_str()), - Some("nvidia.fabric.hermes") - ); - assert_eq!( - plan.adapter_descriptor - .as_ref() - .map(|adapter| adapter.descriptor.adapter_kind), - Some(AdapterKind::Python) - ); - assert_eq!( - plan.adapter_descriptor - .as_ref() - .map(|adapter| adapter.source), - Some(AdapterDescriptorSource::Repository) - ); - assert_eq!(plan.resolution, Some(ResolutionStrategy::Preinstalled)); + assert_eq!(plan.agent_name, "typed-agent"); + assert_eq!(plan.base_dir, base_dir); + assert_eq!(plan.effective_config.base_dir, plan.base_dir); assert_eq!( - plan.telemetry_plan - .as_ref() - .map(|telemetry| telemetry.relay_enabled), - None - ); - assert!( - plan.capability_plan - .native - .mcp_servers - .contains_key("github") + plan.capability_plan.skill_paths, + vec![plan.base_dir.join("skills/review")] ); - assert!(plan.capability_plan.managed.mcp_servers.is_empty()); - assert_eq!(plan.capability_plan.native.skill_paths.len(), 1); - assert!(plan.capability_plan.managed.skill_paths.is_empty()); - } - - #[test] - fn resolves_direct_profile_path_from_agent_directory() { - let plan = resolve_run_plan(file_config_agent_dir(), Some("./profiles/hermes.yaml")) - .expect("run plan"); - - assert_eq!(plan.profiles, vec!["./profiles/hermes.yaml"]); - assert_eq!(plan.config.harness.adapter_id, "nvidia.fabric.hermes"); assert_eq!( plan.adapter_descriptor .as_ref() @@ -2897,120 +1692,26 @@ tools: } #[test] - fn errors_for_unknown_manifest_profile() { - let error = resolve_run_plan(file_config_agent_dir(), Some("missing")).expect_err("error"); - - assert!(matches!(error, FabricError::UnknownProfile { .. })); - } - - #[test] - fn rejects_malformed_adapter_descriptor() { - let root = std::env::temp_dir().join(format!( - "fabric-invalid-adapter-test-{}", - std::process::id() - )); - let _ = std::fs::remove_dir_all(&root); - std::fs::create_dir_all(root.join("adapters/invalid-process")).expect("create adapters"); - std::fs::write( - root.join("agent.yaml"), - r#"schema_version: fabric.agent/v1alpha1 -metadata: - name: invalid-adapter-agent -harness: - adapter_id: acme.fabric.invalid.process -models: - default: - provider: test - model: test-model -runtime: - input_schema: text - output_schema: message -environment: - provider: local -"#, - ) - .expect("write agent config"); - std::fs::write( - root.join("adapters/invalid-process/fabric-adapter.json"), - r#"{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": " ", - "harness": "invalid", - "adapter_kind": "process" -}"#, + fn typed_config_round_trip_has_no_file_provenance() { + let effective = resolve_effective_config_from_config( + typed_config("nvidia.fabric.hermes"), + ResolveContext::new("/tmp/fabric-base"), ) - .expect("write adapter descriptor"); - - let error = resolve_run_plan(&root, None).expect_err("invalid descriptor"); - assert!(matches!( - error, - FabricError::InvalidAdapterDescriptor { message, .. } - if message.contains("adapter_id") - )); + .expect("effective config"); + let value = serde_json::to_value(effective).expect("effective config JSON"); - let _ = std::fs::remove_dir_all(root); + assert_eq!(value["base_dir"], "/tmp/fabric-base"); + assert_eq!(value["config"]["metadata"]["name"], "typed-agent"); + assert_eq!(value.as_object().map(serde_json::Map::len), Some(3)); } #[test] - fn rejects_empty_adapter_id_when_loading_descriptor_directly() { - let root = std::env::temp_dir().join(format!( - "fabric-invalid-adapter-shape-test-{}", - std::process::id() - )); - let _ = std::fs::remove_dir_all(&root); - std::fs::create_dir_all(&root).expect("create temp root"); - let descriptor_path = root.join("fabric-adapter.json"); - std::fs::write( - &descriptor_path, - r#"{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "", - "harness": "invalid", - "adapter_kind": "process" -}"#, - ) - .expect("write adapter descriptor"); - - let error = load_adapter_descriptor(&descriptor_path).expect_err("invalid descriptor"); - assert!(matches!( - error, - FabricError::InvalidAdapterDescriptor { message, .. } - if message.contains("adapter_id") - )); - - let _ = std::fs::remove_dir_all(root); - } + fn loads_and_validates_json_adapter_descriptor() { + let descriptor = + load_adapter_descriptor(repository_root().join("adapters/hermes/fabric-adapter.json")) + .expect("adapter descriptor"); - #[test] - fn rejects_unsupported_adapter_contract_version() { - let root = std::env::temp_dir().join(format!( - "fabric-invalid-adapter-contract-test-{}", - std::process::id() - )); - let _ = std::fs::remove_dir_all(&root); - std::fs::create_dir_all(&root).expect("create temp root"); - let descriptor_path = root.join("fabric-adapter.json"); - std::fs::write( - &descriptor_path, - r#"{ - "contract_version": "fabric.adapter/v9", - "adapter_id": "acme.fabric.future", - "harness": "future", - "adapter_kind": "process" -}"#, - ) - .expect("write adapter descriptor"); - - let error = load_adapter_descriptor(&descriptor_path).expect_err("invalid descriptor"); - assert!(matches!( - error, - FabricError::AdapterDescriptorUnsupported { - field, - value, - .. - } if field == "contract_version" && value == "fabric.adapter/v9" - )); - - let _ = std::fs::remove_dir_all(root); + assert_eq!(descriptor.contract_version, ADAPTER_CONTRACT_VERSION); + assert_eq!(descriptor.adapter_kind, AdapterKind::Python); } } diff --git a/crates/fabric-core/src/doctor.rs b/crates/fabric-core/src/doctor.rs index c4a397677..9f6e5116c 100644 --- a/crates/fabric-core/src/doctor.rs +++ b/crates/fabric-core/src/doctor.rs @@ -46,8 +46,6 @@ pub struct DoctorCheck { pub struct DoctorReport { /// Agent name. pub agent_name: String, - /// Ordered profiles applied to the inspected plan. - pub profiles: Vec, /// Overall status. pub status: DoctorStatus, /// Checks. @@ -68,7 +66,6 @@ pub fn doctor_plan(plan: &RunPlan) -> DoctorReport { }); DoctorReport { agent_name: plan.agent_name.clone(), - profiles: plan.profiles.clone(), status, checks, } @@ -388,7 +385,7 @@ struct BinaryRequirement { fn binary_requirement(plan: &RunPlan, binary: &str) -> BinaryRequirement { let setting_key = binary_command_setting_key(binary); if let Some(Value::String(command)) = plan.config.harness.settings.get(&setting_key) { - let command_path = resolve_command(&plan.config_root, command); + let command_path = resolve_command(&plan.base_dir, command); let display = command_path.to_string_lossy().into_owned(); return BinaryRequirement { available: command_available(&display), @@ -469,146 +466,30 @@ fn worst(left: DoctorStatus, right: DoctorStatus) -> DoctorStatus { #[cfg(test)] mod tests { - use std::path::PathBuf; - - use serde_json::Value; - use super::*; - use crate::config::{ - AdapterKind, CapabilityKind, CapabilityRoute, CapabilityTarget, ResolutionStrategy, - resolve_run_plan, - }; - - fn file_config_agent_dir() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../tests/fixtures/file-config-agent") - } - - #[test] - fn image_provided_uses_environment_image_instead_of_host_requirements() { - let mut plan = resolve_run_plan(file_config_agent_dir(), None).expect("run plan"); - plan.resolution = Some(ResolutionStrategy::ImageProvided); - plan.environment_plan - .as_mut() - .expect("environment") - .settings - .insert( - "image".to_string(), - Value::String("fabric-hermes:latest".to_string()), - ); - - let report = doctor_plan(&plan); - - assert_eq!(report.status, DoctorStatus::Pass); - assert!(report.checks.iter().any(|check| { - check.name == "requirements.image" && check.message.contains("fabric-hermes:latest") - })); - assert!( - !report - .checks - .iter() - .any(|check| check.name == "requirement.binary") - ); - } - - #[test] - fn preinstalled_non_local_environment_does_not_probe_host_requirements() { - let plan = - resolve_run_plan(file_config_agent_dir(), Some("env_opensandbox")).expect("run plan"); - - let report = doctor_plan(&plan); - - assert_eq!(report.status, DoctorStatus::Warn); - let report_json = serde_json::to_value(&report).expect("doctor report json"); - assert!(report_json.get("profile").is_none()); - assert_eq!( - report_json["profiles"], - serde_json::json!(["env_opensandbox"]) - ); - assert!(report.checks.iter().any(|check| { - check.name == "requirements.environment" && check.message.contains("opensandbox") - })); - assert!( - !report - .checks - .iter() - .any(|check| check.name == "requirement.binary") - ); - } - - #[test] - fn binary_requirement_can_use_harness_command_setting() { - let mut plan = resolve_run_plan(file_config_agent_dir(), Some("codex")).expect("run plan"); - plan.adapter_descriptor - .as_mut() - .expect("adapter descriptor") - .descriptor - .requirements - .binaries = vec!["fabric-doctor-test".to_string()]; - plan.config.harness.settings.insert( - "fabric_doctor_test_command".to_string(), - Value::String( - std::env::current_exe() - .expect("current executable") - .to_string_lossy() - .into_owned(), - ), - ); - - let report = doctor_plan(&plan); - - assert!(report.checks.iter().any(|check| { - check.name == "requirement.binary" - && check.status == DoctorStatus::Pass - && check.message.contains("fabric_doctor_test_command") - })); - } + use crate::config::{FabricConfig, ResolveContext, resolve_run_plan_from_config}; #[test] - fn doctor_reports_http_execution_as_modeled_not_implemented() { - let mut plan = resolve_run_plan(file_config_agent_dir(), None).expect("run plan"); - plan.resolution = Some(ResolutionStrategy::Service); - plan.adapter_descriptor - .as_mut() - .expect("adapter descriptor") - .descriptor - .adapter_kind = AdapterKind::Http; + fn diagnoses_a_typed_plan_without_file_source_fields() { + let config: FabricConfig = serde_json::from_value(serde_json::json!({ + "schema_version": "fabric.agent/v1alpha1", + "metadata": {"name": "doctor-agent"}, + "harness": { + "adapter_id": "nvidia.fabric.hermes", + "resolution": "preinstalled" + }, + "runtime": {}, + "environment": {"provider": "local"} + })) + .expect("typed config"); + let base_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../.."); + let plan = resolve_run_plan_from_config(config, ResolveContext::new(base_dir)) + .expect("typed plan"); let report = doctor_plan(&plan); - - assert_eq!(report.status, DoctorStatus::Warn); - assert!(report.checks.iter().any(|check| { - check.name == "runtime.adapter" - && check.status == DoctorStatus::Warn - && check - .message - .contains("runtime dispatch is not implemented") - && check.message.contains("http") - })); - assert!(report.checks.iter().any(|check| { - check.name == "resolution" - && check.status == DoctorStatus::Warn - && check.message.contains("modeled but not implemented") - && check.message.contains("service") - })); - } - - #[test] - fn unsupported_blocked_tools_fail_doctor() { - let mut plan = resolve_run_plan(file_config_agent_dir(), None).expect("run plan"); - plan.capability_plan.routes.push(CapabilityRoute { - kind: CapabilityKind::Tools, - name: "blocked".to_string(), - target: CapabilityTarget::Unsupported, - reason: "adapter does not accept tools".to_string(), - }); - - let report = doctor_plan(&plan); - - assert_eq!(report.status, DoctorStatus::Fail); - assert!(report.checks.iter().any(|check| { - check.name == "capability.unsupported" - && check.status == DoctorStatus::Fail - && check.message.contains("adapter does not accept tools") - })); + let value = serde_json::to_value(&report).expect("doctor JSON"); + assert_eq!(report.agent_name, "doctor-agent"); + assert!(!report.checks.is_empty()); + assert_eq!(value["agent_name"], "doctor-agent"); } } diff --git a/crates/fabric-core/src/error.rs b/crates/fabric-core/src/error.rs index dbed6db8f..bb8cc76de 100644 --- a/crates/fabric-core/src/error.rs +++ b/crates/fabric-core/src/error.rs @@ -16,30 +16,6 @@ pub enum FabricError { /// The requested path does not exist. #[error("path does not exist: {0}")] PathNotFound(PathBuf), - /// A directory did not contain the expected Fabric entrypoint. - #[error("directory does not contain agent.yaml: {0}")] - MissingEntrypoint(PathBuf), - /// A file extension is not recognized for Fabric config loading. - #[error("unsupported config file extension for {0}")] - UnsupportedExtension(PathBuf), - /// A requested profile is not present in an agent manifest. - #[error("unknown profile `{profile}` for agent `{agent}`; available profiles: {available:?}")] - UnknownProfile { - /// Requested profile. - profile: String, - /// Agent name. - agent: String, - /// Available profile names. - available: Vec, - }, - /// A profile config failed validation. - #[error("profile error in {path}: {message}")] - ProfileError { - /// Profile path. - path: PathBuf, - /// Validation message. - message: String, - }, /// A requested adapter id is not present in the agent config. #[error("unknown adapter `{adapter_id}`; available adapters: {available:?}")] UnknownAdapter { @@ -88,17 +64,6 @@ pub enum FabricError { /// Available schema names. available: Vec, }, - /// A profile path resolved to an agent manifest instead of a profile config. - #[error("profile `{profile}` did not resolve to a Fabric config: {path}")] - ProfileTargetNotConfig { - /// Profile name. - profile: String, - /// Resolved path. - path: PathBuf, - }, - /// A profile was requested for a single profile config. - #[error("profile selection is only supported for agent manifests: {0}")] - ProfileSelectionNotSupported(PathBuf), /// Runtime invocation is not supported for the selected adapter. #[error( "runtime invocation is not implemented for harness `{harness}` with adapter `{adapter_kind:?}`" @@ -196,14 +161,6 @@ pub enum FabricError { /// Underlying IO error. source: std::io::Error, }, - /// YAML parse failed. - #[error("failed to parse YAML in {path}: {source}")] - ParseYaml { - /// File path. - path: PathBuf, - /// Underlying YAML error. - source: serde_yaml::Error, - }, /// JSON parse failed. #[error("failed to parse JSON in {path}: {source}")] ParseJson { diff --git a/crates/fabric-core/src/lib.rs b/crates/fabric-core/src/lib.rs index 80e78f511..95f680237 100644 --- a/crates/fabric-core/src/lib.rs +++ b/crates/fabric-core/src/lib.rs @@ -13,14 +13,12 @@ pub use config::{ ADAPTER_CONTRACT_VERSION, AdapterConfigSupport, AdapterDescriptor, AdapterDescriptorSource, AdapterKind, AdapterRequirements, AdapterTelemetryProviderSupport, AdapterTelemetrySupport, CapabilityPlan, ControlLocation, EffectiveConfig, EnvironmentConfig, EnvironmentOwnership, - EnvironmentPlan, FabricConfig, FabricDocument, HarnessConfig, McpConfig, McpExposure, - McpServerPlan, MetadataConfig, ModelConfig, ProfileConfig, ResolutionStrategy, ResolveContext, - ResolvedAdapterDescriptor, RunPlan, RuntimeCapabilities, RuntimeConfig, SkillConfig, - TelemetryConfig, TelemetryPlan, TelemetryProvider, TelemetryProviderConfig, - load_adapter_descriptor, load_fabric_document, resolve_effective_config, - resolve_effective_config_from_config, resolve_effective_config_with_profiles, resolve_run_plan, - resolve_run_plan_from_config, resolve_run_plan_from_effective_config, - resolve_run_plan_with_profiles, validate_agent_directory, + EnvironmentPlan, FabricConfig, HarnessConfig, McpConfig, McpExposure, McpServerPlan, + MetadataConfig, ModelConfig, ResolutionStrategy, ResolveContext, ResolvedAdapterDescriptor, + RunPlan, RuntimeCapabilities, RuntimeConfig, SkillConfig, TelemetryConfig, TelemetryPlan, + TelemetryProvider, TelemetryProviderConfig, load_adapter_descriptor, + resolve_effective_config_from_config, resolve_run_plan_from_config, + resolve_run_plan_from_effective_config, }; pub use doctor::{DoctorCheck, DoctorReport, DoctorStatus, doctor_plan}; pub use error::{FabricError, Result}; diff --git a/crates/fabric-core/src/runtime.rs b/crates/fabric-core/src/runtime.rs index 4443c75e7..5d1af5035 100644 --- a/crates/fabric-core/src/runtime.rs +++ b/crates/fabric-core/src/runtime.rs @@ -57,7 +57,7 @@ pub struct RunRequest { /// Runtime context such as task, rollout, workflow, or caller metadata. #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] pub context: BTreeMap, - /// Per-invocation overrides allowed by the resolved profile. + /// Per-invocation overrides allowed by the resolved config. #[serde(default, skip_serializing_if = "Option::is_none")] pub overrides: Option, } @@ -79,8 +79,6 @@ impl RunRequest { pub struct RunResult { /// Stable agent name. pub agent_name: String, - /// Ordered profiles applied to this run. - pub profiles: Vec, /// Stable machine-readable harness identifier used for this run. pub harness: String, /// Adapter used for this run. @@ -149,7 +147,7 @@ pub struct ErrorInfo { #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ErrorStage { - /// Configuration or profile loading failed. + /// Configuration resolution failed. Config, /// Effective config planning failed. Plan, @@ -388,12 +386,12 @@ pub fn prepare_environment(plan: &RunPlan) -> Result { "local".to_string(), ControlLocation::ExternalControl, EnvironmentOwnership::CallerOwned, - Some(plan.agent_root.clone()), + Some(plan.base_dir.clone()), plan.config .runtime .artifacts .as_ref() - .map(|artifacts| resolve_path(&plan.config_root, artifacts)), + .map(|artifacts| resolve_path(&plan.base_dir, artifacts)), serde_json::Map::new(), serde_json::Map::new(), serde_json::Map::new(), @@ -777,9 +775,9 @@ fn run_process_adapter( let cwd = settings .cwd .as_ref() - .map(|path| resolve_path(&plan.config_root, path)) + .map(|path| resolve_path(&plan.base_dir, path)) .or_else(|| runtime.environment.workspace.clone()) - .unwrap_or_else(|| plan.agent_root.clone()); + .unwrap_or_else(|| plan.base_dir.clone()); let mut artifacts = artifact_manifest(plan)?; let fabric_home = prepare_fabric_home(&artifacts, runtime, &invocation)?; let relay_config = prepare_relay_runtime_config( @@ -964,7 +962,6 @@ fn run_process_adapter( Ok(RunResult { agent_name: plan.agent_name.clone(), - profiles: plan.profiles.clone(), harness: harness(plan), adapter_kind: adapter_kind(plan), adapter_id: adapter_id(plan), @@ -998,11 +995,11 @@ fn run_python_adapter( let cwd = settings .cwd .as_ref() - .map(|path| resolve_path(&plan.config_root, path)) + .map(|path| resolve_path(&plan.base_dir, path)) .or_else(|| runtime.environment.workspace.clone()) - .unwrap_or_else(|| plan.agent_root.clone()); + .unwrap_or_else(|| plan.base_dir.clone()); - let python = resolve_python_command(&plan.config_root, &settings).path; + let python = resolve_python_command(&plan.base_dir, &settings).path; let mut artifacts = artifact_manifest(plan)?; let fabric_home = prepare_fabric_home(&artifacts, runtime, &invocation)?; let relay_config = prepare_relay_runtime_config( @@ -1175,7 +1172,6 @@ fn run_python_adapter( Ok(RunResult { agent_name: plan.agent_name.clone(), - profiles: plan.profiles.clone(), harness: harness(plan), adapter_kind: adapter_kind(plan), adapter_id: adapter_id(plan), @@ -1260,7 +1256,7 @@ fn adapter_exit_error( fn parse_python_settings(plan: &RunPlan) -> Result { let value = Value::Object(merged_adapter_settings(plan)); serde_json::from_value(value).map_err(|source| FabricError::InvalidPythonSettings { - path: plan.config_path.clone(), + path: plan.base_dir.clone(), source, }) } @@ -1268,7 +1264,7 @@ fn parse_python_settings(plan: &RunPlan) -> Result { fn parse_process_settings(plan: &RunPlan) -> Result { let value = Value::Object(merged_adapter_settings(plan)); serde_json::from_value(value).map_err(|source| FabricError::InvalidProcessSettings { - path: plan.config_path.clone(), + path: plan.base_dir.clone(), source, }) } @@ -1285,12 +1281,12 @@ fn merged_adapter_settings(plan: &RunPlan) -> Map { fn adapter_setting_root<'a>(plan: &'a RunPlan, key: &str) -> &'a Path { if plan.config.harness.settings.contains_key(key) { - return &plan.config_root; + return &plan.base_dir; } plan.adapter_descriptor .as_ref() .map(|adapter| adapter.root.as_path()) - .unwrap_or(&plan.config_root) + .unwrap_or(&plan.base_dir) } fn fabric_adapter_payload( @@ -1321,9 +1317,7 @@ fn adapter_invocation( relay_config: Option<&RelayRuntimeConfig>, ) -> Result { let mut effective_config = plan.effective_config.clone(); - effective_config.agent_root = absolute_path(effective_config.agent_root)?; - effective_config.config_path = absolute_path(effective_config.config_path)?; - effective_config.config_root = absolute_path(effective_config.config_root)?; + effective_config.base_dir = absolute_path(effective_config.base_dir)?; Ok(AdapterInvocation { effective_config, runtime_context: RuntimeContext { @@ -1403,7 +1397,7 @@ fn resolve_command_path(root: &Path, path: &Path) -> PathBuf { fn preflight_python_adapter(plan: &RunPlan) -> Result<()> { let settings = parse_python_settings(plan)?; - let command = resolve_python_command(&plan.config_root, &settings); + let command = resolve_python_command(&plan.base_dir, &settings); validate_python_command(&command) } @@ -1658,7 +1652,7 @@ fn artifact_manifest(plan: &RunPlan) -> Result { .runtime .artifacts .as_ref() - .map(|path| resolve_path(&plan.config_root, path)) + .map(|path| resolve_path(&plan.base_dir, path)) .or_else(|| { plan.environment_plan .as_ref() @@ -1891,7 +1885,6 @@ fn prepare_relay_runtime_config( }, "fabric": { "agent_name": plan.agent_name.clone(), - "profiles": plan.profiles.clone(), "harness": harness(plan), "adapter_id": adapter_id(plan), "runtime_id": runtime.runtime_id.clone(), @@ -2012,1075 +2005,3 @@ fn now_millis() -> u128 { .map(|duration| duration.as_millis()) .unwrap_or_default() } - -#[cfg(test)] -mod tests { - use std::fs; - - use super::*; - use crate::config::resolve_run_plan; - - fn fixture_agent_dir() -> PathBuf { - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../tests/fixtures/hermes-shim-agent") - } - - fn temp_process_agent_dir() -> PathBuf { - let root = std::env::temp_dir().join(new_id("fabric-process-adapter-test")); - process_agent_dir(root) - } - - fn process_agent_dir(root: PathBuf) -> PathBuf { - let _ = fs::remove_dir_all(&root); - fs::create_dir_all(&root).expect("create agent dir"); - fs::create_dir_all(root.join("adapters/process")).expect("create adapters dir"); - fs::write( - root.join("agent.yaml"), - r#"schema_version: fabric.agent/v1alpha1 -metadata: - name: process-test-agent -harness: - adapter_id: acme.fabric.process - settings: - command: cat -models: - default: - provider: test - model: test-model -runtime: - input_schema: text - output_schema: text - artifacts: ./artifacts -"#, - ) - .expect("write config"); - fs::write( - root.join("adapters/process/fabric-adapter.json"), - process_adapter_descriptor(), - ) - .expect("write adapter descriptor"); - root - } - - fn process_adapter_descriptor() -> &'static str { - r#"{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "acme.fabric.process", - "harness": "process", - "adapter_kind": "process" -}"# - } - - fn stopped_agents() -> Vec { - TEST_STOPPED_AGENTS.lock().expect("stop tracker").clone() - } - - #[test] - fn prepare_environment_absolutizes_workspace() { - let root = - std::env::temp_dir().join(format!("fabric-workspace-abs-{}", std::process::id())); - let _ = fs::remove_dir_all(&root); - fs::create_dir_all(root.join("adapters/process")).expect("create agent dir"); - fs::write( - root.join("agent.yaml"), - r#"schema_version: fabric.agent/v1alpha1 -metadata: - name: workspace-abs-agent -harness: - adapter_id: acme.fabric.process - settings: - command: cat -models: - default: - provider: test - model: test-model -runtime: - input_schema: text - output_schema: text - artifacts: ./artifacts -environment: - provider: local - workspace: ./repos/my-service -"#, - ) - .expect("write config"); - fs::write( - root.join("adapters/process/fabric-adapter.json"), - process_adapter_descriptor(), - ) - .expect("write adapter descriptor"); - - let mut plan = resolve_run_plan(&root, None).expect("run plan"); - // Force a relative workspace to reproduce the pre-fix condition where an - // adapter would re-join it onto the absolute config_root and double the path. - plan.environment_plan - .as_mut() - .expect("environment plan") - .workspace = Some(PathBuf::from("repos/my-service")); - - let environment = prepare_environment(&plan).expect("prepare environment"); - let workspace = environment.workspace.expect("workspace"); - assert!( - workspace.is_absolute(), - "prepared workspace must be absolute so adapters do not re-resolve it: {workspace:?}" - ); - assert!(workspace.ends_with("repos/my-service"), "{workspace:?}"); - - let _ = fs::remove_dir_all(&root); - } - - fn artifact_content(result: &RunResult, name: &str) -> String { - let artifact = result - .artifacts - .artifacts - .iter() - .find(|artifact| artifact.name == name) - .unwrap_or_else(|| panic!("missing artifact {name}")); - fs::read_to_string(&artifact.path).expect("read artifact") - } - - fn run_command(cwd: &Path, command: &str, args: &[&str]) { - let output = Command::new(command) - .args(args) - .current_dir(cwd) - .output() - .expect("run command"); - assert!( - output.status.success(), - "command failed: {command} {args:?}\nstdout:\n{}\nstderr:\n{}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); - } - - #[test] - fn process_adapter_passes_input_to_stdin() { - let root = temp_process_agent_dir(); - let mut plan = resolve_run_plan(&root, None).expect("run plan"); - plan.profiles = vec!["runtime".to_string(), "telemetry".to_string()]; - let result = run_plan(&plan, RunRequest::text("hello fabric")).expect("run result"); - - assert_eq!(result.status, RunStatus::Succeeded); - assert_eq!(result.output, Value::String("hello fabric".to_string())); - assert_eq!(result.metadata.get("exit_code"), Some(&Value::from(0))); - let result_json = serde_json::to_value(&result).expect("result json"); - assert!(result_json.get("profile").is_none()); - assert_eq!( - result_json["profiles"], - serde_json::json!(["runtime", "telemetry"]) - ); - assert_eq!(artifact_content(&result, "stdout"), "hello fabric"); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn runtime_rejects_blocked_tools_when_adapter_cannot_enforce_them() { - let root = temp_process_agent_dir(); - let config_path = root.join("agent.yaml"); - let mut config = fs::read_to_string(&config_path).expect("read config"); - config.push_str("tools:\n blocked:\n - shell\n"); - fs::write(&config_path, config).expect("write blocked tools config"); - fs::write( - root.join("adapters/process/fabric-adapter.json"), - r#"{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "acme.fabric.process", - "harness": "process", - "adapter_kind": "process", - "config": {"accepts": ["tools"]} -}"#, - ) - .expect("write generic tools descriptor"); - let plan = resolve_run_plan(&root, None).expect("run plan"); - - let error = start_runtime(&plan).expect_err("unsupported tools policy must fail closed"); - - assert!(matches!(error, FabricError::UnsupportedToolsPolicy { .. })); - assert!( - error - .to_string() - .contains("cannot enforce configured blocked tools") - ); - let _ = fs::remove_dir_all(root); - } - - #[test] - fn independent_runtimes_use_distinct_artifact_paths() { - let root = temp_process_agent_dir(); - let plan = resolve_run_plan(&root, None).expect("run plan"); - let first_runtime = start_runtime(&plan).expect("first runtime"); - let second_runtime = start_runtime(&plan).expect("second runtime"); - - let first = invoke_runtime(&plan, &first_runtime, RunRequest::text("first runtime")) - .expect("first invocation"); - let second = invoke_runtime(&plan, &second_runtime, RunRequest::text("second runtime")) - .expect("second invocation"); - let first_stdout = first - .artifacts - .artifacts - .iter() - .find(|artifact| artifact.name == "stdout") - .expect("first stdout artifact"); - let second_stdout = second - .artifacts - .artifacts - .iter() - .find(|artifact| artifact.name == "stdout") - .expect("second stdout artifact"); - - assert_eq!(first.artifacts.root, second.artifacts.root); - assert_ne!(first_stdout.path, second_stdout.path); - assert!( - first_stdout.path.starts_with( - root.join("artifacts") - .join(".fabric") - .join(&first_runtime.runtime_id) - .join(&first.invocation_id) - ) - ); - assert!( - second_stdout.path.starts_with( - root.join("artifacts") - .join(".fabric") - .join(&second_runtime.runtime_id) - .join(&second.invocation_id) - ) - ); - assert_eq!(artifact_content(&first, "stdout"), "first runtime"); - assert_eq!(artifact_content(&second, "stdout"), "second runtime"); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn native_telemetry_skips_relay_and_reaches_adapter_payload() { - let root = temp_process_agent_dir(); - let config_path = root.join("agent.yaml"); - fs::write( - root.join("adapters/process/fabric-adapter.json"), - r#"{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "acme.fabric.process", - "harness": "process", - "adapter_kind": "process", - "telemetry": { - "providers": { - "native": { - "outputs": ["otel"] - } - } - } -}"#, - ) - .expect("write adapter telemetry support"); - let mut config = fs::read_to_string(&config_path).expect("read agent config"); - config.push_str( - r#" -telemetry: - providers: - native: - config: - exporter: test -relay: - project: relay-project - output_dir: ./relay-output -"#, - ); - fs::write(&config_path, config).expect("write native telemetry config"); - let plan = resolve_run_plan(&root, None).expect("run plan"); - let runtime = start_runtime(&plan).expect("runtime"); - let request = RunRequest::text("hello fabric"); - let invocation = InvocationHandle { - invocation_id: "invocation-native-telemetry".to_string(), - request_id: request.request_id.clone(), - runtime_id: runtime.runtime_id.clone(), - }; - let mut artifacts = artifact_manifest(&plan).expect("artifact manifest"); - let artifact_directory = - prepare_fabric_home(&artifacts, &runtime, &invocation).expect("fabric home"); - - let relay = prepare_relay_runtime_config( - &plan, - &runtime, - &invocation, - &request, - &artifact_directory, - &mut artifacts, - ) - .expect("prepare telemetry"); - let payload = adapter_invocation( - &plan, - &runtime, - &invocation, - &request, - &artifacts, - relay.as_ref(), - ) - .expect("adapter invocation"); - let payload = serde_json::to_value(payload).expect("adapter payload json"); - - assert!(relay.is_none()); - assert!( - !artifacts - .artifacts - .iter() - .any(|artifact| artifact.name == "relay_config") - ); - assert_eq!( - payload["telemetry_plan"]["providers"], - serde_json::json!(["native"]) - ); - assert_eq!(payload["telemetry_plan"]["relay_enabled"], false); - assert_eq!( - payload["effective_config"]["config"]["telemetry"]["providers"]["native"]["config"], - serde_json::json!({"exporter": "test"}) - ); - assert!(payload["runtime_context"]["telemetry"].get("env").is_none()); - assert_eq!( - payload["runtime_context"]["telemetry"]["metadata"]["telemetry_providers"], - serde_json::json!(["native"]) - ); - assert!( - payload["runtime_context"]["telemetry"]["metadata"] - .get("relay_project") - .is_none() - ); - assert!( - payload["runtime_context"]["telemetry"]["metadata"] - .get("relay_output_dir") - .is_none() - ); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn run_plan_stops_runtime_after_invoke_error() { - let root = temp_process_agent_dir(); - let mut plan = resolve_run_plan(&root, None).expect("run plan"); - plan.agent_name = new_id("invoke-error-agent"); - plan.effective_config.agent_name = plan.agent_name.clone(); - plan.config.harness.settings.remove("command"); - let agent_name = plan.agent_name.clone(); - - let error = run_plan(&plan, RunRequest::text("hello fabric")).expect_err("invoke error"); - - assert!( - error - .to_string() - .contains("invalid process adapter settings"), - "{error}" - ); - assert!( - stopped_agents().contains(&agent_name), - "run_plan must stop the started runtime for {agent_name}" - ); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn runtime_handle_exposes_single_opaque_binding() { - let root = temp_process_agent_dir(); - let plan = resolve_run_plan(&root, None).expect("run plan"); - let runtime = start_runtime(&plan).expect("runtime"); - - let value = serde_json::to_value(&runtime).expect("runtime json"); - assert!(value.get("runtime_binding").is_some()); - assert!(value.get("plan_fingerprint").is_none()); - assert!(value.get("environment_fingerprint").is_none()); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn runtime_handle_without_binding_is_rejected_during_deserialization() { - let root = temp_process_agent_dir(); - let plan = resolve_run_plan(&root, None).expect("run plan"); - let runtime = start_runtime(&plan).expect("runtime"); - let mut value = serde_json::to_value(&runtime).expect("runtime json"); - value - .as_object_mut() - .expect("runtime object") - .remove("runtime_binding"); - let error = serde_json::from_value::(value) - .expect_err("runtime binding must be required"); - - assert!( - error - .to_string() - .contains("missing field `runtime_binding`"), - "{error}" - ); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn runtime_handle_validation_is_independent_of_current_directory() { - const CHILD_ENV: &str = "FABRIC_TEST_RUNTIME_HANDLE_CWD_CHILD"; - if std::env::var_os(CHILD_ENV).is_none() { - let output = Command::new(std::env::current_exe().expect("current test executable")) - .arg("runtime_handle_validation_is_independent_of_current_directory") - .arg("--nocapture") - .env(CHILD_ENV, "1") - .output() - .expect("run isolated cwd test"); - assert!( - output.status.success(), - "isolated cwd test failed\nstdout:\n{}\nstderr:\n{}", - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); - return; - } - - let parent = std::env::temp_dir().join(new_id("fabric-runtime-cwd-test")); - let root = process_agent_dir(parent.join("agent")); - fs::create_dir_all(parent.join("elsewhere")).expect("create alternate cwd"); - std::env::set_current_dir(&parent).expect("enter fixture parent"); - let plan = resolve_run_plan(Path::new("agent"), None).expect("relative run plan"); - let runtime = start_runtime(&plan).expect("runtime"); - - std::env::set_current_dir(parent.join("elsewhere")).expect("change cwd"); - validate_runtime_handle(&plan, &runtime).expect("valid runtime handle"); - - std::env::set_current_dir(std::env::temp_dir()).expect("leave fixture"); - let _ = fs::remove_dir_all(root.parent().expect("fixture parent")); - } - - #[test] - fn invoke_runtime_rejects_runtime_handle_from_different_plan() { - let root = temp_process_agent_dir(); - let plan = resolve_run_plan(&root, None).expect("run plan"); - let runtime = start_runtime(&plan).expect("runtime"); - let mut other_plan = plan.clone(); - other_plan.agent_name = "other-agent".to_string(); - other_plan.effective_config.agent_name = "other-agent".to_string(); - - let error = invoke_runtime(&other_plan, &runtime, RunRequest::text("hello fabric")) - .expect_err("runtime mismatch"); - - assert!( - error - .to_string() - .contains("runtime handle does not match run plan"), - "{error}" - ); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn invoke_runtime_rejects_runtime_handle_from_mutated_adapter_settings() { - let root = temp_process_agent_dir(); - let plan = resolve_run_plan(&root, None).expect("run plan"); - let runtime = start_runtime(&plan).expect("runtime"); - let mut other_plan = plan.clone(); - other_plan - .config - .harness - .settings - .insert("command".to_string(), Value::String("printf".to_string())); - - let error = invoke_runtime(&other_plan, &runtime, RunRequest::text("hello fabric")) - .expect_err("runtime mismatch"); - - assert!( - error - .to_string() - .contains("runtime handle does not match run plan"), - "{error}" - ); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn invoke_runtime_rejects_mutated_runtime_environment() { - let root = temp_process_agent_dir(); - let plan = resolve_run_plan(&root, None).expect("run plan"); - let mut runtime = start_runtime(&plan).expect("runtime"); - runtime.environment.workspace = Some(root.join("other-workspace")); - - let error = invoke_runtime(&plan, &runtime, RunRequest::text("hello fabric")) - .expect_err("runtime mismatch"); - - assert!( - error - .to_string() - .contains("runtime handle does not match run plan"), - "{error}" - ); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn invoke_runtime_rejects_mutated_runtime_identity() { - let root = temp_process_agent_dir(); - let plan = resolve_run_plan(&root, None).expect("run plan"); - let mut runtime = start_runtime(&plan).expect("runtime"); - runtime.harness = "other-harness".to_string(); - - let error = invoke_runtime(&plan, &runtime, RunRequest::text("hello fabric")) - .expect_err("runtime mismatch"); - - assert!( - error - .to_string() - .contains("runtime handle does not match run plan"), - "{error}" - ); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn stop_runtime_rejects_runtime_handle_from_different_plan() { - let root = temp_process_agent_dir(); - let plan = resolve_run_plan(&root, None).expect("run plan"); - let runtime = start_runtime(&plan).expect("runtime"); - let mut other_plan = plan.clone(); - other_plan.agent_name = "other-agent".to_string(); - other_plan.effective_config.agent_name = "other-agent".to_string(); - - let error = stop_runtime(&other_plan, &runtime).expect_err("runtime mismatch"); - - assert!( - error - .to_string() - .contains("runtime handle does not match run plan"), - "{error}" - ); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn run_promotes_relay_artifacts_into_artifact_manifest() { - let root = std::env::temp_dir().join(format!( - "fabric-relay-artifact-manifest-test-{}", - std::process::id() - )); - let _ = fs::remove_dir_all(&root); - fs::create_dir_all(root.join("adapters/process")).expect("create adapters dir"); - let adapter_script = r#" -from pathlib import Path -import json -relay_dir = Path("artifacts/relay").resolve() -relay_dir.mkdir(parents=True, exist_ok=True) -atof = relay_dir / "events.atof.jsonl" -atif = relay_dir / "trajectory-runtime.atif.json" -atif_extra = relay_dir / "trajectory-child.atif.json" -atof.write_text('{"kind":"scope"}\n', encoding="utf-8") -atif.write_text('{"trajectory":true}', encoding="utf-8") -atif_extra.write_text('{"trajectory":"child"}', encoding="utf-8") -print(json.dumps({ - "response": "ok", - "relay_artifacts": [ - {"kind": "atof", "path": str(atof)}, - {"kind": "atif", "path": str(atif)}, - {"kind": "atif", "path": str(atif_extra)} - ] -})) -"#; - let agent_config = serde_json::json!({ - "schema_version": "fabric.agent/v1alpha1", - "metadata": { - "name": "relay-artifact-test-agent", - }, - "harness": { - "adapter_id": "acme.fabric.process", - "settings": { - "command": "python3", - "args": ["-c", adapter_script], - }, - }, - "models": { - "default": { - "provider": "test", - "model": "test-model", - }, - }, - "runtime": { - "input_schema": "text", - "output_schema": "text", - "artifacts": "./artifacts", - }, - }); - fs::write( - root.join("agent.yaml"), - serde_yaml::to_string(&agent_config).expect("serialize agent config"), - ) - .expect("write config"); - fs::write( - root.join("adapters/process/fabric-adapter.json"), - process_adapter_descriptor(), - ) - .expect("write adapter descriptor"); - - let plan = resolve_run_plan(&root, None).expect("run plan"); - let result = run_plan(&plan, RunRequest::text("collect relay")).expect("run result"); - - assert_eq!(result.status, RunStatus::Succeeded); - let atof = result - .artifacts - .artifacts - .iter() - .find(|artifact| artifact.name == "relay_atof" && artifact.kind == "atof") - .expect("ATOF artifact promoted to manifest"); - let atif = result - .artifacts - .artifacts - .iter() - .find(|artifact| artifact.name == "relay_atif" && artifact.kind == "atif") - .expect("ATIF artifact promoted to manifest"); - let atif_extra = result - .artifacts - .artifacts - .iter() - .find(|artifact| artifact.name == "relay_atif_2" && artifact.kind == "atif") - .expect("second ATIF artifact promoted to manifest with unique name"); - assert!(atof.path.ends_with("events.atof.jsonl")); - assert_eq!(atof.media_type.as_deref(), Some("application/x-ndjson")); - assert!(atif.path.ends_with("trajectory-runtime.atif.json")); - assert_eq!(atif.media_type.as_deref(), Some("application/json")); - assert!(atif_extra.path.ends_with("trajectory-child.atif.json")); - assert_eq!(atif_extra.media_type.as_deref(), Some("application/json")); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn adapter_runtime_context_contains_runtime_and_invocation_ids() { - let root = std::env::temp_dir().join(format!( - "fabric-runtime-context-test-{}", - std::process::id() - )); - let _ = fs::remove_dir_all(&root); - fs::create_dir_all(root.join("adapters/process")).expect("create adapters dir"); - fs::write( - root.join("agent.yaml"), - r#"schema_version: fabric.agent/v1alpha1 -metadata: - name: runtime-context-agent -harness: - adapter_id: acme.fabric.process - settings: - command: python3 - args: - - -c - - | - import json - import sys - payload = json.load(sys.stdin) - print(json.dumps(payload["runtime_context"], sort_keys=True)) - stdin_payload: fabric_request -models: - default: - provider: test - model: test-model -runtime: - input_schema: text - output_schema: text - artifacts: ./artifacts -"#, - ) - .expect("write config"); - fs::write( - root.join("adapters/process/fabric-adapter.json"), - process_adapter_descriptor(), - ) - .expect("write adapter descriptor"); - - let plan = resolve_run_plan(&root, None).expect("run plan"); - let request = RunRequest::text("hello fabric"); - let result = run_plan(&plan, request).expect("run result"); - - assert_eq!(result.status, RunStatus::Succeeded); - assert!(result.output.get("session_id").is_none()); - assert_eq!( - result.output["runtime_id"], - Value::String(result.runtime_id.clone()) - ); - assert_eq!( - result.output["invocation_id"], - Value::String(result.invocation_id.clone()) - ); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn process_adapter_failure_returns_structured_error() { - let root = std::env::temp_dir().join(format!( - "fabric-process-adapter-failure-test-{}", - std::process::id() - )); - let _ = fs::remove_dir_all(&root); - fs::create_dir_all(root.join("adapters/process")).expect("create adapters dir"); - fs::write( - root.join("agent.yaml"), - r#"schema_version: fabric.agent/v1alpha1 -metadata: - name: process-failure-agent -harness: - adapter_id: acme.fabric.process - settings: - command: sh - args: - - -c - - | - echo adapter exploded >&2 - exit 7 -models: - default: - provider: test - model: test-model -runtime: - input_schema: text - output_schema: text - artifacts: ./artifacts -"#, - ) - .expect("write config"); - fs::write( - root.join("adapters/process/fabric-adapter.json"), - process_adapter_descriptor(), - ) - .expect("write adapter descriptor"); - - let plan = resolve_run_plan(&root, None).expect("run plan"); - let result = run_plan(&plan, RunRequest::text("hello fabric")).expect("run result"); - - assert_eq!(result.status, RunStatus::Failed); - let error = result.error.as_ref().expect("structured error"); - assert_eq!(error.stage, ErrorStage::Invoke); - assert_eq!(error.code, "process_exit_nonzero"); - assert!(!error.retryable); - assert!(error.message.contains("adapter exploded")); - assert_eq!( - error.metadata.get("adapter_runner"), - Some(&Value::String("process".to_string())) - ); - assert_eq!(error.metadata.get("exit_code"), Some(&Value::from(7))); - assert_eq!(artifact_content(&result, "stderr"), "adapter exploded\n"); - - let _ = fs::remove_dir_all(root); - } - - fn python_settings() -> PythonAdapterSettings { - PythonAdapterSettings { - module: "test.adapter".to_string(), - python: None, - python_env: None, - args: Vec::new(), - cwd: None, - env: BTreeMap::new(), - } - } - - #[test] - fn python_command_uses_adapter_python_as_default_python_env() { - let root = Path::new("/config"); - let settings = python_settings(); - let command = resolve_python_command_with_env( - root, - &settings, - |name| (name == ADAPTER_PYTHON_ENV).then(|| OsString::from("venv/bin/python")), - |_| false, - None, - ); - - assert_eq!(command.path, root.join("venv/bin/python")); - assert_eq!(command.source, PythonSource::AdapterPythonEnv); - - let command = resolve_python_command_with_env(root, &settings, |_| None, |_| false, None); - assert_eq!(command.path, PathBuf::from(DEFAULT_PYTHON)); - assert_eq!(command.source, PythonSource::DefaultPython3); - } - - #[test] - fn explicit_python_settings_override_adapter_python() { - let root = Path::new("/config"); - let mut settings = python_settings(); - settings.python_env = Some("CUSTOM_PYTHON".to_string()); - let command = resolve_python_command_with_env( - root, - &settings, - |name| match name { - "CUSTOM_PYTHON" => Some(OsString::from("/custom/python")), - ADAPTER_PYTHON_ENV => Some(OsString::from("/adapter/python")), - _ => None, - }, - |_| false, - None, - ); - assert_eq!(command.path, PathBuf::from("/custom/python")); - assert_eq!( - command.source, - PythonSource::SettingEnv("CUSTOM_PYTHON".to_string()) - ); - - let command = resolve_python_command_with_env( - root, - &settings, - |name| (name == ADAPTER_PYTHON_ENV).then(|| OsString::from("/adapter/python")), - |_| false, - None, - ); - assert_eq!(command.path, PathBuf::from(DEFAULT_PYTHON)); - assert_eq!(command.source, PythonSource::DefaultPython3); - - settings.python = Some(PathBuf::from("/configured/python")); - let command = resolve_python_command_with_env( - root, - &settings, - |_| Some(OsString::from("/environment/python")), - |_| false, - None, - ); - assert_eq!(command.path, PathBuf::from("/configured/python")); - assert_eq!(command.source, PythonSource::Setting); - } - - #[test] - fn fallback_targets_active_virtualenv_even_when_missing() { - // VIRTUAL_ENV wins over bare python3 and is returned even when its - // interpreter is absent, so preflight reports a clear error instead of - // silently falling back to an unrelated python3. - let root = Path::new("/config"); - let settings = python_settings(); - let venv_python = Path::new("/venv").join(VENV_BIN_DIR).join(VENV_PYTHON); - let command = resolve_python_command_with_env( - root, - &settings, - |name| (name == VIRTUAL_ENV_ENV).then(|| OsString::from("/venv")), - |_| false, - None, - ); - - assert_eq!(command.path, venv_python); - assert_eq!(command.source, PythonSource::Virtualenv); - - let error = validate_python_command(&command) - .expect_err("missing virtualenv interpreter must fail preflight"); - assert!(matches!( - error, - FabricError::PythonInterpreterUnavailable { .. } - )); - } - - #[test] - fn empty_environment_values_are_ignored() { - let root = Path::new("/config"); - - // ADAPTER_PYTHON set but empty -> ignored, falls through to python3. - let command = resolve_python_command_with_env( - root, - &python_settings(), - |name| (name == ADAPTER_PYTHON_ENV).then(OsString::new), - |_| false, - None, - ); - assert_eq!(command.path, PathBuf::from(DEFAULT_PYTHON)); - assert_eq!(command.source, PythonSource::DefaultPython3); - - // python_env names a variable that is set but empty -> ignored. - let mut settings = python_settings(); - settings.python_env = Some("CUSTOM_PYTHON".to_string()); - let command = resolve_python_command_with_env( - root, - &settings, - |name| (name == "CUSTOM_PYTHON").then(OsString::new), - |_| false, - None, - ); - assert_eq!(command.path, PathBuf::from(DEFAULT_PYTHON)); - assert_eq!(command.source, PythonSource::DefaultPython3); - } - - #[cfg(windows)] - #[test] - fn windows_default_python_is_python_exe() { - // find_on_path joins each PATH entry with the bare name and stats it, so - // on Windows the fallback must name python.exe to resolve on disk. - assert_eq!(DEFAULT_PYTHON, "python.exe"); - let command = resolve_python_command_with_env( - Path::new("C:/config"), - &python_settings(), - |_| None, - |_| false, - None, - ); - assert_eq!(command.path, PathBuf::from("python.exe")); - assert_eq!(command.source, PythonSource::DefaultPython3); - } - - #[test] - fn fallback_uses_host_interpreter_next_to_executable() { - let root = Path::new("/config"); - let settings = python_settings(); - let exe = PathBuf::from("/opt/fabric/bin/nemo-fabric"); - let sibling = PathBuf::from("/opt/fabric/bin").join(VENV_PYTHON); - let expected = sibling.clone(); - let command = resolve_python_command_with_env( - root, - &settings, - |_| None, - move |path| path == expected.as_path(), - Some(exe), - ); - - assert_eq!(command.path, sibling); - assert_eq!(command.source, PythonSource::HostInterpreter); - } - - #[test] - fn adapter_python_preflight_requires_a_file() { - let root = std::env::temp_dir().join(new_id("fabric-adapter-python-test")); - fs::create_dir_all(&root).expect("create test directory"); - let interpreter = root.join("python"); - fs::write(&interpreter, "").expect("create interpreter file"); - - validate_python_command(&PythonCommand { - path: interpreter, - source: PythonSource::AdapterPythonEnv, - }) - .expect("file path should pass preflight"); - - let error = validate_python_command(&PythonCommand { - path: root.join("missing-python"), - source: PythonSource::AdapterPythonEnv, - }) - .expect_err("missing path should fail preflight"); - assert!(matches!( - error, - FabricError::PythonInterpreterUnavailable { .. } - )); - - let error = validate_python_command(&PythonCommand { - path: root.clone(), - source: PythonSource::AdapterPythonEnv, - }) - .expect_err("directory path should fail preflight"); - assert!(matches!( - error, - FabricError::PythonInterpreterUnavailable { .. } - )); - - let _ = fs::remove_dir_all(root); - } - - #[test] - fn preflight_rejects_missing_virtualenv_interpreter() { - // A resolved absolute interpreter that does not exist must fail preflight - // with a clear error instead of surfacing as a mid-run subprocess crash. - let error = validate_python_command(&PythonCommand { - path: Path::new("/venv").join(VENV_BIN_DIR).join(VENV_PYTHON), - source: PythonSource::Virtualenv, - }) - .expect_err("missing virtualenv interpreter should fail preflight"); - assert!(matches!( - error, - FabricError::PythonInterpreterUnavailable { .. } - )); - } - - #[test] - fn python_adapter_runs_hermes_shim() { - let plan = resolve_run_plan(fixture_agent_dir(), Some("env_local")).expect("run plan"); - let result = run_plan(&plan, RunRequest::text("review this")).expect("run result"); - - assert_eq!(result.status, RunStatus::Succeeded); - assert_eq!(result.adapter_kind, AdapterKind::Python); - assert_eq!( - result.output["harness"], - Value::String("hermes".to_string()) - ); - assert_eq!( - result.output["received"], - Value::String("review this".to_string()) - ); - assert_eq!( - result.metadata.get("adapter_runner"), - Some(&Value::String("python".to_string())) - ); - assert_eq!( - result.metadata.get("module"), - Some(&Value::String( - "nemo_fabric_test_adapters.hermes_shim.adapter".to_string() - )) - ); - } - - #[test] - fn run_collects_workspace_patch_artifact() { - let root = - std::env::temp_dir().join(format!("fabric-patch-artifact-test-{}", std::process::id())); - let _ = fs::remove_dir_all(&root); - let workspace = root.join("workspace"); - fs::create_dir_all(&workspace).expect("create workspace"); - fs::write(workspace.join("bug.py"), "def answer():\n return 41\n") - .expect("write baseline"); - run_command(&workspace, "git", &["init", "-q"]); - run_command(&workspace, "git", &["add", "bug.py"]); - fs::write( - root.join("agent.yaml"), - r#"schema_version: fabric.agent/v1alpha1 -metadata: - name: patch-test-agent -harness: - adapter_id: acme.fabric.process - settings: - command: python3 - args: - - -c - - | - from pathlib import Path - Path("bug.py").write_text("def answer():\n return 42\n") - Path("notes.txt").write_text("fixed by fabric\n") - print('patched') -models: - default: - provider: test - model: test-model -runtime: - input_schema: text - output_schema: text - artifacts: ./artifacts -environment: - provider: local - workspace: ./workspace - artifacts: ./artifacts -"#, - ) - .expect("write config"); - fs::create_dir_all(root.join("adapters/process")).expect("create adapters dir"); - fs::write( - root.join("adapters/process/fabric-adapter.json"), - process_adapter_descriptor(), - ) - .expect("write adapter descriptor"); - - let plan = resolve_run_plan(&root, None).expect("run plan"); - let result = run_plan(&plan, RunRequest::text("fix the bug")).expect("run result"); - - assert_eq!(result.status, RunStatus::Succeeded); - let patch_artifact = result - .artifacts - .artifacts - .iter() - .find(|artifact| artifact.name == "workspace_patch") - .expect("workspace patch artifact"); - let patch = fs::read_to_string(&patch_artifact.path).expect("read patch"); - assert!(patch.contains("- return 41")); - assert!(patch.contains("+ return 42")); - assert!(patch.contains("new file mode")); - assert!(patch.contains("notes.txt")); - assert!(patch.contains("+fixed by fabric")); - - let _ = fs::remove_dir_all(root); - } -} diff --git a/crates/fabric-core/src/schema.rs b/crates/fabric-core/src/schema.rs index b5ea09354..e7811aeed 100644 --- a/crates/fabric-core/src/schema.rs +++ b/crates/fabric-core/src/schema.rs @@ -9,7 +9,7 @@ use std::path::{Path, PathBuf}; use schemars::schema_for; use serde_json::Value; -use crate::config::{AdapterDescriptor, EffectiveConfig, FabricConfig, ProfileConfig, RunPlan}; +use crate::config::{AdapterDescriptor, EffectiveConfig, FabricConfig, RunPlan}; use crate::error::{FabricError, Result}; use crate::runtime::{ AdapterInvocation, ArtifactManifest, EnvironmentHandle, ErrorInfo, FabricEvent, @@ -19,10 +19,8 @@ use crate::runtime::{ /// Public schema snapshots generated by Fabric. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SchemaName { - /// Base `agent.yaml` schema. + /// Complete typed Fabric config schema. Agent, - /// Profile overlay schema. - Profile, /// Adapter descriptor schema. AdapterDescriptor, /// Effective merged config schema. @@ -53,9 +51,8 @@ pub enum SchemaName { impl SchemaName { /// All public schemas in stable output order. - pub const ALL: [Self; 15] = [ + pub const ALL: [Self; 14] = [ Self::Agent, - Self::Profile, Self::AdapterDescriptor, Self::EffectiveConfig, Self::RunPlan, @@ -75,7 +72,6 @@ impl SchemaName { pub fn as_str(self) -> &'static str { match self { Self::Agent => "agent", - Self::Profile => "profile", Self::AdapterDescriptor => "adapter-descriptor", Self::EffectiveConfig => "effective-config", Self::RunPlan => "run-plan", @@ -101,7 +97,6 @@ impl SchemaName { pub fn parse(value: &str) -> Result { match value { "agent" => Ok(Self::Agent), - "profile" => Ok(Self::Profile), "adapter-descriptor" | "adapter_descriptor" => Ok(Self::AdapterDescriptor), "effective-config" | "effective_config" => Ok(Self::EffectiveConfig), "run-plan" | "run_plan" => Ok(Self::RunPlan), @@ -130,7 +125,6 @@ impl SchemaName { pub fn generate_schema(schema: SchemaName) -> Result { match schema { SchemaName::Agent => to_value(schema_for!(FabricConfig)), - SchemaName::Profile => to_value(schema_for!(ProfileConfig)), SchemaName::AdapterDescriptor => to_value(schema_for!(AdapterDescriptor)), SchemaName::EffectiveConfig => to_value(schema_for!(EffectiveConfig)), SchemaName::RunPlan => to_value(schema_for!(RunPlan)), diff --git a/crates/fabric-python/src/lib.rs b/crates/fabric-python/src/lib.rs index bd8bb574a..9fbd2db70 100644 --- a/crates/fabric-python/src/lib.rs +++ b/crates/fabric-python/src/lib.rs @@ -6,10 +6,8 @@ use std::path::PathBuf; use nemo_fabric_core::{ - FabricConfig, ProfileConfig, ResolveContext, RunPlan, RunRequest, RuntimeHandle, doctor_plan, - load_fabric_document, resolve_effective_config_from_config, - resolve_effective_config_with_profiles, resolve_run_plan_from_config, - resolve_run_plan_with_profiles, run_plan, + FabricConfig, ResolveContext, RunPlan, RunRequest, RuntimeHandle, doctor_plan, + resolve_effective_config_from_config, resolve_run_plan_from_config, run_plan, }; use pyo3::exceptions::PyRuntimeError; use pyo3::prelude::*; @@ -20,129 +18,41 @@ fn version() -> PyResult { Ok(nemo_fabric_core::version().to_string()) } -/// Validate an agent directory, manifest, or profile config. +/// Resolve typed config JSON into effective config and return JSON. #[pyfunction] -fn validate(path: String) -> PyResult { - load_fabric_document(&path).map_err(to_py_error)?; - Ok(format!("validated {path}")) -} - -/// Resolve and return the effective Fabric config as JSON. -#[pyfunction] -#[pyo3(signature = (path, profile=None))] -fn inspect(py: Python<'_>, path: String, profile: Option>) -> PyResult { - let profiles = profile_values(py, profile)?; - let effective_config = - resolve_effective_config_with_profiles(path, &profiles).map_err(to_py_error)?; - to_json(&effective_config) -} - -/// Resolve typed config/profile JSON into effective config and return JSON. -#[pyfunction] -#[pyo3(signature = (config_json, profiles_json=None, base_dir=None))] -fn resolve_config( - config_json: String, - profiles_json: Option, - base_dir: Option, -) -> PyResult { +#[pyo3(signature = (config_json, base_dir=None))] +fn resolve_config(config_json: String, base_dir: Option) -> PyResult { let config = parse_config(config_json)?; - let profiles = parse_profiles(profiles_json)?; - let effective = - resolve_effective_config_from_config(config, &profiles, resolve_context(base_dir)) - .map_err(to_py_error)?; + let effective = resolve_effective_config_from_config(config, resolve_context(base_dir)) + .map_err(to_py_error)?; to_json(&effective) } -/// Resolve an agent/profile into a runnable plan and return JSON. -#[pyfunction] -#[pyo3(signature = (path, profile=None))] -fn plan(py: Python<'_>, path: String, profile: Option>) -> PyResult { - let profiles = profile_values(py, profile)?; - let plan = resolve_run_plan_with_profiles(path, &profiles).map_err(to_py_error)?; - to_json(&plan) -} - -/// Resolve typed config/profile JSON into a runnable plan and return JSON. +/// Resolve typed config JSON into a runnable plan and return JSON. #[pyfunction] -#[pyo3(signature = (config_json, profiles_json=None, base_dir=None))] -fn plan_config( - config_json: String, - profiles_json: Option, - base_dir: Option, -) -> PyResult { +#[pyo3(signature = (config_json, base_dir=None))] +fn plan_config(config_json: String, base_dir: Option) -> PyResult { let config = parse_config(config_json)?; - let profiles = parse_profiles(profiles_json)?; - let plan = resolve_run_plan_from_config(config, &profiles, resolve_context(base_dir)) - .map_err(to_py_error)?; + let plan = + resolve_run_plan_from_config(config, resolve_context(base_dir)).map_err(to_py_error)?; to_json(&plan) } -/// Diagnose a resolved agent/profile without installing or running it. -#[pyfunction] -#[pyo3(signature = (path, profile=None))] -fn doctor(py: Python<'_>, path: String, profile: Option>) -> PyResult { - let profiles = profile_values(py, profile)?; - let plan = resolve_run_plan_with_profiles(path, &profiles).map_err(to_py_error)?; - to_json(&doctor_plan(&plan)) -} - -/// Diagnose typed config/profile JSON without installing or running it. +/// Diagnose typed config JSON without installing or running it. #[pyfunction] -#[pyo3(signature = (config_json, profiles_json=None, base_dir=None))] -fn doctor_config( - config_json: String, - profiles_json: Option, - base_dir: Option, -) -> PyResult { +#[pyo3(signature = (config_json, base_dir=None))] +fn doctor_config(config_json: String, base_dir: Option) -> PyResult { let config = parse_config(config_json)?; - let profiles = parse_profiles(profiles_json)?; - let plan = resolve_run_plan_from_config(config, &profiles, resolve_context(base_dir)) - .map_err(to_py_error)?; + let plan = + resolve_run_plan_from_config(config, resolve_context(base_dir)).map_err(to_py_error)?; to_json(&doctor_plan(&plan)) } -/// Run an agent/profile through its Fabric adapter and return JSON. +/// Run typed config JSON through its Fabric adapter and return JSON. #[pyfunction] -#[pyo3(signature = (path, profile=None, input_text=None, input_file=None, request_json=None, request_file=None))] -fn run( - py: Python<'_>, - path: String, - profile: Option>, - input_text: Option, - input_file: Option, - request_json: Option, - request_file: Option, -) -> PyResult { - let profiles = profile_values(py, profile)?; - let plan = resolve_run_plan_with_profiles(path, &profiles).map_err(to_py_error)?; - let request = match (request_file, request_json, input_file) { - (Some(path), None, None) => std::fs::read_to_string(PathBuf::from(&path)) - .map_err(|error| PyRuntimeError::new_err(format!("failed to read {path}: {error}"))) - .and_then(parse_run_request)?, - (None, Some(json), None) => parse_run_request(json)?, - (None, None, Some(path)) => { - let input = std::fs::read_to_string(PathBuf::from(&path)).map_err(|error| { - PyRuntimeError::new_err(format!("failed to read {path}: {error}")) - })?; - RunRequest::text(input) - } - (None, None, None) => RunRequest::text(input_text.unwrap_or_default()), - _ => { - return Err(PyRuntimeError::new_err( - "request_file, request_json, and input_file are mutually exclusive", - )); - } - }; - let result = run_plan(&plan, request).map_err(to_py_error)?; - to_json(&result) -} - -/// Run typed config/profile JSON through its Fabric adapter and return JSON. -#[pyfunction] -#[pyo3(signature = (config_json, profiles_json=None, base_dir=None, input_text=None, input_file=None, request_json=None, request_file=None))] +#[pyo3(signature = (config_json, base_dir=None, input_text=None, input_file=None, request_json=None, request_file=None))] fn run_config( config_json: String, - profiles_json: Option, base_dir: Option, input_text: Option, input_file: Option, @@ -150,9 +60,8 @@ fn run_config( request_file: Option, ) -> PyResult { let config = parse_config(config_json)?; - let profiles = parse_profiles(profiles_json)?; - let plan = resolve_run_plan_from_config(config, &profiles, resolve_context(base_dir)) - .map_err(to_py_error)?; + let plan = + resolve_run_plan_from_config(config, resolve_context(base_dir)).map_err(to_py_error)?; let request = match (request_file, request_json, input_file) { (Some(path), None, None) => std::fs::read_to_string(PathBuf::from(&path)) .map_err(|error| PyRuntimeError::new_err(format!("failed to read {path}: {error}"))) @@ -216,14 +125,9 @@ fn stop_runtime(py: Python<'_>, plan_json: String, runtime_json: String) -> PyRe #[pymodule] fn _native(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(version, m)?)?; - m.add_function(wrap_pyfunction!(validate, m)?)?; - m.add_function(wrap_pyfunction!(inspect, m)?)?; m.add_function(wrap_pyfunction!(resolve_config, m)?)?; - m.add_function(wrap_pyfunction!(plan, m)?)?; m.add_function(wrap_pyfunction!(plan_config, m)?)?; - m.add_function(wrap_pyfunction!(doctor, m)?)?; m.add_function(wrap_pyfunction!(doctor_config, m)?)?; - m.add_function(wrap_pyfunction!(run, m)?)?; m.add_function(wrap_pyfunction!(run_config, m)?)?; m.add_function(wrap_pyfunction!(start_runtime, m)?)?; m.add_function(wrap_pyfunction!(invoke_runtime, m)?)?; @@ -242,40 +146,14 @@ fn to_py_error(error: nemo_fabric_core::FabricError) -> PyErr { PyRuntimeError::new_err(error.to_string()) } -fn profile_values(py: Python<'_>, profile: Option>) -> PyResult> { - let Some(profile) = profile else { - return Ok(Vec::new()); - }; - let profile = profile.bind(py); - if profile.is_none() { - return Ok(Vec::new()); - } - if let Ok(profile) = profile.extract::() { - return Ok(vec![profile]); - } - if let Ok(profiles) = profile.extract::>() { - return Ok(profiles); - } - Err(PyRuntimeError::new_err( - "profile must be None, a string, or a sequence of strings", - )) -} - fn resolve_context(base_dir: Option) -> ResolveContext { - ResolveContext::from_agent_root(base_dir.unwrap_or_else(|| ".".to_string())) + ResolveContext::new(base_dir.unwrap_or_else(|| ".".to_string())) } fn parse_config(contents: String) -> PyResult { serde_json::from_str(&contents).map_err(|error| PyRuntimeError::new_err(error.to_string())) } -fn parse_profiles(contents: Option) -> PyResult> { - let Some(contents) = contents else { - return Ok(Vec::new()); - }; - serde_json::from_str(&contents).map_err(|error| PyRuntimeError::new_err(error.to_string())) -} - fn parse_run_request(contents: String) -> PyResult { serde_json::from_str(&contents).map_err(|error| PyRuntimeError::new_err(error.to_string())) } diff --git a/docs/reference/api/python-library-reference/index.md b/docs/reference/api/python-library-reference/index.md index a9194c666..e0720f831 100644 --- a/docs/reference/api/python-library-reference/index.md +++ b/docs/reference/api/python-library-reference/index.md @@ -24,13 +24,11 @@ SPDX-License-Identifier: Apache-2.0 */} - [`models.EnvironmentConfig`](./nemo_fabric.models.md#class-environmentconfig): Execution environment configuration supplied by the consumer. - [`models.FabricBaseModel`](./nemo_fabric.models.md#class-fabricbasemodel): Base class for SDK-facing Pydantic models. - [`models.FabricConfig`](./nemo_fabric.models.md#class-fabricconfig): SDK-facing typed Fabric agent configuration. -- [`models.FabricProfileConfig`](./nemo_fabric.models.md#class-fabricprofileconfig): Typed profile overlay used when a Python caller wants file-style overlays. - [`models.HarnessConfig`](./nemo_fabric.models.md#class-harnessconfig): Harness adapter selection plus adapter-owned settings. - [`models.McpConfig`](./nemo_fabric.models.md#class-mcpconfig): MCP capability configuration. - [`models.McpServerConfig`](./nemo_fabric.models.md#class-mcpserverconfig): MCP server configuration. - [`models.MetadataConfig`](./nemo_fabric.models.md#class-metadataconfig): Human-readable agent identity. - [`models.ModelConfig`](./nemo_fabric.models.md#class-modelconfig): Model alias configuration. -- [`models.ProfileRegistryConfig`](./nemo_fabric.models.md#class-profileregistryconfig): Profile discovery config for portable file-backed agent packages. - [`models.RelayAtifConfig`](./nemo_fabric.models.md#class-relayatifconfig): NeMo Relay ATIF export configuration. - [`models.RelayAtofConfig`](./nemo_fabric.models.md#class-relayatofconfig): NeMo Relay ATOF export configuration. - [`models.RelayAtofEndpointConfig`](./nemo_fabric.models.md#class-relayatofendpointconfig): NeMo Relay ATOF remote endpoint configuration. @@ -52,7 +50,7 @@ SPDX-License-Identifier: Apache-2.0 */} - [`types.ArtifactRef`](./nemo_fabric.types.md#class-artifactref): Reference to one artifact produced by a run. - [`types.DoctorCheck`](./nemo_fabric.types.md#class-doctorcheck): One diagnostic check in a ``DoctorReport``. - [`types.DoctorReport`](./nemo_fabric.types.md#class-doctorreport): Aggregate preflight diagnostics for a resolved run plan. -- [`types.EffectiveConfig`](./nemo_fabric.types.md#class-effectiveconfig): Immutable result of config loading and ordered profile application. +- [`types.EffectiveConfig`](./nemo_fabric.types.md#class-effectiveconfig): Immutable result of resolving a complete typed config. - [`types.ErrorInfo`](./nemo_fabric.types.md#class-errorinfo): Structured failure returned inside a normalized ``RunResult``. - [`types.FabricEvent`](./nemo_fabric.types.md#class-fabricevent): One normalized lifecycle or invocation event. - [`types.RunOutput`](./nemo_fabric.types.md#class-runoutput): Normalized adapter output. @@ -62,7 +60,7 @@ SPDX-License-Identifier: Apache-2.0 */} - [`types.RuntimeHandle`](./nemo_fabric.types.md#class-runtimehandle): Opaque identity and binding for one started runtime. - [`types.TelemetryRef`](./nemo_fabric.types.md#class-telemetryref): Reference to external or persisted telemetry for a run. - [`errors.FabricCapabilityError`](./nemo_fabric.errors.md#class-fabriccapabilityerror): Operation rejected by resolved runtime capabilities or implementation status. -- [`errors.FabricConfigError`](./nemo_fabric.errors.md#class-fabricconfigerror): Invalid SDK input, request shape, profile stack, or resolved config. +- [`errors.FabricConfigError`](./nemo_fabric.errors.md#class-fabricconfigerror): Invalid SDK input, request shape, factory, or resolved config. - [`errors.FabricError`](./nemo_fabric.errors.md#class-fabricerror): Base class for structured SDK-level Fabric errors. - [`errors.FabricNativeUnavailableError`](./nemo_fabric.errors.md#class-fabricnativeunavailableerror): SDK call requires the PyO3 extension, but it is not installed or importable. - [`errors.FabricRuntimeError`](./nemo_fabric.errors.md#class-fabricruntimeerror): Failure while starting, invoking, stopping, or otherwise driving a runtime. diff --git a/docs/reference/api/python-library-reference/nemo_fabric.client.md b/docs/reference/api/python-library-reference/nemo_fabric.client.md index 8ea962d20..ff9706be7 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.client.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.client.md @@ -17,9 +17,9 @@ Native Python client for resolving and running NeMo Fabric agents. ## class `Fabric` Primary Python entrypoint for NeMo Fabric. -The client accepts either a path-backed agent package or a typed ``FabricConfig``. Path-backed sources select profiles by name; typed sources accept ordered ``FabricProfileConfig`` values and may use ``base_dir`` to resolve relative paths. All inspection and execution APIs return typed, read-only mapping models. +Every lifecycle method accepts a complete, typed ``FabricConfig`` plus an optional ``base_dir`` used to resolve relative paths. Compose variants in Python before calling the SDK. All inspection and execution APIs return typed, read-only mapping models. -``Fabric`` is native-only. The ``fabric`` CLI is a separate public surface over the same Rust core; SDK calls raise ``FabricNativeUnavailableError`` when the native extension is not installed. +``Fabric`` is native-only. The ``nemo-fabric`` CLI is backed by this public SDK; SDK calls raise ``FabricNativeUnavailableError`` when the native extension is not installed. See the Getting Started overview for runnable one-shot, typed-config, and multi-turn examples. @@ -33,9 +33,8 @@ See the Getting Started overview for runnable one-shot, typed-config, and multi- ```python doctor( - agent: 'AgentSource', - profiles: 'PathProfiles | TypedProfiles | None' = None, - base_dir: 'PathSource | None' = None + config: 'FabricConfig', + base_dir: 'str | PathLike[str] | None' = None ) → DoctorReport ``` @@ -47,9 +46,8 @@ Doctor checks the resolved adapter, capability mappings, and declared environmen **Args:** - - `agent`: Agent-package directory or config-file path, or a typed ``FabricConfig``. - - `profiles`: One profile name or an ordered sequence of names for a path-backed source. For a typed source, an ordered sequence of ``FabricProfileConfig`` values. - - `base_dir`: Base directory for resolving relative paths in a typed config. Valid only when ``agent`` is a typed config source. + - `config`: Complete typed ``FabricConfig``. + - `base_dir`: Base directory for resolving relative paths. @@ -70,23 +68,21 @@ Doctor checks the resolved adapter, capability mappings, and declared environmen ```python plan( - agent: 'AgentSource', - profiles: 'PathProfiles | TypedProfiles | None' = None, - base_dir: 'PathSource | None' = None + config: 'FabricConfig', + base_dir: 'str | PathLike[str] | None' = None ) → RunPlan ``` Resolve an agent source into an immutable execution plan. -Planning applies profiles, resolves the selected adapter, and reports optional runtime capabilities such as streaming, updates, and cancellation. It does not start the runtime. +Planning resolves the selected adapter and reports optional runtime capabilities such as streaming, updates, and cancellation. Planning does not start the runtime. **Args:** - - `agent`: Agent-package directory or config-file path, or a typed ``FabricConfig``. Raw mappings are not accepted. - - `profiles`: One profile name or an ordered sequence of names for a path-backed source. For a typed source, an ordered sequence of ``FabricProfileConfig`` values. - - `base_dir`: Base directory for resolving relative paths in a typed config. Valid only when ``agent`` is a typed config source. + - `config`: Complete typed ``FabricConfig``. Raw mappings are not accepted. + - `base_dir`: Base directory for resolving relative paths. @@ -97,7 +93,7 @@ Planning applies profiles, resolves the selected adapter, and reports optional r **Raises:** - - `FabricConfigError`: If the source, profile stack, config, or adapter resolution is invalid. + - `FabricConfigError`: If the config or adapter resolution is invalid. - `FabricNativeUnavailableError`: If the native extension is not installed. --- @@ -107,13 +103,12 @@ Planning applies profiles, resolves the selected adapter, and reports optional r ```python resolve( - agent: 'AgentSource', - profiles: 'PathProfiles | TypedProfiles | None' = None, - base_dir: 'PathSource | None' = None + config: 'FabricConfig', + base_dir: 'str | PathLike[str] | None' = None ) → EffectiveConfig ``` -Resolve an agent source and its ordered profile overlays. +Resolve an agent source. Resolution validates and normalizes configuration but does not resolve an adapter or compute runtime capabilities. Use ``plan()`` when those execution details are required. @@ -121,9 +116,8 @@ Resolution validates and normalizes configuration but does not resolve an adapte **Args:** - - `agent`: Agent-package directory or config-file path, or a typed ``FabricConfig``. Raw mappings are not accepted; convert them with ``FabricConfig.from_mapping()``. - - `profiles`: One profile name or an ordered sequence of names for a path-backed source. For a typed source, an ordered sequence of ``FabricProfileConfig`` values. - - `base_dir`: Base directory for resolving relative paths in a typed config. Valid only when ``agent`` is a typed config source. + - `config`: Complete typed ``FabricConfig``. Raw mappings are not accepted; convert them with ``FabricConfig.from_mapping()``. + - `base_dir`: Base directory for resolving relative paths. @@ -134,7 +128,7 @@ Resolution validates and normalizes configuration but does not resolve an adapte **Raises:** - - `FabricConfigError`: If the source, profile stack, or resolved config is invalid. + - `FabricConfigError`: If the config is invalid. - `FabricNativeUnavailableError`: If the native extension is not installed. --- @@ -144,9 +138,8 @@ Resolution validates and normalizes configuration but does not resolve an adapte ```python run( - agent: 'AgentSource', - profiles: 'PathProfiles | TypedProfiles | None' = None, - base_dir: 'PathSource | None' = None, + config: 'FabricConfig', + base_dir: 'str | PathLike[str] | None' = None, input: 'Any' = None, request: 'RunRequest | None' = None ) → RunResult @@ -160,9 +153,8 @@ Execute one complete start, invoke, and stop lifecycle. **Args:** - - `agent`: Agent-package directory or config-file path, or a typed ``FabricConfig``. - - `profiles`: One profile name or an ordered sequence of names for a path-backed source. For a typed source, an ordered sequence of ``FabricProfileConfig`` values. - - `base_dir`: Base directory for resolving relative paths in a typed config. Valid only when ``agent`` is a typed config source. + - `config`: Complete typed ``FabricConfig``. + - `base_dir`: Base directory for resolving relative paths. - `input`: JSON-compatible invocation input. - `request`: Complete validated ``RunRequest``. @@ -186,9 +178,8 @@ Execute one complete start, invoke, and stop lifecycle. ```python start_runtime( - agent: 'AgentSource', - profiles: 'PathProfiles | TypedProfiles | None' = None, - base_dir: 'PathSource | None' = None, + config: 'FabricConfig', + base_dir: 'str | PathLike[str] | None' = None, overrides: 'Mapping[str, Any] | None' = None ) → Runtime ``` @@ -201,9 +192,8 @@ Each call starts a new logical runtime. Runtime-scoped overrides are recursively **Args:** - - `agent`: Agent-package directory or config-file path, or a typed ``FabricConfig``. - - `profiles`: One profile name or an ordered sequence of names for a path-backed source. For a typed source, an ordered sequence of ``FabricProfileConfig`` values. - - `base_dir`: Base directory for resolving relative paths in a typed config. Valid only when ``agent`` is a typed config source. + - `config`: Complete typed ``FabricConfig``. + - `base_dir`: Base directory for resolving relative paths. - `overrides`: JSON-compatible overrides applied to every invocation in the runtime unless superseded by invocation overrides. diff --git a/docs/reference/api/python-library-reference/nemo_fabric.errors.md b/docs/reference/api/python-library-reference/nemo_fabric.errors.md index 61fbf73e9..44bad6a22 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.errors.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.errors.md @@ -61,7 +61,7 @@ Initialize a structured Fabric exception. ## class `FabricConfigError` -Invalid SDK input, request shape, profile stack, or resolved config. +Invalid SDK input, request shape, factory, or resolved config. ### method `__init__` diff --git a/docs/reference/api/python-library-reference/nemo_fabric.models.md b/docs/reference/api/python-library-reference/nemo_fabric.models.md index 540cb5820..2736be7ce 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.models.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.models.md @@ -1361,66 +1361,6 @@ Return a detached JSON-compatible mapping for Rust/core calls. --- -## class `ProfileRegistryConfig` -Profile discovery config for portable file-backed agent packages. - - ---- - -### property extra_fields - -Return fields preserved by the extension point for this model. - ---- - -### property model_extra - -Get extra fields set during validation. - - - -**Returns:** - A dictionary of extra fields, or `None` if `config.extra` is not set to `"allow"`. - ---- - -### property model_fields_set - -Returns the set of fields that have been explicitly set on this model instance. - - - -**Returns:** - A set of strings representing the fields that have been set, i.e. that were not filled from defaults. - - - ---- - - -### classmethod `from_mapping` - -```python -from_mapping(value: 'Mapping[str, Any]') → Self -``` - -Validate a mapping using this Pydantic model. - ---- - - -### method `to_mapping` - -```python -to_mapping() → dict[str, Any] -``` - -Return a detached JSON-compatible mapping for Rust/core calls. - - ---- - - ## class `ToolsConfig` Harness-neutral tool capability configuration. @@ -1620,66 +1560,6 @@ Return a detached mapping matching the Rust ``FabricConfig`` schema. --- -## class `FabricProfileConfig` -Typed profile overlay used when a Python caller wants file-style overlays. - - ---- - -### property extra_fields - -Return fields preserved by the extension point for this model. - ---- - -### property model_extra - -Get extra fields set during validation. - - - -**Returns:** - A dictionary of extra fields, or `None` if `config.extra` is not set to `"allow"`. - ---- - -### property model_fields_set - -Returns the set of fields that have been explicitly set on this model instance. - - - -**Returns:** - A set of strings representing the fields that have been set, i.e. that were not filled from defaults. - - - ---- - - -### classmethod `from_mapping` - -```python -from_mapping(value: 'Mapping[str, Any]') → Self -``` - -Validate a mapping using this Pydantic model. - ---- - - -### method `to_mapping` - -```python -to_mapping() → dict[str, Any] -``` - -Return a detached JSON-compatible mapping for Rust/core calls. - - ---- - - ## class `RunRequest` One validated Fabric invocation request. diff --git a/docs/reference/api/python-library-reference/nemo_fabric.types.md b/docs/reference/api/python-library-reference/nemo_fabric.types.md index 6d70a2676..b18d332fc 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.types.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.types.md @@ -156,17 +156,14 @@ Return a detached, JSON-compatible mapping for serialization. ## class `EffectiveConfig` -Immutable result of config loading and ordered profile application. +Immutable result of resolving a complete typed config. **Attributes:** - `agent_name`: Resolved agent name. - - `profiles`: Applied profile names in caller order. - - `agent_root`: Root directory of the path-backed agent source. - - `config_path`: Source config path, or ``None`` for typed configs. - - `config_root`: Base directory used to resolve relative paths. + - `base_dir`: Base directory used to resolve relative paths. - `config`: Fully resolved typed ``FabricConfig``. @@ -235,7 +232,6 @@ Immutable execution plan produced before a runtime is started. - `effective_config`: Resolved configuration snapshot. - `agent_name`: Resolved agent name. - - `profiles`: Applied profile names in caller order. - `adapter`: Resolved adapter identity. - `capabilities`: Operations declared by the resolved runtime. @@ -373,7 +369,6 @@ Aggregate preflight diagnostics for a resolved run plan. **Attributes:** - `agent_name`: Resolved agent name. - - `profiles`: Applied profile names in caller order. - `status`: Aggregate outcome: ``pass``, ``warn``, or ``fail``. - `checks`: Ordered ``DoctorCheck`` results. @@ -933,7 +928,6 @@ The model is both attribute-accessible and mapping-compatible. A harness failure **Attributes:** - `agent_name`: Resolved agent name. - - `profiles`: Applied profile names. - `harness`: Stable harness identifier. - `adapter_kind`: Adapter execution mechanism. - `adapter_id`: Fabric adapter identifier. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx index 9209ea0f1..5b133e34d 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx @@ -2,7 +2,7 @@ title: "Enum Capability Kind" sidebar-title: "CapabilityKind" description: "Capability kind." -position: 42 +position: 40 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx index 4727c3d29..d51db854e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx @@ -2,7 +2,7 @@ title: "Enum Capability Target" sidebar-title: "CapabilityTarget" description: "Capability routing target." -position: 43 +position: 41 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-fabricdocument.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-fabricdocument.mdx deleted file mode 100644 index 9f52c7e1c..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-fabricdocument.mdx +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: "Enum Fabric Document" -sidebar-title: "FabricDocument" -description: "A loaded Fabric document with resolved source path and agent root." -position: 16 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
PathBuf,\n        root: PathBuf,\n        config: FabricConfig,\n    },\n}"}} />
- -A loaded Fabric document with resolved source path and agent root. - -## Variants - -### `FabricConfig` - -
- -Unified Fabric agent config. - -#### Fields - -### `path: PathBuf` - -Path to the config file. - -### `root: PathBuf` - -Root used for resolving relative paths. - -### `config: FabricConfig` - -Parsed config. - -## Trait Implementations - -### `impl Clone for FabricDocument` - -
Clone for FabricDocument"}} />
- -#### `clone` - -
clone(&self) -> FabricDocument"}} />
- -#### `clone_from` - -
clone_from(&mut self, source: &Self)"}} />
- -### `impl Debug for FabricDocument` - -
Debug for FabricDocument"}} />
- -#### `fmt` - -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
- -### `impl<'de> Deserialize<'de> for FabricDocument` - -
Deserialize<'de> for FabricDocument"}} />
- -#### `deserialize` - -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
- -### `impl JsonSchema for FabricDocument` - -
FabricDocument"}} />
- -#### `schema_name` - -
Cow<'static, str>"}} />
- -#### `schema_id` - -
Cow<'static, str>"}} />
- -#### `json_schema` - -
- -#### `inline_schema` - -
bool"}} />
- -### `impl PartialEq for FabricDocument` - -
PartialEq for FabricDocument"}} />
- -#### `eq` - -
eq(&self, other: &FabricDocument) -> bool"}} />
- -#### `ne` - -
ne(&self, other: &Rhs) -> bool"}} />
- -### `impl Serialize for FabricDocument` - -
Serialize for FabricDocument"}} />
- -#### `serialize` - -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
- -### `impl StructuralPartialEq for FabricDocument` - -
StructuralPartialEq for FabricDocument"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx index dce2481c1..a75f60441 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx @@ -2,7 +2,7 @@ title: "Enum McpExposure" sidebar-title: "McpExposure" description: "MCP exposure strategy." -position: 19 +position: 18 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx index 6e1dd321f..59e753c33 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atif Storage Config" sidebar-title: "RelayAtifStorageConfig" description: "Relay ATIF remote storage configuration." -position: 48 +position: 45 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx index f145956be..a5623ec0d 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Endpoint Field Name Policy" sidebar-title: "RelayAtofEndpointFieldNamePolicy" description: "Relay ATOF endpoint field-name policy." -position: 49 +position: 46 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointtransport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointtransport.mdx index ab2682f91..f1d86d780 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointtransport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointtransport.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Endpoint Transport" sidebar-title: "RelayAtofEndpointTransport" description: "Relay ATOF endpoint transport." -position: 50 +position: 47 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx index ee3ac693a..53dc69eb4 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Mode" sidebar-title: "RelayAtofMode" description: "Relay ATOF file mode." -position: 51 +position: 48 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx index 2fd040adb..bf25bac43 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Otlp Transport" sidebar-title: "RelayOtlpTransport" description: "Relay OTLP transport." -position: 52 +position: 49 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx index 4669a0453..4031718ba 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Unsupported Behavior" sidebar-title: "RelayUnsupportedBehavior" description: "Relay unsupported/unknown config handling." -position: 53 +position: 50 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx index 2f06bcd9e..a85905a89 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx @@ -2,7 +2,7 @@ title: "Enum Resolution Strategy" sidebar-title: "ResolutionStrategy" description: "Adapter install or availability strategy." -position: 24 +position: 22 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx index 851ae8829..9c578453a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx @@ -2,7 +2,7 @@ title: "Enum Telemetry Provider" sidebar-title: "TelemetryProvider" description: "Telemetry runtime provider." -position: 33 +position: 31 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx index 058809419..40e27412e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx @@ -2,7 +2,7 @@ title: "Function load_adapter_descriptor" sidebar-title: "load_adapter_descriptor" description: "Load an adapter descriptor from JSON package metadata." -position: 35 +position: 33 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-fabric-document.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-fabric-document.mdx deleted file mode 100644 index 64820dfa6..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-fabric-document.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "Function load_fabric_document" -sidebar-title: "load_fabric_document" -description: "Load a Fabric document from an agent directory or single agent config." -position: 36 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
AsRef<Path>) -> Result<FabricDocument>"}} />
- -Load a Fabric document from an agent directory or single agent config. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config.mdx index fe82db846..edf9c8ca6 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config.mdx @@ -1,14 +1,14 @@ --- title: "Function resolve_effective_config_from_config" sidebar-title: "resolve_effective_config_from_config" -description: "Resolve typed config/profile overlays into merged effective config." -position: 38 +description: "Resolve a typed Fabric config into an effective config." +position: 34 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
FabricConfig,\n    profiles: &[ProfileConfig],\n    context: ResolveContext,\n) -> Result<EffectiveConfig>"}} />
+
FabricConfig,\n    context: ResolveContext,\n) -> Result<EffectiveConfig>"}} />
-Resolve typed config/profile overlays into merged effective config. +Resolve a typed Fabric config into an effective config. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-with-profiles.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-with-profiles.mdx deleted file mode 100644 index 52fc900eb..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-with-profiles.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "Function resolve_effective_config_with_profiles" -sidebar-title: "resolve_effective_config_with_profiles" -description: "Resolve an agent directory or single agent config with ordered profiles into merged effective config." -position: 39 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
AsRef<Path>,\n    profiles: &[String],\n) -> Result<EffectiveConfig>"}} />
- -Resolve an agent directory or single agent config with ordered profiles into merged effective config. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config.mdx deleted file mode 100644 index 8665e8029..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "Function resolve_effective_config" -sidebar-title: "resolve_effective_config" -description: "Resolve an agent directory or single agent config into merged effective config." -position: 37 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
AsRef<Path>,\n    profile: Option<&str>,\n) -> Result<EffectiveConfig>"}} />
- -Resolve an agent directory or single agent config into merged effective config. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx index 494cb76f1..3412e2fe7 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx @@ -1,16 +1,16 @@ --- title: "Function resolve_run_plan_from_config" sidebar-title: "resolve_run_plan_from_config" -description: "Resolve a typed Fabric config and typed profile overlays into a runnable plan." -position: 41 +description: "Resolve a typed Fabric config into a runnable plan." +position: 35 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
FabricConfig,\n    profiles: &[ProfileConfig],\n    context: ResolveContext,\n) -> Result<RunPlan>"}} />
+
FabricConfig,\n    context: ResolveContext,\n) -> Result<RunPlan>"}} />
-Resolve a typed Fabric config and typed profile overlays into a runnable plan. +Resolve a typed Fabric config into a runnable plan. -This is the SDK-facing entrypoint. File and YAML loading stays outside this function; callers provide already-typed configs and the path context used for resolving relative paths. +Callers provide an already-composed typed config and the explicit base directory used for resolving relative paths. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx index 21299f44d..099b97497 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx @@ -2,7 +2,7 @@ title: "Function resolve_run_plan_from_effective_config" sidebar-title: "resolve_run_plan_from_effective_config" description: "Resolve execution planning metadata from merged effective config." -position: 42 +position: 36 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-with-profiles.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-with-profiles.mdx deleted file mode 100644 index 0b28b5fca..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-with-profiles.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "Function resolve_run_plan_with_profiles" -sidebar-title: "resolve_run_plan_with_profiles" -description: "Resolve an agent directory or single agent config with ordered profile application." -position: 43 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
AsRef<Path>,\n    profiles: &[String],\n) -> Result<RunPlan>"}} />
- -Resolve an agent directory or single agent config with ordered profile application. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan.mdx deleted file mode 100644 index fd80030da..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "Function resolve_run_plan" -sidebar-title: "resolve_run_plan" -description: "Resolve an agent directory or single agent config into a runnable plan." -position: 40 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
AsRef<Path>,\n    profile: Option<&str>,\n) -> Result<RunPlan>"}} />
- -Resolve an agent directory or single agent config into a runnable plan. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-validate-agent-directory.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-validate-agent-directory.mdx deleted file mode 100644 index 7706821b3..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-validate-agent-directory.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "Function validate_agent_directory" -sidebar-title: "validate_agent_directory" -description: "Validate an agent directory or config, including discoverable profile YAMLs." -position: 44 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
AsRef<Path>) -> Result<()>"}} />
- -Validate an agent directory or config, including discoverable profile YAMLs. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx index 45fd6b667..2a3b78bae 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx @@ -2,7 +2,7 @@ title: "Module config" sidebar-title: "config" description: "Fabric config models and loading helpers." -position: 76 +position: 68 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -21,7 +21,7 @@ Fabric config models and loading helpers. - [CapabilityPlan](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityplan): Resolved capability configuration. - [CapabilityRoute](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityroute): One capability routing decision. - [CapabilityTargetPlan](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilitytargetplan): Capabilities routed to one target. -- [EffectiveConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig): Merged Fabric config after applying selected profiles. +- [EffectiveConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig): Resolved Fabric config with explicit path context. - [EnvironmentConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentconfig): Execution environment configuration. - [EnvironmentPlan](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentplan): Resolved environment plan. - [FabricConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig): Versioned Fabric agent config. @@ -31,8 +31,6 @@ Fabric config models and loading helpers. - [McpServerPlan](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan): Resolved MCP server exposure. - [MetadataConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig): Human-readable metadata. - [ModelConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig): Model configuration. -- [ProfileConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-profileconfig): Profile config applied on top of a Fabric config. -- [ProfileRegistryConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-profileregistryconfig): Profile discovery config for curated package profiles. - [RelayAtifConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig): Relay ATIF export configuration. - [RelayAtofConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig): Relay ATOF export configuration. - [RelayAtofEndpointConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofendpointconfig): Relay ATOF endpoint configuration. @@ -61,7 +59,6 @@ Fabric config models and loading helpers. - [CapabilityTarget](/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget): Capability routing target. - [ControlLocation](/reference/api/rust-library-reference/nemo-fabric-core/config/enum-controllocation): Where Fabric control code runs relative to the environment. - [EnvironmentOwnership](/reference/api/rust-library-reference/nemo-fabric-core/config/enum-environmentownership): Whether Fabric owns the underlying environment resource. -- [FabricDocument](/reference/api/rust-library-reference/nemo-fabric-core/config/enum-fabricdocument): A loaded Fabric document with resolved source path and agent root. - [McpExposure](/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure): MCP exposure strategy. - [RelayAtifStorageConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig): Relay ATIF remote storage configuration. - [RelayAtofEndpointFieldNamePolicy](/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointfieldnamepolicy): Relay ATOF endpoint field-name policy. @@ -79,12 +76,6 @@ Fabric config models and loading helpers. ## Functions - [load_adapter_descriptor](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor): Load an adapter descriptor from JSON package metadata. -- [load_fabric_document](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-fabric-document): Load a Fabric document from an agent directory or single agent config. -- [resolve_effective_config](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config): Resolve an agent directory or single agent config into merged effective config. -- [resolve_effective_config_from_config](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config): Resolve typed config/profile overlays into merged effective config. -- [resolve_effective_config_with_profiles](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-with-profiles): Resolve an agent directory or single agent config with ordered profiles into merged effective config. -- [resolve_run_plan](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan): Resolve an agent directory or single agent config into a runnable plan. -- [resolve_run_plan_from_config](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config): Resolve a typed Fabric config and typed profile overlays into a runnable plan. +- [resolve_effective_config_from_config](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config): Resolve a typed Fabric config into an effective config. +- [resolve_run_plan_from_config](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config): Resolve a typed Fabric config into a runnable plan. - [resolve_run_plan_from_effective_config](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-effective-config): Resolve execution planning metadata from merged effective config. -- [resolve_run_plan_with_profiles](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-with-profiles): Resolve an agent directory or single agent config with ordered profile application. -- [validate_agent_directory](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-validate-agent-directory): Validate an agent directory or config, including discoverable profile YAMLs. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx index ffc2d9c9f..e657c33a8 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx @@ -1,7 +1,7 @@ --- title: "Struct Effective Config" sidebar-title: "EffectiveConfig" -description: "Merged Fabric config after applying selected profiles." +description: "Resolved Fabric config with explicit path context." position: 11 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. @@ -9,9 +9,9 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub profiles: Vec<String>,\n    pub agent_root: PathBuf,\n    pub config_path: PathBuf,\n    pub config_root: PathBuf,\n    pub config: FabricConfig,\n}"}} />
+
String,\n    pub base_dir: PathBuf,\n    pub config: FabricConfig,\n}"}} />
-Merged Fabric config after applying selected profiles. +Resolved Fabric config with explicit path context. ## Fields @@ -19,25 +19,13 @@ Merged Fabric config after applying selected profiles. Stable agent name. -### `profiles: Vec` +### `base_dir: PathBuf` -Ordered selected profiles. - -### `agent_root: PathBuf` - -Root used to resolve agent package paths. - -### `config_path: PathBuf` - -Resolved Fabric config path. - -### `config_root: PathBuf` - -Root used to resolve config-local paths. +Base directory used to resolve relative config paths. ### `config: FabricConfig` -Merged Fabric config with authoring-time profile discovery removed. +Complete typed Fabric config. ## Trait Implementations diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx index 04b88254e..5e4e357a0 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub metadata: MetadataConfig,\n    pub harness: HarnessConfig,\n    pub models: BTreeMap<String, ModelConfig>,\n    pub runtime: RuntimeConfig,\n    pub environment: Option<EnvironmentConfig>,\n    pub tools: Option<ToolsConfig>,\n    pub skills: Option<SkillConfig>,\n    pub mcp: Option<McpConfig>,\n    pub telemetry: Option<TelemetryConfig>,\n    pub relay: Option<RelayConfig>,\n    pub profiles: ProfileRegistryConfig,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub metadata: MetadataConfig,\n    pub harness: HarnessConfig,\n    pub models: BTreeMap<String, ModelConfig>,\n    pub runtime: RuntimeConfig,\n    pub environment: Option<EnvironmentConfig>,\n    pub tools: Option<ToolsConfig>,\n    pub skills: Option<SkillConfig>,\n    pub mcp: Option<McpConfig>,\n    pub telemetry: Option<TelemetryConfig>,\n    pub relay: Option<RelayConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Versioned Fabric agent config. @@ -59,10 +59,6 @@ Telemetry configuration. First-class NeMo Relay integration configuration. -### `profiles: ProfileRegistryConfig` - -Optional profile discovery config. - ### `extensions: BTreeMap` Additive fields not yet recognized by this core version. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx index 111869755..fd2177594 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Harness Config" sidebar-title: "HarnessConfig" description: "Harness selection." -position: 17 +position: 16 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx index 33a7d5e50..9c6b81189 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx @@ -2,7 +2,7 @@ title: "Struct McpConfig" sidebar-title: "McpConfig" description: "MCP capability configuration." -position: 18 +position: 17 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx index c51dbefb5..8ab22f57e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx @@ -2,7 +2,7 @@ title: "Struct McpServer Plan" sidebar-title: "McpServerPlan" description: "Resolved MCP server exposure." -position: 20 +position: 19 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx index 40ba1d635..f83f18a1c 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Metadata Config" sidebar-title: "MetadataConfig" description: "Human-readable metadata." -position: 21 +position: 20 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx index 43673736a..579ebc62e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Model Config" sidebar-title: "ModelConfig" description: "Model configuration." -position: 22 +position: 21 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-profileconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-profileconfig.mdx deleted file mode 100644 index 94b6c8443..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-profileconfig.mdx +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: "Struct Profile Config" -sidebar-title: "ProfileConfig" -description: "Profile config applied on top of a Fabric config." -position: 23 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
Option<String>,\n    pub name: Option<String>,\n    pub description: Option<String>,\n    pub overlay: BTreeMap<String, Value>,\n}"}} />
- -Profile config applied on top of a Fabric config. - -## Fields - -### `schema_version: Option` - -Optional profile schema version. - -### `name: Option` - -Optional profile name used for directory discovery. - -### `description: Option` - -Optional profile description. - -### `overlay: BTreeMap` - -Raw config fields recursively merged over the base config. - -## Trait Implementations - -### `impl Clone for ProfileConfig` - -
Clone for ProfileConfig"}} />
- -#### `clone` - -
clone(&self) -> ProfileConfig"}} />
- -#### `clone_from` - -
clone_from(&mut self, source: &Self)"}} />
- -### `impl Debug for ProfileConfig` - -
Debug for ProfileConfig"}} />
- -#### `fmt` - -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
- -### `impl Default for ProfileConfig` - -
Default for ProfileConfig"}} />
- -#### `default` - -
default() -> ProfileConfig"}} />
- -### `impl<'de> Deserialize<'de> for ProfileConfig` - -
Deserialize<'de> for ProfileConfig"}} />
- -#### `deserialize` - -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
- -### `impl JsonSchema for ProfileConfig` - -
ProfileConfig"}} />
- -#### `schema_name` - -
Cow<'static, str>"}} />
- -#### `json_schema` - -
- -#### `inline_schema` - -
bool"}} />
- -#### `schema_id` - -
Cow<'static, str>"}} />
- -### `impl PartialEq for ProfileConfig` - -
PartialEq for ProfileConfig"}} />
- -#### `eq` - -
eq(&self, other: &ProfileConfig) -> bool"}} />
- -#### `ne` - -
ne(&self, other: &Rhs) -> bool"}} />
- -### `impl Serialize for ProfileConfig` - -
Serialize for ProfileConfig"}} />
- -#### `serialize` - -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
- -### `impl StructuralPartialEq for ProfileConfig` - -
StructuralPartialEq for ProfileConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-profileregistryconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-profileregistryconfig.mdx deleted file mode 100644 index 86d980c6d..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-profileregistryconfig.mdx +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: "Struct Profile Registry Config" -sidebar-title: "ProfileRegistryConfig" -description: "Profile discovery config for curated package profiles." -position: 20 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
Vec<PathBuf>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
- -Profile discovery config for curated package profiles. - -## Fields - -### `directories: Vec` - -Directories searched when a caller selects a profile by name. - -### `extensions: BTreeMap` - -Additive profile-discovery fields. - -## Trait Implementations - -### `impl Clone for ProfileRegistryConfig` - -
Clone for ProfileRegistryConfig"}} />
- -#### `clone` - -
clone(&self) -> ProfileRegistryConfig"}} />
- -#### `clone_from` - -
clone_from(&mut self, source: &Self)"}} />
- -### `impl Debug for ProfileRegistryConfig` - -
Debug for ProfileRegistryConfig"}} />
- -#### `fmt` - -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
- -### `impl Default for ProfileRegistryConfig` - -
Default for ProfileRegistryConfig"}} />
- -#### `default` - -
default() -> ProfileRegistryConfig"}} />
- -### `impl<'de> Deserialize<'de> for ProfileRegistryConfig` - -
Deserialize<'de> for ProfileRegistryConfig"}} />
- -#### `deserialize` - -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
- -### `impl JsonSchema for ProfileRegistryConfig` - -
ProfileRegistryConfig"}} />
- -#### `schema_name` - -
Cow<'static, str>"}} />
- -#### `schema_id` - -
Cow<'static, str>"}} />
- -#### `json_schema` - -
- -#### `inline_schema` - -
bool"}} />
- -### `impl PartialEq for ProfileRegistryConfig` - -
PartialEq for ProfileRegistryConfig"}} />
- -#### `eq` - -
eq(&self, other: &ProfileRegistryConfig) -> bool"}} />
- -#### `ne` - -
ne(&self, other: &Rhs) -> bool"}} />
- -### `impl Serialize for ProfileRegistryConfig` - -
Serialize for ProfileRegistryConfig"}} />
- -#### `serialize` - -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
- -### `impl StructuralPartialEq for ProfileRegistryConfig` - -
StructuralPartialEq for ProfileRegistryConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx index 4c28fe811..93aa8ea45 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Atif Config" sidebar-title: "RelayAtifConfig" description: "Relay ATIF export configuration." -position: 21 +position: 19 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx index c667bfddf..01d50a87d 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Atof Config" sidebar-title: "RelayAtofConfig" description: "Relay ATOF export configuration." -position: 22 +position: 20 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofendpointconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofendpointconfig.mdx index 1977b524b..eb203ad7e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofendpointconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofendpointconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Atof Endpoint Config" sidebar-title: "RelayAtofEndpointConfig" description: "Relay ATOF endpoint configuration." -position: 23 +position: 21 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx index dc0043292..3d482e200 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Component Config" sidebar-title: "RelayComponentConfig" description: "Generic NeMo Relay plugin component configuration." -position: 24 +position: 22 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx index 1b8b6bff5..4283ccfb7 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Config" sidebar-title: "RelayConfig" description: "NeMo Relay integration configuration." -position: 25 +position: 23 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx index 3011b139d..74bfd08cb 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Config Policy" sidebar-title: "RelayConfigPolicy" description: "Relay validation policy." -position: 26 +position: 24 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx index 785fdead5..77ce17e4a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Observability Config" sidebar-title: "RelayObservabilityConfig" description: "NeMo Relay observability component configuration." -position: 27 +position: 25 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx index 2965e9161..ad8fc12c2 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Otlp Config" sidebar-title: "RelayOtlpConfig" description: "Relay OpenTelemetry/OpenInference export configuration." -position: 28 +position: 26 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx index 42b530b0e..c8c147843 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx @@ -2,30 +2,22 @@ title: "Struct Resolve Context" sidebar-title: "ResolveContext" description: "Source context used when resolving an in-memory Fabric config." -position: 25 +position: 23 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
PathBuf,\n    pub config_path: PathBuf,\n    pub config_root: PathBuf,\n}"}} />
+
PathBuf,\n}"}} />
Source context used when resolving an in-memory Fabric config. ## Fields -### `agent_root: PathBuf` +### `base_dir: PathBuf` -Root used to resolve agent package paths. - -### `config_path: PathBuf` - -Path recorded as config provenance in the run plan. - -### `config_root: PathBuf` - -Root used to resolve config-local paths. +Base directory used to resolve relative config paths. ## Implementations @@ -33,17 +25,11 @@ Root used to resolve config-local paths.
ResolveContext"}} />
-#### `from_agent_root` - -
Into<PathBuf>) -> Self"}} />
- -Build a context for an agent package root. - -#### `from_config_path` +#### `new` -
Into<PathBuf>,\n    root: impl Into<PathBuf>,\n) -> Self"}} />
+
Into<PathBuf>) -> Self"}} />
-Build a context for a config file and its config root. +Build a context with an explicit base directory. ## Trait Implementations diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx index 0fa3450f7..97724b25a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx @@ -2,7 +2,7 @@ title: "Struct Resolved Adapter Descriptor" sidebar-title: "ResolvedAdapterDescriptor" description: "Adapter descriptor selected for a run plan." -position: 26 +position: 24 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx index c0c331303..53dc18633 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx @@ -2,14 +2,14 @@ title: "Struct RunPlan" sidebar-title: "RunPlan" description: "Resolved Fabric run plan." -position: 27 +position: 25 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
EffectiveConfig,\n    pub agent_name: String,\n    pub profiles: Vec<String>,\n    pub adapter_descriptor: Option<ResolvedAdapterDescriptor>,\n    pub resolution: Option<ResolutionStrategy>,\n    pub environment_plan: Option<EnvironmentPlan>,\n    pub capability_plan: CapabilityPlan,\n    pub capabilities: RuntimeCapabilities,\n    pub telemetry_plan: Option<TelemetryPlan>,\n    pub agent_root: PathBuf,\n    pub config_path: PathBuf,\n    pub config_root: PathBuf,\n    pub config: FabricConfig,\n}"}} />
+
EffectiveConfig,\n    pub agent_name: String,\n    pub adapter_descriptor: Option<ResolvedAdapterDescriptor>,\n    pub resolution: Option<ResolutionStrategy>,\n    pub environment_plan: Option<EnvironmentPlan>,\n    pub capability_plan: CapabilityPlan,\n    pub capabilities: RuntimeCapabilities,\n    pub telemetry_plan: Option<TelemetryPlan>,\n    pub base_dir: PathBuf,\n    pub config: FabricConfig,\n}"}} />
Resolved Fabric run plan. @@ -17,16 +17,12 @@ Resolved Fabric run plan. ### `effective_config: EffectiveConfig` -Merged config and provenance used as the base for this plan. +Resolved config used as the base for this plan. ### `agent_name: String` Stable agent name. -### `profiles: Vec` - -Ordered selected profiles. - ### `adapter_descriptor: Option` Adapter descriptor resolved for this plan, when configured. @@ -51,21 +47,13 @@ Lifecycle behavior implemented by the selected runtime path. Resolved telemetry pass-through plan. -### `agent_root: PathBuf` - -Root used to resolve agent package paths. - -### `config_path: PathBuf` - -Resolved Fabric config path. - -### `config_root: PathBuf` +### `base_dir: PathBuf` -Root used to resolve config-local paths. +Base directory used to resolve relative config paths. ### `config: FabricConfig` -Resolved Fabric profile config. +Complete typed Fabric config. ## Trait Implementations diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx index 2f73a4d20..639ef5796 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx @@ -2,7 +2,7 @@ title: "Struct Runtime Capabilities" sidebar-title: "RuntimeCapabilities" description: "Lifecycle behavior implemented by a resolved runtime path." -position: 28 +position: 26 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx index 8b51cafcc..022813485 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Runtime Config" sidebar-title: "RuntimeConfig" description: "Runtime input/output contract." -position: 29 +position: 27 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx index 8ea5d934d..bf85cefbd 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Skill Config" sidebar-title: "SkillConfig" description: "Skill capability configuration." -position: 30 +position: 28 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx index 9338bbfb4..c35e2db57 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Telemetry Config" sidebar-title: "TelemetryConfig" description: "Telemetry configuration." -position: 31 +position: 29 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx index eee9a11e3..767112630 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx @@ -2,7 +2,7 @@ title: "Struct Telemetry Plan" sidebar-title: "TelemetryPlan" description: "Resolved telemetry plan." -position: 32 +position: 30 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx index 67483880a..afa21e960 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Telemetry Provider Config" sidebar-title: "TelemetryProviderConfig" description: "Provider-specific telemetry configuration." -position: 34 +position: 32 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx index 1d39c422e..a3e932752 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Tools Config" sidebar-title: "ToolsConfig" description: "Harness-neutral tool capability configuration." -position: 38 +position: 36 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx index fa1ed8399..f46908102 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx @@ -2,7 +2,7 @@ title: "Struct Tools Plan" sidebar-title: "ToolsPlan" description: "Normalized tool policy for a run." -position: 39 +position: 37 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx index 4bdf73ab3..50a55eccd 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx @@ -2,7 +2,7 @@ title: "Enum Doctor Status" sidebar-title: "DoctorStatus" description: "Diagnostic status." -position: 47 +position: 39 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx index 64841c884..8ddbe6d24 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx @@ -2,7 +2,7 @@ title: "Function doctor_plan" sidebar-title: "doctor_plan" description: "Inspect a resolved run plan without mutating the environment." -position: 48 +position: 40 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx index c409cd201..b33235362 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx @@ -2,7 +2,7 @@ title: "Module doctor" sidebar-title: "doctor" description: "Plan diagnostics for Fabric." -position: 77 +position: 69 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx index ade547253..d3d3d0190 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx @@ -2,7 +2,7 @@ title: "Struct Doctor Check" sidebar-title: "DoctorCheck" description: "Diagnostic check result." -position: 45 +position: 37 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx index 14a0150e4..5f71661de 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx @@ -2,14 +2,14 @@ title: "Struct Doctor Report" sidebar-title: "DoctorReport" description: "Diagnostic report for a resolved run plan." -position: 46 +position: 38 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub profiles: Vec<String>,\n    pub status: DoctorStatus,\n    pub checks: Vec<DoctorCheck>,\n}"}} />
+
String,\n    pub status: DoctorStatus,\n    pub checks: Vec<DoctorCheck>,\n}"}} />
Diagnostic report for a resolved run plan. @@ -19,10 +19,6 @@ Diagnostic report for a resolved run plan. Agent name. -### `profiles: Vec` - -Ordered profiles applied to the inspected plan. - ### `status: DoctorStatus` Overall status. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx index 8f6cdeecd..0df6d8a6f 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx @@ -2,14 +2,14 @@ title: "Enum Fabric Error" sidebar-title: "FabricError" description: "Errors raised by Fabric config loading and validation." -position: 49 +position: 41 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
PathBuf),\n    MissingEntrypoint(PathBuf),\n    UnsupportedExtension(PathBuf),\n    UnknownProfile {\n        profile: String,\n        agent: String,\n        available: Vec<String>,\n    },\n    ProfileError {\n        path: PathBuf,\n        message: String,\n    },\n    UnknownAdapter {\n        adapter_id: String,\n        available: Vec<String>,\n    },\n    AdapterDescriptorMismatch {\n        path: PathBuf,\n        field: &'static str,\n        expected: String,\n        actual: String,\n    },\n    AdapterDescriptorUnsupported {\n        adapter_id: String,\n        field: &'static str,\n        value: String,\n    },\n    InvalidAdapterDescriptor {\n        path: PathBuf,\n        message: String,\n    },\n    UnknownSchema {\n        schema: String,\n        available: Vec<String>,\n    },\n    ProfileTargetNotConfig {\n        profile: String,\n        path: PathBuf,\n    },\n    ProfileSelectionNotSupported(PathBuf),\n    UnsupportedRuntimeAdapter {\n        harness: String,\n        adapter_kind: AdapterKind,\n    },\n    UnsupportedToolsPolicy {\n        harness: String,\n        reason: String,\n    },\n    RuntimeHandleMismatch {\n        field: &'static str,\n        expected: String,\n        actual: String,\n        runtime_id: String,\n    },\n    UnsupportedEnvironmentProvider {\n        provider: String,\n        adapter_kind: AdapterKind,\n    },\n    InvalidProcessSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    InvalidPythonSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    PythonInterpreterUnavailable {\n        path: PathBuf,\n        origin: String,\n        reason: String,\n    },\n    ProcessRunner {\n        command: String,\n        source: Error,\n    },\n    SerializeJson(Error),\n    Read {\n        path: PathBuf,\n        source: Error,\n    },\n    Write {\n        path: PathBuf,\n        source: Error,\n    },\n    ParseYaml {\n        path: PathBuf,\n        source: Error,\n    },\n    ParseJson {\n        path: PathBuf,\n        source: Error,\n    },\n}"}} />
+
PathBuf),\n    UnknownAdapter {\n        adapter_id: String,\n        available: Vec<String>,\n    },\n    AdapterDescriptorMismatch {\n        path: PathBuf,\n        field: &'static str,\n        expected: String,\n        actual: String,\n    },\n    AdapterDescriptorUnsupported {\n        adapter_id: String,\n        field: &'static str,\n        value: String,\n    },\n    InvalidAdapterDescriptor {\n        path: PathBuf,\n        message: String,\n    },\n    UnknownSchema {\n        schema: String,\n        available: Vec<String>,\n    },\n    UnsupportedRuntimeAdapter {\n        harness: String,\n        adapter_kind: AdapterKind,\n    },\n    UnsupportedToolsPolicy {\n        harness: String,\n        reason: String,\n    },\n    RuntimeHandleMismatch {\n        field: &'static str,\n        expected: String,\n        actual: String,\n        runtime_id: String,\n    },\n    UnsupportedEnvironmentProvider {\n        provider: String,\n        adapter_kind: AdapterKind,\n    },\n    InvalidProcessSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    InvalidPythonSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    PythonInterpreterUnavailable {\n        path: PathBuf,\n        origin: String,\n        reason: String,\n    },\n    ProcessRunner {\n        command: String,\n        source: Error,\n    },\n    SerializeJson(Error),\n    Read {\n        path: PathBuf,\n        source: Error,\n    },\n    Write {\n        path: PathBuf,\n        source: Error,\n    },\n    ParseJson {\n        path: PathBuf,\n        source: Error,\n    },\n}"}} />
Errors raised by Fabric config loading and validation. @@ -21,54 +21,6 @@ Errors raised by Fabric config loading and validation. The requested path does not exist. -### `MissingEntrypoint(PathBuf)` - -
PathBuf)"}} />
- -A directory did not contain the expected Fabric entrypoint. - -### `UnsupportedExtension(PathBuf)` - -
PathBuf)"}} />
- -A file extension is not recognized for Fabric config loading. - -### `UnknownProfile` - -
- -A requested profile is not present in an agent manifest. - -#### Fields - -### `profile: String` - -Requested profile. - -### `agent: String` - -Agent name. - -### `available: Vec` - -Available profile names. - -### `ProfileError` - -
- -A profile config failed validation. - -#### Fields - -### `path: PathBuf` - -Profile path. - -### `message: String` - -Validation message. - ### `UnknownAdapter`
@@ -161,28 +113,6 @@ Requested schema name. Available schema names. -### `ProfileTargetNotConfig` - -
- -A profile path resolved to an agent manifest instead of a profile config. - -#### Fields - -### `profile: String` - -Profile name. - -### `path: PathBuf` - -Resolved path. - -### `ProfileSelectionNotSupported(PathBuf)` - -
PathBuf)"}} />
- -A profile was requested for a single profile config. - ### `UnsupportedRuntimeAdapter`
@@ -361,22 +291,6 @@ File path. Underlying IO error. -### `ParseYaml` - -
- -YAML parse failed. - -#### Fields - -### `path: PathBuf` - -File path. - -### `source: Error` - -Underlying YAML error. - ### `ParseJson`
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx index 569e37226..d8ddd4843 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx @@ -2,7 +2,7 @@ title: "Module error" sidebar-title: "error" description: "Error types for Fabric core." -position: 78 +position: 70 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx index 64e9cf199..618862e5d 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx @@ -2,7 +2,7 @@ title: "Type Alias Result" sidebar-title: "Result" description: "Core Fabric result type." -position: 50 +position: 42 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx index b677a330b..a3206a15a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx @@ -2,7 +2,7 @@ title: "Function version" sidebar-title: "version" description: "Returns the crate version compiled into this build." -position: 81 +position: 73 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx index c52d13b64..2d35ab987 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx @@ -28,14 +28,12 @@ Core config and runtime contract for NeMo Fabric. - `pub use config::EnvironmentOwnership;` - `pub use config::EnvironmentPlan;` - `pub use config::FabricConfig;` -- `pub use config::FabricDocument;` - `pub use config::HarnessConfig;` - `pub use config::McpConfig;` - `pub use config::McpExposure;` - `pub use config::McpServerPlan;` - `pub use config::MetadataConfig;` - `pub use config::ModelConfig;` -- `pub use config::ProfileConfig;` - `pub use config::ResolutionStrategy;` - `pub use config::ResolveContext;` - `pub use config::ResolvedAdapterDescriptor;` @@ -48,15 +46,9 @@ Core config and runtime contract for NeMo Fabric. - `pub use config::TelemetryProvider;` - `pub use config::TelemetryProviderConfig;` - `pub use config::load_adapter_descriptor;` -- `pub use config::load_fabric_document;` -- `pub use config::resolve_effective_config;` - `pub use config::resolve_effective_config_from_config;` -- `pub use config::resolve_effective_config_with_profiles;` -- `pub use config::resolve_run_plan;` - `pub use config::resolve_run_plan_from_config;` - `pub use config::resolve_run_plan_from_effective_config;` -- `pub use config::resolve_run_plan_with_profiles;` -- `pub use config::validate_agent_directory;` - `pub use doctor::DoctorCheck;` - `pub use doctor::DoctorReport;` - `pub use doctor::DoctorStatus;` diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-errorstage.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-errorstage.mdx index 16442abed..48aa7dd9c 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-errorstage.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-errorstage.mdx @@ -30,7 +30,7 @@ Fabric lifecycle stage associated with an error.
-Configuration or profile loading failed. +Configuration resolution failed. ### `Plan` diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx index 8e8af21f9..daca55dec 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx @@ -2,7 +2,7 @@ title: "Module runtime" sidebar-title: "runtime" description: "Runtime invocation helpers." -position: 79 +position: 71 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runrequest.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runrequest.mdx index f6f5fa597..0912f2d46 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runrequest.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runrequest.mdx @@ -29,7 +29,7 @@ Runtime context such as task, rollout, workflow, or caller metadata. ### `overrides: Option` -Per-invocation overrides allowed by the resolved profile. +Per-invocation overrides allowed by the resolved config. ## Implementations diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runresult.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runresult.mdx index 3f0018cbb..34db337ff 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runresult.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runresult.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub profiles: Vec<String>,\n    pub harness: String,\n    pub adapter_kind: AdapterKind,\n    pub adapter_id: Option<String>,\n    pub runtime_id: String,\n    pub invocation_id: String,\n    pub request_id: String,\n    pub status: RunStatus,\n    pub output: Value,\n    pub error: Option<ErrorInfo>,\n    pub artifacts: ArtifactManifest,\n    pub telemetry: Option<TelemetryRef>,\n    pub events: Vec<FabricEvent>,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub harness: String,\n    pub adapter_kind: AdapterKind,\n    pub adapter_id: Option<String>,\n    pub runtime_id: String,\n    pub invocation_id: String,\n    pub request_id: String,\n    pub status: RunStatus,\n    pub output: Value,\n    pub error: Option<ErrorInfo>,\n    pub artifacts: ArtifactManifest,\n    pub telemetry: Option<TelemetryRef>,\n    pub events: Vec<FabricEvent>,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
Result from a Fabric-managed harness invocation. @@ -19,10 +19,6 @@ Result from a Fabric-managed harness invocation. Stable agent name. -### `profiles: Vec` - -Ordered profiles applied to this run. - ### `harness: String` Stable machine-readable harness identifier used for this run. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx index 97a1e7f7c..ab644e11e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx @@ -11,8 +11,7 @@ Generated from `cargo doc --no-deps -p nemo-fabric-core`. ```rust pub enum SchemaName { -Show 15 variants Agent, - Profile, +Show 14 variants Agent, AdapterDescriptor, EffectiveConfig, RunPlan, @@ -37,13 +36,7 @@ Public schema snapshots generated by Fabric.
-Base `agent.yaml` schema. - -### `Profile` - -
- -Profile overlay schema. +Complete typed Fabric config schema. ### `AdapterDescriptor` @@ -131,7 +124,7 @@ Fabric lifecycle event schema. #### `ALL` -
15]"}} />
+
14]"}} />
All public schemas in stable output order. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx index 88d03afa2..42b126261 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx @@ -2,7 +2,7 @@ title: "Module schema" sidebar-title: "schema" description: "JSON Schema generation for the public Fabric contract." -position: 80 +position: 72 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/examples/harbor/calculator/task/environment/fabric/adapters/scripted/run.py b/examples/harbor/calculator/task/environment/fabric/adapters/scripted/run.py index fe12b6b50..7c6bc74c8 100644 --- a/examples/harbor/calculator/task/environment/fabric/adapters/scripted/run.py +++ b/examples/harbor/calculator/task/environment/fabric/adapters/scripted/run.py @@ -2,6 +2,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 + """Deterministic adapter used by the credential-free Harbor example.""" import json diff --git a/python/pyproject.toml b/python/pyproject.toml index 4e952d051..041e9db09 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -27,6 +27,9 @@ dependencies = [ ] dynamic = ["version"] +[project.scripts] +nemo-fabric = "nemo_fabric.cli:main" + [project.urls] Repository = "https://github.com/NVIDIA/nemo-fabric/" Documentation = "https://nvidia-nemo-fabric.docs.buildwithfern.com/nemo/fabric" diff --git a/python/src/nemo_fabric/__init__.py b/python/src/nemo_fabric/__init__.py index 96cceeaa0..c4f847324 100644 --- a/python/src/nemo_fabric/__init__.py +++ b/python/src/nemo_fabric/__init__.py @@ -13,13 +13,11 @@ from nemo_fabric.models import EnvironmentConfig from nemo_fabric.models import FabricBaseModel from nemo_fabric.models import FabricConfig -from nemo_fabric.models import FabricProfileConfig from nemo_fabric.models import HarnessConfig from nemo_fabric.models import McpConfig from nemo_fabric.models import McpServerConfig from nemo_fabric.models import MetadataConfig from nemo_fabric.models import ModelConfig -from nemo_fabric.models import ProfileRegistryConfig from nemo_fabric.models import RelayAtifConfig from nemo_fabric.models import RelayAtofConfig from nemo_fabric.models import RelayAtofEndpointConfig @@ -65,7 +63,6 @@ "Fabric", "FabricBaseModel", "FabricConfig", - "FabricProfileConfig", "FabricCapabilityError", "FabricConfigError", "FabricError", @@ -75,7 +72,6 @@ "McpServerConfig", "MetadataConfig", "ModelConfig", - "ProfileRegistryConfig", "RelayAtifConfig", "RelayAtofConfig", "RelayAtofEndpointConfig", diff --git a/python/src/nemo_fabric/_bundled/adapters/claude/fabric-adapter.json b/python/src/nemo_fabric/_bundled/adapters/claude/fabric-adapter.json new file mode 100644 index 000000000..876e1620e --- /dev/null +++ b/python/src/nemo_fabric/_bundled/adapters/claude/fabric-adapter.json @@ -0,0 +1,9 @@ +{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "nvidia.fabric.claude", + "harness": "claude", + "adapter_kind": "python", + "runner": {"module": "nemo_fabric_adapters.claude.adapter", "callable": "run"}, + "config": {"accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"]}, + "telemetry": {"providers": {"relay": {"outputs": ["atif", "otel", "openinference"], "integration_modes": ["hooks", "gateway"]}}} +} diff --git a/python/src/nemo_fabric/_bundled/adapters/codex/fabric-adapter.json b/python/src/nemo_fabric/_bundled/adapters/codex/fabric-adapter.json new file mode 100644 index 000000000..5cb09f538 --- /dev/null +++ b/python/src/nemo_fabric/_bundled/adapters/codex/fabric-adapter.json @@ -0,0 +1,9 @@ +{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "nvidia.fabric.codex", + "harness": "codex", + "adapter_kind": "python", + "runner": {"module": "nemo_fabric_adapters.codex.adapter", "callable": "run"}, + "config": {"accepts": ["models", "telemetry"]}, + "telemetry": {"providers": {"relay": {"outputs": ["atif", "otel", "openinference"], "integration_modes": ["hooks", "gateway"]}, "native": {"outputs": ["otel"]}}} +} diff --git a/python/src/nemo_fabric/_bundled/adapters/deepagents/fabric-adapter.json b/python/src/nemo_fabric/_bundled/adapters/deepagents/fabric-adapter.json new file mode 100644 index 000000000..314fbad22 --- /dev/null +++ b/python/src/nemo_fabric/_bundled/adapters/deepagents/fabric-adapter.json @@ -0,0 +1,10 @@ +{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "nvidia.fabric.langchain.deepagents", + "harness": "deepagents", + "adapter_kind": "python", + "runner": {"module": "nemo_fabric_adapters.deepagents.adapter", "callable": "run"}, + "requirements": {}, + "config": {"accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"]}, + "telemetry": {"providers": {"relay": {"outputs": ["atif", "otel", "openinference"]}, "native": {"outputs": ["otel", "openinference"]}}} +} diff --git a/python/src/nemo_fabric/_bundled/adapters/hermes/fabric-adapter.json b/python/src/nemo_fabric/_bundled/adapters/hermes/fabric-adapter.json new file mode 100644 index 000000000..35ab6e5de --- /dev/null +++ b/python/src/nemo_fabric/_bundled/adapters/hermes/fabric-adapter.json @@ -0,0 +1,10 @@ +{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "nvidia.fabric.hermes", + "harness": "hermes", + "adapter_kind": "python", + "runner": {"module": "nemo_fabric_adapters.hermes.adapter", "callable": "run"}, + "requirements": {"env": ["NVIDIA_API_KEY"]}, + "config": {"accepts": ["tools", "tools.blocked", "mcp", "skills", "telemetry"]}, + "telemetry": {"providers": {"relay": {"outputs": ["atif", "otel", "openinference"]}}} +} diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/repo/calculator.py b/python/src/nemo_fabric/_bundled/examples/code_review_agent/repo/calculator.py new file mode 100644 index 000000000..c53bc9482 --- /dev/null +++ b/python/src/nemo_fabric/_bundled/examples/code_review_agent/repo/calculator.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Tiny workspace used by the installed code-review example.""" + + +def divide(total: float, count: float) -> float: + return total / count diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/skills/code-review.md b/python/src/nemo_fabric/_bundled/examples/code_review_agent/skills/code-review.md new file mode 100644 index 000000000..c888def9b --- /dev/null +++ b/python/src/nemo_fabric/_bundled/examples/code_review_agent/skills/code-review.md @@ -0,0 +1,8 @@ + + +# Code review + +Inspect the workspace, prioritize correctness issues, and explain concrete fixes. diff --git a/python/src/nemo_fabric/_config_sources.py b/python/src/nemo_fabric/_config_sources.py deleted file mode 100644 index 7b3337d33..000000000 --- a/python/src/nemo_fabric/_config_sources.py +++ /dev/null @@ -1,90 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -"""Agent source normalization for the Fabric Python SDK.""" - -from __future__ import annotations - -import json -import os -from collections.abc import Mapping, Sequence -from typing import Any - -from nemo_fabric.errors import FabricConfigError -from nemo_fabric.models import FabricConfig, FabricProfileConfig - -PathSource = str | os.PathLike[str] -TypedConfigSource = FabricConfig -AgentSource = PathSource | TypedConfigSource -PathProfiles = str | Sequence[str] -TypedProfiles = Sequence[FabricProfileConfig] - - -def is_config_source(value: Any) -> bool: - return isinstance(value, FabricConfig) - - -def path_arg(value: Any) -> str: - if isinstance(value, (str, os.PathLike)): - return os.fspath(value) - if isinstance(value, Mapping): - raise FabricConfigError( - "agent mappings are not accepted directly; " - "use FabricConfig.from_mapping(...) first" - ) - raise FabricConfigError( - "agent must be a path-like source or FabricConfig" - ) - - -def path_profiles(profiles: PathProfiles | None) -> list[str]: - if profiles is None: - return [] - if isinstance(profiles, str): - values = [profiles] - elif isinstance(profiles, bytes): - raise FabricConfigError("profiles must be profile names, not bytes") - elif isinstance(profiles, Mapping): - raise FabricConfigError("profiles must be profile names, not a mapping") - else: - values = list(profiles) - if not all(isinstance(profile, str) and profile for profile in values): - raise FabricConfigError("path profiles must contain only non-empty strings") - return values - - -def config_profiles( - profiles: TypedProfiles | None, -) -> list[FabricProfileConfig]: - if profiles is None: - return [] - if isinstance(profiles, (str, bytes)): - raise FabricConfigError( - "FabricConfig profiles must contain FabricProfileConfig values" - ) - values = list(profiles) - if not all(isinstance(profile, FabricProfileConfig) for profile in values): - raise FabricConfigError( - "FabricConfig profiles must contain FabricProfileConfig values" - ) - return values - - -def validate_base_dir(agent: AgentSource, base_dir: PathSource | None) -> str | None: - if not is_config_source(agent): - if base_dir is not None: - raise FabricConfigError("base_dir is only valid with a typed config source") - return None - return None if base_dir is None else os.fspath(base_dir) - - -def config_json(config: TypedConfigSource) -> str: - if not is_config_source(config): - raise FabricConfigError("config must be a FabricConfig") - return json.dumps(config.to_mapping()) - - -def profiles_json(profiles: Sequence[FabricProfileConfig]) -> str | None: - if not profiles: - return None - return json.dumps([profile.to_mapping() for profile in profiles]) diff --git a/python/src/nemo_fabric/_native.pyi b/python/src/nemo_fabric/_native.pyi index 1f7ad6b85..588831dcd 100644 --- a/python/src/nemo_fabric/_native.pyi +++ b/python/src/nemo_fabric/_native.pyi @@ -2,36 +2,20 @@ # SPDX-License-Identifier: Apache-2.0 def version() -> str: ... -def validate(path: str) -> str: ... -def inspect(path: str, profile: str | list[str] | None = None) -> str: ... def resolve_config( config_json: str, - profiles_json: str | None = None, base_dir: str | None = None, ) -> str: ... -def plan(path: str, profile: str | list[str] | None = None) -> str: ... def plan_config( config_json: str, - profiles_json: str | None = None, base_dir: str | None = None, ) -> str: ... -def doctor(path: str, profile: str | list[str] | None = None) -> str: ... def doctor_config( config_json: str, - profiles_json: str | None = None, base_dir: str | None = None, ) -> str: ... -def run( - path: str, - profile: str | list[str] | None = None, - input_text: str | None = None, - input_file: str | None = None, - request_json: str | None = None, - request_file: str | None = None, -) -> str: ... def run_config( config_json: str, - profiles_json: str | None = None, base_dir: str | None = None, input_text: str | None = None, input_file: str | None = None, diff --git a/python/src/nemo_fabric/cli.py b/python/src/nemo_fabric/cli.py new file mode 100644 index 000000000..3807c8865 --- /dev/null +++ b/python/src/nemo_fabric/cli.py @@ -0,0 +1,229 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""SDK-backed command line interface for NeMo Fabric.""" + +from __future__ import annotations + +import argparse +import asyncio +import json +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +from nemo_fabric.client import Fabric +from nemo_fabric.errors import FabricError +from nemo_fabric.factories import load_config_factory +from nemo_fabric.models import FabricConfig, RunRequest +from nemo_fabric.presets import EXAMPLES, PRESETS + + +class SourceError(ValueError): + """Invalid CLI source selector.""" + + +@dataclass(frozen=True) +class SelectedSource: + """A complete typed config and its path-resolution base.""" + + config: FabricConfig + base_dir: Path + label: str + + +def load_factory(spec: str) -> FabricConfig: + """Import and invoke a ``module:callable`` Fabric config factory.""" + + try: + return load_config_factory(spec) + except Exception as error: + raise SourceError(str(error)) from error + + +def select_source(args: argparse.Namespace) -> SelectedSource: + """Resolve one mutually exclusive CLI selector.""" + + override = Path(args.base_dir).resolve() if args.base_dir else None + if args.preset: + try: + preset = PRESETS[args.preset] + except KeyError as error: + raise SourceError(_unknown("preset", args.preset, PRESETS)) from error + return SelectedSource(preset.factory(), override or preset.base_dir, f"preset:{preset.name}") + if args.example: + try: + example = EXAMPLES[args.example] + except KeyError as error: + raise SourceError(_unknown("example", args.example, EXAMPLES)) from error + variant = args.variant or example.default_variant + try: + factory = example.variants[variant] + except KeyError as error: + choices = ", ".join(sorted(example.variants)) + raise SourceError(f"unknown variant {variant!r} for {example.name}; available: {choices}") from error + return SelectedSource(factory(), override or example.base_dir, f"example:{example.name}:{variant}") + if args.factory: + return SelectedSource(load_factory(args.factory), override or Path.cwd(), f"factory:{args.factory}") + raise SourceError("select exactly one of --preset, --example, or --factory") + + +def _unknown(kind: str, name: str, registry: dict[str, Any]) -> str: + return f"unknown {kind} {name!r}; available: {', '.join(sorted(registry))}" + + +def _add_selector(parser: argparse.ArgumentParser) -> None: + sources = parser.add_mutually_exclusive_group(required=True) + sources.add_argument("--preset", help="Use a maintained, complete built-in config.") + sources.add_argument("--example", help="Use an installed example config.") + sources.add_argument("--factory", help="Import a custom module:callable returning FabricConfig.") + parser.add_argument("--variant", help="Variant for --example (defaults to the example default).") + parser.add_argument("--base-dir", help="Override the source base directory for relative paths.") + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(prog="nemo-fabric", description=__doc__) + subcommands = parser.add_subparsers(dest="command", required=True) + + for name in ("plan", "doctor", "run", "chat"): + command = subcommands.add_parser(name) + _add_selector(command) + if name in {"run", "chat"}: + command.add_argument("--input", default=None, help="Invocation text; defaults to an empty string.") + if name == "run": + command.add_argument("--request-json", help="JSON object or @path containing a RunRequest.") + command.add_argument("--output", type=Path, help="Write JSON output to this path instead of stdout.") + + preset = subcommands.add_parser("preset", help="Discover built-in presets.") + preset_commands = preset.add_subparsers(dest="preset_command", required=True) + preset_commands.add_parser("list") + preset_show = preset_commands.add_parser("show") + preset_show.add_argument("name") + + example = subcommands.add_parser("example", help="Discover installed examples.") + example_commands = example.add_subparsers(dest="example_command", required=True) + example_commands.add_parser("list") + example_show = example_commands.add_parser("show") + example_show.add_argument("name") + return parser + + +def _request(args: argparse.Namespace) -> RunRequest: + if args.request_json and args.input is not None: + raise SourceError("--input and --request-json are mutually exclusive") + if not args.request_json: + return RunRequest(input=args.input or "") + raw = args.request_json + if raw.startswith("@"): + raw = Path(raw[1:]).read_text(encoding="utf-8") + try: + value = json.loads(raw) + except json.JSONDecodeError as error: + raise SourceError(f"invalid request JSON: {error}") from error + if not isinstance(value, dict): + raise SourceError("request JSON must be an object") + return RunRequest.from_mapping(value) + + +def _emit(value: Any, output: Path | None) -> None: + mapping = value.to_mapping() if hasattr(value, "to_mapping") else value + rendered = json.dumps(mapping, indent=2) + if output is None: + print(rendered) + return + output.parent.mkdir(parents=True, exist_ok=True) + output.write_text(rendered + "\n", encoding="utf-8") + + +async def _execute(args: argparse.Namespace) -> int: + selected = select_source(args) + fabric = Fabric() + if args.command == "plan": + result = fabric.plan(selected.config, base_dir=selected.base_dir) + elif args.command == "doctor": + result = await fabric.doctor(selected.config, base_dir=selected.base_dir) + elif args.command == "run": + result = await fabric.run(selected.config, base_dir=selected.base_dir, request=_request(args)) + else: + return await _chat(fabric, selected, args) + _emit(result, args.output) + return 1 if getattr(result, "status", None) == "fail" else 0 + + +async def _chat(fabric: Fabric, selected: SelectedSource, args: argparse.Namespace) -> int: + transcript: list[dict[str, Any]] = [] + async with await fabric.start_runtime(selected.config, base_dir=selected.base_dir) as runtime: + pending = args.input + while True: + if pending is None: + try: + pending = input("you> ") + except EOFError: + break + if pending.strip().lower() in {"/exit", "/quit"}: + break + result = await runtime.invoke(input=pending) + transcript.append(result.to_mapping()) + response = getattr(result.output, "response", None) + print(f"agent> {response if response is not None else result.status}") + pending = None + if args.output is not None: + _emit(transcript, args.output) + return 0 + + +def _discovery(args: argparse.Namespace) -> int | None: + if args.command == "preset": + if args.preset_command == "list": + for name in sorted(PRESETS): + print(name) + else: + try: + item = PRESETS[args.name] + except KeyError as error: + raise SourceError(_unknown("preset", args.name, PRESETS)) from error + _emit({"name": item.name, "description": item.description}, None) + return 0 + if args.command == "example": + if args.example_command == "list": + for name in sorted(EXAMPLES): + print(name) + else: + try: + item = EXAMPLES[args.name] + except KeyError as error: + raise SourceError(_unknown("example", args.name, EXAMPLES)) from error + _emit( + { + "name": item.name, + "description": item.description, + "default_variant": item.default_variant, + "variants": sorted(item.variants), + }, + None, + ) + return 0 + return None + + +def cli(argv: list[str] | None = None) -> int: + """Run the CLI and return a process exit code.""" + + args = build_parser().parse_args(argv) + try: + discovery = _discovery(args) + return discovery if discovery is not None else asyncio.run(_execute(args)) + except (FabricError, SourceError, OSError, ValueError) as error: + print(f"nemo-fabric: {error}", file=sys.stderr) + return 2 + + +def main() -> None: + """Console-script entrypoint.""" + + raise SystemExit(cli()) + + +if __name__ == "__main__": + main() diff --git a/python/src/nemo_fabric/client.py b/python/src/nemo_fabric/client.py index 9224fdb5a..a1889ce1e 100644 --- a/python/src/nemo_fabric/client.py +++ b/python/src/nemo_fabric/client.py @@ -8,22 +8,9 @@ import asyncio import importlib import json -from collections.abc import Mapping, Sequence -from typing import Any, overload - -from nemo_fabric._config_sources import ( - AgentSource, - PathProfiles, - PathSource, - TypedProfiles, - config_json, - config_profiles, - is_config_source, - path_arg, - path_profiles, - profiles_json, - validate_base_dir, -) +import os +from collections.abc import Mapping +from typing import Any from nemo_fabric.errors import ( FabricConfigError, FabricError, @@ -54,14 +41,13 @@ class Fabric: """Primary Python entrypoint for NeMo Fabric. - The client accepts either a path-backed agent package or a typed - ``FabricConfig``. Path-backed sources select profiles by name; typed sources - accept ordered ``FabricProfileConfig`` values and may use - ``base_dir`` to resolve relative paths. All inspection and execution APIs - return typed, read-only mapping models. + Every lifecycle method accepts a complete, typed ``FabricConfig`` plus an + optional ``base_dir`` used to resolve relative paths. Compose variants in + Python before calling the SDK. All inspection and execution APIs return + typed, read-only mapping models. - ``Fabric`` is native-only. The ``fabric`` CLI is a separate public - surface over the same Rust core; SDK calls raise + ``Fabric`` is native-only. The ``nemo-fabric`` CLI is backed by this public + SDK; SDK calls raise ``FabricNativeUnavailableError`` when the native extension is not installed. @@ -69,174 +55,89 @@ class Fabric: multi-turn examples. """ - @overload - def resolve( - self, - agent: PathSource, - *, - profiles: PathProfiles | None = None, - base_dir: None = None, - ) -> EffectiveConfig: ... - - @overload def resolve( self, - agent: FabricConfig, + config: FabricConfig, *, - profiles: TypedProfiles | None = None, - base_dir: PathSource | None = None, - ) -> EffectiveConfig: ... - - def resolve( - self, - agent: AgentSource, - *, - profiles: PathProfiles | TypedProfiles | None = None, - base_dir: PathSource | None = None, + base_dir: str | os.PathLike[str] | None = None, ) -> EffectiveConfig: - """Resolve an agent source and its ordered profile overlays. + """Resolve an agent source. Resolution validates and normalizes configuration but does not resolve an adapter or compute runtime capabilities. Use ``plan()`` when those execution details are required. Args: - agent: Agent-package directory or config-file path, or a typed - ``FabricConfig``. Raw - mappings are not accepted; convert them with + config: Complete typed ``FabricConfig``. Raw mappings are not + accepted; convert them with ``FabricConfig.from_mapping()``. - profiles: One profile name or an ordered sequence of names for a - path-backed source. For a typed source, an ordered sequence of - ``FabricProfileConfig`` values. - base_dir: Base directory for resolving relative paths in a typed - config. Valid only when ``agent`` is a typed config source. + base_dir: Base directory for resolving relative paths. Returns: The normalized ``EffectiveConfig`` snapshot. Raises: - FabricConfigError: If the source, profile stack, or resolved config - is invalid. + FabricConfigError: If the config is invalid. FabricNativeUnavailableError: If the native extension is not installed. """ native = self._require_native_module("resolve") try: - if is_config_source(agent): - typed_profiles = config_profiles(profiles) # type: ignore[arg-type] - raw = native.resolve_config( - config_json(agent), - profiles_json(typed_profiles), - validate_base_dir(agent, base_dir), - ) - else: - validate_base_dir(agent, base_dir) - raw = native.inspect( - path_arg(agent), path_profiles(profiles) # type: ignore[arg-type] - ) + raw = native.resolve_config( + _config_json(config), + _base_dir_arg(base_dir), + ) return EffectiveConfig.from_mapping(json.loads(raw)) except FabricError: raise except Exception as error: raise FabricConfigError(str(error)) from error - @overload - def plan( - self, - agent: PathSource, - *, - profiles: PathProfiles | None = None, - base_dir: None = None, - ) -> RunPlan: ... - - @overload - def plan( - self, - agent: FabricConfig, - *, - profiles: TypedProfiles | None = None, - base_dir: PathSource | None = None, - ) -> RunPlan: ... - def plan( self, - agent: AgentSource, + config: FabricConfig, *, - profiles: PathProfiles | TypedProfiles | None = None, - base_dir: PathSource | None = None, + base_dir: str | os.PathLike[str] | None = None, ) -> RunPlan: """Resolve an agent source into an immutable execution plan. - Planning applies profiles, resolves the selected adapter, and reports - optional runtime capabilities such as streaming, updates, and - cancellation. It does not start the runtime. + Planning resolves the selected adapter and reports optional runtime + capabilities such as streaming, updates, and cancellation. Planning + does not start the runtime. Args: - agent: Agent-package directory or config-file path, or a typed - ``FabricConfig``. Raw - mappings are not accepted. - profiles: One profile name or an ordered sequence of names for a - path-backed source. For a typed source, an ordered sequence of - ``FabricProfileConfig`` values. - base_dir: Base directory for resolving relative paths in a typed - config. Valid only when ``agent`` is a typed config source. + config: Complete typed ``FabricConfig``. Raw mappings are not + accepted. + base_dir: Base directory for resolving relative paths. Returns: A ``RunPlan`` containing the effective config, adapter, and declared runtime capabilities. Raises: - FabricConfigError: If the source, profile stack, config, or adapter - resolution is invalid. + FabricConfigError: If the config or adapter resolution is invalid. FabricNativeUnavailableError: If the native extension is not installed. """ native = self._require_native_module("plan") try: - if is_config_source(agent): - typed_profiles = config_profiles(profiles) # type: ignore[arg-type] - raw = native.plan_config( - config_json(agent), - profiles_json(typed_profiles), - validate_base_dir(agent, base_dir), - ) - else: - validate_base_dir(agent, base_dir) - raw = native.plan( - path_arg(agent), path_profiles(profiles) # type: ignore[arg-type] - ) + raw = native.plan_config( + _config_json(config), + _base_dir_arg(base_dir), + ) return RunPlan.from_mapping(json.loads(raw)) except FabricError: raise except Exception as error: raise FabricConfigError(str(error)) from error - @overload - async def doctor( - self, - agent: PathSource, - *, - profiles: PathProfiles | None = None, - base_dir: None = None, - ) -> DoctorReport: ... - - @overload async def doctor( self, - agent: FabricConfig, + config: FabricConfig, *, - profiles: TypedProfiles | None = None, - base_dir: PathSource | None = None, - ) -> DoctorReport: ... - - async def doctor( - self, - agent: AgentSource, - *, - profiles: PathProfiles | TypedProfiles | None = None, - base_dir: PathSource | None = None, + base_dir: str | os.PathLike[str] | None = None, ) -> DoctorReport: """Diagnose a planned agent without starting its runtime. @@ -244,13 +145,8 @@ async def doctor( environment requirements using the native Fabric core. Args: - agent: Agent-package directory or config-file path, or a typed - ``FabricConfig``. - profiles: One profile name or an ordered sequence of names for a - path-backed source. For a typed source, an ordered sequence of - ``FabricProfileConfig`` values. - base_dir: Base directory for resolving relative paths in a typed - config. Valid only when ``agent`` is a typed config source. + config: Complete typed ``FabricConfig``. + base_dir: Base directory for resolving relative paths. Returns: A ``DoctorReport`` with aggregate status and ordered checks. @@ -265,18 +161,10 @@ async def doctor( native = self._require_native_module("doctor") def diagnose() -> DoctorReport: - if is_config_source(agent): - typed_profiles = config_profiles(profiles) # type: ignore[arg-type] - raw = native.doctor_config( - config_json(agent), - profiles_json(typed_profiles), - validate_base_dir(agent, base_dir), - ) - else: - validate_base_dir(agent, base_dir) - raw = native.doctor( - path_arg(agent), path_profiles(profiles) # type: ignore[arg-type] - ) + raw = native.doctor_config( + _config_json(config), + _base_dir_arg(base_dir), + ) return DoctorReport.from_mapping(json.loads(raw)) try: @@ -286,34 +174,11 @@ def diagnose() -> DoctorReport: except Exception as error: raise FabricConfigError(str(error)) from error - @overload async def run( self, - agent: PathSource, + config: FabricConfig, *, - profiles: PathProfiles | None = None, - base_dir: None = None, - input: Any = None, - request: RunRequest | None = None, - ) -> RunResult: ... - - @overload - async def run( - self, - agent: FabricConfig, - *, - profiles: TypedProfiles | None = None, - base_dir: PathSource | None = None, - input: Any = None, - request: RunRequest | None = None, - ) -> RunResult: ... - - async def run( - self, - agent: AgentSource, - *, - profiles: PathProfiles | TypedProfiles | None = None, - base_dir: PathSource | None = None, + base_dir: str | os.PathLike[str] | None = None, input: Any = None, request: RunRequest | None = None, ) -> RunResult: @@ -325,13 +190,8 @@ async def run( Fabric attempts to stop a started runtime even when invocation fails. Args: - agent: Agent-package directory or config-file path, or a typed - ``FabricConfig``. - profiles: One profile name or an ordered sequence of names for a - path-backed source. For a typed source, an ordered sequence of - ``FabricProfileConfig`` values. - base_dir: Base directory for resolving relative paths in a typed - config. Valid only when ``agent`` is a typed config source. + config: Complete typed ``FabricConfig``. + base_dir: Base directory for resolving relative paths. input: JSON-compatible invocation input. request: Complete validated ``RunRequest``. @@ -348,11 +208,7 @@ async def run( normalized result can be returned. """ - plan = await _call_blocking( - lambda: self.plan( # type: ignore[arg-type] - agent, profiles=profiles, base_dir=base_dir - ) - ) + plan = await _call_blocking(lambda: self.plan(config, base_dir=base_dir)) request_payload = _run_request_payload( input=input, request=request, @@ -362,32 +218,11 @@ async def run( await _run_native_lifecycle(native, plan.to_mapping(), request_payload) ) - @overload async def start_runtime( self, - agent: PathSource, + config: FabricConfig, *, - profiles: PathProfiles | None = None, - base_dir: None = None, - overrides: Mapping[str, Any] | None = None, - ) -> Runtime: ... - - @overload - async def start_runtime( - self, - agent: FabricConfig, - *, - profiles: TypedProfiles | None = None, - base_dir: PathSource | None = None, - overrides: Mapping[str, Any] | None = None, - ) -> Runtime: ... - - async def start_runtime( - self, - agent: AgentSource, - *, - profiles: PathProfiles | TypedProfiles | None = None, - base_dir: PathSource | None = None, + base_dir: str | os.PathLike[str] | None = None, overrides: Mapping[str, Any] | None = None, ) -> Runtime: """Start a stateful runtime for one or more ordered invocations. @@ -396,13 +231,8 @@ async def start_runtime( recursively merged below invocation-scoped overrides. Args: - agent: Agent-package directory or config-file path, or a typed - ``FabricConfig``. - profiles: One profile name or an ordered sequence of names for a - path-backed source. For a typed source, an ordered sequence of - ``FabricProfileConfig`` values. - base_dir: Base directory for resolving relative paths in a typed - config. Valid only when ``agent`` is a typed config source. + config: Complete typed ``FabricConfig``. + base_dir: Base directory for resolving relative paths. overrides: JSON-compatible overrides applied to every invocation in the runtime unless superseded by invocation overrides. @@ -418,11 +248,7 @@ async def start_runtime( """ runtime_overrides = _json_mapping(overrides, "runtime overrides") - plan = await _call_blocking( - lambda: self.plan( # type: ignore[arg-type] - agent, profiles=profiles, base_dir=base_dir - ) - ) + plan = await _call_blocking(lambda: self.plan(config, base_dir=base_dir)) native = self._require_native_module("start_runtime") started_runtime: dict[str, Any] | None = None @@ -470,3 +296,18 @@ def _require_native_module(self, method: str) -> Any: code="native_unavailable", ) return native + + +def _config_json(config: FabricConfig) -> str: + if not isinstance(config, FabricConfig): + if isinstance(config, Mapping): + raise FabricConfigError( + "config mappings are not accepted directly; " + "use FabricConfig.from_mapping(...) first" + ) + raise FabricConfigError("config must be a FabricConfig") + return json.dumps(config.to_mapping()) + + +def _base_dir_arg(base_dir: str | os.PathLike[str] | None) -> str | None: + return None if base_dir is None else os.fspath(base_dir) diff --git a/python/src/nemo_fabric/errors.py b/python/src/nemo_fabric/errors.py index 401704a9a..2af0a058a 100644 --- a/python/src/nemo_fabric/errors.py +++ b/python/src/nemo_fabric/errors.py @@ -51,7 +51,7 @@ def __init__( class FabricConfigError(FabricError): - """Invalid SDK input, request shape, profile stack, or resolved config.""" + """Invalid SDK input, request shape, factory, or resolved config.""" class FabricRuntimeError(FabricError): diff --git a/python/src/nemo_fabric/factories.py b/python/src/nemo_fabric/factories.py new file mode 100644 index 000000000..943fac2bb --- /dev/null +++ b/python/src/nemo_fabric/factories.py @@ -0,0 +1,36 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Import helpers for code-defined Fabric configurations.""" + +from __future__ import annotations + +import importlib + +from nemo_fabric.errors import FabricConfigError +from nemo_fabric.models import FabricConfig + + +def load_config_factory(spec: str) -> FabricConfig: + """Import and invoke a ``module:callable`` returning ``FabricConfig``.""" + + module_name, separator, attribute = spec.partition(":") + if not separator or not module_name or not attribute: + raise FabricConfigError("factory must use module:callable syntax") + try: + module = importlib.import_module(module_name) + except Exception as error: + raise FabricConfigError(f"could not import factory module {module_name!r}: {error}") from error + try: + factory = getattr(module, attribute) + except AttributeError as error: + raise FabricConfigError(f"factory module {module_name!r} has no attribute {attribute!r}") from error + if not callable(factory): + raise FabricConfigError(f"factory target {spec!r} is not callable") + try: + config = factory() + except Exception as error: + raise FabricConfigError(f"factory {spec!r} failed: {error}") from error + if not isinstance(config, FabricConfig): + raise FabricConfigError(f"factory {spec!r} returned {type(config).__name__}; expected FabricConfig") + return config diff --git a/python/src/nemo_fabric/models.py b/python/src/nemo_fabric/models.py index ee6b956a8..3b7dd813d 100644 --- a/python/src/nemo_fabric/models.py +++ b/python/src/nemo_fabric/models.py @@ -388,12 +388,6 @@ def remove_provider(self, provider: Literal["relay", "native"]) -> Self: return self -class ProfileRegistryConfig(FabricBaseModel): - """Profile discovery config for portable file-backed agent packages.""" - - directories: list[str | Path] = Field(default_factory=list) - - class ToolsConfig(FabricBaseModel): """Harness-neutral tool capability configuration.""" @@ -413,7 +407,6 @@ class FabricConfig(FabricBaseModel): skills: SkillConfig | None = None telemetry: TelemetryConfig | None = None relay: RelayConfig | dict[str, Any] | None = None - profiles: ProfileRegistryConfig | dict[str, Any] | None = None tools: ToolsConfig | dict[str, Any] | None = None @classmethod @@ -526,23 +519,6 @@ def enable_relay( return self -class FabricProfileConfig(FabricBaseModel): - """Typed profile overlay used when a Python caller wants file-style overlays.""" - - schema_version: str = "fabric.profile/v1alpha1" - name: str = Field(min_length=1) - description: str | None = None - harness: HarnessConfig | dict[str, Any] | None = None - runtime: RuntimeConfig | dict[str, Any] | None = None - environment: EnvironmentConfig | dict[str, Any] | None = None - models: dict[str, ModelConfig | dict[str, Any]] | None = None - mcp: McpConfig | dict[str, Any] | None = None - skills: SkillConfig | dict[str, Any] | None = None - telemetry: TelemetryConfig | dict[str, Any] | None = None - relay: RelayConfig | dict[str, Any] | None = None - tools: ToolsConfig | dict[str, Any] | None = None - - class RunRequest(FabricBaseModel): """One validated Fabric invocation request.""" diff --git a/python/src/nemo_fabric/presets.py b/python/src/nemo_fabric/presets.py new file mode 100644 index 000000000..d4eabecae --- /dev/null +++ b/python/src/nemo_fabric/presets.py @@ -0,0 +1,128 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Built-in, code-defined Fabric CLI presets and examples.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from pathlib import Path + +from nemo_fabric.models import EnvironmentConfig, FabricConfig, HarnessConfig, MetadataConfig, ModelConfig + +ConfigFactory = Callable[[], FabricConfig] +BUNDLED_BASE_DIR = Path(__file__).resolve().parent / "_bundled" + + +@dataclass(frozen=True) +class Preset: + """One maintained, complete Fabric configuration.""" + + name: str + description: str + factory: ConfigFactory + base_dir: Path = BUNDLED_BASE_DIR + + +@dataclass(frozen=True) +class Example: + """One installed example with one or more complete variants.""" + + name: str + description: str + variants: dict[str, ConfigFactory] + default_variant: str + base_dir: Path + + +def _preset( + *, + name: str, + adapter_id: str, + provider: str, + model: str, + settings: dict[str, object] | None = None, +) -> FabricConfig: + return FabricConfig( + metadata=MetadataConfig(name=f"{name}-agent", description=f"NeMo Fabric {name} CLI preset."), + harness=HarnessConfig(adapter_id=adapter_id, resolution="preinstalled", settings=settings or {}), + models={"default": ModelConfig(provider=provider, model=model)}, + environment=EnvironmentConfig(provider="local"), + ) + + +def hermes() -> FabricConfig: + """Return a fresh Hermes preset.""" + + return _preset( + name="hermes", + adapter_id="nvidia.fabric.hermes", + provider="nvidia", + model="nvidia/nemotron-3-nano-30b-a3b", + settings={"base_url": "https://integrate.api.nvidia.com/v1"}, + ) + + +def claude() -> FabricConfig: + """Return a fresh Claude preset.""" + + return _preset( + name="claude", + adapter_id="nvidia.fabric.claude", + provider="anthropic", + model="anthropic/claude-sonnet-4-5", + settings={"permission_mode": "dontAsk"}, + ) + + +def codex() -> FabricConfig: + """Return a fresh Codex preset.""" + + return _preset( + name="codex", + adapter_id="nvidia.fabric.codex", + provider="openai", + model="openai/gpt-5.4", + settings={"sandbox": "workspace-write"}, + ) + + +def deepagents() -> FabricConfig: + """Return a fresh Deep Agents preset.""" + + return _preset( + name="deepagents", + adapter_id="nvidia.fabric.langchain.deepagents", + provider="nvidia", + model="nvidia/nemotron-3-nano-30b-a3b", + ) + + +def _code_review(variant: str) -> FabricConfig: + config = PRESETS[variant].factory() + config.metadata = MetadataConfig( + name="code-review-agent", + description="Reviews a small Python workspace for correctness risks.", + ) + config.environment = EnvironmentConfig(provider="local", workspace="examples/code_review_agent/repo") + config.add_skill_path("examples/code_review_agent/skills/code-review.md") + return config + + +PRESETS: dict[str, Preset] = { + "hermes": Preset("hermes", "Hermes Agent with an NVIDIA-hosted model.", hermes), + "claude": Preset("claude", "Claude Code with an Anthropic model.", claude), + "codex": Preset("codex", "Codex with an OpenAI model.", codex), + "deepagents": Preset("deepagents", "LangChain Deep Agents with an NVIDIA-hosted model.", deepagents), +} + +EXAMPLES: dict[str, Example] = { + "examples.code_review_agent": Example( + name="examples.code_review_agent", + description="Installed, editable-in-Python code review example.", + variants={name: (lambda name=name: _code_review(name)) for name in PRESETS}, + default_variant="hermes", + base_dir=BUNDLED_BASE_DIR, + ) +} diff --git a/python/src/nemo_fabric/runtime.py b/python/src/nemo_fabric/runtime.py index 3fd736b4e..c94434504 100644 --- a/python/src/nemo_fabric/runtime.py +++ b/python/src/nemo_fabric/runtime.py @@ -6,10 +6,12 @@ from __future__ import annotations import asyncio +import concurrent.futures import json from collections.abc import Mapping, Sequence from copy import deepcopy from enum import Enum +from threading import Thread from typing import Any from pydantic import ValidationError @@ -381,13 +383,25 @@ def run() -> dict[str, Any]: async def _call_blocking(func: Any) -> Any: - worker = asyncio.create_task(asyncio.to_thread(func)) + worker: concurrent.futures.Future[Any] = concurrent.futures.Future() + + def run() -> None: + try: + result = func() + except BaseException as error: + worker.set_exception(error) + else: + worker.set_result(result) + + Thread(target=run, name="nemo-fabric-worker", daemon=True).start() try: - return await asyncio.shield(worker) + while not worker.done(): + await asyncio.sleep(0.001) + return worker.result() except asyncio.CancelledError as cancelled: while not worker.done(): try: - await asyncio.shield(worker) + await asyncio.sleep(0.001) except asyncio.CancelledError: continue try: diff --git a/python/src/nemo_fabric/types.py b/python/src/nemo_fabric/types.py index 1298b71af..dab4e40f4 100644 --- a/python/src/nemo_fabric/types.py +++ b/python/src/nemo_fabric/types.py @@ -64,15 +64,6 @@ def _required_text(value: Any, name: str) -> str: return value -def _required_profiles(data: Mapping[str, Any], owner: str) -> tuple[str, ...]: - if "profiles" not in data: - raise FabricConfigError(f"{owner} profiles is required") - profiles = data["profiles"] - if isinstance(profiles, (str, bytes)) or not isinstance(profiles, Sequence): - raise FabricConfigError(f"{owner} profiles must be an ordered sequence of strings") - return tuple(_required_text(profile, f"{owner} profile") for profile in profiles) - - def _boolean(value: Any, name: str) -> bool: if not isinstance(value, bool): raise FabricConfigError(f"{name} must be a boolean") @@ -325,8 +316,8 @@ def from_mapping(cls, value: Mapping[str, Any]) -> "_EnvironmentConfig": class _SkillConfig(_ConfigMapping): """Skill capability configuration. - The shape matches the ``skills`` section in ``agent.yaml`` while providing - small authoring helpers for Python callers. + The shape matches the ``skills`` section of ``FabricConfig`` while + providing small authoring helpers for Python callers. """ _fields = frozenset({"paths"}) @@ -553,9 +544,9 @@ def _provider(cls, provider: str) -> str: class _ResolvedFabricConfig(_ConfigMapping): """Mutable typed representation of a Fabric agent configuration. - The object follows the same schema as ``agent.yaml``. It is mutable while - callers compose a job, then copied into immutable resolution and plan - snapshots. Unknown fields survive round trips through ``extra_fields``. + It is mutable while callers compose a job, then copied into immutable + resolution and plan snapshots. Unknown fields survive round trips through + ``extra_fields``. Attributes: schema_version: Agent schema identifier. @@ -568,7 +559,6 @@ class _ResolvedFabricConfig(_ConfigMapping): skills: Optional skill configuration. telemetry: Optional telemetry configuration. relay: Optional Relay integration configuration. - profiles: Optional profile-discovery configuration. tools: Optional harness-neutral tool configuration. extra_fields: Preserved extension fields not recognized by this SDK. """ @@ -585,7 +575,6 @@ class _ResolvedFabricConfig(_ConfigMapping): "skills", "telemetry", "relay", - "profiles", "tools", } ) @@ -604,7 +593,6 @@ def __init__( skills: Mapping[str, Any] | None = None, telemetry: Mapping[str, Any] | None = None, relay: Mapping[str, Any] | None = None, - profiles: Mapping[str, Any] | None = None, tools: Mapping[str, Any] | None = None, extra_fields: Mapping[str, Any] | None = None, ) -> None: @@ -634,7 +622,6 @@ def __init__( ("skills", skills_value), ("telemetry", telemetry_value), ("relay", relay_value), - ("profiles", profiles), ("tools", tools_value), ): if item is not None: @@ -643,7 +630,7 @@ def __init__( @classmethod def from_mapping(cls, value: Mapping[str, Any]) -> "_ResolvedFabricConfig": - """Build a typed agent config from the ``agent.yaml`` mapping shape.""" + """Build a typed agent config from a mapping.""" data = _mapping(value, "FabricConfig") if "metadata" not in data: @@ -661,7 +648,6 @@ def from_mapping(cls, value: Mapping[str, Any]) -> "_ResolvedFabricConfig": skills=data.get("skills"), telemetry=data.get("telemetry"), relay=data.get("relay"), - profiles=data.get("profiles"), tools=data.get("tools"), extra_fields={key: item for key, item in data.items() if key not in cls._fields}, ) @@ -944,31 +930,22 @@ def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: class EffectiveConfig(FabricMapping): - """Immutable result of config loading and ordered profile application. + """Immutable result of resolving a complete typed config. Attributes: agent_name: Resolved agent name. - profiles: Applied profile names in caller order. - agent_root: Root directory of the path-backed agent source. - config_path: Source config path, or ``None`` for typed configs. - config_root: Base directory used to resolve relative paths. + base_dir: Base directory used to resolve relative paths. config: Fully resolved typed ``FabricConfig``. """ agent_name: str - profiles: Sequence[str] - agent_root: Path - config_path: Path | None - config_root: Path + base_dir: Path config: _ResolvedFabricConfig - _fields = frozenset({"agent_name", "profiles", "agent_root", "config_path", "config_root", "config"}) + _fields = frozenset({"agent_name", "base_dir", "config"}) @classmethod def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: - data["profiles"] = _required_profiles(data, "EffectiveConfig") - data["agent_root"] = Path(data.get("agent_root", ".")) - data["config_root"] = Path(data.get("config_root", ".")) - data["config_path"] = None if data.get("config_path") is None else Path(data["config_path"]) + data["base_dir"] = Path(data.get("base_dir", ".")) data["config"] = _ResolvedFabricConfig.from_mapping(data.get("config", {})) return data @@ -979,17 +956,15 @@ class RunPlan(FabricMapping): Attributes: effective_config: Resolved configuration snapshot. agent_name: Resolved agent name. - profiles: Applied profile names in caller order. adapter: Resolved adapter identity. capabilities: Operations declared by the resolved runtime. """ effective_config: EffectiveConfig agent_name: str - profiles: Sequence[str] adapter: AdapterInfo capabilities: RuntimeCapabilities - _fields = frozenset({"effective_config", "agent_name", "profiles", "adapter", "capabilities"}) + _fields = frozenset({"effective_config", "agent_name", "adapter", "capabilities"}) @classmethod def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: @@ -997,7 +972,6 @@ def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: if descriptor is None: descriptor = (data.get("adapter_descriptor") or {}).get("descriptor", {}) data["effective_config"] = EffectiveConfig.from_mapping(data["effective_config"]) - data["profiles"] = _required_profiles(data, "RunPlan") data["adapter"] = AdapterInfo.from_mapping(descriptor) data["capabilities"] = RuntimeCapabilities.from_mapping(data.get("capabilities", {})) return data @@ -1031,20 +1005,17 @@ class DoctorReport(FabricMapping): Attributes: agent_name: Resolved agent name. - profiles: Applied profile names in caller order. status: Aggregate outcome: ``pass``, ``warn``, or ``fail``. checks: Ordered ``DoctorCheck`` results. """ agent_name: str - profiles: Sequence[str] status: str checks: Sequence[DoctorCheck] - _fields = frozenset({"agent_name", "profiles", "status", "checks"}) + _fields = frozenset({"agent_name", "status", "checks"}) @classmethod def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: - data["profiles"] = _required_profiles(data, "DoctorReport") data["checks"] = tuple(DoctorCheck.from_mapping(check) for check in data.get("checks", [])) return data @@ -1265,7 +1236,6 @@ class RunResult(FabricMapping): Attributes: agent_name: Resolved agent name. - profiles: Applied profile names. harness: Stable harness identifier. adapter_kind: Adapter execution mechanism. adapter_id: Fabric adapter identifier. @@ -1283,7 +1253,6 @@ class RunResult(FabricMapping): """ agent_name: str - profiles: Sequence[str] harness: str adapter_kind: str adapter_id: str @@ -1300,7 +1269,6 @@ class RunResult(FabricMapping): _fields = frozenset( { "agent_name", - "profiles", "harness", "adapter_kind", "adapter_id", @@ -1320,7 +1288,6 @@ class RunResult(FabricMapping): @classmethod def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: - data["profiles"] = _required_profiles(data, "RunResult") for field in ( "agent_name", "harness", diff --git a/schemas/SCHEMA.md b/schemas/SCHEMA.md index ef262463c..d1fbe3809 100644 --- a/schemas/SCHEMA.md +++ b/schemas/SCHEMA.md @@ -15,16 +15,15 @@ schema-alignment tests in the same change. ## Exported Schemas -`fabric schema` exports the current public typed contract. +The core schema generator exports the current public typed contract. ### Config And Planning -- `agent`: portable base `agent.yaml` config. -- `profile`: profile config applied over an agent config. +- `agent`: complete typed `FabricConfig`. - `adapter-descriptor`: minimal adapter descriptor consumed by Fabric. Each descriptor declares a `contract_version`; Fabric rejects descriptors for unsupported adapter contracts during planning. -- `effective-config`: merged config after profile resolution. +- `effective-config`: validated config plus its explicit base directory. - `run-plan`: executable plan derived from effective config. ### Adapter Invocation @@ -57,16 +56,10 @@ and export them here. ## How To Maintain -Use the Fabric CLI to regenerate them after intentional contract changes: +Use the core generator to regenerate them after intentional contract changes: ```bash -cargo run -p nemo-fabric-cli -- schema --output-dir schemas -``` - -Use the CLI to inspect one schema: - -```bash -cargo run -p nemo-fabric-cli -- schema --name agent +cargo run -p nemo-fabric-core --example generate-schemas -- schemas ``` To add a new schema-backed typed model: @@ -76,8 +69,7 @@ To add a new schema-backed typed model: 3. Add a `SchemaName` variant in `crates/fabric-core/src/schema.rs`. 4. Add the variant to `SchemaName::ALL`, `as_str()`, `parse()`, and `generate_schema()`. -5. Regenerate schemas with `cargo run -p nemo-fabric-cli -- schema --output-dir - schemas`. +5. Regenerate schemas with the command above. 6. Add the new schema to the exported list above. Run `cargo test` after regenerating schemas. The snapshot tests compare the diff --git a/schemas/adapter-descriptor.schema.json b/schemas/adapter-descriptor.schema.json index 7eb806702..6c93bfe13 100644 --- a/schemas/adapter-descriptor.schema.json +++ b/schemas/adapter-descriptor.schema.json @@ -217,4 +217,4 @@ ], "title": "AdapterDescriptor", "type": "object" -} +} \ No newline at end of file diff --git a/schemas/adapter-invocation.schema.json b/schemas/adapter-invocation.schema.json index d1624c1ca..5422b954a 100644 --- a/schemas/adapter-invocation.schema.json +++ b/schemas/adapter-invocation.schema.json @@ -217,42 +217,24 @@ ] }, "EffectiveConfig": { - "description": "Merged Fabric config after applying selected profiles.", + "description": "Resolved Fabric config with explicit path context.", "properties": { "agent_name": { "description": "Stable agent name.", "type": "string" }, - "agent_root": { - "description": "Root used to resolve agent package paths.", + "base_dir": { + "description": "Base directory used to resolve relative config paths.", "type": "string" }, "config": { "$ref": "#/$defs/FabricConfig", - "description": "Merged Fabric config with authoring-time profile discovery removed." - }, - "config_path": { - "description": "Resolved Fabric config path.", - "type": "string" - }, - "config_root": { - "description": "Root used to resolve config-local paths.", - "type": "string" - }, - "profiles": { - "description": "Ordered selected profiles.", - "items": { - "type": "string" - }, - "type": "array" + "description": "Complete typed Fabric config." } }, "required": [ "agent_name", - "profiles", - "agent_root", - "config_path", - "config_root", + "base_dir", "config" ], "type": "object" @@ -418,10 +400,6 @@ "description": "Model aliases.", "type": "object" }, - "profiles": { - "$ref": "#/$defs/ProfileRegistryConfig", - "description": "Optional profile discovery config." - }, "relay": { "anyOf": [ { @@ -649,20 +627,6 @@ ], "type": "object" }, - "ProfileRegistryConfig": { - "additionalProperties": true, - "description": "Profile discovery config for curated package profiles.", - "properties": { - "directories": { - "description": "Directories searched when a caller selects a profile by name.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, "RelayAtifConfig": { "additionalProperties": true, "description": "Relay ATIF export configuration.", @@ -1287,7 +1251,7 @@ "description": "Request payload for the harness." }, "overrides": { - "description": "Per-invocation overrides allowed by the resolved profile." + "description": "Per-invocation overrides allowed by the resolved config." }, "request_id": { "description": "Request id.", diff --git a/schemas/agent.schema.json b/schemas/agent.schema.json index f664b81ed..f00e5ec78 100644 --- a/schemas/agent.schema.json +++ b/schemas/agent.schema.json @@ -226,20 +226,6 @@ ], "type": "object" }, - "ProfileRegistryConfig": { - "additionalProperties": true, - "description": "Profile discovery config for curated package profiles.", - "properties": { - "directories": { - "description": "Directories searched when a caller selects a profile by name.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, "RelayAtifConfig": { "additionalProperties": true, "description": "Relay ATIF export configuration.", @@ -969,10 +955,6 @@ "description": "Model aliases.", "type": "object" }, - "profiles": { - "$ref": "#/$defs/ProfileRegistryConfig", - "description": "Optional profile discovery config." - }, "relay": { "anyOf": [ { diff --git a/schemas/effective-config.schema.json b/schemas/effective-config.schema.json index 6fc533a5b..cabac9c7f 100644 --- a/schemas/effective-config.schema.json +++ b/schemas/effective-config.schema.json @@ -124,10 +124,6 @@ "description": "Model aliases.", "type": "object" }, - "profiles": { - "$ref": "#/$defs/ProfileRegistryConfig", - "description": "Optional profile discovery config." - }, "relay": { "anyOf": [ { @@ -332,20 +328,6 @@ ], "type": "object" }, - "ProfileRegistryConfig": { - "additionalProperties": true, - "description": "Profile discovery config for curated package profiles.", - "properties": { - "directories": { - "description": "Directories searched when a caller selects a profile by name.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, "RelayAtifConfig": { "additionalProperties": true, "description": "Relay ATIF export configuration.", @@ -1035,42 +1017,24 @@ } }, "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "Merged Fabric config after applying selected profiles.", + "description": "Resolved Fabric config with explicit path context.", "properties": { "agent_name": { "description": "Stable agent name.", "type": "string" }, - "agent_root": { - "description": "Root used to resolve agent package paths.", + "base_dir": { + "description": "Base directory used to resolve relative config paths.", "type": "string" }, "config": { "$ref": "#/$defs/FabricConfig", - "description": "Merged Fabric config with authoring-time profile discovery removed." - }, - "config_path": { - "description": "Resolved Fabric config path.", - "type": "string" - }, - "config_root": { - "description": "Root used to resolve config-local paths.", - "type": "string" - }, - "profiles": { - "description": "Ordered selected profiles.", - "items": { - "type": "string" - }, - "type": "array" + "description": "Complete typed Fabric config." } }, "required": [ "agent_name", - "profiles", - "agent_root", - "config_path", - "config_root", + "base_dir", "config" ], "title": "EffectiveConfig", diff --git a/schemas/error-info.schema.json b/schemas/error-info.schema.json index 7f72f0e76..b989b1cc1 100644 --- a/schemas/error-info.schema.json +++ b/schemas/error-info.schema.json @@ -5,7 +5,7 @@ "oneOf": [ { "const": "config", - "description": "Configuration or profile loading failed.", + "description": "Configuration resolution failed.", "type": "string" }, { diff --git a/schemas/profile.schema.json b/schemas/profile.schema.json deleted file mode 100644 index 63ebc6e0d..000000000 --- a/schemas/profile.schema.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "$defs": { - "ToolsConfig": { - "additionalProperties": true, - "description": "Harness-neutral tool capability configuration.", - "properties": { - "blocked": { - "description": "Adapter-native tool names or toolset names to block.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - } - }, - "$schema": "https://json-schema.org/draft/2020-12/schema", - "additionalProperties": true, - "properties": { - "description": { - "description": "Optional profile description.", - "type": [ - "string", - "null" - ] - }, - "environment": { - "additionalProperties": true, - "description": "Partial environment overlay.", - "type": [ - "object", - "null" - ] - }, - "harness": { - "additionalProperties": true, - "description": "Partial harness overlay.", - "type": [ - "object", - "null" - ] - }, - "mcp": { - "additionalProperties": true, - "description": "Partial MCP overlay.", - "type": [ - "object", - "null" - ] - }, - "models": { - "additionalProperties": true, - "description": "Partial model overlays by alias.", - "type": [ - "object", - "null" - ] - }, - "name": { - "description": "Optional profile name used for directory discovery.", - "type": [ - "string", - "null" - ] - }, - "relay": { - "additionalProperties": true, - "description": "Partial Relay integration overlay.", - "type": [ - "object", - "null" - ] - }, - "runtime": { - "additionalProperties": true, - "description": "Partial runtime overlay.", - "type": [ - "object", - "null" - ] - }, - "schema_version": { - "description": "Optional profile schema version.", - "type": [ - "string", - "null" - ] - }, - "skills": { - "additionalProperties": true, - "description": "Partial skill overlay.", - "type": [ - "object", - "null" - ] - }, - "telemetry": { - "additionalProperties": true, - "description": "Partial telemetry overlay.", - "type": [ - "object", - "null" - ] - }, - "tools": { - "anyOf": [ - { - "$ref": "#/$defs/ToolsConfig" - }, - { - "type": "null" - } - ], - "description": "Tool capability overlay." - } - }, - "title": "ProfileConfig", - "type": "object" -} \ No newline at end of file diff --git a/schemas/run-plan.schema.json b/schemas/run-plan.schema.json index a5f6efd4e..86a842b26 100644 --- a/schemas/run-plan.schema.json +++ b/schemas/run-plan.schema.json @@ -367,42 +367,24 @@ ] }, "EffectiveConfig": { - "description": "Merged Fabric config after applying selected profiles.", + "description": "Resolved Fabric config with explicit path context.", "properties": { "agent_name": { "description": "Stable agent name.", "type": "string" }, - "agent_root": { - "description": "Root used to resolve agent package paths.", + "base_dir": { + "description": "Base directory used to resolve relative config paths.", "type": "string" }, "config": { "$ref": "#/$defs/FabricConfig", - "description": "Merged Fabric config with authoring-time profile discovery removed." - }, - "config_path": { - "description": "Resolved Fabric config path.", - "type": "string" - }, - "config_root": { - "description": "Root used to resolve config-local paths.", - "type": "string" - }, - "profiles": { - "description": "Ordered selected profiles.", - "items": { - "type": "string" - }, - "type": "array" + "description": "Complete typed Fabric config." } }, "required": [ "agent_name", - "profiles", - "agent_root", - "config_path", - "config_root", + "base_dir", "config" ], "type": "object" @@ -568,10 +550,6 @@ "description": "Model aliases.", "type": "object" }, - "profiles": { - "$ref": "#/$defs/ProfileRegistryConfig", - "description": "Optional profile discovery config." - }, "relay": { "anyOf": [ { @@ -799,20 +777,6 @@ ], "type": "object" }, - "ProfileRegistryConfig": { - "additionalProperties": true, - "description": "Profile discovery config for curated package profiles.", - "properties": { - "directories": { - "description": "Directories searched when a caller selects a profile by name.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, "RelayAtifConfig": { "additionalProperties": true, "description": "Relay ATIF export configuration.", @@ -1654,8 +1618,8 @@ "description": "Stable agent name.", "type": "string" }, - "agent_root": { - "description": "Root used to resolve agent package paths.", + "base_dir": { + "description": "Base directory used to resolve relative config paths.", "type": "string" }, "capabilities": { @@ -1681,19 +1645,11 @@ }, "config": { "$ref": "#/$defs/FabricConfig", - "description": "Resolved Fabric profile config." - }, - "config_path": { - "description": "Resolved Fabric config path.", - "type": "string" - }, - "config_root": { - "description": "Root used to resolve config-local paths.", - "type": "string" + "description": "Complete typed Fabric config." }, "effective_config": { "$ref": "#/$defs/EffectiveConfig", - "description": "Merged config and provenance used as the base for this plan." + "description": "Resolved config used as the base for this plan." }, "environment_plan": { "anyOf": [ @@ -1706,13 +1662,6 @@ ], "description": "Resolved environment plan." }, - "profiles": { - "description": "Ordered selected profiles.", - "items": { - "type": "string" - }, - "type": "array" - }, "resolution": { "anyOf": [ { @@ -1739,13 +1688,10 @@ "required": [ "effective_config", "agent_name", - "profiles", "capabilities", - "agent_root", - "config_path", - "config_root", + "base_dir", "config" ], "title": "RunPlan", "type": "object" -} +} \ No newline at end of file diff --git a/schemas/run-request.schema.json b/schemas/run-request.schema.json index 506d095c6..8397f0225 100644 --- a/schemas/run-request.schema.json +++ b/schemas/run-request.schema.json @@ -12,7 +12,7 @@ "description": "Request payload for the harness." }, "overrides": { - "description": "Per-invocation overrides allowed by the resolved profile." + "description": "Per-invocation overrides allowed by the resolved config." }, "request_id": { "description": "Request id.", diff --git a/schemas/run-result.schema.json b/schemas/run-result.schema.json index ca1a2a3d9..54272bb3b 100644 --- a/schemas/run-result.schema.json +++ b/schemas/run-result.schema.json @@ -113,7 +113,7 @@ "oneOf": [ { "const": "config", - "description": "Configuration or profile loading failed.", + "description": "Configuration resolution failed.", "type": "string" }, { @@ -285,13 +285,6 @@ "default": null, "description": "Primary output." }, - "profiles": { - "description": "Ordered profiles applied to this run.", - "items": { - "type": "string" - }, - "type": "array" - }, "request_id": { "description": "Request id.", "type": "string" @@ -318,7 +311,6 @@ }, "required": [ "agent_name", - "profiles", "harness", "adapter_kind", "runtime_id", diff --git a/tests/_utils/configs.py b/tests/_utils/configs.py new file mode 100644 index 000000000..c72d23ed6 --- /dev/null +++ b/tests/_utils/configs.py @@ -0,0 +1,90 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Complete typed configs shared by Fabric tests.""" + +from __future__ import annotations + +from nemo_fabric import EnvironmentConfig, FabricConfig, HarnessConfig, MetadataConfig, ModelConfig, RuntimeConfig + + +def hermes_shim_config() -> FabricConfig: + config = FabricConfig( + metadata=MetadataConfig(name="hermes-shim-agent", description="Test-only Hermes-shaped agent."), + harness=HarnessConfig( + adapter_id="test.fabric.hermes_shim", + resolution="preinstalled", + settings={"workspace": "./repos/my-service"}, + ), + models={"default": ModelConfig(provider="test", model="test-model", temperature=0.0)}, + runtime=RuntimeConfig(input_schema="chat", output_schema="message", artifacts="./artifacts"), + environment=EnvironmentConfig( + provider="local", + workspace="./repos/my-service", + artifacts="./artifacts/local", + ), + ) + config.add_skill_path("./skills/code-review") + config.add_mcp_server( + "github", + transport="streamable-http", + url="${GITHUB_MCP_URL}", + exposure="harness_native", + ) + return config + + +def swebench_shim_config() -> FabricConfig: + config = hermes_shim_config() + config.harness = HarnessConfig( + adapter_id="test.fabric.hermes_shim", + resolution="preinstalled", + settings={ + "mode": "swebench_shim", + "workspace": "./repos/my-service", + "target_file": "calculator.py", + "expected_before": "return 41", + "replacement": "return 42", + "new_file": "generated/fix-notes.txt", + "new_file_contents": "patched by Fabric\n", + }, + ) + config.runtime = RuntimeConfig( + input_schema="swe_bench_task", + output_schema="patch_result", + artifacts="./artifacts/swebench", + ) + config.environment = EnvironmentConfig( + provider="local", + workspace="./repos/my-service", + artifacts="./artifacts/swebench", + ) + return config + + +def harbor_swebench_config() -> FabricConfig: + config = hermes_shim_config() + workspace = "./repos/swebench-django-13741" + config.harness = HarnessConfig( + adapter_id="test.fabric.hermes_shim", + resolution="preinstalled", + settings={ + "mode": "swebench_shim", + "workspace": workspace, + "target_file": "django/contrib/auth/forms.py", + "expected_before": " kwargs.setdefault(\"required\", False)\n super().__init__(*args, **kwargs)", + "replacement": " kwargs.setdefault(\"required\", False)\n kwargs.setdefault('disabled', True)\n super().__init__(*args, **kwargs)", + }, + ) + config.runtime = RuntimeConfig( + input_schema="harbor_swe_bench_task", + output_schema="patch_result", + artifacts="./artifacts/harbor-swebench-django-13741", + ) + config.environment = EnvironmentConfig( + provider="local", + workspace=workspace, + artifacts="./artifacts/harbor-swebench-django-13741", + metadata={"source": "harbor_swebench", "instance_id": "django__django-13741"}, + ) + return config diff --git a/tests/_utils/utils.py b/tests/_utils/utils.py index 00c565beb..f0c05848f 100644 --- a/tests/_utils/utils.py +++ b/tests/_utils/utils.py @@ -2,15 +2,15 @@ # SPDX-License-Identifier: Apache-2.0 import json +import os import subprocess +import sys from collections.abc import Mapping from pathlib import Path from typing import Any -import yaml - REPO_ROOT = Path(__file__).resolve().parents[2] -FABRIC_COMMAND = ("cargo", "run", "-q", "-p", "nemo-fabric-cli", "--") +FABRIC_COMMAND = (sys.executable, "-m", "nemo_fabric.cli") def _relay_event_total_tokens(event: dict) -> int: @@ -72,9 +72,15 @@ def run_fabric_cli( timeout: float = 60.0, ) -> subprocess.CompletedProcess[str]: """Run the Fabric CLI from the repository root and capture its output.""" + env = os.environ.copy() + python_path = [str(REPO_ROOT / "python" / "src"), str(REPO_ROOT / "tests")] + if env.get("PYTHONPATH"): + python_path.append(env["PYTHONPATH"]) + env["PYTHONPATH"] = os.pathsep.join(python_path) return subprocess.run( [*FABRIC_COMMAND, *(str(arg) for arg in args)], cwd=REPO_ROOT, + env=env, input=stdin, text=True, capture_output=True, @@ -111,18 +117,3 @@ def assert_process_adapter_native_observability(result: dict): assert_relay_disabled_native_observability(result) assert result["output"]["returncode"] == 0 assert result["output"]["stderr"] == "" - - -def update_base_url(profile_path: Path, api_server: str): - """ - Update the base URL in a profile. - - Since the api_server uses a random available TCP port, the base_url needs to be updated for each test. - - Args: - profile_path (Path): The absolute path to the profile YAML file. - api_server (str): The API server URL. - """ - profile = yaml.safe_load(profile_path.read_text(encoding="utf-8")) - profile["harness"]["settings"]["base_url"] = f"{api_server}/v1" - profile_path.write_text(yaml.safe_dump(profile, sort_keys=False), encoding="utf-8") diff --git a/tests/adapters/test_adapaters_common_utils.py b/tests/adapters/test_adapaters_common_utils.py index 7b4716f40..42b7d0831 100644 --- a/tests/adapters/test_adapaters_common_utils.py +++ b/tests/adapters/test_adapaters_common_utils.py @@ -154,7 +154,7 @@ def test_selected_model_config( def test_payload_accessors_prefer_effective_config(): payload = { "agent_name": "outer-agent", - "config_root": "/outer", + "base_dir": "/outer", "request": {"input": "hello"}, "environment": {"workspace": "/outer-workspace"}, "settings": {"outer": True}, @@ -165,7 +165,7 @@ def test_payload_accessors_prefer_effective_config(): }, "effective_config": { "agent_name": "effective-agent", - "config_root": "/effective", + "base_dir": "/effective", "config": { "harness": {"settings": {"inner": True}}, "models": {"inner": {"model": "inner-model"}}, @@ -177,7 +177,7 @@ def test_payload_accessors_prefer_effective_config(): assert common_utils.effective_config(payload) == payload["effective_config"] assert common_utils.fabric_config(payload) == payload["effective_config"]["config"] assert common_utils.agent_name(payload) == "effective-agent" - assert common_utils.config_root(payload) == "/effective" + assert common_utils.base_dir(payload) == "/effective" assert common_utils.runtime_context(payload) == payload["runtime_context"] assert common_utils.environment_payload(payload) == {"workspace": "/runtime-workspace"} assert common_utils.settings_payload(payload) == {"inner": True} @@ -307,7 +307,7 @@ def test_load_relay_plugin_config_wraps_and_normalizes_bare_observability_config payload = { "effective_config": { "agent_name": "review-agent", - "config_root": str(tmp_path), + "base_dir": str(tmp_path), "config": { "harness": {"settings": {"model": "review"}}, "models": {"review": {"model": "nvidia/review-model"}}, diff --git a/tests/adapters/test_claude_adapter.py b/tests/adapters/test_claude_adapter.py index 26376b1b0..96d10f1d9 100644 --- a/tests/adapters/test_claude_adapter.py +++ b/tests/adapters/test_claude_adapter.py @@ -83,7 +83,7 @@ def claude_payload_fixture(tmp_path) -> dict[str, Any]: return { "effective_config": { "agent_name": "claude-test", - "config_root": str(tmp_path), + "base_dir": str(tmp_path), "config": { "harness": { "adapter_id": "nvidia.fabric.claude", diff --git a/tests/adapters/test_codex_adapter.py b/tests/adapters/test_codex_adapter.py index 991605de0..118a9f1c7 100644 --- a/tests/adapters/test_codex_adapter.py +++ b/tests/adapters/test_codex_adapter.py @@ -22,7 +22,7 @@ def codex_payload_fixture(tmp_path): return { "effective_config": { "agent_name": "codex-test", - "config_root": str(tmp_path), + "base_dir": str(tmp_path), "config": { "harness": { "adapter_id": "nvidia.fabric.codex", @@ -207,7 +207,7 @@ def test_sdk_can_use_an_explicit_codex_runtime(codex_payload, mock_codex, tmp_pa @pytest.mark.parametrize("codex_bin", ["bin/codex", "~/bin/codex"]) -def test_sdk_resolves_relative_codex_runtime_from_config_root( +def test_sdk_resolves_relative_codex_runtime_from_base_dir( codex_payload, codex_bin ): codex_payload["effective_config"]["config"]["harness"]["settings"][ @@ -216,8 +216,8 @@ def test_sdk_resolves_relative_codex_runtime_from_config_root( config = adapter.sdk_config(codex_payload, relay=None) - config_root = Path(codex_payload["effective_config"]["config_root"]) - assert config.codex_bin == str((config_root / codex_bin).resolve()) + base_dir = Path(codex_payload["effective_config"]["base_dir"]) + assert config.codex_bin == str((base_dir / codex_bin).resolve()) def test_sdk_keeps_absolute_codex_runtime_path(codex_payload, tmp_path): @@ -415,7 +415,7 @@ def test_prepare_relay_reuses_one_resolved_executable( assert relay.gateway.executable == executable assert relay.gateway.url == "http://127.0.0.1:43210" resolve.assert_called_once_with( - Path(codex_payload["effective_config"]["config_root"]).resolve(), + Path(codex_payload["effective_config"]["base_dir"]).resolve(), "nemo-relay", ) contract.assert_called_once_with(executable) diff --git a/tests/adapters/test_deepagents.py b/tests/adapters/test_deepagents.py index 40b904828..017f6aea6 100644 --- a/tests/adapters/test_deepagents.py +++ b/tests/adapters/test_deepagents.py @@ -127,7 +127,7 @@ def make_payload_fixture(): def make(tmp_path: Path, *, runtime_id: str = "run-1") -> dict[str, Any]: return { "effective_config": { - "config_root": str(tmp_path), + "base_dir": str(tmp_path), "config": { "harness": {"settings": {"system_prompt": "be concise"}}, "models": { diff --git a/tests/adapters/test_hermes_adapter.py b/tests/adapters/test_hermes_adapter.py index 5cd6b408a..fdd9b0f95 100644 --- a/tests/adapters/test_hermes_adapter.py +++ b/tests/adapters/test_hermes_adapter.py @@ -221,7 +221,7 @@ def test_hermes_config_variation_matrix_surfaces_supported_capabilities( }, "effective_config": { "agent_name": "matrix-agent", - "config_root": str(tmp_path), + "base_dir": str(tmp_path), "config": { "harness": { "settings": { @@ -409,7 +409,7 @@ async def test_fabric_runtime_id_drives_hermes_session_id_and_db_history( payload = { "effective_config": { "agent_name": "demo", - "config_root": str(tmp_path), + "base_dir": str(tmp_path), "config": { "harness": { "settings": { diff --git a/tests/conftest.py b/tests/conftest.py index 15a8ffaa8..f68f47af6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -92,22 +92,6 @@ def code_review_agent_dir_fixture(repo_root: Path, tmp_path: Path) -> Path: ) -@pytest.fixture(name="file_config_agent_dir_src", scope="session") -def file_config_agent_dir_src_fixture(repo_root: Path) -> Path: - """Return the test-only portable config package.""" - - return repo_root / "tests" / "fixtures" / "file-config-agent" - - -@pytest.fixture(name="file_config_agent_dir") -def file_config_agent_dir_fixture( - file_config_agent_dir_src: Path, - tmp_path: Path, -) -> Path: - """Create a writable copy for CLI and file-profile tests.""" - - return _copy_agent_dir(file_config_agent_dir_src, tmp_path, "file-config-agent") - @pytest.fixture(name="api_server") def api_server_fixture(unused_tcp_port: int) -> Iterator[str]: from _utils.mock_api_server import mock_api_server diff --git a/tests/e2e/test_cli.py b/tests/e2e/test_cli.py index b6100c3f0..ec11cbcb5 100644 --- a/tests/e2e/test_cli.py +++ b/tests/e2e/test_cli.py @@ -1,200 +1,130 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -"""Smoke test for CLI examples used in the Fabric README/design snippets.""" +"""End-to-end coverage for the SDK-backed ``nemo-fabric`` CLI.""" from __future__ import annotations import json +import os import subprocess +import sys from pathlib import Path +from typing import Any -from _utils.utils import assert_relay_disabled_native_observability -from _utils.utils import run_fabric_cli - - -def test_cli( - tmp_path: Path, - file_config_agent_dir: Path, - hermes_shim_agent_dir: Path, -): - temp_example = file_config_agent_dir - temp_fixture = hermes_shim_agent_dir - - assert call_text("validate", temp_example).startswith("validated") - inspected = call_json("inspect", temp_example) - assert inspected["agent_name"] == "code-review-agent" - assert inspected.get("profiles", []) == [] - assert inspected["config"]["metadata"]["name"] == "code-review-agent" - - plan = call_json("plan", temp_example, "--profile", "env_local") - assert plan["agent_name"] == "code-review-agent" - assert plan["effective_config"]["agent_name"] == "code-review-agent" - assert plan["effective_config"]["profiles"] == ["env_local"] - assert plan["adapter_descriptor"]["source"] == "repository" - assert ( - plan["adapter_descriptor"]["descriptor"]["adapter_id"] - == "nvidia.fabric.hermes" - ) +ROOT = Path(__file__).resolve().parents[2] + + +def test_discovery_commands() -> None: + presets = run("preset", "list") + examples = run("example", "list") + + assert presets.returncode == 0, presets.stderr + assert {"hermes", "claude", "codex", "deepagents"}.issubset(presets.stdout.splitlines()) + assert examples.returncode == 0, examples.stderr + assert "examples.code_review_agent" in examples.stdout.splitlines() + + shown = call_json("example", "show", "examples.code_review_agent") + assert shown["default_variant"] == "hermes" + assert set(shown["variants"]) == {"hermes", "claude", "codex", "deepagents"} - agent_schema = call_json("schema", "--name", "agent") - assert agent_schema["title"] == "FabricConfig" - - schema_dir = tmp_path / "schemas" - call_text("schema", "--output-dir", schema_dir) - assert (schema_dir / "agent.schema.json").is_file() - assert (schema_dir / "adapter-descriptor.schema.json").is_file() - assert (schema_dir / "effective-config.schema.json").is_file() - assert (schema_dir / "adapter-invocation.schema.json").is_file() - assert (schema_dir / "runtime-context.schema.json").is_file() - assert (schema_dir / "environment-handle.schema.json").is_file() - assert (schema_dir / "runtime-handle.schema.json").is_file() - assert (schema_dir / "invocation-handle.schema.json").is_file() - assert (schema_dir / "error-info.schema.json").is_file() - assert (schema_dir / "fabric-event.schema.json").is_file() - - direct_profile = temp_example / "profiles" / "hermes.yaml" - direct_plan = call_json("plan", temp_example, "--profile", direct_profile) - assert direct_plan["profiles"] == [str(direct_profile)] - assert ( - direct_plan["adapter_descriptor"]["descriptor"]["adapter_id"] - == "nvidia.fabric.hermes" - ) - profile_plans = [ - (("hermes",), "nvidia.fabric.hermes", "python", False), - (("hermes", "relay"), "nvidia.fabric.hermes", "python", True), - ] - for profiles, adapter_id, adapter_kind, relay_enabled in profile_plans: - profile_args = [arg for profile in profiles for arg in ("--profile", profile)] - profile_plan = call_json("plan", temp_example, *profile_args) - assert profile_plan["profiles"] == list(profiles) - descriptor = profile_plan["adapter_descriptor"]["descriptor"] - assert descriptor["adapter_id"] == adapter_id - assert descriptor["adapter_kind"] == adapter_kind - assert "mode" not in profile_plan["config"]["runtime"] - assert profile_plan["capability_plan"]["native"]["skill_paths"] - assert "github" in profile_plan["capability_plan"]["native"]["mcp_servers"] - if relay_enabled: - telemetry_plan = profile_plan["telemetry_plan"] - assert telemetry_plan["relay_enabled"] is True - assert telemetry_plan["relay_output_dir"] - else: - assert profile_plan.get("telemetry_plan") is None - - multi_plan = call_json( +def test_preset_and_example_reach_the_same_sdk_plan_path() -> None: + preset = call_json("plan", "--preset", "hermes") + example = call_json( "plan", - temp_fixture, - "--profile", - "env_local", - "--profile", - "mcp_github", + "--example", + "examples.code_review_agent", + "--variant", + "hermes", ) - assert multi_plan["profiles"] == ["env_local", "mcp_github"] - assert "profile" not in multi_plan - assert multi_plan["telemetry_plan"]["relay_enabled"] is True - doctor = call_json("doctor", temp_fixture, "--profile", "env_local") + assert preset["adapter_descriptor"]["descriptor"]["adapter_id"] == "nvidia.fabric.hermes" + assert example["adapter_descriptor"]["descriptor"]["adapter_id"] == "nvidia.fabric.hermes" + assert preset["effective_config"]["base_dir"].endswith("nemo_fabric/_bundled") + assert example["capability_plan"]["native"]["skill_paths"] + + +def test_factory_plan_doctor_and_run(hermes_shim_agent_dir: Path) -> None: + selector = ( + "--factory", + "_utils.configs:hermes_shim_config", + "--base-dir", + hermes_shim_agent_dir, + ) + plan = call_json("plan", *selector) + doctor = call_json("doctor", *selector) + result = call_json("run", *selector, "--input", "hello factory") + + assert plan["agent_name"] == "hermes-shim-agent" + assert plan["adapter_descriptor"]["descriptor"]["adapter_id"] == "test.fabric.hermes_shim" assert doctor["agent_name"] == "hermes-shim-agent" assert doctor["checks"] + assert result["status"] == "succeeded" + assert result["output"]["received"] == "hello factory" - hermes = call_json( - "run", temp_fixture, "--profile", "env_local", "--input", "hello hermes" - ) - assert hermes["status"] == "succeeded" - assert hermes["adapter_kind"] == "python" - assert hermes["output"]["mode"] == "shim" - assert hermes["output"]["native_skill_paths"] - assert hermes["output"]["native_mcp_servers"] == ["github"] - assert hermes["output"]["managed_skill_paths"] == [] - assert hermes["output"]["managed_mcp_servers"] == [] - assert_relay_disabled_native_observability(hermes) - - second_run = call_json( - "run", - temp_fixture, - "--profile", - "env_local", - "--input", - "hello runtime hermes", - ) - assert second_run["status"] == "succeeded" - assert second_run["output"]["runtime_id"] == second_run["runtime_id"] +def test_factory_request_json_and_output_file(hermes_shim_agent_dir: Path, tmp_path: Path) -> None: + output = tmp_path / "result.json" request = json.dumps( { - "request_id": "cli-structured-request", - "input": "hello structured hermes", - "context": {"task": {"source": "smoke"}}, + "input": "hello request", + "request_id": "cli-request-1", + "context": {"source": "test"}, } ) - structured = call_json( - "run", temp_fixture, "--profile", "env_local", "--request-json", request - ) - assert structured["request_id"] == "cli-structured-request" - assert structured["output"]["received"] == "hello structured hermes" - - chat = run_with_stdin( - "/help\n/verbose on\nhello chat\n/verbose off\n/clear\n/info\n/exit\n", - "chat", - temp_fixture, - "--profile", - "env_local", - "--verbose", + completed = run( + "run", + "--factory", + "_utils.configs:hermes_shim_config", + "--base-dir", + hermes_shim_agent_dir, + "--request-json", + request, + "--output", + output, ) - assert chat.stdout == "" - assert '"received": "hello chat"' in chat.stderr - assert '"runtime_id": "runtime-' in chat.stderr - assert "NEMO FABRIC" in chat.stderr - assert "interactive runtime" in chat.stderr - assert "agent: hermes-shim-agent" in chat.stderr - assert "profile: env_local" in chat.stderr - assert "harness: hermes" in chat.stderr - assert "adapter: python" in chat.stderr - assert chat.stderr.count("runtime_id: runtime-") >= 2 - assert "you[env_local:runtime-" in chat.stderr - assert "> \nagent> {" in chat.stderr - assert "agent> {" in chat.stderr - assert "runtime_id: runtime-" in chat.stderr - assert "/verbose on|off" in chat.stderr - assert "/clear" in chat.stderr - assert "verbose: on" in chat.stderr - assert "verbose: off" in chat.stderr - assert "\x1b[2J\x1b[H" in chat.stderr - assert "\n\n+-- turn 1 metadata" in chat.stderr - assert "+-- turn 1 metadata" in chat.stderr - assert "| status: succeeded" in chat.stderr - assert "| request_id: request-" in chat.stderr - assert "| invocation_id: invocation-" in chat.stderr - assert "| artifact_count:" in chat.stderr - -def call_text(*args: object) -> str: - completed = run(*args) - return completed.stdout.strip() + assert completed.returncode == 0, completed.stderr + assert completed.stdout == "" + result = json.loads(output.read_text(encoding="utf-8")) + assert result["request_id"] == "cli-request-1" + assert result["output"]["received"] == "hello request" -def call_json(*args: object) -> dict: - completed = run(*args) - return json.loads(completed.stdout) - - -def run(*args: object) -> subprocess.CompletedProcess[str]: - completed = run_fabric_cli(*args) - if completed.returncode != 0: - raise AssertionError( - f"command failed: {completed.args}\nstdout:\n{completed.stdout}\nstderr:\n{completed.stderr}" - ) - return completed +def test_selector_errors_are_actionable() -> None: + unknown = run("plan", "--preset", "missing") + malformed = run("plan", "--factory", "missing-factory") + conflict = run("plan", "--preset", "hermes", "--factory", "mod:factory") -def run_with_stdin(stdin: str, *args: object) -> subprocess.CompletedProcess[str]: - completed = run_raw(stdin, *args) - if completed.returncode != 0: - raise AssertionError( - f"command failed: {completed.args}\nstdout:\n{completed.stdout}\nstderr:\n{completed.stderr}" - ) - return completed + assert unknown.returncode == 2 + assert "available:" in unknown.stderr + assert malformed.returncode == 2 + assert "module:callable" in malformed.stderr + assert conflict.returncode == 2 + assert "not allowed with argument" in conflict.stderr -def run_raw(stdin: str, *args: object) -> subprocess.CompletedProcess[str]: - return run_fabric_cli(*args, stdin=stdin) +def call_json(*args: Any) -> dict[str, Any]: + completed = run(*args) + assert completed.returncode == 0, completed.stderr + value = json.loads(completed.stdout) + assert isinstance(value, dict) + return value + + +def run(*args: Any) -> subprocess.CompletedProcess[str]: + env = os.environ.copy() + python_path = [str(ROOT / "python" / "src"), str(ROOT / "tests")] + if env.get("PYTHONPATH"): + python_path.append(env["PYTHONPATH"]) + env["PYTHONPATH"] = os.pathsep.join(python_path) + return subprocess.run( + [sys.executable, "-m", "nemo_fabric.cli", *(str(arg) for arg in args)], + cwd=ROOT, + env=env, + text=True, + capture_output=True, + check=False, + timeout=30, + ) diff --git a/tests/e2e/test_harbor_swebench_task.py b/tests/e2e/test_harbor_swebench_task.py index 05c49a682..c23be7f67 100644 --- a/tests/e2e/test_harbor_swebench_task.py +++ b/tests/e2e/test_harbor_swebench_task.py @@ -5,7 +5,7 @@ This smoke is opt-in because it needs Docker and a local SWE-Bench image. It keeps Harbor responsible for task materialization and verification while Fabric -is responsible for invoking the selected harness profile and collecting a patch. +is responsible for invoking the selected typed harness config and collecting a patch. """ from __future__ import annotations @@ -51,11 +51,12 @@ def test_harbor_swebench_task(hermes_shim_agent_dir: Path): result = call_json( "run", + "--factory", + "_utils.configs:harbor_swebench_config", + "--base-dir", hermes_shim_agent_dir, - "--profile", - "harbor_swebench_django_13741", - "--request-file", - request_file, + "--request-json", + f"@{request_file}", ) assert result["status"] == "succeeded", result diff --git a/tests/e2e/test_hermes_config_mapping.py b/tests/e2e/test_hermes_config_mapping.py index b231ae74b..9920260c2 100644 --- a/tests/e2e/test_hermes_config_mapping.py +++ b/tests/e2e/test_hermes_config_mapping.py @@ -51,7 +51,7 @@ def test_hermes_config_mapping(tmp_path: Path): def payload(tmpdir: str) -> dict: return { "agent_name": "code-review-agent", - "config_root": tmpdir, + "base_dir": tmpdir, "environment": { "workspace": f"{tmpdir}/workspace", }, diff --git a/tests/e2e/test_hermes_e2e.py b/tests/e2e/test_hermes_e2e.py index 118ba7d4f..2ef94a5e6 100644 --- a/tests/e2e/test_hermes_e2e.py +++ b/tests/e2e/test_hermes_e2e.py @@ -118,7 +118,7 @@ async def test_artifacts(self): relay_config = json.loads(relay_config_path.read_text(encoding="utf-8")) assert relay_config["schema_version"] == "fabric.relay/v1alpha1" assert relay_config["relay"]["enabled"] is True - assert relay_config["fabric"]["profiles"] == [] + assert relay_config["fabric"]["agent_name"] == "code-review-agent" async def test_atof_artifacts(self): kinds = {artifact["kind"] for artifact in self.relay_artifacts} diff --git a/tests/e2e/test_local_env_e2e.py b/tests/e2e/test_local_env_e2e.py index baa65c78c..4c6965eca 100644 --- a/tests/e2e/test_local_env_e2e.py +++ b/tests/e2e/test_local_env_e2e.py @@ -12,16 +12,20 @@ def test_local_env_e2e(hermes_shim_agent_dir: Path): - plan = call_json("plan", hermes_shim_agent_dir, "--profile", "env_local") + selector = ( + "--factory", + "_utils.configs:hermes_shim_config", + "--base-dir", + hermes_shim_agent_dir, + ) + plan = call_json("plan", *selector) assert plan["environment_plan"]["provider"] == "local" assert plan["environment_plan"]["workspace"].endswith("repos/my-service") assert plan["adapter_descriptor"]["source"] == "local" result = call_json( "run", - hermes_shim_agent_dir, - "--profile", - "env_local", + *selector, "--request-json", json.dumps( { diff --git a/tests/e2e/test_swebench_style.py b/tests/e2e/test_swebench_style.py index 562854e82..c6a86fffb 100644 --- a/tests/e2e/test_swebench_style.py +++ b/tests/e2e/test_swebench_style.py @@ -19,9 +19,10 @@ def test_swebench_style(hermes_shim_agent_dir: Path): result = call_json( "run", + "--factory", + "_utils.configs:swebench_shim_config", + "--base-dir", hermes_shim_agent_dir, - "--profile", - "swebench_shim", "--input", "Fix the bug so answer() returns 42.", ) diff --git a/tests/fixtures/file-config-agent/agent.yaml b/tests/fixtures/file-config-agent/agent.yaml deleted file mode 100644 index ef77f7063..000000000 --- a/tests/fixtures/file-config-agent/agent.yaml +++ /dev/null @@ -1,48 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.agent/v1alpha1 - -metadata: - name: code-review-agent - description: Test fixture for file-backed configuration and profiles. - -harness: - adapter_id: nvidia.fabric.hermes - resolution: preinstalled - settings: - workspace: ./repos/my-service - -models: - default: - provider: nvidia - model: nvidia/nemotron-3-nano-30b-a3b - temperature: 0.0 - api_key_env: NVIDIA_API_KEY - -runtime: - input_schema: chat - output_schema: message - artifacts: ./artifacts - -environment: - provider: local - workspace: ./repos/my-service - artifacts: ./artifacts/local - -skills: - paths: - - ./skills/code-review - -mcp: - servers: - github: - transport: streamable-http - url: ${GITHUB_MCP_URL} - exposure: harness_native - -telemetry: null - -profiles: - directories: - - ./profiles diff --git a/tests/fixtures/file-config-agent/profiles/codex.yaml b/tests/fixtures/file-config-agent/profiles/codex.yaml deleted file mode 100644 index 9abca5c06..000000000 --- a/tests/fixtures/file-config-agent/profiles/codex.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: codex -description: Test Codex SDK file-profile resolution. - -harness: - adapter_id: nvidia.fabric.codex - resolution: preinstalled - settings: - sandbox: workspace-write - reasoning_effort: high - -models: - default: - provider: openai - model: openai/gpt-5.4 - api_key_env: null - temperature: null - -skills: null -mcp: null - -runtime: - input_schema: text - output_schema: message - artifacts: ./artifacts/codex - -environment: - provider: local - workspace: ./repos/my-service - artifacts: ./artifacts/codex - -telemetry: null diff --git a/tests/fixtures/file-config-agent/profiles/deepagents.yaml b/tests/fixtures/file-config-agent/profiles/deepagents.yaml deleted file mode 100644 index 8f5d507f7..000000000 --- a/tests/fixtures/file-config-agent/profiles/deepagents.yaml +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: deepagents -description: Test Deep Agents file-profile resolution. - -harness: - adapter_id: nvidia.fabric.langchain.deepagents - resolution: preinstalled - settings: - workspace: ./repos/my-service - system_prompt: You are a concise smoke test assistant. - -models: - default: - provider: nvidia - model: nvidia/nemotron-3-nano-30b-a3b - temperature: 0.0 - api_key_env: NVIDIA_API_KEY - -skills: null -mcp: null - -runtime: - input_schema: chat - output_schema: message - artifacts: ./artifacts/deepagents - -environment: - provider: local - workspace: ./repos/my-service - artifacts: ./artifacts/deepagents - -telemetry: - enabled: false diff --git a/tests/fixtures/file-config-agent/profiles/env-local.yaml b/tests/fixtures/file-config-agent/profiles/env-local.yaml deleted file mode 100644 index fe770a7b6..000000000 --- a/tests/fixtures/file-config-agent/profiles/env-local.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: env_local -description: Test local environment file-profile resolution. - -environment: - provider: local - workspace: ./repos/my-service - artifacts: ./artifacts/local - -telemetry: null diff --git a/tests/fixtures/file-config-agent/profiles/env-opensandbox.yaml b/tests/fixtures/file-config-agent/profiles/env-opensandbox.yaml deleted file mode 100644 index d0603fd67..000000000 --- a/tests/fixtures/file-config-agent/profiles/env-opensandbox.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: env_opensandbox -description: Test OpenSandbox environment file-profile resolution. - -environment: - provider: opensandbox - control_location: external_control - workspace: /workspace - artifacts: /workspace/artifacts - metadata: - server_url: http://127.0.0.1:8080 - image: nvcr.io/nvidia/nemo/fabric-hermes:latest - -telemetry: - providers: - relay: {} - -relay: - output_dir: ./artifacts/opensandbox - observability: - atif: - enabled: true - output_directory: ./artifacts/opensandbox diff --git a/tests/fixtures/file-config-agent/profiles/hermes.yaml b/tests/fixtures/file-config-agent/profiles/hermes.yaml deleted file mode 100644 index 8f7870f2c..000000000 --- a/tests/fixtures/file-config-agent/profiles/hermes.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: hermes -description: Test Hermes file-profile resolution. - -harness: - adapter_id: nvidia.fabric.hermes - resolution: preinstalled - settings: - workspace: ./repos/my-service - hermes_home: ./artifacts/hermes-home - base_url: https://integrate.api.nvidia.com/v1 - max_iterations: 1 - max_tokens: 512 - temperature: 0.0 - reasoning_config: - effort: none - enabled_toolsets: [] - system_prompt: You are a concise smoke test assistant. - -runtime: - input_schema: chat - output_schema: message - artifacts: ./artifacts/hermes - -environment: - provider: local - workspace: ./repos/my-service - artifacts: ./artifacts/hermes - -telemetry: null diff --git a/tests/fixtures/file-config-agent/profiles/mcp-github.yaml b/tests/fixtures/file-config-agent/profiles/mcp-github.yaml deleted file mode 100644 index b45bbb80b..000000000 --- a/tests/fixtures/file-config-agent/profiles/mcp-github.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: mcp_github -description: Test MCP and telemetry file-profile merging. - -mcp: - servers: - github: - transport: streamable-http - url: ${GITHUB_MCP_URL} - exposure: fabric_managed - -telemetry: - providers: - relay: {} - -relay: - output_dir: ./artifacts/mcp-github - observability: - atif: - enabled: true - output_directory: ./artifacts/mcp-github - filename_template: "trajectory-{session_id}.atif.json" - atof: - enabled: true - output_directory: ./artifacts/mcp-github - filename: events.atof.jsonl - mode: overwrite diff --git a/tests/fixtures/file-config-agent/profiles/native-otel.yaml b/tests/fixtures/file-config-agent/profiles/native-otel.yaml deleted file mode 100644 index e49a1ee3a..000000000 --- a/tests/fixtures/file-config-agent/profiles/native-otel.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: native_otel -description: Test native telemetry file-profile resolution. - -telemetry: - providers: - native: - config: - version: 1 - components: - - kind: observability - enabled: true - config: - version: 1 - opentelemetry: - enabled: true - transport: http_binary - endpoint: http://localhost:4318/v1/traces - resource_attributes: - deployment.environment: dev diff --git a/tests/fixtures/file-config-agent/profiles/relay-openinference.yaml b/tests/fixtures/file-config-agent/profiles/relay-openinference.yaml deleted file mode 100644 index 0191fcb9a..000000000 --- a/tests/fixtures/file-config-agent/profiles/relay-openinference.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: relay_openinference -description: Test Relay OpenInference file-profile resolution. - -telemetry: - providers: - relay: {} - -relay: - output_dir: ./artifacts/relay-openinference - observability: - atif: - enabled: true - output_directory: ./artifacts/relay-openinference - filename_template: "trajectory-{session_id}.atif.json" - agent_name: code-review-agent - agent_version: fabric-poc - atof: - enabled: true - output_directory: ./artifacts/relay-openinference - filename: events.atof.jsonl - mode: overwrite - openinference: - enabled: true - transport: http_binary - endpoint: http://localhost:6006/v1/traces diff --git a/tests/fixtures/file-config-agent/profiles/relay-otel.yaml b/tests/fixtures/file-config-agent/profiles/relay-otel.yaml deleted file mode 100644 index 662f7d48e..000000000 --- a/tests/fixtures/file-config-agent/profiles/relay-otel.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: relay_otel -description: Test Relay OpenTelemetry file-profile resolution. - -telemetry: - providers: - relay: {} - -relay: - output_dir: ./artifacts/relay-otel - observability: - opentelemetry: - enabled: true - transport: http_binary - endpoint: http://localhost:4318/v1/traces - service_name: code-review-agent - service_namespace: fabric - service_version: fabric-poc - instrumentation_scope: nemo-relay-otel - timeout_millis: 3000 - resource_attributes: - deployment.environment: dev diff --git a/tests/fixtures/file-config-agent/profiles/relay.yaml b/tests/fixtures/file-config-agent/profiles/relay.yaml deleted file mode 100644 index 9550bbd80..000000000 --- a/tests/fixtures/file-config-agent/profiles/relay.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: relay -description: Test Relay ATOF/ATIF file-profile resolution. - -telemetry: - providers: - relay: {} - -relay: - output_dir: ./artifacts/relay - observability: - atif: - enabled: true - output_directory: ./artifacts/relay - filename_template: "trajectory-{session_id}.atif.json" - agent_name: code-review-agent - agent_version: fabric-poc - atof: - enabled: true - output_directory: ./artifacts/relay - filename: events.atof.jsonl - mode: overwrite diff --git a/tests/fixtures/hermes-shim-agent/agent.yaml b/tests/fixtures/hermes-shim-agent/agent.yaml deleted file mode 100644 index 92b17d861..000000000 --- a/tests/fixtures/hermes-shim-agent/agent.yaml +++ /dev/null @@ -1,47 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.agent/v1alpha1 - -metadata: - name: hermes-shim-agent - description: Test-only Hermes-shaped agent package. - -harness: - adapter_id: test.fabric.hermes_shim - resolution: preinstalled - settings: - workspace: ./repos/my-service - -models: - default: - provider: test - model: test-model - temperature: 0.0 - -runtime: - input_schema: chat - output_schema: message - artifacts: ./artifacts - -environment: - provider: local - workspace: ./repos/my-service - artifacts: ./artifacts/local - -skills: - paths: - - ./skills/code-review - -mcp: - servers: - github: - transport: streamable-http - url: ${GITHUB_MCP_URL} - exposure: harness_native - -telemetry: null - -profiles: - directories: - - ./profiles diff --git a/tests/fixtures/hermes-shim-agent/profiles/env-local.yaml b/tests/fixtures/hermes-shim-agent/profiles/env-local.yaml deleted file mode 100644 index 3ad46b047..000000000 --- a/tests/fixtures/hermes-shim-agent/profiles/env-local.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: env_local -description: Local test profile with no isolation and Relay disabled. - -environment: - provider: local - workspace: ./repos/my-service - artifacts: ./artifacts/local - -telemetry: null diff --git a/tests/fixtures/hermes-shim-agent/profiles/harbor-swebench-django-13741.yaml b/tests/fixtures/hermes-shim-agent/profiles/harbor-swebench-django-13741.yaml deleted file mode 100644 index ea1d60aac..000000000 --- a/tests/fixtures/hermes-shim-agent/profiles/harbor-swebench-django-13741.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: harbor_swebench_django_13741 -description: Test-only Harbor SWE-Bench django__django-13741 patch run. - -harness: - adapter_id: test.fabric.hermes_shim - resolution: preinstalled - settings: - mode: swebench_shim - workspace: ./repos/swebench-django-13741 - target_file: django/contrib/auth/forms.py - expected_before: " kwargs.setdefault(\"required\", False)\n super().__init__(*args, **kwargs)" - replacement: " kwargs.setdefault(\"required\", False)\n kwargs.setdefault('disabled', True)\n super().__init__(*args, **kwargs)" - -runtime: - input_schema: harbor_swe_bench_task - output_schema: patch_result - artifacts: ./artifacts/harbor-swebench-django-13741 - -environment: - provider: local - workspace: ./repos/swebench-django-13741 - artifacts: ./artifacts/harbor-swebench-django-13741 - metadata: - source: harbor_swebench - instance_id: django__django-13741 - -telemetry: null diff --git a/tests/fixtures/hermes-shim-agent/profiles/mcp-github.yaml b/tests/fixtures/hermes-shim-agent/profiles/mcp-github.yaml deleted file mode 100644 index a5e9896c7..000000000 --- a/tests/fixtures/hermes-shim-agent/profiles/mcp-github.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: mcp_github -description: Test MCP capability variation. - -mcp: - servers: - github: - transport: streamable-http - url: ${GITHUB_MCP_URL} - exposure: harness_native - -telemetry: - providers: - relay: {} - -relay: - output_dir: ./artifacts/relay diff --git a/tests/fixtures/hermes-shim-agent/profiles/swebench-shim.yaml b/tests/fixtures/hermes-shim-agent/profiles/swebench-shim.yaml deleted file mode 100644 index 850a5661d..000000000 --- a/tests/fixtures/hermes-shim-agent/profiles/swebench-shim.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -schema_version: fabric.profile/v1alpha1 -name: swebench_shim -description: Test-only SWE-Bench-style run that mutates a repo and captures a patch artifact. - -harness: - adapter_id: test.fabric.hermes_shim - resolution: preinstalled - settings: - mode: swebench_shim - workspace: ./repos/my-service - target_file: calculator.py - expected_before: "return 41" - replacement: "return 42" - new_file: generated/fix-notes.txt - new_file_contents: "patched by Fabric\n" - -runtime: - input_schema: swe_bench_task - output_schema: patch_result - artifacts: ./artifacts/swebench - -environment: - provider: local - workspace: ./repos/my-service - artifacts: ./artifacts/swebench - -telemetry: null diff --git a/tests/integrations/test_harbor_runner.py b/tests/integrations/test_harbor_runner.py index 7cb870889..0b1ffbc17 100644 --- a/tests/integrations/test_harbor_runner.py +++ b/tests/integrations/test_harbor_runner.py @@ -413,7 +413,6 @@ def test_swebench_matrix_translates_harbor_inputs_to_typed_config(tmp_path: Path }, ) - assert base.profiles is None assert base.environment is not None assert str(base.environment.workspace) == "/testbed" assert base.models == {} @@ -470,7 +469,6 @@ def test_harbor_lifecycle_populates_context_after_run(tmp_path: Path): result = { "agent_name": "harbor-hermes", - "profiles": [], "harness": "hermes", "adapter_kind": "python", "adapter_id": "nvidia.fabric.hermes", diff --git a/tests/integrations/test_harbor_telemetry.py b/tests/integrations/test_harbor_telemetry.py index 8c7aa2d37..6ca787f35 100644 --- a/tests/integrations/test_harbor_telemetry.py +++ b/tests/integrations/test_harbor_telemetry.py @@ -16,7 +16,6 @@ def make_result(*artifacts: dict[str, str]) -> RunResult: return RunResult.from_mapping( { "agent_name": "harbor-demo", - "profiles": [], "harness": "hermes", "adapter_kind": "python", "adapter_id": "nvidia.fabric.hermes", diff --git a/tests/python/test_code_review_example.py b/tests/python/test_code_review_example.py index 80d5a80d9..61c624122 100644 --- a/tests/python/test_code_review_example.py +++ b/tests/python/test_code_review_example.py @@ -83,12 +83,12 @@ def test_capability_and_telemetry_variants_do_not_mutate_their_input(): assert "relay" in variants[3].telemetry.providers -def test_variants_plan_without_file_profiles(): +def test_variants_plan_from_complete_configs(): client = Fabric() for config in (hermes_config(), codex_config(), claude_config()): plan = client.plan(config, base_dir=BASE_DIR) - assert plan.profiles == () + assert plan.effective_config.base_dir == BASE_DIR assert plan.agent_name == "code-review-agent" assert plan.adapter.adapter_id == config.harness.adapter_id @@ -119,7 +119,6 @@ def test_example_entrypoint_plans_without_starting_a_runtime(): assert completed.returncode == 0, completed.stderr plan = json.loads(completed.stdout) assert plan["agent_name"] == "code-review-agent" - assert plan["profiles"] == [] assert plan["adapter_descriptor"]["descriptor"]["adapter_id"] == adapter_id telemetry_plan = plan.get("telemetry_plan") if relay_enabled: diff --git a/tests/python/test_harbor_integration.py b/tests/python/test_harbor_integration.py index 699aba770..ac6b1145c 100644 --- a/tests/python/test_harbor_integration.py +++ b/tests/python/test_harbor_integration.py @@ -57,7 +57,6 @@ async def exec( self.files[result_path] = json.dumps( { "agent_name": "harbor-demo", - "profiles": [], "harness": "hermes", "adapter_kind": "python", "adapter_id": "nvidia.fabric.hermes", @@ -172,7 +171,6 @@ async def test_harbor_integration(tmp_path: Path): assert context.metadata assert context.metadata["fabric"]["status"] == "succeeded" - assert "profiles" not in context.metadata["fabric"] assert context.metadata["fabric"]["adapter_id"] == "nvidia.fabric.hermes" artifacts = context.metadata["fabric"]["artifacts"]["artifacts"] assert {artifact["name"] for artifact in artifacts} == {"stdout", "workspace_patch"} diff --git a/tests/python/test_native_sdk.py b/tests/python/test_native_sdk.py index 40ea45cb0..f1ff4232b 100644 --- a/tests/python/test_native_sdk.py +++ b/tests/python/test_native_sdk.py @@ -11,11 +11,11 @@ import nemo_fabric._native as native import pytest +from _utils.configs import hermes_shim_config from examples.code_review_agent import BASE_DIR from examples.code_review_agent import base_config from nemo_fabric import Fabric from nemo_fabric import FabricConfig -from nemo_fabric import FabricProfileConfig from nemo_fabric import FabricRuntimeError @@ -31,8 +31,8 @@ async def test_adapter_python_selects_python_adapter_interpreter( os.environ["ADAPTER_PYTHON"] = sys.executable result = await Fabric().run( - hermes_shim_agent_dir, - profiles=["env_local"], + hermes_shim_config(), + base_dir=hermes_shim_agent_dir, input="hello adapter python", ) @@ -47,8 +47,8 @@ async def test_adapter_python_rejects_invalid_path_before_start( with pytest.raises(FabricRuntimeError, match="ADAPTER_PYTHON") as caught: await Fabric().start_runtime( - hermes_shim_agent_dir, - profiles=["env_local"], + hermes_shim_config(), + base_dir=hermes_shim_agent_dir, ) assert caught.value.stage == "start" @@ -59,7 +59,7 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: inspected = client.resolve(example_config, base_dir=BASE_DIR) assert inspected["agent_name"] == "code-review-agent" - assert inspected.profiles == () + assert inspected.base_dir == BASE_DIR assert inspected["config"]["metadata"]["name"] == "code-review-agent" plan = client.plan(example_config, base_dir=BASE_DIR) @@ -71,10 +71,6 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: assert plan["capability_plan"]["native"]["mcp_servers"]["github"] assert plan["capability_plan"]["native"]["skill_paths"] - multi_plan = client.plan(fixture_agent, profiles=["env_local", "mcp_github"]) - assert multi_plan.profiles == ("env_local", "mcp_github") - assert multi_plan["telemetry_plan"]["relay_enabled"] is True - minimal = FabricConfig.from_mapping( { "metadata": {"name": "minimal-typed-agent"}, @@ -92,7 +88,10 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: "harness": { "adapter_id": "test.fabric.hermes_shim", "resolution": "preinstalled", - "settings": {"workspace": "./repos/my-service"}, + "settings": { + "workspace": "./repos/my-service", + "timeout_seconds": 30, + }, }, "models": { "default": { @@ -120,36 +119,25 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: } } }, - "telemetry": None, + "telemetry": {"providers": {"relay": {}}}, + "relay": {"output_dir": "./artifacts/relay"}, "consumer_extension": { "base": True, - "nested": {"first": 1}, + "custom": True, + "nested": {"first": 1, "second": 2}, }, } ) - typed_profile = FabricProfileConfig( - name="typed_relay", - harness={"settings": {"timeout_seconds": 30}}, - telemetry={"providers": {"relay": {}}}, - relay={"output_dir": "./artifacts/relay"}, - consumer_extension={ - "profile": True, - "nested": {"second": 2}, - }, - ) typed_config_resolved = client.resolve( typed_config, - profiles=[typed_profile], base_dir=fixture_agent, ) typed_plan = client.plan( typed_config, - profiles=[typed_profile], base_dir=fixture_agent, ) assert typed_config_resolved.agent_name == "typed-hermes-shim-agent" assert typed_plan["agent_name"] == "typed-hermes-shim-agent" - assert typed_plan.profiles == ("typed_relay",) assert typed_plan["adapter_descriptor"]["source"] == "local" assert typed_plan["telemetry_plan"]["relay_enabled"] is True resolved_config = typed_config_resolved.config.to_mapping() @@ -158,24 +146,23 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: assert resolved_config["harness"]["settings"]["timeout_seconds"] == 30 assert resolved_config["consumer_extension"] == { "base": True, - "profile": True, + "custom": True, "nested": {"first": 1, "second": 2}, } result = await client.run( - fixture_agent, - profiles=["env_local"], + hermes_shim_config(), + base_dir=fixture_agent, input="hello native", ) async with await client.start_runtime( - fixture_agent, - profiles=["env_local"], + hermes_shim_config(), + base_dir=fixture_agent, ) as runtime: first = await runtime.invoke(input="hello runtime one") second = await runtime.invoke(input="hello runtime two") assert result["status"] == "succeeded" - assert result.profiles == ("env_local",) assert result.harness == "hermes" assert result["adapter_kind"] == "python" assert result["metadata"]["adapter_runner"] == "python" @@ -184,7 +171,6 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: assert any(artifact.name == "stdout" for artifact in result.artifacts.artifacts) assert first["status"] == "succeeded" assert second["status"] == "succeeded" - assert first.profiles == ("env_local",) assert first.harness == "hermes" assert first["runtime_id"] == second["runtime_id"] assert runtime.handle["runtime_id"] == first["runtime_id"] diff --git a/tests/python/test_runtime.py b/tests/python/test_runtime.py index e354b0249..b26947091 100644 --- a/tests/python/test_runtime.py +++ b/tests/python/test_runtime.py @@ -40,13 +40,9 @@ def _plan() -> dict[str, Any]: } return { "agent_name": "demo", - "profiles": ["typed"], "effective_config": { "agent_name": "demo", - "profiles": ["typed"], - "agent_root": ".", - "config_path": "agent.yaml", - "config_root": ".", + "base_dir": ".", "config": config, }, "config": config, @@ -91,14 +87,19 @@ def _config() -> FabricConfig: ) +async def _wait_for(event: threading.Event, timeout: float = 2.0) -> bool: + loop = asyncio.get_running_loop() + deadline = loop.time() + timeout + while not event.is_set() and loop.time() < deadline: + await asyncio.sleep(0.001) + return event.is_set() + + @pytest.fixture(name="mock_native") def mock_native_fixture() -> MagicMock: mock_native = MagicMock() mock_native.requests = [] - mock_native.plan.side_effect = lambda path, profiles: json.dumps(_plan()) - mock_native.plan_config.side_effect = ( - lambda config_json, profiles_json, base_dir: json.dumps(_plan()) - ) + mock_native.plan_config.side_effect = lambda config_json, base_dir: json.dumps(_plan()) mock_native.start_runtime.return_value = json.dumps(_runtime()) def invoke(plan_json: str, runtime_json: str, request_json: str) -> str: @@ -109,7 +110,6 @@ def invoke(plan_json: str, runtime_json: str, request_json: str) -> str: return json.dumps( { "agent_name": "demo", - "profiles": ["typed"], "harness": "hermes", "adapter_kind": "python", "adapter_id": "test.fabric.shim", @@ -158,21 +158,17 @@ def _runtime_wrapper( ) -async def test_start_runtime_supports_path_and_typed_sources( +async def test_start_runtime_supports_typed_source_and_base_dir( native_client: Fabric, mock_native: MagicMock, ): - path_runtime = await native_client.start_runtime("agent", profiles=["typed"]) typed_runtime = await native_client.start_runtime( _config(), - profiles=[], base_dir=".", ) - assert path_runtime.runtime_id == "runtime-1" assert typed_runtime.runtime_id == "runtime-1" - assert mock_native.plan.call_args.args == ("agent", ["typed"]) - assert mock_native.plan_config.called + assert mock_native.plan_config.call_args.args[1] == "." async def test_start_runtime_preserves_start_stage( @@ -182,7 +178,7 @@ async def test_start_runtime_preserves_start_stage( mock_native.start_runtime.side_effect = RuntimeError("start failed") with pytest.raises(FabricRuntimeError, match="start failed") as caught: - await native_client.start_runtime("agent") + await native_client.start_runtime(_config()) assert caught.value.stage == "start" @@ -193,7 +189,7 @@ async def test_start_runtime_rejects_invalid_overrides_before_start( ): with pytest.raises(FabricConfigError, match="keys must be strings"): await native_client.start_runtime( - "agent", + _config(), overrides={"nested": {1: "invalid"}}, # type: ignore[dict-item] ) @@ -208,7 +204,7 @@ async def test_start_runtime_rejects_cyclic_overrides_before_start( overrides["cycle"] = overrides with pytest.raises(FabricConfigError, match="JSON-compatible"): - await native_client.start_runtime("agent", overrides=overrides) + await native_client.start_runtime(_config(), overrides=overrides) mock_native.start_runtime.assert_not_called() @@ -333,7 +329,7 @@ def blocking_invoke(*args: Any) -> str: mock_native.invoke_runtime.side_effect = blocking_invoke runtime = _runtime_wrapper(mock_native) turn = asyncio.create_task(runtime.invoke(input="hello")) - assert await asyncio.to_thread(started.wait, 2) + assert await _wait_for(started) with pytest.raises(FabricStateError, match="in flight"): await runtime.stop() @@ -357,7 +353,7 @@ def blocking_invoke(*args: Any) -> str: mock_native.invoke_runtime.side_effect = blocking_invoke runtime = _runtime_wrapper(mock_native) first = asyncio.create_task(runtime.invoke(input="one")) - assert await asyncio.to_thread(started.wait, 2) + assert await _wait_for(started) with pytest.raises(FabricStateError, match="already running"): await runtime.invoke(input="two") @@ -390,7 +386,7 @@ def blocking_invoke(*args: Any) -> str: first = asyncio.create_task(first_runtime.invoke(input="one")) second = asyncio.create_task(second_runtime.invoke(input="two")) - assert await asyncio.to_thread(both_started.wait, 2) + assert await _wait_for(both_started) assert not first.done() assert not second.done() @@ -425,7 +421,7 @@ def blocking_invoke(*args: Any) -> str: mock_native.invoke_runtime.side_effect = blocking_invoke runtime = _runtime_wrapper(mock_native) turn = asyncio.create_task(runtime.invoke(input="hello")) - assert await asyncio.to_thread(started.wait, 2) + assert await _wait_for(started) turn.cancel() await asyncio.sleep(0) @@ -455,8 +451,8 @@ def blocking_start(*args: Any) -> str: return json.dumps(_runtime()) mock_native.start_runtime.side_effect = blocking_start - start = asyncio.create_task(native_client.start_runtime("agent")) - assert await asyncio.to_thread(started.wait, 2) + start = asyncio.create_task(native_client.start_runtime(_config())) + assert await _wait_for(started) start.cancel() await asyncio.sleep(0) @@ -483,8 +479,8 @@ def blocking_invoke(*args: Any) -> str: return invoke(*args) mock_native.invoke_runtime.side_effect = blocking_invoke - run = asyncio.create_task(native_client.run("agent", input="hello")) - assert await asyncio.to_thread(started.wait, 2) + run = asyncio.create_task(native_client.run(_config(), input="hello")) + assert await _wait_for(started) run.cancel() await asyncio.sleep(0) @@ -501,13 +497,13 @@ async def test_run_stops_runtime_after_success_and_failure( native_client: Fabric, mock_native: MagicMock, ): - result = await native_client.run("agent", input="hello") + result = await native_client.run(_config(), input="hello") assert result.status == "succeeded" assert mock_native.stop_runtime.call_count == 1 mock_native.invoke_runtime.side_effect = RuntimeError("invoke failed") with pytest.raises(FabricRuntimeError, match="invoke failed"): - await native_client.run("agent", input="hello") + await native_client.run(_config(), input="hello") assert mock_native.stop_runtime.call_count == 2 @@ -524,8 +520,8 @@ def record_plan(*args: Any, **kwargs: Any): monkeypatch.setattr(native_client, "plan", record_plan) - await native_client.run("agent", input="hello") - runtime = await native_client.start_runtime("agent") + await native_client.run(_config(), input="hello") + runtime = await native_client.start_runtime(_config()) await runtime.stop() assert len(planning_calls) == 2 @@ -538,7 +534,7 @@ async def test_run_surfaces_cleanup_failure_after_success( mock_native.stop_runtime.side_effect = RuntimeError("stop failed") with pytest.raises(FabricRuntimeError, match="stop failed") as caught: - await native_client.run("agent", input="hello") + await native_client.run(_config(), input="hello") assert caught.value.stage == "run" assert mock_native.stop_runtime.call_count == 1 @@ -557,4 +553,4 @@ async def test_native_unavailable_uses_typed_error(monkeypatch: pytest.MonkeyPat monkeypatch.setattr(client_mod, "_native", None) with pytest.raises(FabricNativeUnavailableError, match="native extension"): - Fabric().plan("agent") + Fabric().plan(_config()) diff --git a/tests/python/test_sdk_concurrency.py b/tests/python/test_sdk_concurrency.py index 54761e17f..a1bf01bb1 100644 --- a/tests/python/test_sdk_concurrency.py +++ b/tests/python/test_sdk_concurrency.py @@ -9,18 +9,19 @@ from pathlib import Path from shutil import copytree +from _utils.configs import hermes_shim_config from nemo_fabric import Fabric async def run_runtime(client: Fabric, agent: Path, name: str) -> dict: - async with await client.start_runtime(agent, profiles=["env_local"]) as runtime: + async with await client.start_runtime(hermes_shim_config(), base_dir=agent) as runtime: return await runtime.invoke(input=f"hello from {name}") async def run_copy(client: Fabric, fixture_agent: Path, root: Path, name: str) -> dict: agent = root / name copytree(fixture_agent, agent) - return await client.run(agent, profiles=["env_local"], input=f"hello from {name}") + return await client.run(hermes_shim_config(), base_dir=agent, input=f"hello from {name}") async def test_sdk_concurrency(hermes_shim_agent_dir_src: Path, tmp_path: Path): diff --git a/tests/python/test_sdk_contract.py b/tests/python/test_sdk_contract.py index f66f93692..0945dc89c 100644 --- a/tests/python/test_sdk_contract.py +++ b/tests/python/test_sdk_contract.py @@ -24,7 +24,6 @@ from nemo_fabric import FabricConfigError from nemo_fabric import FabricError from nemo_fabric import FabricNativeUnavailableError -from nemo_fabric import FabricProfileConfig from nemo_fabric import FabricRuntimeError from nemo_fabric import FabricStateError from nemo_fabric import HarnessConfig @@ -61,7 +60,7 @@ def test_public_contract_has_no_unreleased_aliases(): assert not hasattr(Fabric, name) for name in ("resolve", "plan", "doctor", "run", "start_runtime"): - assert len(get_overloads(getattr(Fabric, name))) == 2, name + assert not get_overloads(getattr(Fabric, name)), name assert not hasattr(fabric_errors, "FabricCliError") @@ -384,13 +383,9 @@ def test_inspection_models_are_typed_read_only_mappings(): plan = RunPlan.from_mapping( { "agent_name": "demo", - "profiles": ["runtime", "telemetry"], "effective_config": { "agent_name": "demo", - "profiles": ["runtime", "telemetry"], - "agent_root": ".", - "config_path": "agent.yaml", - "config_root": ".", + "base_dir": ".", "config": { "metadata": {"name": "demo"}, "harness": {"adapter_id": "test.fabric.shim"}, @@ -418,7 +413,7 @@ def test_inspection_models_are_typed_read_only_mappings(): assert isinstance(plan.effective_config, EffectiveConfig) assert isinstance(plan.adapter, AdapterInfo) assert isinstance(plan.capabilities, RuntimeCapabilities) - assert plan.profiles == ("runtime", "telemetry") + assert plan.effective_config.base_dir == Path(".") assert plan.adapter.harness == "hermes" assert "harness_type" not in plan.adapter assert plan.adapter.extra_fields["future"] == "value" @@ -471,27 +466,6 @@ def test_runtime_handle_requires_native_contract_fields(field): RuntimeHandle.from_mapping(raw) -@pytest.mark.parametrize( - ("model", "payload"), - ( - (EffectiveConfig, {"config": {}}), - (DoctorReport, {}), - (RunResult, {}), - ), -) -def test_snapshot_models_require_profiles(model, payload): - with pytest.raises(FabricConfigError, match="profiles is required"): - model.from_mapping(payload) - - -def test_run_plan_requires_profiles(): - raw = _plan() - del raw["profiles"] - - with pytest.raises(FabricConfigError, match="RunPlan profiles is required"): - RunPlan.from_mapping(raw) - - def test_run_plan_config_enable_relay_preserves_existing_relay_fields(): config = _ResolvedFabricConfig.from_mapping(_plan()["config"]) @@ -528,7 +502,6 @@ def test_doctor_report_and_errors_expose_typed_contract_fields(): report = DoctorReport.from_mapping( { "agent_name": "demo", - "profiles": [], "status": "warn", "checks": [ { @@ -565,13 +538,9 @@ def _plan() -> dict[str, Any]: } return { "agent_name": "demo", - "profiles": ["typed"], "effective_config": { "agent_name": "demo", - "profiles": ["typed"], - "agent_root": ".", - "config_path": "agent.yaml", - "config_root": ".", + "base_dir": ".", "config": config, }, "config": config, @@ -611,7 +580,6 @@ def _runtime() -> dict[str, Any]: def _run_result(**updates: Any) -> dict[str, Any]: result = { "agent_name": "demo", - "profiles": [], "harness": "hermes", "adapter_kind": "python", "adapter_id": "test.fabric.shim", @@ -638,39 +606,26 @@ def _fabric_config() -> FabricConfig: class NativeRecorder: def __init__(self) -> None: self.requests: list[dict[str, Any]] = [] - self.path_profile_calls: list[Any] = [] - self.config_profile_calls: list[Any] = [] + self.config_base_dir_calls: list[str | None] = [] self.stopped = 0 self.fail_invoke = False - def plan(self, path: str, profile: Any = None) -> str: - assert path == "agent" - self.path_profile_calls.append(profile) - return json.dumps(_plan()) - - def inspect(self, path: str, profile: Any = None) -> str: - assert path == "agent" - self.path_profile_calls.append(profile) - return json.dumps(_plan()["effective_config"]) - def resolve_config( self, config_json: str, - profiles_json: str | None = None, base_dir: str | None = None, ) -> str: assert json.loads(config_json)["metadata"]["name"] == "demo" - self.config_profile_calls.append(None if profiles_json is None else json.loads(profiles_json)) + self.config_base_dir_calls.append(base_dir) return json.dumps(_plan()["effective_config"]) def plan_config( self, config_json: str, - profiles_json: str | None = None, base_dir: str | None = None, ) -> str: assert json.loads(config_json)["metadata"]["name"] == "demo" - self.config_profile_calls.append(None if profiles_json is None else json.loads(profiles_json)) + self.config_base_dir_calls.append(base_dir) return json.dumps(_plan()) def start_runtime(self, plan_json: str) -> str: @@ -685,7 +640,6 @@ def invoke_runtime(self, plan_json: str, runtime_json: str, request_json: str) - return json.dumps( { "agent_name": "demo", - "profiles": ["typed"], "harness": "hermes", "adapter_kind": "python", "adapter_id": "test.fabric.shim", @@ -995,12 +949,12 @@ def test_run_result_requires_schema_identity_fields(field): RunResult.from_mapping(raw) -async def test_run_accepts_full_run_request_on_native_path(): +async def test_run_accepts_full_run_request(): native = NativeRecorder() client = NativeClient(native) result = await client.run( - "agent", + _fabric_config(), request=RunRequest( input="hello", request_id="request-4", @@ -1171,53 +1125,9 @@ def model_dump(self, *, mode: str, exclude_none: bool) -> dict[str, Any]: metadata={"name": "demo"}, harness={"adapter_id": "test.fabric.shim"}, ) - client.plan(config, profiles=[FabricProfileConfig(name="typed")]) - - assert native.config_profile_calls == [[{"schema_version": "fabric.profile/v1alpha1", "name": "typed"}]] - - -def test_typed_config_profiles_require_profile_models(): - client = NativeClient(NativeRecorder()) - - client.plan( - _fabric_config(), - profiles=[FabricProfileConfig(name="typed_relay")], - ) - - with pytest.raises(FabricConfigError, match="FabricProfileConfig"): - client.plan( - _fabric_config(), - profiles=[{"name": "typed_relay"}], # type: ignore[list-item] - ) - - with pytest.raises(FabricConfigError, match="FabricProfileConfig"): - client.plan(_fabric_config(), profiles="typed_relay") # type: ignore[arg-type] - - with pytest.raises(FabricConfigError, match="FabricProfileConfig"): - client.plan( - _fabric_config(), - profiles=["typed_relay"], # type: ignore[list-item] - ) - - -def test_path_source_accepts_single_profile_name(): - native = NativeRecorder() - - NativeClient(native).plan("agent", profiles="hermes_runtime") - - assert native.path_profile_calls == [["hermes_runtime"]] - - -def test_path_source_rejects_mapping_profiles_before_native_planning(): - native = NativeRecorder() - - with pytest.raises(FabricConfigError, match="profile names"): - NativeClient(native).plan( - "agent", - profiles={"name": "hermes_runtime"}, # type: ignore[arg-type] - ) + client.plan(config, base_dir=".") - assert native.path_profile_calls == [] + assert native.config_base_dir_calls == ["."] def test_fabric_config_constructors_emit_schema_shaped_mappings(): @@ -1243,21 +1153,19 @@ def test_fabric_config_constructors_emit_schema_shaped_mappings(): assert config.harness.settings["workspace"] == "./ws" client = NativeClient(NativeRecorder()) - client.plan(config, profiles=[FabricProfileConfig(name="typed_relay")]) + client.plan(config) -def test_resolve_accepts_path_and_fabric_config_sources(): +def test_resolve_accepts_a_complete_fabric_config(): client = NativeClient(NativeRecorder()) - path_config = client.resolve("agent") typed_config = client.resolve(_fabric_config()) - assert path_config["config"]["runtime"]["input_schema"] == "chat" assert typed_config["config"]["runtime"]["input_schema"] == "chat" async def test_start_runtime_returns_runtime_with_typed_handle(): - runtime = await NativeClient(NativeRecorder()).start_runtime("agent") + runtime = await NativeClient(NativeRecorder()).start_runtime(_fabric_config()) assert runtime.runtime_id == "runtime-1" assert isinstance(runtime.handle, RuntimeHandle) diff --git a/tests/python/test_sdk_runtimes.py b/tests/python/test_sdk_runtimes.py index ff442c387..2622a961f 100644 --- a/tests/python/test_sdk_runtimes.py +++ b/tests/python/test_sdk_runtimes.py @@ -22,13 +22,9 @@ def _plan() -> dict[str, Any]: } return { "agent_name": "demo", - "profiles": ["hermes"], "effective_config": { "agent_name": "demo", - "profiles": ["hermes"], - "agent_root": ".", - "config_path": "agent.yaml", - "config_root": ".", + "base_dir": ".", "config": config, }, "config": config, @@ -80,7 +76,6 @@ def invoke_runtime( return json.dumps( { "agent_name": "demo", - "profiles": ["hermes"], "harness": "hermes", "adapter_kind": "python", "adapter_id": "test.fabric.shim", diff --git a/tests/python/test_typed_config.py b/tests/python/test_typed_config.py index 710ea064f..499460494 100644 --- a/tests/python/test_typed_config.py +++ b/tests/python/test_typed_config.py @@ -1,43 +1,35 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -"""Smoke test: typed (in-memory) config is first-class, with no agent directory. +"""Smoke test: typed config is the first-class Fabric input. -The unified SDK methods accept a typed config object and resolve, diagnose, and -run it without an on-disk agent package: +The SDK methods accept a complete typed config and an optional base directory: * ``plan`` / ``doctor`` resolve a maintained (repository) adapter - with ``base_dir=None`` -- zero filesystem layout, no ``agent.yaml``. + with ``base_dir=None``. * ``run`` drives a real core runtime run using only a local adapter directory - (still no agent package). + and a typed config. This complements ``test_native_sdk.py``, which exercises ``plan`` with a ``base_dir`` pointed at an agent package. """ from __future__ import annotations -import json -import subprocess import tempfile from pathlib import Path from shutil import copytree -import yaml from nemo_fabric import Fabric from nemo_fabric import FabricConfig -from nemo_fabric import FabricProfileConfig from nemo_fabric import RunRequest from nemo_fabric import RunResult ROOT = Path(__file__).resolve().parents[2] -COMMAND = ("cargo", "run", "-q", "-p", "nemo-fabric-cli", "--") -# The test adapter (needs only python3, no secrets), shipped as a fixture. -SHIM_AGENT = ROOT / "tests" / "fixtures" / "hermes-shim-agent" SHIM_ADAPTERS = ROOT / "tests" / "fixtures" / "hermes-shim-agent" / "adapters" def _repository_adapter_config() -> FabricConfig: - """Config referencing a maintained adapter resolvable without any package.""" + """Config referencing a maintained adapter.""" return FabricConfig.from_mapping( { @@ -85,25 +77,20 @@ def _shim_adapter_config() -> FabricConfig: return FabricConfig.from_mapping(config) -async def resolves_and_diagnoses_without_a_directory(client: Fabric) -> None: - """plan / doctor resolve a maintained adapter with no package.""" +async def resolves_and_diagnoses_typed_config(client: Fabric) -> None: + """Plan and doctor resolve a complete typed config.""" config = _repository_adapter_config() - # base_dir=None: the literal "no path at all" call must still resolve. plan_no_path = client.plan(config) assert plan_no_path["agent_name"] == "typed-only-agent" - # Point base_dir at an EMPTY directory (not an agent package): resolution can - # then only succeed via the baked-in repository adapter dir, so the source is - # deterministic regardless of the process CWD. with tempfile.TemporaryDirectory(prefix="typed-no-dir-") as empty: plan = client.plan(config, base_dir=empty) report = await client.doctor(config, base_dir=empty) descriptor = plan["adapter_descriptor"] assert descriptor["descriptor"]["adapter_id"] == "nvidia.fabric.hermes" - # "repository" (not "local") proves it resolved without any on-disk package. assert descriptor["source"] == "repository", descriptor["source"] assert report["agent_name"] == "typed-only-agent" @@ -111,17 +98,14 @@ async def resolves_and_diagnoses_without_a_directory(client: Fabric) -> None: assert report["status"] in {"pass", "warn", "fail"}, report["status"] -async def runs_without_an_agent_package(client: Fabric) -> None: - """run drives a core run with only an adapter dir (no agent.yaml).""" +async def runs_with_typed_config_and_adapter_directory(client: Fabric) -> None: + """Run with a typed config and local adapter descriptor.""" config = _shim_adapter_config() with tempfile.TemporaryDirectory(prefix="typed-run-") as tmpdir: base = Path(tmpdir) / "scratch" - # Only the adapter lives here -- this is deliberately NOT an agent - # package (no agent.yaml / profiles / repos / skills). copytree(SHIM_ADAPTERS, base / "adapters") (base / "ws").mkdir() - assert not (base / "agent.yaml").exists() result = await client.run( config, base_dir=base, @@ -141,64 +125,7 @@ async def runs_without_an_agent_package(client: Fabric) -> None: assert result["output"]["received"] == "hello typed" -def sdk_and_cli_profile_stacks_match(client: Fabric) -> None: - """The same config/profile stack plans identically through CLI and SDK.""" - - config = FabricConfig.from_mapping(_load_yaml(SHIM_AGENT / "agent.yaml")) - profiles = [ - FabricProfileConfig.model_validate( - _load_yaml(SHIM_AGENT / "profiles" / "env-local.yaml") - ), - FabricProfileConfig.model_validate( - _load_yaml(SHIM_AGENT / "profiles" / "mcp-github.yaml") - ), - ] - - sdk_plan = client.plan(config, profiles=profiles, base_dir=SHIM_AGENT) - cli_plan = _cli_plan(SHIM_AGENT, "env_local", "mcp_github") - - assert ( - sdk_plan.profiles == tuple(cli_plan["profiles"]) == ("env_local", "mcp_github") - ) - assert "profile" not in sdk_plan - assert "profile" not in cli_plan - sdk_mapping = sdk_plan.to_mapping() - assert sdk_mapping["config"] == cli_plan["config"] - assert ( - sdk_mapping["effective_config"]["config"] - == cli_plan["effective_config"]["config"] - ) - assert sdk_mapping["adapter_descriptor"] == cli_plan["adapter_descriptor"] - assert sdk_mapping["capabilities"] == cli_plan["capabilities"] - assert sdk_mapping["capability_plan"] == cli_plan["capability_plan"] - assert sdk_mapping["environment_plan"] == cli_plan["environment_plan"] - assert sdk_mapping["telemetry_plan"] == cli_plan["telemetry_plan"] - assert sdk_mapping["resolution"] == cli_plan["resolution"] - - async def test_typed_config(): client = Fabric() - sdk_and_cli_profile_stacks_match(client) - await resolves_and_diagnoses_without_a_directory(client) - await runs_without_an_agent_package(client) - - -def _load_yaml(path: Path) -> dict: - with path.open(encoding="utf-8") as stream: - return yaml.safe_load(stream) - - -def _cli_plan(agent: Path, *profiles: str) -> dict: - args = [*COMMAND, "plan", str(agent)] - for profile in profiles: - args.extend(["--profile", profile]) - completed = subprocess.run( - args, - cwd=ROOT, - text=True, - capture_output=True, - check=False, - ) - if completed.returncode != 0: - raise AssertionError(completed.stderr) - return json.loads(completed.stdout) + await resolves_and_diagnoses_typed_config(client) + await runs_with_typed_config_and_adapter_directory(client) diff --git a/uv.lock b/uv.lock index 93cd31877..25d2d73eb 100644 --- a/uv.lock +++ b/uv.lock @@ -2969,7 +2969,7 @@ name = "python-dateutil" version = "2.9.0.post0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "six" }, + { name = "six", marker = "python_full_version < '3.14'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } wheels = [ From cf654ad5f359be7e4b58f5884e2f01efbf2aebaf Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Thu, 16 Jul 2026 20:07:16 -0700 Subject: [PATCH 02/20] Make the SDK-backed CLI a maintained experiment surface Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- CLI.md | 59 ++++++---- README.md | 73 ++++++++----- adapters/hermes/README.md | 8 +- docs/getting-started/overview.mdx | 30 +++--- docs/sdk/python.mdx | 29 +++-- pypi.md | 19 ++-- pyproject.toml | 4 +- .../adapters/scripted/fabric-adapter.json | 11 ++ .../_bundled/adapters/scripted/run.py | 20 ++++ .../examples/code_review_agent/README.md | 20 ++++ .../examples/code_review_agent/__init__.py | 8 ++ .../adapters/scripted/fabric-adapter.json | 11 ++ .../adapters/scripted/run.py | 13 +++ .../examples/code_review_agent/config.py | 29 +++++ python/src/nemo_fabric/cli.py | 95 ++++++++++++++-- python/src/nemo_fabric/presets.py | 102 ++++++++++++++++-- tests/e2e/test_cli.py | 69 +++++++++++- uv.lock | 4 + 18 files changed, 489 insertions(+), 115 deletions(-) create mode 100644 python/src/nemo_fabric/_bundled/adapters/scripted/fabric-adapter.json create mode 100644 python/src/nemo_fabric/_bundled/adapters/scripted/run.py create mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md create mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/__init__.py create mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/fabric-adapter.json create mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/run.py create mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/config.py diff --git a/CLI.md b/CLI.md index d8cbe7d79..e6db9525e 100644 --- a/CLI.md +++ b/CLI.md @@ -3,14 +3,20 @@ SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All SPDX-License-Identifier: Apache-2.0 --> -# NeMo Fabric CLI Experiments +# NeMo Fabric Experimentation CLI -This is a working note for experimentation and team discussion. It is not a -committed public interface or release contract. +`nemo-fabric` is a maintained developer experimentation interface over the +NeMo Fabric SDK. It provides a fast way to try harnesses, run examples, inspect +plans, diagnose integrations, and execute user-owned Python configurations. +The SDK remains the canonical configuration and execution contract. + +The CLI is intentionally an experimentation surface. Its command grammar may +evolve as workflows mature; typed SDK applications should be used when API +stability or production integration is required. ## Intent -Keep `nemo-fabric` as a thin SDK-backed runner: +Keep the CLI as a thin SDK-backed runner: - every run starts from a complete, typed `FabricConfig`; - the CLI only selects where that config comes from; @@ -19,9 +25,9 @@ Keep `nemo-fabric` as a thin SDK-backed runner: - Fabric does not load a persisted YAML, TOML, or JSON configuration. Profiles, overlays, config discovery, and compatibility loaders are out of -scope. Fabric has not been released, so this experiment makes a clean break. +scope. Fabric has not been released, so the CLI makes a clean break. -## Current Variation +## Supported Inputs ### 1. Preset @@ -45,6 +51,12 @@ A runnable SDK example with complete variants. Examples are Python source and assets that people can read, copy, and edit. A variant is a factory, not a profile. +Copy the bundled example into an editable Python package with: + +```bash +nemo-fabric example init examples.code_review_agent my_agent +``` + ### 3. User factory ```bash @@ -57,8 +69,7 @@ nemo-fabric run \ The unrestricted customization path. The callable takes no arguments and returns a complete `FabricConfig`. -`--factory` is the current spelling of the earlier `--custom` idea because it -makes the Python contract explicit. +The `--factory` name makes the executable Python contract explicit. All three selectors feed the same commands: @@ -66,17 +77,23 @@ All three selectors feed the same commands: plan doctor run chat ``` -## Variations to Discuss +## Designed For + +- quick harness and model experiments; +- smoke tests and troubleshooting; +- comparing complete preset or example variants; +- copying and editing Python examples; +- invoking custom `FabricConfig` factories; and +- inspecting plans and diagnostics. + +## Not Designed For -| Question | Lean option | Other reasonable experiments | -| --- | --- | --- | -| Selector grammar | `--preset`, `--example`, `--factory` | source subcommands; `preset:name` references | -| Editable starting points | copy an example's Python source | `example init`; `preset eject`; repository-only samples | -| Example distribution | bundle a small runnable example in the wheel | repository examples only; plugin/entry-point discovery | -| Preset customization | keep presets complete and intentionally small | a few typed flags for model/workspace; no arbitrary `--set` | -| Custom source name | `--factory module:callable` | `--custom module:callable`; `--agent module:callable` | -| Interactive surface | retain `chat` for harness probing | ship only `plan`, `doctor`, and `run` | -| Output | JSON as the automation contract | explicit human-readable output mode | +- production deployment or scheduling; +- persistent configuration or secrets management; +- profiles, inheritance, or configuration overlays; +- arbitrary CLI configuration mutation; +- evaluation orchestration or experiment tracking; or +- replacing direct SDK applications. ## Boundaries @@ -89,9 +106,9 @@ plan doctor run chat - Private serialization across Python/Rust or Harbor process boundaries is not a public authoring format. -## Current Bias +## Design Bias Start with the three explicit selectors. Keep presets tiny, make examples the editable learning surface, and use Python factories for real customization. -Add scaffolding or plugin discovery only after the basic run path proves -useful. +Keep registration package-owned for now; consider plugin discovery only after +the workflows prove useful. diff --git a/README.md b/README.md index 971356ac3..e51516cc4 100644 --- a/README.md +++ b/README.md @@ -19,18 +19,17 @@ artifacts from agent harnesses. Fabric provides: -- a versioned typed config contract, with `agent.yaml` as the portable file - format; -- profile-based config variation for evaluation and ablation runs; +- a versioned, typed `FabricConfig` contract constructed through the SDK; +- ordinary Python composition for harness and experiment variants; - adapter descriptors for harness-specific launch and control; -- a Rust core with a CLI and Python bindings; +- a Rust core, Python SDK, and SDK-backed experimentation CLI; - JSON Schema snapshots for the public config and runtime contract; - normalized run results, artifact manifests, and telemetry references. ```mermaid flowchart TB Consumer["Consumer\nCLI | Python SDK | integrations"] - Config["Agent source\nagent.yaml or FabricConfig"] + Config["Typed source\nFabricConfig"] Core["Fabric Rust core\nresolve | plan | create | invoke | destroy"] Adapter["Selected Fabric adapter"] Harness["Agent harness runtime\nHermes | Codex | custom"] @@ -47,6 +46,30 @@ flowchart TB Harness -. harness telemetry .-> Relay ``` +## Quick Start: Experimentation CLI + +The `nemo-fabric` CLI is a maintained developer interface for quick harness +experiments, smoke tests, examples, planning, and diagnostics. The Python SDK +remains the stable application-facing contract. + +Build and install the local wheels, then run the credential-free preset: + +```bash +just wheels +python -m pip install --find-links dist nemo-fabric + +nemo-fabric preset show scripted +nemo-fabric run --preset scripted --input "fabric works" +``` + +Copy an editable Python starting point with: + +```bash +nemo-fabric example init examples.code_review_agent ./my_agent +``` + +See [CLI.md](CLI.md) for the CLI's intent and boundaries. + ## Quick Start: Hermes Agent This path installs Fabric, installs Hermes Agent in a separate Python environment, @@ -81,7 +104,7 @@ just build-all just wheels ``` -Install Hermes into its own environment (the nemo-fabric[hermes] extra will install Hermes Agent, and the Hermes Agent adapter but not Fabric itself): +Install Fabric, Hermes Agent, and the Hermes adapter into an environment: ```bash # Use any Python 3.11-3.13 interpreter for Hermes. @@ -134,23 +157,19 @@ authentication, and execution details. ## Core Concepts -- **Agent source:** callers provide either an agent package path or a typed - `FabricConfig`. Start with `examples/code_review_agent/config.py` for the - application-facing Pydantic pattern. -- **Typed config:** SDK consumers can construct configuration in memory without - materializing an agent directory. `agent.yaml` remains the portable - representation for CLI use, CI, and reproducible runs. -- **Profiles:** named variations of the base config. Use profiles to vary the - harness, model, MCP, tools, skills, telemetry, or environment context without - editing `agent.yaml`. +- **Typed config:** callers construct a complete `FabricConfig` in Python. + Start with `examples/code_review_agent/config.py` for the application-facing + Pydantic pattern. +- **Variants:** ordinary Python functions copy and modify complete configs to + vary the harness, model, MCP, tools, skills, telemetry, or environment. +- **Experimentation CLI:** presets provide quick probes, examples provide + editable starting points, and `--factory module:callable` executes a + user-owned config through the public SDK. - **Tools policy:** use top-level `tools.blocked` for harness-neutral blocked tool policy. Names are interpreted by the selected adapter: - ```yaml - tools: - blocked: - - browser - - shell + ```python + config.block_tools("browser", "shell") ``` Hermes maps these names to disabled toolsets, Claude maps them to @@ -166,14 +185,10 @@ authentication, and execution details. - **Artifacts:** normalized output, logs, patches, and telemetry references returned through an `ArtifactManifest`. -For legacy path sources, Fabric applies profiles in caller order and validates -the final effective config before planning or running. - -Path sources temporarily select legacy profiles by name. Typed `FabricConfig` -sources must contain the complete configuration and do not accept profile -overlays; compose variants in Python before calling the SDK. See the -[Python SDK guide](docs/sdk/python.mdx) for the complete public API, -type definitions, lifecycle semantics, and error behavior. +Fabric accepts complete typed configs and does not load agent or profile +configuration files. Compose variants in Python before calling the SDK. See +the [Python SDK guide](docs/sdk/python.mdx) for the complete public API, type +definitions, lifecycle semantics, and error behavior. `run(...)` owns the complete start, invoke, and stop lifecycle. For typed in-memory configuration, planning and diagnostics, explicit requests, @@ -185,6 +200,8 @@ the [Python SDK guide](docs/sdk/python.mdx). Exact signatures are in the - [Python SDK guide](docs/sdk/python.mdx): typed configuration, planning, diagnostics, requests, multi-turn runtimes, parallelism, results, and errors. +- [Experimentation CLI](CLI.md): presets, examples, factories, and explicit + non-goals. - [Getting Started overview](docs/getting-started/overview.mdx): interface selection and the end-to-end Fabric workflow. - [Harbor examples](examples/harbor/README.md): validate the integration with a diff --git a/adapters/hermes/README.md b/adapters/hermes/README.md index 4d125f8bc..4d560ba2e 100644 --- a/adapters/hermes/README.md +++ b/adapters/hermes/README.md @@ -53,7 +53,7 @@ Keep `fabric-adapter.json` aligned with the Python implementation: the adapter can produce or forward. Do not put end-user agent settings in this directory. Users vary harness, -model, skills, MCP, tools, telemetry, and runtime behavior through `agent.yaml` -and profiles. The adapter descriptor describes adapter capabilities; it is not a -profile. Add descriptor fields only when Fabric core or the SDK actually uses -them. +model, skills, MCP, tools, telemetry, and runtime behavior through complete +typed `FabricConfig` values and ordinary Python composition. The adapter +descriptor describes adapter capabilities; it is not an agent configuration. +Add descriptor fields only when Fabric core or the SDK actually uses them. diff --git a/docs/getting-started/overview.mdx b/docs/getting-started/overview.mdx index 4312c1fd9..931879ee0 100644 --- a/docs/getting-started/overview.mdx +++ b/docs/getting-started/overview.mdx @@ -20,10 +20,9 @@ into the caller. ## What Fabric gives you - - Use a versioned `agent.yaml` package or construct the same typed - `FabricConfig` in Python. Applications create variants from typed copies; - portable file packages can use ordered profiles. + + Construct a complete, versioned `FabricConfig` in Python. Applications + create variants with ordinary functions and typed copies. Plan and invoke different harnesses through one Rust core, CLI, and Python @@ -44,7 +43,7 @@ into the caller. ```text Application or evaluation harness | - | Python SDK or fabric CLI + | Python SDK or nemo-fabric experimentation CLI v NeMo Fabric Rust core config -> plan -> lifecycle @@ -57,10 +56,10 @@ Hermes | Codex SDK | custom harness RunResult + artifacts + events + telemetry references ``` -The CLI and Python SDK are separate interfaces over the same core. File-backed -and typed configs converge on the same resolved config and run-plan contracts. -Adapters own harness-specific preparation and invocation; consumers own the -request and the use of returned evidence. +The experimentation CLI is a thin client of the Python SDK. Presets, examples, +and user factories all produce a complete `FabricConfig` before using the same +resolved config and run-plan contracts. Adapters own harness-specific +preparation and invocation; consumers own the request and returned evidence. ## Quick start @@ -129,20 +128,19 @@ requests, and multi-turn runtime examples. | --- | --- | --- | | Python SDK | Your application owns job config, runtime lifecycle, or multi-turn state | [Client API](/reference/api/python-library-reference/client) | | Runtime API | You need multiple ordered turns over one live harness runtime | [Runtime](/reference/api/python-library-reference/runtime) | -| `fabric` CLI | You are validating packages, debugging profiles, or running reproducible local/CI jobs | `fabric validate`, `plan`, `doctor`, `run`, and `chat` | +| `nemo-fabric` CLI | You are experimenting with harnesses, running examples, or troubleshooting configs | `preset`, `example`, `plan`, `doctor`, `run`, and `chat` | | JSON Schema | You are building editors, validation, code generation, or another language binding | Committed schemas in the [repository](https://github.com/NVIDIA/NeMo-Fabric/tree/main/schemas) | -Use `agent.yaml` when the agent package is the portable artifact. Use -`FabricConfig` when a platform already owns a larger job or deployment object -and wants to pass only its Fabric slice in memory. +Use `FabricConfig` as the canonical configuration contract. The CLI does not +load a persisted YAML, TOML, or JSON Fabric config; its selectors obtain a +typed config from maintained presets, examples, or user-owned Python factories. ## Core workflow -1. **Configure** an agent package or typed `FabricConfig` with a harness adapter, +1. **Configure** a typed `FabricConfig` with a harness adapter, environment, models, tools, skills, MCP, and telemetry. 2. **Create variants** from deep copies to vary harness, model, environment, or - observability settings without mutating the base config. File-backed - packages may instead apply ordered profiles. + observability settings without mutating the base config. 3. **Plan and diagnose** to resolve the adapter and check capabilities and requirements before spending work on a runtime. 4. **Run or start a runtime** through the shared start, invoke, and stop diff --git a/docs/sdk/python.mdx b/docs/sdk/python.mdx index 7689ceebd..40c424e5d 100644 --- a/docs/sdk/python.mdx +++ b/docs/sdk/python.mdx @@ -14,9 +14,9 @@ telemetry references. The SDK is config-first. Applications should construct a Pydantic `FabricConfig` from their own job, deployment, or evaluation config. -Portable file formats such as `agent.yaml` remain useful for examples, CI, and -reproducibility, but the SDK does not require callers to write intermediate -files before invoking Fabric. +Fabric does not load a persisted YAML, TOML, or JSON agent configuration. +Examples, CI jobs, and applications construct the typed config directly and +may use ordinary Python functions to create variants. Generated API reference pages remain the source of truth for exact signatures. This guide explains how the pieces are intended to fit together. @@ -153,8 +153,7 @@ Telemetry is enabled by adding entries to `telemetry.providers`; Relay-specific settings live in the top-level `relay` block. For evaluation or deployment variations, use ordinary Python functions and -copies of the typed config. Supply the final config to Fabric when the variation -does not need file-profile merge semantics. +copies of the typed config. Supply the complete final config to Fabric. ```python def review_agent_config(base, *, github_mcp: bool, relay: bool): @@ -195,10 +194,6 @@ The repository's uses this pattern for complete Hermes, Codex SDK, Deep Agents, environment, MCP, and telemetry variants. -When an in-memory caller needs the same ordered overlay behavior as file-backed -profiles, construct `FabricProfileConfig` values and pass them through -`profiles=[...]`. Fabric does not accept raw profile mappings. - If a config contains relative paths, pass a `base_dir` to `resolve(...)`, `plan(...)`, `doctor(...)`, `run(...)`, or `start_runtime(...)`. The base directory anchors skills, workspaces, artifacts, and other relative paths to @@ -444,9 +439,9 @@ around that contract. ### Schemas And Python Models The SDK's Pydantic models are maintained against the Rust-generated public -schemas. Use the schemas for persisted field definitions and the generated API -reference for exact Python signatures. New application code should prefer -`FabricConfig` and `RunRequest` for validated SDK inputs. +schemas. The schemas describe typed cross-language contracts; they are not a +supported Fabric config-file format. Use the generated API reference for exact +Python signatures. | Contract | Source | | --- | --- | @@ -462,20 +457,20 @@ reference for exact Python signatures. New application code should prefer Fabric uses explicit contract versions where persisted or independently maintained artifacts cross package boundaries: -- `schema_version` identifies portable config documents such as `agent.yaml` - and profile files. +- `schema_version` identifies the typed Fabric config contract transported + across SDK and language boundaries. - `contract_version` identifies the adapter descriptor contract implemented by a `fabric-adapter.json` file. - Python and Rust package versions identify the installed SDK/core release. -Fabric versions top-level persisted documents and independently maintained -contracts. It does not version each config subsection independently. For +Fabric versions top-level typed contracts and independently maintained adapter +metadata. It does not version each config subsection independently. For example, MCP, skills, models, telemetry, and runtime fields evolve under the enclosing `schema_version`. Fabric validates adapter descriptor contract versions during planning. Package semver identifies the installed implementation, but it is not the compatibility -contract for saved agent packages or independently maintained adapters. +contract for independently maintained adapters. ### Config Extensibility diff --git a/pypi.md b/pypi.md index a8b7b63e6..cd5e074f8 100644 --- a/pypi.md +++ b/pypi.md @@ -12,9 +12,9 @@ NeMo Fabric standardizes how applications configure, launch, invoke, and collect artifacts from agent harnesses. It provides: - a versioned, typed configuration contract; -- profile-based configuration for evaluation and ablation runs; +- ordinary Python composition for experiment variants; - adapter integrations for harness-specific launch and control; -- a Python SDK backed by a Rust core; +- a Python SDK, experimentation CLI, and Rust core; - normalized run results, artifact manifests, and telemetry references. ## Install @@ -22,7 +22,7 @@ collect artifacts from agent harnesses. It provides: Install the core runtime and Python SDK: ```bash -pip install "nemo-fabric[runtime]" +pip install nemo-fabric ``` To use a supported agent harness, install its adapter extra: @@ -58,17 +58,18 @@ NeMo Fabric supports Python versions 3.11-3.14, however some of the integrations ## Core Concepts -- **Agent source:** Provide either an agent package path or a typed - `FabricConfig`. -- **Typed configuration:** Construct configuration in memory with the Python - SDK, or use `agent.yaml` as a portable representation. -- **Profiles:** Vary the harness, model, MCP servers, tools, skills, telemetry, - or environment without editing the base configuration. +- **Typed configuration:** Construct a complete `FabricConfig` in Python and + use ordinary functions to create experiment variants. +- **Experimentation CLI:** Run curated presets, copy editable examples, or + invoke a user-owned Python factory through the SDK-backed `nemo-fabric` CLI. - **Adapters:** Select harness-specific integrations with `harness.adapter_id`. - **Artifacts:** Receive normalized output, logs, patches, and telemetry references through an `ArtifactManifest`. +The CLI does not load persisted YAML, TOML, or JSON Fabric configuration. +Applications that need a stable integration surface should use the Python SDK. + ## Learn More Refer to the [NVIDIA NeMo Fabric documentation](https://nvidia-nemo-fabric.docs.buildwithfern.com/nemo/fabric) diff --git a/pyproject.toml b/pyproject.toml index 971b9f387..0a63c7ba4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,9 @@ license = "Apache-2.0" license-files = ["LICENSE"] readme = "pypi.md" requires-python = ">=3.11" -dependencies = [] +dependencies = [ + "nemo-fabric-runtime == 0.1.0", +] [project.urls] Repository = "https://github.com/NVIDIA/nemo-fabric/" diff --git a/python/src/nemo_fabric/_bundled/adapters/scripted/fabric-adapter.json b/python/src/nemo_fabric/_bundled/adapters/scripted/fabric-adapter.json new file mode 100644 index 000000000..298b1e4e3 --- /dev/null +++ b/python/src/nemo_fabric/_bundled/adapters/scripted/fabric-adapter.json @@ -0,0 +1,11 @@ +{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "nvidia.fabric.scripted", + "harness": "scripted", + "adapter_kind": "process", + "runner": { + "command": "python3", + "script": "run.py", + "stdin_payload": "fabric_request" + } +} diff --git a/python/src/nemo_fabric/_bundled/adapters/scripted/run.py b/python/src/nemo_fabric/_bundled/adapters/scripted/run.py new file mode 100644 index 000000000..1f0f3cce6 --- /dev/null +++ b/python/src/nemo_fabric/_bundled/adapters/scripted/run.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Deterministic process adapter for credential-free CLI experiments.""" + +import json +import sys + + +payload = json.load(sys.stdin) +request = payload["request"] +print( + json.dumps( + { + "response": request.get("input"), + "request_id": request["request_id"], + } + ) +) diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md b/python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md new file mode 100644 index 000000000..ed038981f --- /dev/null +++ b/python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md @@ -0,0 +1,20 @@ + + +# Editable code-review example + +This directory is ordinary Python source copied by `nemo-fabric example init`. +Edit `config.py` to choose a real harness, model, tools, or telemetry. The +credential-free default uses the bundled scripted adapter so the scaffold can +be validated immediately. + +From the directory containing this package, run: + +```bash +nemo-fabric run \ + --factory my_agent.config:build_config \ + --base-dir my_agent \ + --input "Review the workspace" +``` diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/__init__.py b/python/src/nemo_fabric/_bundled/examples/code_review_agent/__init__.py new file mode 100644 index 000000000..2cdb92dfe --- /dev/null +++ b/python/src/nemo_fabric/_bundled/examples/code_review_agent/__init__.py @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Editable code-review example scaffold.""" + +from .config import build_config + +__all__ = ["build_config"] diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/fabric-adapter.json b/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/fabric-adapter.json new file mode 100644 index 000000000..298b1e4e3 --- /dev/null +++ b/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/fabric-adapter.json @@ -0,0 +1,11 @@ +{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "nvidia.fabric.scripted", + "harness": "scripted", + "adapter_kind": "process", + "runner": { + "command": "python3", + "script": "run.py", + "stdin_payload": "fabric_request" + } +} diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/run.py b/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/run.py new file mode 100644 index 000000000..308555541 --- /dev/null +++ b/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/run.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Deterministic process adapter included with the editable scaffold.""" + +import json +import sys + + +payload = json.load(sys.stdin) +request = payload["request"] +print(json.dumps({"response": request.get("input"), "request_id": request["request_id"]})) diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/config.py b/python/src/nemo_fabric/_bundled/examples/code_review_agent/config.py new file mode 100644 index 000000000..74304ea9f --- /dev/null +++ b/python/src/nemo_fabric/_bundled/examples/code_review_agent/config.py @@ -0,0 +1,29 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Complete, editable Fabric configuration for this example.""" + +from nemo_fabric import EnvironmentConfig +from nemo_fabric import FabricConfig +from nemo_fabric import HarnessConfig +from nemo_fabric import MetadataConfig +from nemo_fabric import RuntimeConfig + + +def build_config() -> FabricConfig: + """Return a credential-free starting point for experimentation.""" + + config = FabricConfig( + metadata=MetadataConfig( + name="code-review-agent", + description="Editable SDK example for reviewing a small Python workspace.", + ), + harness=HarnessConfig( + adapter_id="nvidia.fabric.scripted", + resolution="preinstalled", + ), + runtime=RuntimeConfig(input_schema="text", output_schema="message"), + environment=EnvironmentConfig(provider="local", workspace="repo"), + ) + config.add_skill_path("skills/code-review.md") + return config diff --git a/python/src/nemo_fabric/cli.py b/python/src/nemo_fabric/cli.py index 3807c8865..cbeaa53fb 100644 --- a/python/src/nemo_fabric/cli.py +++ b/python/src/nemo_fabric/cli.py @@ -1,13 +1,15 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -"""SDK-backed command line interface for NeMo Fabric.""" +"""Developer experimentation interface backed by the NeMo Fabric SDK.""" from __future__ import annotations import argparse import asyncio +import importlib.metadata import json +import shutil import sys from dataclasses import dataclass from pathlib import Path @@ -46,6 +48,8 @@ def select_source(args: argparse.Namespace) -> SelectedSource: """Resolve one mutually exclusive CLI selector.""" override = Path(args.base_dir).resolve() if args.base_dir else None + if args.variant and not args.example: + raise SourceError("--variant requires --example") if args.preset: try: preset = PRESETS[args.preset] @@ -83,14 +87,28 @@ def _add_selector(parser: argparse.ArgumentParser) -> None: def build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser(prog="nemo-fabric", description=__doc__) + parser = argparse.ArgumentParser( + prog="nemo-fabric", + description=__doc__, + epilog="For stable application integration, construct FabricConfig and use the SDK directly.", + ) subcommands = parser.add_subparsers(dest="command", required=True) for name in ("plan", "doctor", "run", "chat"): command = subcommands.add_parser(name) _add_selector(command) if name in {"run", "chat"}: - command.add_argument("--input", default=None, help="Invocation text; defaults to an empty string.") + inputs = command.add_mutually_exclusive_group() + inputs.add_argument( + "--input", + default=None, + help="Invocation text; defaults to an empty string.", + ) + inputs.add_argument( + "--input-file", + type=Path, + help="Read invocation text from this path, or use - for stdin.", + ) if name == "run": command.add_argument("--request-json", help="JSON object or @path containing a RunRequest.") command.add_argument("--output", type=Path, help="Write JSON output to this path instead of stdout.") @@ -106,14 +124,27 @@ def build_parser() -> argparse.ArgumentParser: example_commands.add_parser("list") example_show = example_commands.add_parser("show") example_show.add_argument("name") + example_init = example_commands.add_parser("init") + example_init.add_argument("name") + example_init.add_argument("destination", type=Path) + + subcommands.add_parser("version", help="Show the installed Fabric runtime version.") return parser +def _input(args: argparse.Namespace) -> str: + if args.input_file is None: + return args.input or "" + if args.input_file == Path("-"): + return sys.stdin.read() + return args.input_file.read_text(encoding="utf-8") + + def _request(args: argparse.Namespace) -> RunRequest: - if args.request_json and args.input is not None: - raise SourceError("--input and --request-json are mutually exclusive") + if args.request_json and (args.input is not None or args.input_file is not None): + raise SourceError("--input/--input-file and --request-json are mutually exclusive") if not args.request_json: - return RunRequest(input=args.input or "") + return RunRequest(input=_input(args)) raw = args.request_json if raw.startswith("@"): raw = Path(raw[1:]).read_text(encoding="utf-8") @@ -154,7 +185,7 @@ async def _execute(args: argparse.Namespace) -> int: async def _chat(fabric: Fabric, selected: SelectedSource, args: argparse.Namespace) -> int: transcript: list[dict[str, Any]] = [] async with await fabric.start_runtime(selected.config, base_dir=selected.base_dir) as runtime: - pending = args.input + pending = _input(args) if args.input is not None or args.input_file is not None else None while True: if pending is None: try: @@ -174,6 +205,9 @@ async def _chat(fabric: Fabric, selected: SelectedSource, args: argparse.Namespa def _discovery(args: argparse.Namespace) -> int | None: + if args.command == "version": + print(_runtime_version()) + return 0 if args.command == "preset": if args.preset_command == "list": for name in sorted(PRESETS): @@ -183,13 +217,13 @@ def _discovery(args: argparse.Namespace) -> int | None: item = PRESETS[args.name] except KeyError as error: raise SourceError(_unknown("preset", args.name, PRESETS)) from error - _emit({"name": item.name, "description": item.description}, None) + _emit(item.discovery(), None) return 0 if args.command == "example": if args.example_command == "list": for name in sorted(EXAMPLES): print(name) - else: + elif args.example_command == "show": try: item = EXAMPLES[args.name] except KeyError as error: @@ -200,20 +234,61 @@ def _discovery(args: argparse.Namespace) -> int | None: "description": item.description, "default_variant": item.default_variant, "variants": sorted(item.variants), + "init_supported": item.template_dir is not None, }, None, ) + else: + _init_example(args.name, args.destination) return 0 return None +def _runtime_version() -> str: + try: + return importlib.metadata.version("nemo-fabric-runtime") + except importlib.metadata.PackageNotFoundError: + from nemo_fabric import _native + + return _native.version() + + +def _init_example(name: str, destination: Path) -> None: + try: + item = EXAMPLES[name] + except KeyError as error: + raise SourceError(_unknown("example", name, EXAMPLES)) from error + if item.template_dir is None: + raise SourceError(f"example {name!r} cannot be copied") + destination = destination.resolve() + if not destination.name.isidentifier(): + raise SourceError("destination name must be a valid Python package name") + if destination.exists(): + raise SourceError(f"destination already exists: {destination}") + shutil.copytree(item.template_dir, destination) + print(f"Created {name} in {destination}") + print( + f"From {destination.parent}, run: nemo-fabric run " + f"--factory {destination.name}.config:build_config " + f"--base-dir {destination} --input 'Review the workspace'" + ) + + def cli(argv: list[str] | None = None) -> int: """Run the CLI and return a process exit code.""" - args = build_parser().parse_args(argv) + arguments = list(argv) if argv is not None else sys.argv[1:] + parser = build_parser() + if not arguments: + parser.print_help() + return 0 + args = parser.parse_args(arguments) try: discovery = _discovery(args) return discovery if discovery is not None else asyncio.run(_execute(args)) + except KeyboardInterrupt: + print("nemo-fabric: interrupted", file=sys.stderr) + return 130 except (FabricError, SourceError, OSError, ValueError) as error: print(f"nemo-fabric: {error}", file=sys.stderr) return 2 diff --git a/python/src/nemo_fabric/presets.py b/python/src/nemo_fabric/presets.py index d4eabecae..0749bee4b 100644 --- a/python/src/nemo_fabric/presets.py +++ b/python/src/nemo_fabric/presets.py @@ -5,11 +5,18 @@ from __future__ import annotations +import importlib.util +import os from collections.abc import Callable from dataclasses import dataclass from pathlib import Path -from nemo_fabric.models import EnvironmentConfig, FabricConfig, HarnessConfig, MetadataConfig, ModelConfig +from nemo_fabric.models import EnvironmentConfig +from nemo_fabric.models import FabricConfig +from nemo_fabric.models import HarnessConfig +from nemo_fabric.models import MetadataConfig +from nemo_fabric.models import ModelConfig +from nemo_fabric.models import RuntimeConfig ConfigFactory = Callable[[], FabricConfig] BUNDLED_BASE_DIR = Path(__file__).resolve().parent / "_bundled" @@ -23,6 +30,35 @@ class Preset: description: str factory: ConfigFactory base_dir: Path = BUNDLED_BASE_DIR + install_extra: str | None = None + required_env: tuple[str, ...] = () + probe_module: str | None = None + + @property + def available(self) -> bool: + """Return whether the preset's adapter implementation is importable.""" + + if self.probe_module is None: + return True + try: + return importlib.util.find_spec(self.probe_module) is not None + except (ImportError, ModuleNotFoundError, ValueError): + return False + + def discovery(self) -> dict[str, object]: + """Return user-facing preset requirements without serializing config.""" + + install = "pip install nemo-fabric" + if self.install_extra is not None: + install = f"pip install 'nemo-fabric[{self.install_extra}]'" + return { + "name": self.name, + "description": self.description, + "available": self.available, + "install": install, + "required_env": list(self.required_env), + "missing_env": [name for name in self.required_env if not os.environ.get(name)], + } @dataclass(frozen=True) @@ -34,6 +70,7 @@ class Example: variants: dict[str, ConfigFactory] default_variant: str base_dir: Path + template_dir: Path | None = None def _preset( @@ -52,6 +89,23 @@ def _preset( ) +def scripted() -> FabricConfig: + """Return a deterministic credential-free experimentation preset.""" + + return FabricConfig( + metadata=MetadataConfig( + name="scripted-agent", + description="Credential-free NeMo Fabric CLI smoke preset.", + ), + harness=HarnessConfig( + adapter_id="nvidia.fabric.scripted", + resolution="preinstalled", + ), + runtime=RuntimeConfig(input_schema="text", output_schema="message"), + environment=EnvironmentConfig(provider="local"), + ) + + def hermes() -> FabricConfig: """Return a fresh Hermes preset.""" @@ -111,18 +165,54 @@ def _code_review(variant: str) -> FabricConfig: PRESETS: dict[str, Preset] = { - "hermes": Preset("hermes", "Hermes Agent with an NVIDIA-hosted model.", hermes), - "claude": Preset("claude", "Claude Code with an Anthropic model.", claude), - "codex": Preset("codex", "Codex with an OpenAI model.", codex), - "deepagents": Preset("deepagents", "LangChain Deep Agents with an NVIDIA-hosted model.", deepagents), + "scripted": Preset( + "scripted", + "Credential-free deterministic smoke preset.", + scripted, + ), + "hermes": Preset( + "hermes", + "Hermes Agent with an NVIDIA-hosted model.", + hermes, + install_extra="hermes", + required_env=("NVIDIA_API_KEY",), + probe_module="nemo_fabric_adapters.hermes.adapter", + ), + "claude": Preset( + "claude", + "Claude Code with an Anthropic model.", + claude, + install_extra="claude", + required_env=("ANTHROPIC_API_KEY",), + probe_module="nemo_fabric_adapters.claude.adapter", + ), + "codex": Preset( + "codex", + "Codex with an OpenAI model.", + codex, + install_extra="codex", + required_env=("OPENAI_API_KEY",), + probe_module="nemo_fabric_adapters.codex.adapter", + ), + "deepagents": Preset( + "deepagents", + "LangChain Deep Agents with an NVIDIA-hosted model.", + deepagents, + install_extra="deepagents", + required_env=("NVIDIA_API_KEY",), + probe_module="nemo_fabric_adapters.deepagents.adapter", + ), } +EXAMPLE_VARIANTS = ("hermes", "claude", "codex", "deepagents") + EXAMPLES: dict[str, Example] = { "examples.code_review_agent": Example( name="examples.code_review_agent", description="Installed, editable-in-Python code review example.", - variants={name: (lambda name=name: _code_review(name)) for name in PRESETS}, + variants={name: (lambda name=name: _code_review(name)) for name in EXAMPLE_VARIANTS}, default_variant="hermes", base_dir=BUNDLED_BASE_DIR, + template_dir=BUNDLED_BASE_DIR / "examples" / "code_review_agent", ) } diff --git a/tests/e2e/test_cli.py b/tests/e2e/test_cli.py index ec11cbcb5..2788b1dd9 100644 --- a/tests/e2e/test_cli.py +++ b/tests/e2e/test_cli.py @@ -20,13 +20,65 @@ def test_discovery_commands() -> None: examples = run("example", "list") assert presets.returncode == 0, presets.stderr - assert {"hermes", "claude", "codex", "deepagents"}.issubset(presets.stdout.splitlines()) + assert {"scripted", "hermes", "claude", "codex", "deepagents"}.issubset( + presets.stdout.splitlines() + ) assert examples.returncode == 0, examples.stderr assert "examples.code_review_agent" in examples.stdout.splitlines() shown = call_json("example", "show", "examples.code_review_agent") assert shown["default_variant"] == "hermes" assert set(shown["variants"]) == {"hermes", "claude", "codex", "deepagents"} + assert shown["init_supported"] is True + + scripted = call_json("preset", "show", "scripted") + assert scripted["available"] is True + assert scripted["required_env"] == [] + assert scripted["missing_env"] == [] + assert scripted["install"] == "pip install nemo-fabric" + + +def test_scripted_preset_runs_without_credentials(tmp_path: Path) -> None: + input_file = tmp_path / "input.txt" + input_file.write_text("credential-free smoke", encoding="utf-8") + + result = call_json("run", "--preset", "scripted", "--input-file", input_file) + + assert result["status"] == "succeeded" + assert result["adapter_id"] == "nvidia.fabric.scripted" + assert result["output"]["response"] == "credential-free smoke" + + +def test_help_and_version_are_available() -> None: + help_result = run() + version = run("version") + + assert help_result.returncode == 0 + assert "experimentation interface" in help_result.stdout + assert version.returncode == 0 + assert version.stdout.strip() + + +def test_example_can_be_copied_and_run_as_a_factory(tmp_path: Path) -> None: + destination = tmp_path / "my_agent" + initialized = run("example", "init", "examples.code_review_agent", destination) + + assert initialized.returncode == 0, initialized.stderr + assert (destination / "config.py").is_file() + assert (destination / "adapters" / "scripted" / "fabric-adapter.json").is_file() + + result = call_json_from( + tmp_path, + "run", + "--factory", + "my_agent.config:build_config", + "--base-dir", + destination, + "--input", + "review this", + ) + assert result["status"] == "succeeded" + assert result["output"]["response"] == "review this" def test_preset_and_example_reach_the_same_sdk_plan_path() -> None: @@ -96,6 +148,7 @@ def test_selector_errors_are_actionable() -> None: unknown = run("plan", "--preset", "missing") malformed = run("plan", "--factory", "missing-factory") conflict = run("plan", "--preset", "hermes", "--factory", "mod:factory") + misplaced_variant = run("plan", "--preset", "scripted", "--variant", "hermes") assert unknown.returncode == 2 assert "available:" in unknown.stderr @@ -103,10 +156,16 @@ def test_selector_errors_are_actionable() -> None: assert "module:callable" in malformed.stderr assert conflict.returncode == 2 assert "not allowed with argument" in conflict.stderr + assert misplaced_variant.returncode == 2 + assert "--variant requires --example" in misplaced_variant.stderr def call_json(*args: Any) -> dict[str, Any]: - completed = run(*args) + return call_json_from(ROOT, *args) + + +def call_json_from(cwd: Path, *args: Any) -> dict[str, Any]: + completed = run_from(cwd, *args) assert completed.returncode == 0, completed.stderr value = json.loads(completed.stdout) assert isinstance(value, dict) @@ -114,6 +173,10 @@ def call_json(*args: Any) -> dict[str, Any]: def run(*args: Any) -> subprocess.CompletedProcess[str]: + return run_from(ROOT, *args) + + +def run_from(cwd: Path, *args: Any) -> subprocess.CompletedProcess[str]: env = os.environ.copy() python_path = [str(ROOT / "python" / "src"), str(ROOT / "tests")] if env.get("PYTHONPATH"): @@ -121,7 +184,7 @@ def run(*args: Any) -> subprocess.CompletedProcess[str]: env["PYTHONPATH"] = os.pathsep.join(python_path) return subprocess.run( [sys.executable, "-m", "nemo_fabric.cli", *(str(arg) for arg in args)], - cwd=ROOT, + cwd=cwd, env=env, text=True, capture_output=True, diff --git a/uv.lock b/uv.lock index 25d2d73eb..483140ecb 100644 --- a/uv.lock +++ b/uv.lock @@ -1949,6 +1949,9 @@ wheels = [ name = "nemo-fabric" version = "0.1.0" source = { editable = "." } +dependencies = [ + { name = "nemo-fabric-runtime" }, +] [package.optional-dependencies] adapters-claude = [ @@ -2033,6 +2036,7 @@ requires-dist = [ { name = "nemo-fabric-adapters-deepagents", marker = "extra == 'deepagents'", editable = "adapters/deepagents" }, { name = "nemo-fabric-adapters-hermes", marker = "python_full_version < '3.14' and extra == 'adapters-hermes'", editable = "adapters/hermes" }, { name = "nemo-fabric-adapters-hermes", marker = "python_full_version < '3.14' and extra == 'hermes'", editable = "adapters/hermes" }, + { name = "nemo-fabric-runtime", editable = "python" }, { name = "nemo-fabric-runtime", marker = "extra == 'runtime'", editable = "python" }, { name = "nemo-relay", marker = "extra == 'relay'", specifier = "~=0.5.0" }, { name = "pyyaml", marker = "extra == 'harbor'", specifier = ">=6.0" }, From e760f2b58fa64872b2ffe687c35a3fdafdbb09df Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:22:10 -0700 Subject: [PATCH 03/20] fix: address CLI migration review feedback Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- README.md | 9 ++- .../deepagents/adapter.py | 25 ++++--- crates/fabric-core/src/config.rs | 73 +++++++++++++++++-- crates/fabric-core/src/doctor.rs | 30 +++++++- crates/fabric-core/src/error.rs | 16 ++++ crates/fabric-core/src/runtime.rs | 5 +- crates/fabric-python/src/lib.rs | 46 ++++++++---- docs/about-nemo-fabric/overview.mdx | 2 +- .../nemo_fabric.client.md | 4 +- .../config/struct-effectiveconfig.mdx | 2 +- .../config/struct-resolvecontext.mdx | 2 +- .../config/struct-runplan.mdx | 16 +++- .../error/enum-fabricerror.mdx | 30 +++++++- .../runtime/struct-adapterinvocation.mdx | 2 +- docs/sdk/python.mdx | 2 +- .../examples/code_review_agent/README.md | 2 +- .../code_review_agent/skills/code-review.md | 2 +- python/src/nemo_fabric/cli.py | 21 +++++- python/src/nemo_fabric/client.py | 4 +- python/src/nemo_fabric/runtime.py | 20 +---- python/src/nemo_fabric/types.py | 9 ++- schemas/SCHEMA.md | 2 +- schemas/adapter-invocation.schema.json | 4 +- schemas/effective-config.schema.json | 2 +- schemas/run-plan.schema.json | 6 +- skills/nemo-fabric-integrate/SKILL.md | 6 +- tests/adapters/test_deepagents.py | 37 +++++++++- tests/docs/test_python_api_docs.py | 2 +- tests/e2e/test_cli.py | 57 +++++++++++---- tests/python/test_native_sdk.py | 13 ++++ tests/python/test_sdk_contract.py | 22 ++++++ 31 files changed, 371 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 72608cfae..5792edb7d 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,10 @@ flowchart TB ## Quick Start: Experimentation CLI -The `nemo-fabric` CLI is a maintained developer interface for quick harness -experiments, smoke tests, examples, planning, and diagnostics. The Python SDK -remains the stable application-facing contract. +The `nemo-fabric` CLI is a maintained, experimental developer interface for +quick harness experiments, smoke tests, examples, planning, and diagnostics. +Its command contract can evolve as experiments mature. The Python SDK remains +the stable application-facing contract. Build and install the local wheels, then run the credential-free preset: @@ -109,7 +110,7 @@ Install Fabric, Hermes Agent, and the Hermes adapter into an environment: ```bash # Use any Python 3.11-3.13 interpreter for Hermes. python3 -m venv .tmp/hermes-venv -.tmp/hermes-venv/bin/python -m pip install --find-links dist "nemo-fabric[hermes]" +.tmp/hermes-venv/bin/python -m pip install --find-links dist "nemo-fabric[adapters-hermes]" ``` If you are working from a local Hermes checkout, replace the final install line diff --git a/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py b/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py index 081973eb1..0adc94c2d 100644 --- a/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py +++ b/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py @@ -507,16 +507,16 @@ async def run_deepagents(payload: dict[str, Any]) -> dict[str, Any]: import importlib.util if importlib.util.find_spec("nemo_relay") is None: - raise RuntimeError( - "telemetry is enabled but the 'nemo-relay' package is not installed; " - "install the Relay extra (pip install 'nemo-fabric-adapters-deepagents[relay]')." + raise _relay_dependency_error() + try: + api_config = common_utils.relay_api_plugin_config(observability.plugin_config) + from nemo_relay import ScopeType, plugin, scope + from nemo_relay.integrations.deepagents import ( + NemoRelayDeepAgentsCallbackHandler, + add_nemo_relay_integration, ) - api_config = common_utils.relay_api_plugin_config(observability.plugin_config) - from nemo_relay import ScopeType, plugin, scope - from nemo_relay.integrations.deepagents import ( - NemoRelayDeepAgentsCallbackHandler, - add_nemo_relay_integration, - ) + except (ImportError, AttributeError) as exc: + raise _relay_dependency_error() from exc # add_nemo_relay_integration injects the Deep Agents middleware (model/tool # calls, skill/subagent config marks) into create_deep_agent's kwargs and the @@ -645,6 +645,13 @@ class Observability(NamedTuple): collect_artifacts: bool +def _relay_dependency_error() -> RuntimeError: + return RuntimeError( + "telemetry is enabled but a compatible 'nemo-relay' package is not installed; " + "install the Relay extra (pip install 'nemo-fabric-adapters-deepagents[relay]')." + ) + + def resolve_observability( payload: dict[str, Any], telemetry_provider: str, relay_enabled: bool ) -> Observability | None: diff --git a/crates/fabric-core/src/config.rs b/crates/fabric-core/src/config.rs index 038d82d90..aa086dbec 100644 --- a/crates/fabric-core/src/config.rs +++ b/crates/fabric-core/src/config.rs @@ -360,7 +360,7 @@ pub struct AdapterTelemetryProviderSupport { /// Source context used when resolving an in-memory Fabric config. #[derive(Debug, Clone, PartialEq)] pub struct ResolveContext { - /// Base directory used to resolve relative config paths. + /// Base directory used to resolve relative Fabric paths. pub base_dir: PathBuf, } @@ -978,9 +978,16 @@ pub fn resolve_effective_config_from_config( context: ResolveContext, ) -> Result { validate_config(&config)?; + let supplied_base_dir = context.base_dir; + let base_dir = std::path::absolute(&supplied_base_dir) + .map(normalize_path) + .map_err(|source| FabricError::ResolveBaseDirectory { + path: supplied_base_dir, + source, + })?; Ok(EffectiveConfig { agent_name: config.metadata.name.clone(), - base_dir: context.base_dir, + base_dir, config, }) } @@ -1436,7 +1443,7 @@ fn normalize_path(path: PathBuf) -> PathBuf { pub struct EffectiveConfig { /// Stable agent name. pub agent_name: String, - /// Base directory used to resolve relative config paths. + /// Base directory used to resolve relative Fabric paths. pub base_dir: PathBuf, /// Complete typed Fabric config. pub config: FabricConfig, @@ -1445,7 +1452,7 @@ pub struct EffectiveConfig { /// Resolved Fabric run plan. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct RunPlan { - /// Resolved config used as the base for this plan. + /// Resolved typed configuration and path context used by this plan. pub effective_config: EffectiveConfig, /// Stable agent name. pub agent_name: String, @@ -1466,12 +1473,30 @@ pub struct RunPlan { /// Resolved telemetry pass-through plan. #[serde(default, skip_serializing_if = "Option::is_none")] pub telemetry_plan: Option, - /// Base directory used to resolve relative config paths. + /// Base directory used to resolve relative Fabric paths. pub base_dir: PathBuf, /// Complete typed Fabric config. pub config: FabricConfig, } +impl RunPlan { + /// Reject conflicting duplicated state before a plan enters a lifecycle boundary. + pub fn validate_consistency(&self) -> Result<()> { + if self.agent_name != self.effective_config.agent_name { + return Err(FabricError::RunPlanMismatch { + field: "agent_name", + }); + } + if self.base_dir != self.effective_config.base_dir { + return Err(FabricError::RunPlanMismatch { field: "base_dir" }); + } + if self.config != self.effective_config.config { + return Err(FabricError::RunPlanMismatch { field: "config" }); + } + Ok(()) + } +} + /// Lifecycle behavior implemented by a resolved runtime path. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct RuntimeCapabilities { @@ -1675,6 +1700,7 @@ mod tests { ResolveContext::new(&base_dir), ) .expect("typed plan"); + let base_dir = std::path::absolute(base_dir).expect("absolute base directory"); assert_eq!(plan.agent_name, "typed-agent"); assert_eq!(plan.base_dir, base_dir); @@ -1691,6 +1717,43 @@ mod tests { ); } + #[test] + fn relative_base_dir_becomes_absolute_before_deriving_paths() { + let plan = resolve_run_plan_from_config( + typed_config("nvidia.fabric.hermes"), + ResolveContext::new("."), + ) + .expect("typed plan"); + let expected_base = std::env::current_dir().expect("current directory"); + + assert_eq!(plan.base_dir, expected_base); + assert_eq!( + plan.environment_plan + .as_ref() + .and_then(|environment| environment.workspace.as_ref()), + Some(&expected_base.join("workspace")) + ); + assert_eq!( + plan.capability_plan.skill_paths, + vec![expected_base.join("skills/review")] + ); + } + + #[test] + fn rejects_conflicting_duplicated_plan_state() { + let mut plan = resolve_run_plan_from_config( + typed_config("nvidia.fabric.hermes"), + ResolveContext::new(repository_root()), + ) + .expect("typed plan"); + plan.effective_config.base_dir = plan.base_dir.join("other"); + + assert!(matches!( + plan.validate_consistency(), + Err(FabricError::RunPlanMismatch { field: "base_dir" }) + )); + } + #[test] fn typed_config_round_trip_has_no_file_provenance() { let effective = resolve_effective_config_from_config( diff --git a/crates/fabric-core/src/doctor.rs b/crates/fabric-core/src/doctor.rs index 9f6e5116c..11739eac9 100644 --- a/crates/fabric-core/src/doctor.rs +++ b/crates/fabric-core/src/doctor.rs @@ -55,6 +55,7 @@ pub struct DoctorReport { /// Inspect a resolved run plan without mutating the environment. pub fn doctor_plan(plan: &RunPlan) -> DoctorReport { let mut checks = Vec::new(); + checks.push(check_plan_consistency(plan)); checks.push(check_adapter_descriptor(plan)); checks.push(check_resolution(plan)); checks.extend(check_runtime_execution_surface(plan)); @@ -71,6 +72,17 @@ pub fn doctor_plan(plan: &RunPlan) -> DoctorReport { } } +fn check_plan_consistency(plan: &RunPlan) -> DoctorCheck { + match plan.validate_consistency() { + Ok(()) => check( + "plan.consistency", + DoctorStatus::Pass, + "run plan configuration state is internally consistent", + ), + Err(error) => check("plan.consistency", DoctorStatus::Fail, error.to_string()), + } +} + fn check_adapter_descriptor(plan: &RunPlan) -> DoctorCheck { if let Some(adapter) = &plan.adapter_descriptor { let mut metadata = BTreeMap::new(); @@ -475,14 +487,15 @@ mod tests { "schema_version": "fabric.agent/v1alpha1", "metadata": {"name": "doctor-agent"}, "harness": { - "adapter_id": "nvidia.fabric.hermes", - "resolution": "preinstalled" + "adapter_id": "test.fabric.hermes_shim", + "resolution": "preinstalled", + "settings": {"python3_command": "bin/tool"} }, "runtime": {}, "environment": {"provider": "local"} })) .expect("typed config"); - let base_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../.."); + let base_dir = PathBuf::from("../../tests/fixtures/hermes-shim-agent"); let plan = resolve_run_plan_from_config(config, ResolveContext::new(base_dir)) .expect("typed plan"); @@ -491,5 +504,16 @@ mod tests { assert_eq!(report.agent_name, "doctor-agent"); assert!(!report.checks.is_empty()); assert_eq!(value["agent_name"], "doctor-agent"); + let expected_command = + std::path::absolute("../../tests/fixtures/hermes-shim-agent/bin/tool") + .expect("absolute command path") + .to_string_lossy() + .into_owned(); + assert!( + report + .checks + .iter() + .any(|check| check.message.contains(&expected_command)) + ); } } diff --git a/crates/fabric-core/src/error.rs b/crates/fabric-core/src/error.rs index bb8cc76de..6908a7e64 100644 --- a/crates/fabric-core/src/error.rs +++ b/crates/fabric-core/src/error.rs @@ -13,6 +13,14 @@ pub type Result = std::result::Result; /// Errors raised by Fabric config loading and validation. #[derive(Debug, thiserror::Error)] pub enum FabricError { + /// The base directory could not be resolved to an absolute path. + #[error("failed to resolve base directory {path}: {source}")] + ResolveBaseDirectory { + /// Base directory supplied by the caller. + path: PathBuf, + /// Underlying path-resolution error. + source: std::io::Error, + }, /// The requested path does not exist. #[error("path does not exist: {0}")] PathNotFound(PathBuf), @@ -96,6 +104,14 @@ pub enum FabricError { /// Runtime handle id. runtime_id: String, }, + /// A run plan contains conflicting copies of resolved configuration state. + #[error( + "run plan contains conflicting `{field}` values between its top-level and effective config" + )] + RunPlanMismatch { + /// Conflicting run-plan field. + field: &'static str, + }, /// An environment provider is not runnable for the selected adapter in this POC. #[error("environment provider `{provider}` is not implemented for adapter `{adapter_kind:?}`")] UnsupportedEnvironmentProvider { diff --git a/crates/fabric-core/src/runtime.rs b/crates/fabric-core/src/runtime.rs index 5d1af5035..f0be45809 100644 --- a/crates/fabric-core/src/runtime.rs +++ b/crates/fabric-core/src/runtime.rs @@ -309,7 +309,7 @@ pub struct RuntimeTelemetryContext { /// Adapter-facing invocation payload. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct AdapterInvocation { - /// Merged agent config and provenance. + /// Resolved typed configuration with explicit path context. pub effective_config: EffectiveConfig, /// Per-runtime/per-invocation execution context. pub runtime_context: RuntimeContext, @@ -359,6 +359,7 @@ pub fn run_plan(plan: &RunPlan, request: RunRequest) -> Result { /// Resolve or attach to the execution environment context for a run plan. pub fn prepare_environment(plan: &RunPlan) -> Result { + plan.validate_consistency()?; let mut metadata = BTreeMap::new(); let mut connection = BTreeMap::new(); let ( @@ -442,6 +443,7 @@ pub fn invoke_runtime( runtime: &RuntimeHandle, request: RunRequest, ) -> Result { + plan.validate_consistency()?; validate_blocked_tools_support(plan)?; validate_runtime_handle(plan, runtime)?; match adapter_kind(plan) { @@ -468,6 +470,7 @@ fn validate_blocked_tools_support(plan: &RunPlan) -> Result<()> { /// Stop or detach from a harness runtime. pub fn stop_runtime(plan: &RunPlan, runtime: &RuntimeHandle) -> Result> { + plan.validate_consistency()?; validate_runtime_handle(plan, runtime)?; match runtime.adapter_kind { AdapterKind::Process => ProcessAdapter.stop(runtime), diff --git a/crates/fabric-python/src/lib.rs b/crates/fabric-python/src/lib.rs index 9fbd2db70..a1e4d4f30 100644 --- a/crates/fabric-python/src/lib.rs +++ b/crates/fabric-python/src/lib.rs @@ -31,20 +31,26 @@ fn resolve_config(config_json: String, base_dir: Option) -> PyResult) -> PyResult { +fn plan_config(py: Python<'_>, config_json: String, base_dir: Option) -> PyResult { let config = parse_config(config_json)?; - let plan = - resolve_run_plan_from_config(config, resolve_context(base_dir)).map_err(to_py_error)?; + let plan = py + .detach(|| resolve_run_plan_from_config(config, resolve_context(base_dir))) + .map_err(to_py_error)?; to_json(&plan) } /// Diagnose typed config JSON without installing or running it. #[pyfunction] #[pyo3(signature = (config_json, base_dir=None))] -fn doctor_config(config_json: String, base_dir: Option) -> PyResult { +fn doctor_config( + py: Python<'_>, + config_json: String, + base_dir: Option, +) -> PyResult { let config = parse_config(config_json)?; - let plan = - resolve_run_plan_from_config(config, resolve_context(base_dir)).map_err(to_py_error)?; + let plan = py + .detach(|| resolve_run_plan_from_config(config, resolve_context(base_dir))) + .map_err(to_py_error)?; to_json(&doctor_plan(&plan)) } @@ -52,6 +58,7 @@ fn doctor_config(config_json: String, base_dir: Option) -> PyResult, config_json: String, base_dir: Option, input_text: Option, @@ -60,27 +67,31 @@ fn run_config( request_file: Option, ) -> PyResult { let config = parse_config(config_json)?; - let plan = - resolve_run_plan_from_config(config, resolve_context(base_dir)).map_err(to_py_error)?; - let request = match (request_file, request_json, input_file) { - (Some(path), None, None) => std::fs::read_to_string(PathBuf::from(&path)) + let plan = py + .detach(|| resolve_run_plan_from_config(config, resolve_context(base_dir))) + .map_err(to_py_error)?; + let request = match (request_file, request_json, input_file, input_text) { + (Some(path), None, None, None) => std::fs::read_to_string(PathBuf::from(&path)) .map_err(|error| PyRuntimeError::new_err(format!("failed to read {path}: {error}"))) .and_then(parse_run_request)?, - (None, Some(json), None) => parse_run_request(json)?, - (None, None, Some(path)) => { + (None, Some(json), None, None) => parse_run_request(json)?, + (None, None, Some(path), None) => { let input = std::fs::read_to_string(PathBuf::from(&path)).map_err(|error| { PyRuntimeError::new_err(format!("failed to read {path}: {error}")) })?; RunRequest::text(input) } - (None, None, None) => RunRequest::text(input_text.unwrap_or_default()), + (None, None, None, Some(text)) => RunRequest::text(text), + (None, None, None, None) => RunRequest::text(""), _ => { return Err(PyRuntimeError::new_err( - "request_file, request_json, and input_file are mutually exclusive", + "input_text, input_file, request_json, and request_file are mutually exclusive", )); } }; - let result = run_plan(&plan, request).map_err(to_py_error)?; + let result = py + .detach(|| run_plan(&plan, request)) + .map_err(to_py_error)?; to_json(&result) } @@ -159,7 +170,10 @@ fn parse_run_request(contents: String) -> PyResult { } fn parse_run_plan(contents: String) -> PyResult { - serde_json::from_str(&contents).map_err(|error| PyRuntimeError::new_err(error.to_string())) + let plan: RunPlan = serde_json::from_str(&contents) + .map_err(|error| PyRuntimeError::new_err(error.to_string()))?; + plan.validate_consistency().map_err(to_py_error)?; + Ok(plan) } fn parse_runtime_handle(contents: String) -> PyResult { diff --git a/docs/about-nemo-fabric/overview.mdx b/docs/about-nemo-fabric/overview.mdx index ccbf011e7..df4b95dc3 100644 --- a/docs/about-nemo-fabric/overview.mdx +++ b/docs/about-nemo-fabric/overview.mdx @@ -133,7 +133,7 @@ Use `FabricConfig` as the canonical configuration contract. The CLI does not load a persisted YAML, TOML, or JSON Fabric config; its selectors obtain a typed config from maintained presets, examples, or user-owned Python factories. -## Core workflow +## Core Workflow 1. **Configure** a typed `FabricConfig` with a harness adapter, environment, models, tools, skills, MCP, and telemetry. diff --git a/docs/reference/api/python-library-reference/nemo_fabric.client.md b/docs/reference/api/python-library-reference/nemo_fabric.client.md index ff9706be7..42f5f392b 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.client.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.client.md @@ -73,7 +73,7 @@ plan( ) → RunPlan ``` -Resolve an agent source into an immutable execution plan. +Resolve a complete typed configuration into an immutable execution plan. Planning resolves the selected adapter and reports optional runtime capabilities such as streaming, updates, and cancellation. Planning does not start the runtime. @@ -108,7 +108,7 @@ resolve( ) → EffectiveConfig ``` -Resolve an agent source. +Resolve a complete typed configuration. Resolution validates and normalizes configuration but does not resolve an adapter or compute runtime capabilities. Use ``plan()`` when those execution details are required. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx index e657c33a8..379aead3d 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx @@ -21,7 +21,7 @@ Stable agent name. ### `base_dir: PathBuf` -Base directory used to resolve relative config paths. +Base directory used to resolve relative Fabric paths. ### `config: FabricConfig` diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx index c8c147843..60f1e8808 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx @@ -17,7 +17,7 @@ Source context used when resolving an in-memory Fabric config. ### `base_dir: PathBuf` -Base directory used to resolve relative config paths. +Base directory used to resolve relative Fabric paths. ## Implementations diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx index 53dc18633..8b822f019 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx @@ -17,7 +17,7 @@ Resolved Fabric run plan. ### `effective_config: EffectiveConfig` -Resolved config used as the base for this plan. +Resolved typed configuration and path context used by this plan. ### `agent_name: String` @@ -49,12 +49,24 @@ Resolved telemetry pass-through plan. ### `base_dir: PathBuf` -Base directory used to resolve relative config paths. +Base directory used to resolve relative Fabric paths. ### `config: FabricConfig` Complete typed Fabric config. +## Implementations + +### `impl RunPlan` + +
RunPlan"}} />
+ +#### `validate_consistency` + +
Result<()>"}} />
+ +Reject conflicting duplicated state before a plan enters a lifecycle boundary. + ## Trait Implementations ### `impl Clone for RunPlan` diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx index 0df6d8a6f..a773136b0 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx @@ -9,12 +9,28 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
PathBuf),\n    UnknownAdapter {\n        adapter_id: String,\n        available: Vec<String>,\n    },\n    AdapterDescriptorMismatch {\n        path: PathBuf,\n        field: &'static str,\n        expected: String,\n        actual: String,\n    },\n    AdapterDescriptorUnsupported {\n        adapter_id: String,\n        field: &'static str,\n        value: String,\n    },\n    InvalidAdapterDescriptor {\n        path: PathBuf,\n        message: String,\n    },\n    UnknownSchema {\n        schema: String,\n        available: Vec<String>,\n    },\n    UnsupportedRuntimeAdapter {\n        harness: String,\n        adapter_kind: AdapterKind,\n    },\n    UnsupportedToolsPolicy {\n        harness: String,\n        reason: String,\n    },\n    RuntimeHandleMismatch {\n        field: &'static str,\n        expected: String,\n        actual: String,\n        runtime_id: String,\n    },\n    UnsupportedEnvironmentProvider {\n        provider: String,\n        adapter_kind: AdapterKind,\n    },\n    InvalidProcessSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    InvalidPythonSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    PythonInterpreterUnavailable {\n        path: PathBuf,\n        origin: String,\n        reason: String,\n    },\n    ProcessRunner {\n        command: String,\n        source: Error,\n    },\n    SerializeJson(Error),\n    Read {\n        path: PathBuf,\n        source: Error,\n    },\n    Write {\n        path: PathBuf,\n        source: Error,\n    },\n    ParseJson {\n        path: PathBuf,\n        source: Error,\n    },\n}"}} />
+
PathBuf,\n        source: Error,\n    },\n    PathNotFound(PathBuf),\n    UnknownAdapter {\n        adapter_id: String,\n        available: Vec<String>,\n    },\n    AdapterDescriptorMismatch {\n        path: PathBuf,\n        field: &'static str,\n        expected: String,\n        actual: String,\n    },\n    AdapterDescriptorUnsupported {\n        adapter_id: String,\n        field: &'static str,\n        value: String,\n    },\n    InvalidAdapterDescriptor {\n        path: PathBuf,\n        message: String,\n    },\n    UnknownSchema {\n        schema: String,\n        available: Vec<String>,\n    },\n    UnsupportedRuntimeAdapter {\n        harness: String,\n        adapter_kind: AdapterKind,\n    },\n    UnsupportedToolsPolicy {\n        harness: String,\n        reason: String,\n    },\n    RuntimeHandleMismatch {\n        field: &'static str,\n        expected: String,\n        actual: String,\n        runtime_id: String,\n    },\n    RunPlanMismatch {\n        field: &'static str,\n    },\n    UnsupportedEnvironmentProvider {\n        provider: String,\n        adapter_kind: AdapterKind,\n    },\n    InvalidProcessSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    InvalidPythonSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    PythonInterpreterUnavailable {\n        path: PathBuf,\n        origin: String,\n        reason: String,\n    },\n    ProcessRunner {\n        command: String,\n        source: Error,\n    },\n    SerializeJson(Error),\n    Read {\n        path: PathBuf,\n        source: Error,\n    },\n    Write {\n        path: PathBuf,\n        source: Error,\n    },\n    ParseJson {\n        path: PathBuf,\n        source: Error,\n    },\n}"}} />
Errors raised by Fabric config loading and validation. ## Variants +### `ResolveBaseDirectory` + +
+ +The base directory could not be resolved to an absolute path. + +#### Fields + +### `path: PathBuf` + +Base directory supplied by the caller. + +### `source: Error` + +Underlying path-resolution error. + ### `PathNotFound(PathBuf)`
PathBuf)"}} />
@@ -169,6 +185,18 @@ Actual value from the runtime handle. Runtime handle id. +### `RunPlanMismatch` + +
+ +A run plan contains conflicting copies of resolved configuration state. + +#### Fields + +### `field: &'static str` + +Conflicting run-plan field. + ### `UnsupportedEnvironmentProvider`
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx index 4d332bd2e..6d73bbd0b 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx @@ -17,7 +17,7 @@ Adapter-facing invocation payload. ### `effective_config: EffectiveConfig` -Merged agent config and provenance. +Resolved typed configuration with explicit path context. ### `runtime_context: RuntimeContext` diff --git a/docs/sdk/python.mdx b/docs/sdk/python.mdx index 00799586f..c355f1091 100644 --- a/docs/sdk/python.mdx +++ b/docs/sdk/python.mdx @@ -14,7 +14,7 @@ The SDK is config-first. Applications should construct a Pydantic `FabricConfig` from their own job, deployment, or evaluation config. NeMo Fabric does not load a persisted YAML, TOML, or JSON agent configuration. Examples, CI jobs, and applications construct the typed config directly and -may use ordinary Python functions to create variants. +can use ordinary Python functions to create variants. Generated API reference pages remain the source of truth for exact signatures. This guide explains how the pieces are intended to fit together. diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md b/python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md index ed038981f..728b08498 100644 --- a/python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md +++ b/python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md @@ -3,7 +3,7 @@ SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All SPDX-License-Identifier: Apache-2.0 --> -# Editable code-review example +# Editable Code Review Example This directory is ordinary Python source copied by `nemo-fabric example init`. Edit `config.py` to choose a real harness, model, tools, or telemetry. The diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/skills/code-review.md b/python/src/nemo_fabric/_bundled/examples/code_review_agent/skills/code-review.md index c888def9b..cbc0f18db 100644 --- a/python/src/nemo_fabric/_bundled/examples/code_review_agent/skills/code-review.md +++ b/python/src/nemo_fabric/_bundled/examples/code_review_agent/skills/code-review.md @@ -3,6 +3,6 @@ SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All SPDX-License-Identifier: Apache-2.0 --> -# Code review +# Code Review Inspect the workspace, prioritize correctness issues, and explain concrete fixes. diff --git a/python/src/nemo_fabric/cli.py b/python/src/nemo_fabric/cli.py index cbeaa53fb..5514aac09 100644 --- a/python/src/nemo_fabric/cli.py +++ b/python/src/nemo_fabric/cli.py @@ -44,6 +44,17 @@ def load_factory(spec: str) -> FabricConfig: raise SourceError(str(error)) from error +def _invoke_factory(label: str, factory: Any) -> FabricConfig: + """Invoke a bundled config factory with CLI-friendly failures.""" + + try: + return factory() + except (FabricError, SourceError, OSError, ValueError): + raise + except Exception as error: + raise SourceError(f"failed to build {label}: {error}") from error + + def select_source(args: argparse.Namespace) -> SelectedSource: """Resolve one mutually exclusive CLI selector.""" @@ -55,7 +66,8 @@ def select_source(args: argparse.Namespace) -> SelectedSource: preset = PRESETS[args.preset] except KeyError as error: raise SourceError(_unknown("preset", args.preset, PRESETS)) from error - return SelectedSource(preset.factory(), override or preset.base_dir, f"preset:{preset.name}") + label = f"preset:{preset.name}" + return SelectedSource(_invoke_factory(label, preset.factory), override or preset.base_dir, label) if args.example: try: example = EXAMPLES[args.example] @@ -67,7 +79,8 @@ def select_source(args: argparse.Namespace) -> SelectedSource: except KeyError as error: choices = ", ".join(sorted(example.variants)) raise SourceError(f"unknown variant {variant!r} for {example.name}; available: {choices}") from error - return SelectedSource(factory(), override or example.base_dir, f"example:{example.name}:{variant}") + label = f"example:{example.name}:{variant}" + return SelectedSource(_invoke_factory(label, factory), override or example.base_dir, label) if args.factory: return SelectedSource(load_factory(args.factory), override or Path.cwd(), f"factory:{args.factory}") raise SourceError("select exactly one of --preset, --example, or --factory") @@ -179,7 +192,7 @@ async def _execute(args: argparse.Namespace) -> int: else: return await _chat(fabric, selected, args) _emit(result, args.output) - return 1 if getattr(result, "status", None) == "fail" else 0 + return 1 if getattr(result, "status", None) in {"fail", "failed"} else 0 async def _chat(fabric: Fabric, selected: SelectedSource, args: argparse.Namespace) -> int: @@ -189,7 +202,7 @@ async def _chat(fabric: Fabric, selected: SelectedSource, args: argparse.Namespa while True: if pending is None: try: - pending = input("you> ") + pending = await asyncio.to_thread(input, "you> ") except EOFError: break if pending.strip().lower() in {"/exit", "/quit"}: diff --git a/python/src/nemo_fabric/client.py b/python/src/nemo_fabric/client.py index a1889ce1e..fda860f6c 100644 --- a/python/src/nemo_fabric/client.py +++ b/python/src/nemo_fabric/client.py @@ -61,7 +61,7 @@ def resolve( *, base_dir: str | os.PathLike[str] | None = None, ) -> EffectiveConfig: - """Resolve an agent source. + """Resolve a complete typed configuration. Resolution validates and normalizes configuration but does not resolve an adapter or compute runtime capabilities. Use ``plan()`` when those @@ -100,7 +100,7 @@ def plan( *, base_dir: str | os.PathLike[str] | None = None, ) -> RunPlan: - """Resolve an agent source into an immutable execution plan. + """Resolve a complete typed configuration into an immutable execution plan. Planning resolves the selected adapter and reports optional runtime capabilities such as streaming, updates, and cancellation. Planning diff --git a/python/src/nemo_fabric/runtime.py b/python/src/nemo_fabric/runtime.py index c94434504..3fd736b4e 100644 --- a/python/src/nemo_fabric/runtime.py +++ b/python/src/nemo_fabric/runtime.py @@ -6,12 +6,10 @@ from __future__ import annotations import asyncio -import concurrent.futures import json from collections.abc import Mapping, Sequence from copy import deepcopy from enum import Enum -from threading import Thread from typing import Any from pydantic import ValidationError @@ -383,25 +381,13 @@ def run() -> dict[str, Any]: async def _call_blocking(func: Any) -> Any: - worker: concurrent.futures.Future[Any] = concurrent.futures.Future() - - def run() -> None: - try: - result = func() - except BaseException as error: - worker.set_exception(error) - else: - worker.set_result(result) - - Thread(target=run, name="nemo-fabric-worker", daemon=True).start() + worker = asyncio.create_task(asyncio.to_thread(func)) try: - while not worker.done(): - await asyncio.sleep(0.001) - return worker.result() + return await asyncio.shield(worker) except asyncio.CancelledError as cancelled: while not worker.done(): try: - await asyncio.sleep(0.001) + await asyncio.shield(worker) except asyncio.CancelledError: continue try: diff --git a/python/src/nemo_fabric/types.py b/python/src/nemo_fabric/types.py index dab4e40f4..9cf47ad94 100644 --- a/python/src/nemo_fabric/types.py +++ b/python/src/nemo_fabric/types.py @@ -633,6 +633,11 @@ def from_mapping(cls, value: Mapping[str, Any]) -> "_ResolvedFabricConfig": """Build a typed agent config from a mapping.""" data = _mapping(value, "FabricConfig") + if "profiles" in data: + raise FabricConfigError( + "FabricConfig profiles are no longer supported; " + "compose a complete typed config before calling the SDK" + ) if "metadata" not in data: raise FabricConfigError("FabricConfig metadata is required") if "harness" not in data: @@ -945,7 +950,9 @@ class EffectiveConfig(FabricMapping): @classmethod def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: - data["base_dir"] = Path(data.get("base_dir", ".")) + if "base_dir" not in data: + raise FabricConfigError("EffectiveConfig base_dir is required") + data["base_dir"] = Path(data["base_dir"]) data["config"] = _ResolvedFabricConfig.from_mapping(data.get("config", {})) return data diff --git a/schemas/SCHEMA.md b/schemas/SCHEMA.md index d1fbe3809..4856bfedf 100644 --- a/schemas/SCHEMA.md +++ b/schemas/SCHEMA.md @@ -17,7 +17,7 @@ schema-alignment tests in the same change. The core schema generator exports the current public typed contract. -### Config And Planning +### Config and Planning - `agent`: complete typed `FabricConfig`. - `adapter-descriptor`: minimal adapter descriptor consumed by Fabric. Each diff --git a/schemas/adapter-invocation.schema.json b/schemas/adapter-invocation.schema.json index 5422b954a..bac594532 100644 --- a/schemas/adapter-invocation.schema.json +++ b/schemas/adapter-invocation.schema.json @@ -224,7 +224,7 @@ "type": "string" }, "base_dir": { - "description": "Base directory used to resolve relative config paths.", + "description": "Base directory used to resolve relative Fabric paths.", "type": "string" }, "config": { @@ -1514,7 +1514,7 @@ }, "effective_config": { "$ref": "#/$defs/EffectiveConfig", - "description": "Merged agent config and provenance." + "description": "Resolved typed configuration with explicit path context." }, "request": { "$ref": "#/$defs/RunRequest", diff --git a/schemas/effective-config.schema.json b/schemas/effective-config.schema.json index cabac9c7f..26c1570f1 100644 --- a/schemas/effective-config.schema.json +++ b/schemas/effective-config.schema.json @@ -1024,7 +1024,7 @@ "type": "string" }, "base_dir": { - "description": "Base directory used to resolve relative config paths.", + "description": "Base directory used to resolve relative Fabric paths.", "type": "string" }, "config": { diff --git a/schemas/run-plan.schema.json b/schemas/run-plan.schema.json index 86a842b26..7cfd47d97 100644 --- a/schemas/run-plan.schema.json +++ b/schemas/run-plan.schema.json @@ -374,7 +374,7 @@ "type": "string" }, "base_dir": { - "description": "Base directory used to resolve relative config paths.", + "description": "Base directory used to resolve relative Fabric paths.", "type": "string" }, "config": { @@ -1619,7 +1619,7 @@ "type": "string" }, "base_dir": { - "description": "Base directory used to resolve relative config paths.", + "description": "Base directory used to resolve relative Fabric paths.", "type": "string" }, "capabilities": { @@ -1649,7 +1649,7 @@ }, "effective_config": { "$ref": "#/$defs/EffectiveConfig", - "description": "Resolved config used as the base for this plan." + "description": "Resolved typed configuration and path context used by this plan." }, "environment_plan": { "anyOf": [ diff --git a/skills/nemo-fabric-integrate/SKILL.md b/skills/nemo-fabric-integrate/SKILL.md index fc1baf760..4f66b825b 100644 --- a/skills/nemo-fabric-integrate/SKILL.md +++ b/skills/nemo-fabric-integrate/SKILL.md @@ -126,9 +126,9 @@ Pick the smallest lifecycle the consumer needs: Errors). A runtime accepts one active invocation at a time; overlapping calls raise `FabricStateError`. -The example below shows both lifecycles as a complete, runnable program; the -shorter async snippets elsewhere in this skill are fragments that assume the same -async context: +The lifecycle fragment below shows both forms. It assumes the caller has already +set `config = to_fabric_config(job)` and chosen `base`, as described in the +configuration example above: ```python import asyncio diff --git a/tests/adapters/test_deepagents.py b/tests/adapters/test_deepagents.py index 9843fa040..98bd125ff 100644 --- a/tests/adapters/test_deepagents.py +++ b/tests/adapters/test_deepagents.py @@ -14,6 +14,7 @@ import importlib.machinery import sys import types +from collections.abc import Iterator from pathlib import Path from typing import Any from unittest.mock import AsyncMock @@ -174,14 +175,14 @@ class ScopeType: Agent = "agent" @contextlib.contextmanager - def scope_ctx(name, scope_type, **_): + def scope_ctx(name: str, scope_type: object, **_: object) -> Iterator[None]: # Record every scope entered so tests can assert the top-level # ``deepagents-request`` Agent scope wraps the invocation. calls.setdefault("scopes", []).append((name, scope_type)) yield class NemoRelayDeepAgentsCallbackHandler: - def __init__(self, *_args, **_kwargs): + def __init__(self, *_args: object, **_kwargs: object) -> None: calls["callback_handler"] = self relay_root = types.ModuleType("nemo_relay") @@ -269,7 +270,9 @@ async def test_missing_deepagents_package_is_normalized(tmp_path, make_payload, real_find_spec = importlib_util.find_spec - def fake_find_spec(name, *args, **kwargs): + def fake_find_spec( + name: str, *args: object, **kwargs: object + ) -> importlib.machinery.ModuleSpec | None: if name == "deepagents": return None return real_find_spec(name, *args, **kwargs) @@ -401,7 +404,9 @@ async def test_missing_nemo_relay_with_native_telemetry_is_normalized(tmp_path, real_find_spec = importlib_util.find_spec - def fake_find_spec(name, *args, **kwargs): + def fake_find_spec( + name: str, *args: object, **kwargs: object + ) -> importlib.machinery.ModuleSpec | None: if name == "nemo_relay": return None return real_find_spec(name, *args, **kwargs) @@ -426,6 +431,30 @@ def fake_find_spec(name, *args, **kwargs): assert "[relay]" in output["error"] +async def test_incomplete_nemo_relay_install_is_normalized( + tmp_path, make_payload, monkeypatch, fake_relay +): + monkeypatch.delitem(sys.modules, "nemo_relay.integrations.deepagents") + payload = make_payload(tmp_path) + payload["telemetry_plan"] = { + "providers": ["native"], + "relay_enabled": False, + "native_config": { + "version": 1, + "components": [ + {"kind": "observability", "enabled": True, "config": {"version": 1}} + ], + }, + "adapter_outputs": [], + } + + output = await adapter.run_deepagents(payload) + + assert output["failed"] is True + assert "compatible 'nemo-relay' package" in output["error"] + assert "[relay]" in output["error"] + + def test_apply_callbacks_preserves_existing_ahead_of_new(): # A consumer-provided callback already on the run config must be kept, with the # Relay callback appended after it rather than replacing it. diff --git a/tests/docs/test_python_api_docs.py b/tests/docs/test_python_api_docs.py index d9105b735..e501714cf 100644 --- a/tests/docs/test_python_api_docs.py +++ b/tests/docs/test_python_api_docs.py @@ -100,7 +100,7 @@ def test_landing_page_routes_new_users_through_the_product() -> None: "## How NeMo Fabric fits", "## Quick start", "## Choose your interface", - "## Core workflow", + "## Core Workflow", "## Next steps", ): assert heading in landing diff --git a/tests/e2e/test_cli.py b/tests/e2e/test_cli.py index 2788b1dd9..9a48d914c 100644 --- a/tests/e2e/test_cli.py +++ b/tests/e2e/test_cli.py @@ -11,11 +11,15 @@ import sys from pathlib import Path from typing import Any +from unittest.mock import AsyncMock, MagicMock + +import nemo_fabric.cli as cli_module +import pytest ROOT = Path(__file__).resolve().parents[2] -def test_discovery_commands() -> None: +def test_discovery_commands(): presets = run("preset", "list") examples = run("example", "list") @@ -38,7 +42,7 @@ def test_discovery_commands() -> None: assert scripted["install"] == "pip install nemo-fabric" -def test_scripted_preset_runs_without_credentials(tmp_path: Path) -> None: +def test_scripted_preset_runs_without_credentials(tmp_path: Path): input_file = tmp_path / "input.txt" input_file.write_text("credential-free smoke", encoding="utf-8") @@ -49,7 +53,7 @@ def test_scripted_preset_runs_without_credentials(tmp_path: Path) -> None: assert result["output"]["response"] == "credential-free smoke" -def test_help_and_version_are_available() -> None: +def test_help_and_version_are_available(): help_result = run() version = run("version") @@ -59,7 +63,7 @@ def test_help_and_version_are_available() -> None: assert version.stdout.strip() -def test_example_can_be_copied_and_run_as_a_factory(tmp_path: Path) -> None: +def test_example_can_be_copied_and_run_as_a_factory(tmp_path: Path): destination = tmp_path / "my_agent" initialized = run("example", "init", "examples.code_review_agent", destination) @@ -81,7 +85,7 @@ def test_example_can_be_copied_and_run_as_a_factory(tmp_path: Path) -> None: assert result["output"]["response"] == "review this" -def test_preset_and_example_reach_the_same_sdk_plan_path() -> None: +def test_preset_and_example_reach_the_same_sdk_plan_path(): preset = call_json("plan", "--preset", "hermes") example = call_json( "plan", @@ -93,11 +97,12 @@ def test_preset_and_example_reach_the_same_sdk_plan_path() -> None: assert preset["adapter_descriptor"]["descriptor"]["adapter_id"] == "nvidia.fabric.hermes" assert example["adapter_descriptor"]["descriptor"]["adapter_id"] == "nvidia.fabric.hermes" - assert preset["effective_config"]["base_dir"].endswith("nemo_fabric/_bundled") + expected_base = (ROOT / "python" / "src" / "nemo_fabric" / "_bundled").resolve() + assert Path(preset["effective_config"]["base_dir"]).resolve() == expected_base assert example["capability_plan"]["native"]["skill_paths"] -def test_factory_plan_doctor_and_run(hermes_shim_agent_dir: Path) -> None: +def test_factory_plan_doctor_and_run(hermes_shim_agent_dir: Path): selector = ( "--factory", "_utils.configs:hermes_shim_config", @@ -116,7 +121,7 @@ def test_factory_plan_doctor_and_run(hermes_shim_agent_dir: Path) -> None: assert result["output"]["received"] == "hello factory" -def test_factory_request_json_and_output_file(hermes_shim_agent_dir: Path, tmp_path: Path) -> None: +def test_factory_request_json_and_output_file(hermes_shim_agent_dir: Path, tmp_path: Path): output = tmp_path / "result.json" request = json.dumps( { @@ -144,7 +149,7 @@ def test_factory_request_json_and_output_file(hermes_shim_agent_dir: Path, tmp_p assert result["output"]["received"] == "hello request" -def test_selector_errors_are_actionable() -> None: +def test_selector_errors_are_actionable(): unknown = run("plan", "--preset", "missing") malformed = run("plan", "--factory", "missing-factory") conflict = run("plan", "--preset", "hermes", "--factory", "mod:factory") @@ -160,11 +165,37 @@ def test_selector_errors_are_actionable() -> None: assert "--variant requires --example" in misplaced_variant.stderr -def call_json(*args: Any) -> dict[str, Any]: +async def test_failed_run_returns_nonzero(monkeypatch): + result = MagicMock() + result.status = "failed" + result.to_mapping.return_value = {"status": "failed"} + fabric = MagicMock() + fabric.run = AsyncMock(return_value=result) + monkeypatch.setattr(cli_module, "Fabric", MagicMock(return_value=fabric)) + args = cli_module.build_parser().parse_args( + ["run", "--preset", "scripted", "--input", "fail"] + ) + + assert await cli_module._execute(args) == 1 + + +def test_bundled_factory_errors_are_normalized(monkeypatch): + preset = MagicMock() + preset.name = "broken" + preset.base_dir = ROOT + preset.factory.side_effect = RuntimeError("builder exploded") + monkeypatch.setitem(cli_module.PRESETS, "broken", preset) + args = cli_module.build_parser().parse_args(["plan", "--preset", "broken"]) + + with pytest.raises(cli_module.SourceError, match="failed to build preset:broken"): + cli_module.select_source(args) + + +def call_json(*args: object) -> dict[str, Any]: return call_json_from(ROOT, *args) -def call_json_from(cwd: Path, *args: Any) -> dict[str, Any]: +def call_json_from(cwd: Path, *args: object) -> dict[str, Any]: completed = run_from(cwd, *args) assert completed.returncode == 0, completed.stderr value = json.loads(completed.stdout) @@ -172,11 +203,11 @@ def call_json_from(cwd: Path, *args: Any) -> dict[str, Any]: return value -def run(*args: Any) -> subprocess.CompletedProcess[str]: +def run(*args: object) -> subprocess.CompletedProcess[str]: return run_from(ROOT, *args) -def run_from(cwd: Path, *args: Any) -> subprocess.CompletedProcess[str]: +def run_from(cwd: Path, *args: object) -> subprocess.CompletedProcess[str]: env = os.environ.copy() python_path = [str(ROOT / "python" / "src"), str(ROOT / "tests")] if env.get("PYTHONPATH"): diff --git a/tests/python/test_native_sdk.py b/tests/python/test_native_sdk.py index f1ff4232b..497d812b2 100644 --- a/tests/python/test_native_sdk.py +++ b/tests/python/test_native_sdk.py @@ -5,6 +5,7 @@ from __future__ import annotations +import json import os import sys from pathlib import Path @@ -54,6 +55,18 @@ async def test_adapter_python_rejects_invalid_path_before_start( assert caught.value.stage == "start" +def test_native_run_rejects_multiple_request_sources(hermes_shim_agent_dir: Path): + with pytest.raises(RuntimeError, match="mutually exclusive"): + native.run_config( + json.dumps(hermes_shim_config().model_dump(mode="json", exclude_none=True)), + str(hermes_shim_agent_dir), + "text", + None, + "{}", + None, + ) + + async def smoke(client: Fabric, fixture_agent: Path) -> None: example_config = base_config() diff --git a/tests/python/test_sdk_contract.py b/tests/python/test_sdk_contract.py index 0945dc89c..9939009dc 100644 --- a/tests/python/test_sdk_contract.py +++ b/tests/python/test_sdk_contract.py @@ -425,6 +425,28 @@ def test_inspection_models_are_typed_read_only_mappings(): plan["agent_name"] = "mutated" # type: ignore[index] +def test_resolved_config_rejects_removed_profiles_and_missing_base_dir(): + with pytest.raises(FabricConfigError, match="profiles are no longer supported"): + _ResolvedFabricConfig.from_mapping( + { + "metadata": {"name": "demo"}, + "harness": {"adapter_id": "test.fabric.shim"}, + "profiles": {"review": {}}, + } + ) + + with pytest.raises(FabricConfigError, match="base_dir is required"): + EffectiveConfig.from_mapping( + { + "agent_name": "demo", + "config": { + "metadata": {"name": "demo"}, + "harness": {"adapter_id": "test.fabric.shim"}, + }, + } + ) + + def test_runtime_handle_distinguishes_contract_and_extension_fields(): handle = RuntimeHandle.from_mapping( { From 15cbbda7035f6f3b752be41b3b4f71577473cb8b Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:23:52 -0700 Subject: [PATCH 04/20] docs: align quickstart and overview style Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- docs/about-nemo-fabric/overview.mdx | 10 +++++----- docs/getting-started/quickstart.mdx | 4 ++-- tests/docs/test_python_api_docs.py | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/about-nemo-fabric/overview.mdx b/docs/about-nemo-fabric/overview.mdx index df4b95dc3..c58c4edae 100644 --- a/docs/about-nemo-fabric/overview.mdx +++ b/docs/about-nemo-fabric/overview.mdx @@ -15,7 +15,7 @@ configuration, selects an adapter, drives the runtime lifecycle, and returns normalized evidence without leaking harness-specific control code into the caller. -## What NeMo Fabric gives you +## What NeMo Fabric Gives You @@ -36,7 +36,7 @@ into the caller. -## How NeMo Fabric fits +## How NeMo Fabric Fits ```text Application or evaluation harness @@ -59,7 +59,7 @@ and user factories all produce a complete `FabricConfig` before using the same resolved config and run-plan contracts. Adapters own harness-specific preparation and invocation; consumers own the request and returned evidence. -## Quick start +## Quick Start Install `just` 1.50.0+ if it is not already available. @@ -120,7 +120,7 @@ contains the complete Hermes environment recipe. Refer to the [Python SDK guide](/sdk/python) for planning, diagnostics, typed requests, and multi-turn runtime examples. -## Choose your interface +## Choose Your Interface | Interface | Use it when | Start with | | --- | --- | --- | @@ -146,7 +146,7 @@ typed config from maintained presets, examples, or user-owned Python factories. 5. **Consume evidence** from `RunResult`: output, structured failure details, artifacts, events, and telemetry references. -## Next steps +## Next Steps None: assert " - section: APIs\n" not in navigation for heading in ( - "## What NeMo Fabric gives you", - "## How NeMo Fabric fits", - "## Quick start", - "## Choose your interface", + "## What NeMo Fabric Gives You", + "## How NeMo Fabric Fits", + "## Quick Start", + "## Choose Your Interface", "## Core Workflow", - "## Next steps", + "## Next Steps", ): assert heading in landing @@ -113,8 +113,8 @@ def test_landing_page_routes_new_users_through_the_product() -> None: ): assert destination in landing - quick_start = landing.split("## Quick start", maxsplit=1)[1].split( - "## Choose your interface", maxsplit=1 + quick_start = landing.split("## Quick Start", maxsplit=1)[1].split( + "## Choose Your Interface", maxsplit=1 )[0] assert "client.plan(" not in quick_start assert "client.doctor(" not in quick_start From 7df971e5c821608c3677df11b672fe10ea669615 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:32:35 -0700 Subject: [PATCH 05/20] docs: remove stale file configuration references Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- CLI.md | 16 +++++-------- CONTRIBUTING.md | 2 +- README.md | 7 +++--- adapters/codex/README.md | 8 +++---- adapters/deepagents/README.md | 12 +++++----- crates/fabric-cli/README.md | 24 ------------------- crates/fabric-core/README.md | 6 ++--- crates/fabric-core/src/config.rs | 2 +- docs/about-nemo-fabric/overview.mdx | 6 ++--- .../config/struct-skillconfig.mdx | 2 +- docs/sdk/python.mdx | 8 +++---- examples/code_review_agent/README.md | 2 +- pypi.md | 5 ++-- schemas/adapter-invocation.schema.json | 2 +- schemas/agent.schema.json | 2 +- schemas/effective-config.schema.json | 2 +- schemas/run-plan.schema.json | 2 +- skills/nemo-fabric-integrate/SKILL.md | 15 ++++++------ .../references/config-mapping.md | 7 +++--- .../skills/code-review/README.md | 6 ++--- 20 files changed, 52 insertions(+), 84 deletions(-) delete mode 100644 crates/fabric-cli/README.md diff --git a/CLI.md b/CLI.md index e6db9525e..18981d00e 100644 --- a/CLI.md +++ b/CLI.md @@ -19,13 +19,10 @@ stability or production integration is required. Keep the CLI as a thin SDK-backed runner: - every run starts from a complete, typed `FabricConfig`; -- the CLI only selects where that config comes from; +- the CLI selects a preset, example variant, or Python factory; - planning and execution go through the public Python SDK; - `base_dir` only resolves relative resource paths; and -- Fabric does not load a persisted YAML, TOML, or JSON configuration. - -Profiles, overlays, config discovery, and compatibility loaders are out of -scope. Fabric has not been released, so the CLI makes a clean break. +- each selector returns a complete config without implicit merging. ## Supported Inputs @@ -48,8 +45,8 @@ nemo-fabric run \ ``` A runnable SDK example with complete variants. Examples are Python source and -assets that people can read, copy, and edit. A variant is a factory, not a -profile. +assets that people can read, copy, and edit. Each variant is an independent +factory. Copy the bundled example into an editable Python package with: @@ -90,15 +87,14 @@ plan doctor run chat - production deployment or scheduling; - persistent configuration or secrets management; -- profiles, inheritance, or configuration overlays; +- implicit configuration merging or inheritance; - arbitrary CLI configuration mutation; - evaluation orchestration or experiment tracking; or - replacing direct SDK applications. ## Boundaries -- No `agent.yaml` or profile directories. -- No Fabric config loader for YAML, TOML, or JSON. +- Presets, examples, and factories each return a complete `FabricConfig`. - No preset inheritance or core merge semantics. - No implicit user/project/system config discovery. - JSON request input is still valid; it describes an invocation, not an agent. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d64b2dd3f..ec1fc0a9f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -249,7 +249,7 @@ Valid types: Examples: ```text -feat: add typed runtime configuration overlays +feat: add typed runtime diagnostics fix: preserve adapter errors in run results docs: clarify Hermes adapter installation test: cover concurrent Python runtime invocations diff --git a/README.md b/README.md index 5792edb7d..99fb90ebc 100644 --- a/README.md +++ b/README.md @@ -186,10 +186,9 @@ authentication, and execution details. - **Artifacts:** normalized output, logs, patches, and telemetry references returned through an `ArtifactManifest`. -Fabric accepts complete typed configs and does not load agent or profile -configuration files. Compose variants in Python before calling the SDK. See -the [Python SDK guide](docs/sdk/python.mdx) for the complete public API, type -definitions, lifecycle semantics, and error behavior. +Fabric accepts complete typed configs. Compose variants in Python before +calling the SDK. Refer to the [Python SDK guide](docs/sdk/python.mdx) for the +complete public API, type definitions, lifecycle semantics, and error behavior. `run(...)` owns the complete start, invoke, and stop lifecycle. For typed in-memory configuration, planning and diagnostics, explicit requests, diff --git a/adapters/codex/README.md b/adapters/codex/README.md index 4275e9c76..a311dd401 100644 --- a/adapters/codex/README.md +++ b/adapters/codex/README.md @@ -57,7 +57,7 @@ This adapter pins `openai-codex==0.1.0b3`, which pins `openai-codex-cli-bin==0.137.0a4`. A newer `codex` command on `PATH` is not used implicitly. When testing a newer compatible runtime, set `harness.settings.codex_bin` to an app-server path that is absolute or relative -to the Fabric config root. Fabric passes the resolved path through +to the explicit `base_dir`. Fabric passes the resolved path through `CodexConfig.codex_bin`; the SDK remains the execution driver. ## Execution Model @@ -94,9 +94,8 @@ Codex-specific controls belong in `harness.settings`: - `env` for variables explicitly forwarded to the Codex runtime - `nemo_relay_command` for the optional external Relay gateway executable -The removed CLI settings `codex_command`, `codex_args`, `codex_profile`, -`codex_state_dir`, and `skip_git_repo_check` are errors. `model_name` and `cwd` -must use the normalized model and environment fields. +Set model selection through `models` and the working directory through +`environment.workspace`. The adapter filters the inherited environment. It retains portable OS and Codex state variables, the selected model's `api_key_env`, and explicit @@ -157,4 +156,3 @@ For Phoenix, native Codex OpenTelemetry targets the OTLP collector at Relay OpenInference provides the semantic chain, LLM, and tool hierarchy with decoded prompt, response, and token attributes. Prefer Relay OpenInference for agent-turn inspection. - diff --git a/adapters/deepagents/README.md b/adapters/deepagents/README.md index 5566220d2..2077546e1 100644 --- a/adapters/deepagents/README.md +++ b/adapters/deepagents/README.md @@ -109,10 +109,9 @@ artifacts directory). Fabric owns the runtime-to-thread correlation record; LangGraph owns the transcript. The `deepagents_config()` builder in `examples/code_review_agent` is the SDK -example; the `deepagents` profile under -`tests/fixtures/file-config-agent/profiles/` covers file-based resolution. Run it -from the CLI with `python -m examples.code_review_agent --variant deepagents ---input "..."`, or drive the SDK directly: +example. Run it from the CLI with +`python -m examples.code_review_agent --variant deepagents --input "..."`, or +drive the SDK directly: ```python from examples.code_review_agent import BASE_DIR, deepagents_config @@ -162,8 +161,9 @@ pip install "nemo-fabric-adapters-deepagents[relay]" # -> nemo-relay[deepagent Runs emit ATOF/ATIF artifacts to the configured output directory, referenced in the normalized result's `relay_artifacts` (and the `RunResult` `ArtifactManifest`). - OTel/OpenInference export is available through the relay plugin config (see the - `relay-otel` and `relay-openinference` profiles). + OTel/OpenInference export is available through the relay plugin config; the + example provides `with_relay_otel(...)` and + `with_relay_openinference(...)` variants. - **Native** (`telemetry.providers.native.config`): the provider config OpenTelemetry/OpenInference exporter is applied and spans export directly to the configured collector, without writing ATOF/ATIF relay artifacts. diff --git a/crates/fabric-cli/README.md b/crates/fabric-cli/README.md deleted file mode 100644 index 55d0f5b02..000000000 --- a/crates/fabric-cli/README.md +++ /dev/null @@ -1,24 +0,0 @@ - - -# NVIDIA NeMo Fabric CLI - -`nemo-fabric-cli` provides the `fabric` command-line interface for NVIDIA NeMo -Fabric, a runtime execution layer for agents. - -Install the CLI from crates.io: - -```bash -cargo install nemo-fabric-cli --locked -``` - -Use `fabric` to validate and inspect agent configurations, resolve run plans, -diagnose configuration issues, run or chat with agents through Fabric adapters, -and generate JSON Schema for the public configuration and runtime contracts. - -Run `fabric --help` for the complete command reference. - -For installation prerequisites, configuration concepts, adapters, and examples, -refer to the [NVIDIA NeMo Fabric repository](https://github.com/NVIDIA/nemo-fabric). diff --git a/crates/fabric-core/README.md b/crates/fabric-core/README.md index e8b3c3b28..d369b9bf4 100644 --- a/crates/fabric-core/README.md +++ b/crates/fabric-core/README.md @@ -14,9 +14,9 @@ Add the crate to a Rust project: cargo add nemo-fabric-core ``` -This crate provides typed agent configuration, profile resolution, validation, -run planning, runtime lifecycle operations, normalized results and artifact -manifests, telemetry references, diagnostics, and JSON Schema generation. +This crate provides typed agent configuration, validation, run planning, +runtime lifecycle operations, normalized results and artifact manifests, +telemetry references, diagnostics, and JSON Schema generation. For architecture, configuration concepts, adapters, and examples, refer to the [NVIDIA NeMo Fabric repository](https://github.com/NVIDIA/nemo-fabric). diff --git a/crates/fabric-core/src/config.rs b/crates/fabric-core/src/config.rs index aa086dbec..03ca5bfd5 100644 --- a/crates/fabric-core/src/config.rs +++ b/crates/fabric-core/src/config.rs @@ -475,7 +475,7 @@ fn default_environment_ownership() -> EnvironmentOwnership { /// Skill capability configuration. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Default)] pub struct SkillConfig { - /// Skill paths relative to the agent root. + /// Skill paths resolved relative to the config base directory. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub paths: Vec, /// Additive skill fields. diff --git a/docs/about-nemo-fabric/overview.mdx b/docs/about-nemo-fabric/overview.mdx index c58c4edae..b805b8e5b 100644 --- a/docs/about-nemo-fabric/overview.mdx +++ b/docs/about-nemo-fabric/overview.mdx @@ -129,9 +129,9 @@ requests, and multi-turn runtime examples. | `nemo-fabric` CLI | You are experimenting with harnesses, running examples, or troubleshooting configs | `preset`, `example`, `plan`, `doctor`, `run`, and `chat` | | JSON Schema | You are building editors, validation, code generation, or another language binding | Committed schemas in the [repository](https://github.com/NVIDIA/NeMo-Fabric/tree/main/schemas) | -Use `FabricConfig` as the canonical configuration contract. The CLI does not -load a persisted YAML, TOML, or JSON Fabric config; its selectors obtain a -typed config from maintained presets, examples, or user-owned Python factories. +Use `FabricConfig` as the canonical configuration contract. CLI selectors +obtain complete typed configs from maintained presets, examples, or user-owned +Python factories. ## Core Workflow diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx index bf85cefbd..1b3c1e1fe 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx @@ -17,7 +17,7 @@ Skill capability configuration. ### `paths: Vec` -Skill paths relative to the agent root. +Skill paths resolved relative to the config base directory. ### `extensions: BTreeMap` diff --git a/docs/sdk/python.mdx b/docs/sdk/python.mdx index c355f1091..509864050 100644 --- a/docs/sdk/python.mdx +++ b/docs/sdk/python.mdx @@ -12,7 +12,6 @@ telemetry references. The SDK is config-first. Applications should construct a Pydantic `FabricConfig` from their own job, deployment, or evaluation config. -NeMo Fabric does not load a persisted YAML, TOML, or JSON agent configuration. Examples, CI jobs, and applications construct the typed config directly and can use ordinary Python functions to create variants. @@ -401,7 +400,7 @@ environment, skills, MCP, telemetry, tools, artifacts, and request context. Use `harness.settings` for adapter-owned configuration that the selected adapter understands. Examples include Hermes-specific launch options, Codex SDK controls, -or adapter-specific config file locations. +or adapter-specific launch paths. Use `metadata` for caller-owned annotations that NeMo Fabric should preserve and echo back, but not interpret. @@ -437,9 +436,8 @@ around that contract. ### Schemas And Python Models The SDK's Pydantic models are maintained against the Rust-generated public -schemas. The schemas describe typed cross-language contracts; they are not a -supported Fabric config-file format. Use the generated API reference for exact -Python signatures. +schemas. The schemas describe the typed cross-language contracts. Use the +generated API reference for exact Python signatures. | Contract | Source | | --- | --- | diff --git a/examples/code_review_agent/README.md b/examples/code_review_agent/README.md index d6efeb8a6..9762d8e98 100644 --- a/examples/code_review_agent/README.md +++ b/examples/code_review_agent/README.md @@ -9,7 +9,7 @@ This example reviews the repository under `repos/my-service`. It constructs a complete `FabricConfig` with the public Pydantic models and passes it directly to the Python SDK. Variants are independent deep copies of that config. -The example does not serialize configs to YAML or use profiles. +Each variant is an independent Python factory that returns a complete config. ## Set up diff --git a/pypi.md b/pypi.md index cd5e074f8..1297acb9e 100644 --- a/pypi.md +++ b/pypi.md @@ -67,8 +67,9 @@ NeMo Fabric supports Python versions 3.11-3.14, however some of the integrations - **Artifacts:** Receive normalized output, logs, patches, and telemetry references through an `ArtifactManifest`. -The CLI does not load persisted YAML, TOML, or JSON Fabric configuration. -Applications that need a stable integration surface should use the Python SDK. +The CLI selects complete typed configs from presets, examples, or Python +factories. Applications that need a stable integration surface should use the +Python SDK. ## Learn More diff --git a/schemas/adapter-invocation.schema.json b/schemas/adapter-invocation.schema.json index bac594532..dc6992596 100644 --- a/schemas/adapter-invocation.schema.json +++ b/schemas/adapter-invocation.schema.json @@ -1368,7 +1368,7 @@ "description": "Skill capability configuration.", "properties": { "paths": { - "description": "Skill paths relative to the agent root.", + "description": "Skill paths resolved relative to the config base directory.", "items": { "type": "string" }, diff --git a/schemas/agent.schema.json b/schemas/agent.schema.json index f00e5ec78..8e0be3c05 100644 --- a/schemas/agent.schema.json +++ b/schemas/agent.schema.json @@ -866,7 +866,7 @@ "description": "Skill capability configuration.", "properties": { "paths": { - "description": "Skill paths relative to the agent root.", + "description": "Skill paths resolved relative to the config base directory.", "items": { "type": "string" }, diff --git a/schemas/effective-config.schema.json b/schemas/effective-config.schema.json index 26c1570f1..e83b16490 100644 --- a/schemas/effective-config.schema.json +++ b/schemas/effective-config.schema.json @@ -968,7 +968,7 @@ "description": "Skill capability configuration.", "properties": { "paths": { - "description": "Skill paths relative to the agent root.", + "description": "Skill paths resolved relative to the config base directory.", "items": { "type": "string" }, diff --git a/schemas/run-plan.schema.json b/schemas/run-plan.schema.json index 7cfd47d97..f28ee2bc8 100644 --- a/schemas/run-plan.schema.json +++ b/schemas/run-plan.schema.json @@ -1476,7 +1476,7 @@ "description": "Skill capability configuration.", "properties": { "paths": { - "description": "Skill paths relative to the agent root.", + "description": "Skill paths resolved relative to the config base directory.", "items": { "type": "string" }, diff --git a/skills/nemo-fabric-integrate/SKILL.md b/skills/nemo-fabric-integrate/SKILL.md index 4f66b825b..a85cd088a 100644 --- a/skills/nemo-fabric-integrate/SKILL.md +++ b/skills/nemo-fabric-integrate/SKILL.md @@ -23,8 +23,8 @@ Fabric itself, use the maintainer skills in `.agents/skills/` instead. Stay on the public, in-memory contract. These rules keep a consumer integration supported and upgrade-safe: -- Import only from the public `nemo_fabric` package. Never import `_native`, - `_config_sources`, or any adapter-internal module. +- Import only from the public `nemo_fabric` package. Never import `_native` or + any adapter-internal module. - Build configuration as a typed `FabricConfig` in memory and pass it directly to Fabric. Create every deployment or evaluation variant with ordinary Python functions and `model_copy(deep=True)`. A platform integration can serialize @@ -230,13 +230,14 @@ result-field and error inventory, and - Run the consumer project's own build and test commands. For a source checkout of Fabric, `just build-all` rebuilds the native extension and `just test-python` runs the Python suite. -- Confirm no config files were written and no non-public imports were added. +- Confirm the typed config is passed directly to Fabric and no non-public + imports were added. ## Checklist - [ ] The consumer config object is translated directly into an in-memory `FabricConfig`. -- [ ] Only public `nemo_fabric` symbols are imported; no `_native`, `_config_sources`, or adapter internals. -- [ ] The consumer config is built in memory and passed directly to Fabric, with no intermediate config file. +- [ ] Only public `nemo_fabric` symbols are imported; no `_native` or adapter internals. +- [ ] The consumer config is built in memory and passed directly to Fabric. - [ ] The right lifecycle is chosen: `run(...)` for one-shot, `start_runtime(...)` with `async with` for multi-turn. - [ ] `plan(...)` and `doctor(...)` validate adapter selection, capabilities, and environment before execution. - [ ] Installation, adapter dependencies, and credentials are owned by the environment, not consumer code. @@ -266,5 +267,5 @@ Link to these canonical sources instead of duplicating them: [nemo_fabric.integrations.harbor](https://github.com/NVIDIA/NeMo-Fabric/tree/main/python/src/nemo_fabric/integrations/harbor). Harbor constructs a typed config from explicit agent inputs and transports it inside a private transient run specification at the task-process boundary. - Follow the code-review example for consumer integration code; do not treat - Harbor's transport representation as a public Fabric config-file format. + Follow the code-review example for consumer integration code; Harbor's + transport representation is an internal process-boundary contract. diff --git a/skills/nemo-fabric-integrate/references/config-mapping.md b/skills/nemo-fabric-integrate/references/config-mapping.md index 2d2bea345..20578f517 100644 --- a/skills/nemo-fabric-integrate/references/config-mapping.md +++ b/skills/nemo-fabric-integrate/references/config-mapping.md @@ -92,9 +92,8 @@ package or job layout, so nothing depends on the process working directory. Do not surface these mechanics in the consumer-facing integration: -- Serializing `FabricConfig` to disk as the integration path (`to_mapping()` is - for inspection and logging, not a required file step). -- Importing `nemo_fabric._native`, `nemo_fabric._config_sources`, or - adapter-internal modules. +- Replacing direct SDK calls with a private serialized transport + (`to_mapping()` is for inspection, logging, and internal process boundaries). +- Importing `nemo_fabric._native` or adapter-internal modules. - Reimplementing harness start, invoke, or stop logic, or managing adapter threads, sessions, or processes. diff --git a/tests/fixtures/file-config-agent/skills/code-review/README.md b/tests/fixtures/file-config-agent/skills/code-review/README.md index 3fd88c8fc..e9ce2eb55 100644 --- a/tests/fixtures/file-config-agent/skills/code-review/README.md +++ b/tests/fixtures/file-config-agent/skills/code-review/README.md @@ -3,7 +3,7 @@ SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All SPDX-License-Identifier: Apache-2.0 --> -# File Config Fixture Skill +# Relative Skill Fixture -This directory exists only to exercise relative skill paths in file-backed -configuration tests. +This directory exists only to exercise skill paths relative to a typed config's +explicit base directory. From 564c5369230b059edd0793c972fbe82adacdac0c Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:14:57 -0700 Subject: [PATCH 06/20] refactor CLI around typed core Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- ATTRIBUTIONS-Rust.md | 1081 +++++++++++++++++ CLI.md | 114 +- Cargo.lock | 61 + Cargo.toml | 3 + README.md | 10 +- .../src/nemo_fabric_adapters/common/utils.py | 10 +- crates/fabric-cli/Cargo.toml | 23 + crates/fabric-cli/README.md | 9 + .../adapters/scripted/fabric-adapter.json | 0 .../assets}/adapters/scripted/run.py | 0 .../examples/code-review}/repo/calculator.py | 0 .../code-review}/skills/code-review.md | 0 crates/fabric-cli/src/app.rs | 334 +++++ crates/fabric-cli/src/assets.rs | 77 ++ crates/fabric-cli/src/examples.rs | 186 +++ crates/fabric-cli/src/lib.rs | 10 + crates/fabric-cli/src/main.rs | 16 + crates/fabric-cli/src/presets.rs | 312 +++++ crates/fabric-cli/src/scaffold.rs | 301 +++++ crates/fabric-cli/templates/README.md.tmpl | 11 + .../fabric-cli/templates/python/main.py.tmpl | 49 + .../templates/python/pyproject.toml.tmpl | 10 +- .../fabric-cli/templates/rust/Cargo.toml.tmpl | 11 + crates/fabric-cli/templates/rust/main.rs.tmpl | 67 + crates/fabric-core/src/config.rs | 105 +- crates/fabric-core/src/doctor.rs | 12 - crates/fabric-core/src/error.rs | 8 - crates/fabric-core/src/lib.rs | 11 +- crates/fabric-core/src/runtime.rs | 19 +- crates/fabric-core/src/schema.rs | 10 +- crates/fabric-python/Cargo.toml | 1 + crates/fabric-python/src/lib.rs | 20 +- .../api/python-library-reference/index.md | 1 - .../nemo_fabric.client.md | 37 +- .../nemo_fabric.types.md | 71 +- .../config/enum-capabilitykind.mdx | 2 +- .../config/enum-capabilitytarget.mdx | 2 +- .../config/enum-environmentownership.mdx | 2 +- .../config/enum-mcpexposure.mdx | 2 +- .../config/enum-relayatifstorageconfig.mdx | 2 +- .../enum-relayatofendpointfieldnamepolicy.mdx | 2 +- .../enum-relayatofendpointtransport.mdx | 2 +- .../config/enum-relayatofmode.mdx | 2 +- .../config/enum-relayotlptransport.mdx | 2 +- .../config/enum-relayunsupportedbehavior.mdx | 2 +- .../config/enum-resolutionstrategy.mdx | 2 +- .../config/enum-telemetryprovider.mdx | 2 +- .../config/fn-load-adapter-descriptor.mdx | 2 +- ...n-resolve-effective-config-from-config.mdx | 14 - .../fn-resolve-run-plan-from-config.mdx | 2 +- ...resolve-run-plan-from-effective-config.mdx | 14 - .../nemo-fabric-core/config/index.mdx | 5 +- .../config/struct-capabilityplan.mdx | 2 +- .../config/struct-effectiveconfig.mdx | 102 -- .../config/struct-environmentconfig.mdx | 2 +- .../config/struct-environmentplan.mdx | 2 +- .../config/struct-fabricconfig.mdx | 2 +- .../config/struct-harnessconfig.mdx | 2 +- .../config/struct-mcpconfig.mdx | 2 +- .../config/struct-mcpserverconfig.mdx | 2 +- .../config/struct-mcpserverplan.mdx | 2 +- .../config/struct-metadataconfig.mdx | 2 +- .../config/struct-modelconfig.mdx | 2 +- .../config/struct-relayatifconfig.mdx | 2 +- .../config/struct-relayatofconfig.mdx | 2 +- .../config/struct-relayatofendpointconfig.mdx | 2 +- .../config/struct-relaycomponentconfig.mdx | 2 +- .../config/struct-relayconfig.mdx | 2 +- .../config/struct-relayconfigpolicy.mdx | 2 +- .../struct-relayobservabilityconfig.mdx | 2 +- .../config/struct-relayotlpconfig.mdx | 2 +- .../config/struct-resolvecontext.mdx | 2 +- .../struct-resolvedadapterdescriptor.mdx | 2 +- .../config/struct-runplan.mdx | 36 +- .../config/struct-runtimecapabilities.mdx | 2 +- .../config/struct-runtimeconfig.mdx | 2 +- .../config/struct-skillconfig.mdx | 2 +- .../config/struct-telemetryconfig.mdx | 2 +- .../config/struct-telemetryplan.mdx | 2 +- .../config/struct-telemetryproviderconfig.mdx | 2 +- .../config/struct-toolsconfig.mdx | 2 +- .../config/struct-toolsplan.mdx | 2 +- .../doctor/enum-doctorstatus.mdx | 2 +- .../doctor/fn-doctor-plan.mdx | 2 +- .../nemo-fabric-core/doctor/index.mdx | 2 +- .../doctor/struct-doctorcheck.mdx | 2 +- .../doctor/struct-doctorreport.mdx | 2 +- .../error/enum-fabricerror.mdx | 16 +- .../nemo-fabric-core/error/index.mdx | 2 +- .../nemo-fabric-core/error/type-result.mdx | 2 +- .../nemo-fabric-core/fn-version.mdx | 2 +- .../nemo-fabric-core/index.mdx | 3 - .../nemo-fabric-core/runtime/index.mdx | 2 +- .../runtime/struct-adapterinvocation.mdx | 14 +- .../schema/enum-schemaname.mdx | 11 +- .../nemo-fabric-core/schema/index.mdx | 2 +- docs/sdk/python.mdx | 3 +- examples/notebooks/01_quickstart.ipynb | 4 +- examples/notebooks/02_variations.ipynb | 4 +- python/src/nemo_fabric/__init__.py | 2 - .../adapters/claude/fabric-adapter.json | 9 - .../adapters/codex/fabric-adapter.json | 9 - .../adapters/deepagents/fabric-adapter.json | 10 - .../adapters/hermes/fabric-adapter.json | 10 - .../examples/code_review_agent/README.md | 20 - .../adapters/scripted/fabric-adapter.json | 11 - .../adapters/scripted/run.py | 13 - .../examples/code_review_agent/config.py | 29 - python/src/nemo_fabric/_native.pyi | 5 +- python/src/nemo_fabric/cli.py | 309 +---- python/src/nemo_fabric/client.py | 44 +- python/src/nemo_fabric/factories.py | 36 - python/src/nemo_fabric/presets.py | 218 ---- python/src/nemo_fabric/types.py | 54 +- schemas/SCHEMA.md | 4 +- schemas/adapter-invocation.schema.json | 43 +- schemas/effective-config.schema.json | 1042 ---------------- schemas/run-plan.schema.json | 34 +- skills/nemo-fabric-integrate/SKILL.md | 2 - .../references/sdk-api-inventory.md | 1 - tests/_utils/utils.py | 32 - tests/adapters/test_adapaters_common_utils.py | 39 +- tests/adapters/test_claude_adapter.py | 18 +- tests/adapters/test_codex_adapter.py | 34 +- tests/adapters/test_deepagents.py | 36 +- tests/adapters/test_hermes_adapter.py | 32 +- tests/e2e/test_cli.py | 254 +--- tests/e2e/test_harbor_swebench_task.py | 37 +- tests/e2e/test_local_env_e2e.py | 50 +- tests/e2e/test_swebench_style.py | 38 +- .../hermes_shim/adapter.py | 6 +- tests/integrations/test_harbor_runner.py | 6 +- tests/python/test_code_review_example.py | 2 +- tests/python/test_native_sdk.py | 20 +- tests/python/test_runtime.py | 6 +- tests/python/test_sdk_contract.py | 65 +- tests/python/test_sdk_runtimes.py | 6 +- 137 files changed, 2967 insertions(+), 2939 deletions(-) create mode 100644 crates/fabric-cli/Cargo.toml create mode 100644 crates/fabric-cli/README.md rename {python/src/nemo_fabric/_bundled => crates/fabric-cli/assets}/adapters/scripted/fabric-adapter.json (100%) rename {python/src/nemo_fabric/_bundled => crates/fabric-cli/assets}/adapters/scripted/run.py (100%) rename {python/src/nemo_fabric/_bundled/examples/code_review_agent => crates/fabric-cli/assets/examples/code-review}/repo/calculator.py (100%) rename {python/src/nemo_fabric/_bundled/examples/code_review_agent => crates/fabric-cli/assets/examples/code-review}/skills/code-review.md (100%) create mode 100644 crates/fabric-cli/src/app.rs create mode 100644 crates/fabric-cli/src/assets.rs create mode 100644 crates/fabric-cli/src/examples.rs create mode 100644 crates/fabric-cli/src/lib.rs create mode 100644 crates/fabric-cli/src/main.rs create mode 100644 crates/fabric-cli/src/presets.rs create mode 100644 crates/fabric-cli/src/scaffold.rs create mode 100644 crates/fabric-cli/templates/README.md.tmpl create mode 100644 crates/fabric-cli/templates/python/main.py.tmpl rename python/src/nemo_fabric/_bundled/examples/code_review_agent/__init__.py => crates/fabric-cli/templates/python/pyproject.toml.tmpl (53%) create mode 100644 crates/fabric-cli/templates/rust/Cargo.toml.tmpl create mode 100644 crates/fabric-cli/templates/rust/main.rs.tmpl delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config.mdx delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx delete mode 100644 docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx delete mode 100644 python/src/nemo_fabric/_bundled/adapters/claude/fabric-adapter.json delete mode 100644 python/src/nemo_fabric/_bundled/adapters/codex/fabric-adapter.json delete mode 100644 python/src/nemo_fabric/_bundled/adapters/deepagents/fabric-adapter.json delete mode 100644 python/src/nemo_fabric/_bundled/adapters/hermes/fabric-adapter.json delete mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md delete mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/fabric-adapter.json delete mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/run.py delete mode 100644 python/src/nemo_fabric/_bundled/examples/code_review_agent/config.py delete mode 100644 python/src/nemo_fabric/factories.py delete mode 100644 python/src/nemo_fabric/presets.py delete mode 100644 schemas/effective-config.schema.json diff --git a/ATTRIBUTIONS-Rust.md b/ATTRIBUTIONS-Rust.md index 9e2f597d6..7a8ac6ab7 100644 --- a/ATTRIBUTIONS-Rust.md +++ b/ATTRIBUTIONS-Rust.md @@ -9,6 +9,1056 @@ This project uses the following third-party libraries. Each library is open-sour This file is automatically generated. Please do not edit it directly. Regenerate with `./scripts/generate_attributions.sh rust`. +## anstyle - 1.0.14 +**Repository URL**: https://github.com/rust-cli/anstyle.git +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +``` + +## clap - 4.6.2 +**Repository URL**: https://github.com/clap-rs/clap +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +``` + +## clap_builder - 4.6.2 +**Repository URL**: https://github.com/clap-rs/clap +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +``` + +## clap_derive - 4.6.1 +**Repository URL**: https://github.com/clap-rs/clap +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +``` + +## clap_lex - 1.1.0 +**Repository URL**: https://github.com/clap-rs/clap +**License Type(s)**: Apache-2.0 +### License: https://spdx.org/licenses/Apache-2.0.html +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +``` + ## dyn-clone - 1.0.20 **Repository URL**: https://github.com/dtolnay/dyn-clone **License Type(s)**: Apache-2.0 @@ -1972,6 +3022,37 @@ limitations under the License. ``` +## strsim - 0.11.1 +**Repository URL**: https://github.com/rapidfuzz/strsim-rs +**License Type(s)**: MIT +### License: https://spdx.org/licenses/MIT.html +``` +The MIT License (MIT) + +Copyright (c) 2015 Danny Guo +Copyright (c) 2016 Titus Wormer +Copyright (c) 2018 Akash Kurdekar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +``` + ## syn - 2.0.117 **Repository URL**: https://github.com/dtolnay/syn **License Type(s)**: Apache-2.0 diff --git a/CLI.md b/CLI.md index 18981d00e..f06941a38 100644 --- a/CLI.md +++ b/CLI.md @@ -5,106 +5,52 @@ SPDX-License-Identifier: Apache-2.0 # NeMo Fabric Experimentation CLI -`nemo-fabric` is a maintained developer experimentation interface over the -NeMo Fabric SDK. It provides a fast way to try harnesses, run examples, inspect -plans, diagnose integrations, and execute user-owned Python configurations. -The SDK remains the canonical configuration and execution contract. +`nemo-fabric` is a small interface for trying harnesses, inspecting plans, and +starting from maintained examples. It is intentionally for experimentation; +applications that need a stable integration should use a language API directly. -The CLI is intentionally an experimentation surface. Its command grammar may -evolve as workflows mature; typed SDK applications should be used when API -stability or production integration is required. +The command implementation and catalogs live in the Rust `fabric-cli` crate. +The `nemo-fabric-runtime` Python package exposes the same Rust implementation +through a thin console-script bridge, so package and standalone installs do not +maintain separate CLIs. -## Intent +## Variations -Keep the CLI as a thin SDK-backed runner: - -- every run starts from a complete, typed `FabricConfig`; -- the CLI selects a preset, example variant, or Python factory; -- planning and execution go through the public Python SDK; -- `base_dir` only resolves relative resource paths; and -- each selector returns a complete config without implicit merging. - -## Supported Inputs - -### 1. Preset +Use a preset for the shortest complete probe: ```bash nemo-fabric run --preset hermes --input "Say hello" ``` -A small, complete config maintained with the CLI. Useful for smoke tests and -quick harness probes. Presets do not inherit or merge. - -### 2. Example - -```bash -nemo-fabric run \ - --example examples.code_review_agent \ - --variant hermes \ - --input "Review this workspace" -``` - -A runnable SDK example with complete variants. Examples are Python source and -assets that people can read, copy, and edit. Each variant is an independent -factory. - -Copy the bundled example into an editable Python package with: +Use a maintained example to exercise a complete workflow and variant: ```bash -nemo-fabric example init examples.code_review_agent my_agent +nemo-fabric run --example code-review --variant hermes \ + --input "Review the workspace" ``` -### 3. User factory +Generate ordinary editable application code when the catalog is no longer +enough: ```bash -nemo-fabric run \ - --factory my_agent.config:build_config \ - --base-dir . \ - --input "Review this workspace" +nemo-fabric example init code-review my-agent --language python --variant hermes +nemo-fabric example init code-review my-agent-rs --language rust --variant hermes ``` -The unrestricted customization path. The callable takes no arguments and -returns a complete `FabricConfig`. - -The `--factory` name makes the executable Python contract explicit. - -All three selectors feed the same commands: - -```text -plan doctor run chat -``` - -## Designed For - -- quick harness and model experiments; -- smoke tests and troubleshooting; -- comparing complete preset or example variants; -- copying and editing Python examples; -- invoking custom `FabricConfig` factories; and -- inspecting plans and diagnostics. - -## Not Designed For - -- production deployment or scheduling; -- persistent configuration or secrets management; -- implicit configuration merging or inheritance; -- arbitrary CLI configuration mutation; -- evaluation orchestration or experiment tracking; or -- replacing direct SDK applications. +The Python scaffold calls the Python SDK; the Rust scaffold calls +`fabric-core`. Neither is loaded back into the central CLI. ## Boundaries -- Presets, examples, and factories each return a complete `FabricConfig`. -- No preset inheritance or core merge semantics. -- No implicit user/project/system config discovery. -- JSON request input is still valid; it describes an invocation, not an agent. -- Adapter-generated harness files remain valid implementation details. -- Private serialization across Python/Rust or Harbor process boundaries is not - a public authoring format. - -## Design Bias - -Start with the three explicit selectors. Keep presets tiny, make examples the -editable learning surface, and use Python factories for real customization. -Keep registration package-owned for now; consider plugin discovery only after -the workflows prove useful. +- Every preset and example variant constructs a complete typed `FabricConfig`. +- Examples reuse preset defaults and one shared workspace/skill asset tree. +- Variants do not inherit, merge, or behave like profiles. +- There is no `--config`, `--profile`, or `--factory` input. +- Fabric does not discover or persist YAML, TOML, or JSON agent configuration. +- JSON request payloads and harness-generated files remain valid; they are not + Fabric configuration sources. +- The CLI is not a scheduler, evaluation framework, or production deployment + interface. + +Current lifecycle commands are `plan`, `doctor`, and `run`. Catalog discovery +is available through `preset list/show` and `example list/show`. diff --git a/Cargo.lock b/Cargo.lock index 01634ab24..541b2c1e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,51 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "clap" +version = "4.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -12,6 +57,7 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" name = "fabric-python" version = "0.1.0" dependencies = [ + "nemo-fabric-cli", "nemo-fabric-core", "pyo3", "serde", @@ -42,6 +88,15 @@ version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +[[package]] +name = "nemo-fabric-cli" +version = "0.1.0" +dependencies = [ + "clap", + "nemo-fabric-core", + "serde_json", +] + [[package]] name = "nemo-fabric-core" version = "0.1.0" @@ -239,6 +294,12 @@ dependencies = [ "zmij", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.117" diff --git a/Cargo.toml b/Cargo.toml index 126d28a37..fc77c16f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,12 @@ [workspace] members = [ "crates/fabric-core", + "crates/fabric-cli", "crates/fabric-python", ] default-members = [ "crates/fabric-core", + "crates/fabric-cli", ] resolver = "2" @@ -20,6 +22,7 @@ repository = "https://github.com/NVIDIA/nemo-fabric" [workspace.dependencies] nemo-fabric-core = { path = "crates/fabric-core", version = "0.1.0" } +clap = { version = "4", default-features = false, features = ["derive", "std", "help", "usage", "error-context", "suggestions"] } schemars = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/README.md b/README.md index 99fb90ebc..68726e6cf 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ nemo-fabric run --preset scripted --input "fabric works" Copy an editable Python starting point with: ```bash -nemo-fabric example init examples.code_review_agent ./my_agent +nemo-fabric example init code-review ./my-agent --language python ``` See [CLI.md](CLI.md) for the CLI's intent and boundaries. @@ -164,8 +164,8 @@ authentication, and execution details. - **Variants:** ordinary Python functions copy and modify complete configs to vary the harness, model, MCP, tools, skills, telemetry, or environment. - **Experimentation CLI:** presets provide quick probes, examples provide - editable starting points, and `--factory module:callable` executes a - user-owned config through the public SDK. + maintained runnable workflows, and `example init` generates editable Python + or Rust applications that call a language API directly. - **Tools policy:** use top-level `tools.blocked` for harness-neutral blocked tool policy. Names are interpreted by the selected adapter: @@ -200,8 +200,8 @@ the [Python SDK guide](docs/sdk/python.mdx). Exact signatures are in the - [Python SDK guide](docs/sdk/python.mdx): typed configuration, planning, diagnostics, requests, multi-turn runtimes, parallelism, results, and errors. -- [Experimentation CLI](CLI.md): presets, examples, factories, and explicit - non-goals. +- [Experimentation CLI](CLI.md): presets, maintained examples, editable + application scaffolds, and explicit non-goals. - [Consumer integration skills](skills/README.md): repository-local coding-agent skills for integrating Fabric into an application through the Python SDK. - [Getting Started overview](docs/about-nemo-fabric/overview.mdx): interface diff --git a/adapters/common/src/nemo_fabric_adapters/common/utils.py b/adapters/common/src/nemo_fabric_adapters/common/utils.py index 09d363540..968fd348a 100644 --- a/adapters/common/src/nemo_fabric_adapters/common/utils.py +++ b/adapters/common/src/nemo_fabric_adapters/common/utils.py @@ -54,20 +54,16 @@ def request_payload(payload: dict[str, Any]) -> dict[str, Any]: return payload.get("request") or {} -def effective_config(payload: dict[str, Any]) -> dict[str, Any]: - return payload.get("effective_config") or {} - - def fabric_config(payload: dict[str, Any]) -> dict[str, Any]: - return effective_config(payload).get("config") or {} + return payload.get("config") or {} def base_dir(payload: dict[str, Any]) -> str: - return effective_config(payload).get("base_dir") or payload.get("base_dir") or "." + return payload.get("base_dir") or "." def agent_name(payload: dict[str, Any]) -> str: - return effective_config(payload).get("agent_name") or payload.get("agent_name") or "fabric-agent" + return payload.get("agent_name") or "fabric-agent" def load_payload() -> dict[str, Any]: diff --git a/crates/fabric-cli/Cargo.toml b/crates/fabric-cli/Cargo.toml new file mode 100644 index 000000000..2645f9743 --- /dev/null +++ b/crates/fabric-cli/Cargo.toml @@ -0,0 +1,23 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "nemo-fabric-cli" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +description = "Experimentation CLI for NeMo Fabric." +readme = "README.md" + +[[bin]] +name = "nemo-fabric" +path = "src/main.rs" + +[lints] +workspace = true + +[dependencies] +clap.workspace = true +nemo-fabric-core.workspace = true +serde_json.workspace = true diff --git a/crates/fabric-cli/README.md b/crates/fabric-cli/README.md new file mode 100644 index 000000000..cc5f043ef --- /dev/null +++ b/crates/fabric-cli/README.md @@ -0,0 +1,9 @@ + + +# NeMo Fabric CLI + +`nemo-fabric` is a small experimentation interface for presets and maintained +examples. Applications should construct `FabricConfig` through a language API. diff --git a/python/src/nemo_fabric/_bundled/adapters/scripted/fabric-adapter.json b/crates/fabric-cli/assets/adapters/scripted/fabric-adapter.json similarity index 100% rename from python/src/nemo_fabric/_bundled/adapters/scripted/fabric-adapter.json rename to crates/fabric-cli/assets/adapters/scripted/fabric-adapter.json diff --git a/python/src/nemo_fabric/_bundled/adapters/scripted/run.py b/crates/fabric-cli/assets/adapters/scripted/run.py similarity index 100% rename from python/src/nemo_fabric/_bundled/adapters/scripted/run.py rename to crates/fabric-cli/assets/adapters/scripted/run.py diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/repo/calculator.py b/crates/fabric-cli/assets/examples/code-review/repo/calculator.py similarity index 100% rename from python/src/nemo_fabric/_bundled/examples/code_review_agent/repo/calculator.py rename to crates/fabric-cli/assets/examples/code-review/repo/calculator.py diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/skills/code-review.md b/crates/fabric-cli/assets/examples/code-review/skills/code-review.md similarity index 100% rename from python/src/nemo_fabric/_bundled/examples/code_review_agent/skills/code-review.md rename to crates/fabric-cli/assets/examples/code-review/skills/code-review.md diff --git a/crates/fabric-cli/src/app.rs b/crates/fabric-cli/src/app.rs new file mode 100644 index 000000000..bbe4545c0 --- /dev/null +++ b/crates/fabric-cli/src/app.rs @@ -0,0 +1,334 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! NeMo Fabric experimentation CLI. + +use clap::error::ErrorKind; +use clap::{ArgGroup, Args, Parser, Subcommand}; +use nemo_fabric_core::{ + ResolveContext, RunRequest, doctor_plan, resolve_run_plan_from_config, run_plan, +}; + +use crate::examples; +use crate::presets; +use crate::scaffold::{self, Language}; + +#[derive(Debug, Parser)] +#[command(name = "nemo-fabric")] +#[command(about = "Experiment with NeMo Fabric presets and maintained examples")] +#[command(arg_required_else_help = true)] +#[command(version)] +struct Cli { + #[command(subcommand)] + command: Command, +} + +#[derive(Debug, Subcommand)] +enum Command { + /// Discover complete built-in presets. + Preset { + #[command(subcommand)] + command: PresetCommand, + }, + /// Discover and initialize maintained examples. + Example { + #[command(subcommand)] + command: ExampleCommand, + }, + /// Resolve and print a run plan as JSON. + Plan(Selector), + /// Diagnose a selected configuration without running it. + Doctor(Selector), + /// Execute one request through a selected configuration. + Run { + #[command(flatten)] + selector: Selector, + /// Inline request text. + #[arg(long, default_value = "")] + input: String, + }, + /// Print the Fabric core version. + Version, +} + +#[derive(Debug, Subcommand)] +enum PresetCommand { + /// List available presets. + List, + /// Describe one preset without serializing its configuration. + Show { + /// Preset name. + name: String, + }, +} + +#[derive(Debug, Subcommand)] +enum ExampleCommand { + /// List maintained runnable examples. + List, + /// Describe one example and its complete variants. + Show { + /// Example name. + name: String, + }, + /// Generate an ordinary editable SDK application. + Init { + /// Example name. + name: String, + /// Destination directory; defaults to the example name. + destination: Option, + /// Language API used by the generated application. + #[arg(long, value_enum, default_value_t = Language::Python)] + language: Language, + /// Complete example variant rendered into source code. + #[arg(long)] + variant: Option, + }, +} + +#[derive(Debug, Args)] +#[command(group( + ArgGroup::new("source") + .required(true) + .multiple(false) + .args(["preset", "example"]) +))] +struct Selector { + /// Use a maintained, complete built-in preset. + #[arg(long, value_name = "NAME")] + preset: Option, + /// Use a maintained runnable example. + #[arg(long, value_name = "NAME")] + example: Option, + /// Select a complete example variant. + #[arg( + long, + value_name = "NAME", + requires = "example", + conflicts_with = "preset" + )] + variant: Option, +} + +enum SelectedSource { + Preset(presets::SelectedPreset), + Example(examples::SelectedExample), +} + +impl SelectedSource { + fn config(&self) -> &nemo_fabric_core::FabricConfig { + match self { + Self::Preset(selected) => &selected.config, + Self::Example(selected) => &selected.config, + } + } + + fn base_dir(&self) -> &std::path::Path { + match self { + Self::Preset(selected) => selected.base_dir(), + Self::Example(selected) => selected.base_dir(), + } + } +} + +/// Parse command-line arguments and execute the experimentation CLI. +pub fn execute_from(args: I) -> Result<(), String> +where + I: IntoIterator, + T: Into + Clone, +{ + let cli = match Cli::try_parse_from(args) { + Ok(cli) => cli, + Err(error) + if matches!( + error.kind(), + ErrorKind::DisplayHelp + | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand + | ErrorKind::DisplayVersion + ) => + { + error.print().map_err(|error| error.to_string())?; + return Ok(()); + } + Err(error) => return Err(error.to_string()), + }; + run(cli).map_err(|error| error.to_string()) +} + +fn run(cli: Cli) -> Result<(), Box> { + match cli.command { + Command::Preset { + command: PresetCommand::List, + } => { + for preset in presets::all() { + println!("{:<12} {}", preset.name, preset.description); + } + } + Command::Preset { + command: PresetCommand::Show { name }, + } => { + let preset = find_preset(&name)?; + println!("{}\n {}", preset.name, preset.description); + if preset.required_env.is_empty() { + println!(" credentials: none"); + } else { + println!(" required environment: {}", preset.required_env.join(", ")); + } + } + Command::Example { + command: ExampleCommand::List, + } => { + for example in examples::all() { + println!("{:<12} {}", example.name, example.description); + } + } + Command::Example { + command: ExampleCommand::Show { name }, + } => { + let example = find_example(&name)?; + println!("{}\n {}", example.name, example.description); + println!(" default variant: {}", example.default_variant); + println!(" variants: {}", example.variants.join(", ")); + } + Command::Example { + command: + ExampleCommand::Init { + name, + destination, + language, + variant, + }, + } => { + let example = find_example(&name)?; + let destination = destination.unwrap_or_else(|| std::path::PathBuf::from(&name)); + let destination = scaffold::init(example, variant.as_deref(), language, &destination)?; + println!("created {}", destination.display()); + } + Command::Plan(selector) => { + let selected = select_source(&selector)?; + let plan = resolve_run_plan_from_config( + selected.config().clone(), + ResolveContext::new(selected.base_dir()), + )?; + println!("{}", serde_json::to_string_pretty(&plan)?); + } + Command::Doctor(selector) => { + let selected = select_source(&selector)?; + let plan = resolve_run_plan_from_config( + selected.config().clone(), + ResolveContext::new(selected.base_dir()), + )?; + println!("{}", serde_json::to_string_pretty(&doctor_plan(&plan))?); + } + Command::Run { selector, input } => { + let selected = select_source(&selector)?; + let plan = resolve_run_plan_from_config( + selected.config().clone(), + ResolveContext::new(selected.base_dir()), + )?; + println!( + "{}", + serde_json::to_string_pretty(&run_plan(&plan, RunRequest::text(input))?)? + ); + } + Command::Version => println!("{}", nemo_fabric_core::version()), + } + Ok(()) +} + +fn select_source(selector: &Selector) -> Result> { + if let Some(name) = &selector.preset { + return Ok(SelectedSource::Preset(find_preset(name)?.stage()?)); + } + if let Some(name) = &selector.example { + return Ok(SelectedSource::Example( + find_example(name)?.select(selector.variant.as_deref())?, + )); + } + unreachable!("Clap requires exactly one source selector") +} + +fn find_preset(name: &str) -> Result { + presets::find(name).ok_or_else(|| { + format!( + "unknown preset {name:?}; available: {}", + presets::all() + .iter() + .map(|preset| preset.name) + .collect::>() + .join(", ") + ) + }) +} + +fn find_example(name: &str) -> Result { + examples::find(name).ok_or_else(|| { + format!( + "unknown example {name:?}; available: {}", + examples::all() + .iter() + .map(|example| example.name) + .collect::>() + .join(", ") + ) + }) +} + +#[cfg(test)] +mod tests { + use clap::Parser; + + use super::*; + + #[test] + fn parses_preset_plan() { + let cli = Cli::try_parse_from(["nemo-fabric", "plan", "--preset", "scripted"]) + .expect("parse plan"); + assert!(matches!(cli.command, Command::Plan(_))); + } + + #[test] + fn parses_example_variant() { + let cli = Cli::try_parse_from([ + "nemo-fabric", + "run", + "--example", + "code-review", + "--variant", + "hermes", + ]) + .expect("parse example run"); + assert!(matches!(cli.command, Command::Run { .. })); + } + + #[test] + fn selectors_are_mutually_exclusive() { + assert!( + Cli::try_parse_from([ + "nemo-fabric", + "plan", + "--preset", + "scripted", + "--example", + "code-review", + ]) + .is_err() + ); + assert!( + Cli::try_parse_from([ + "nemo-fabric", + "plan", + "--preset", + "scripted", + "--variant", + "hermes", + ]) + .is_err() + ); + } + + #[test] + fn plan_requires_a_preset() { + assert!(Cli::try_parse_from(["nemo-fabric", "plan"]).is_err()); + } +} diff --git a/crates/fabric-cli/src/assets.rs b/crates/fabric-cli/src/assets.rs new file mode 100644 index 000000000..1aef9e4d4 --- /dev/null +++ b/crates/fabric-cli/src/assets.rs @@ -0,0 +1,77 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Embedded assets used by installation-safe CLI experiments. + +use std::fs; +use std::path::{Path, PathBuf}; +use std::time::{SystemTime, UNIX_EPOCH}; + +/// One embedded file and its path relative to a staged asset root. +#[derive(Debug, Clone, Copy)] +pub struct EmbeddedFile { + /// Relative path written under the staged root. + pub path: &'static str, + /// UTF-8 file contents. + pub contents: &'static str, +} + +/// Temporary asset root retained for the lifetime of a selected experiment. +#[derive(Debug)] +pub struct StagedAssets { + path: PathBuf, +} + +impl StagedAssets { + /// Stage embedded files and return their temporary base directory. + pub fn create(files: &[EmbeddedFile]) -> std::io::Result { + let nonce = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_nanos(); + let path = std::env::temp_dir().join(format!("nemo-fabric-{}-{nonce}", std::process::id())); + fs::create_dir(&path)?; + for file in files { + let destination = path.join(file.path); + if let Some(parent) = destination.parent() { + fs::create_dir_all(parent)?; + } + fs::write(destination, file.contents)?; + } + Ok(Self { path }) + } + + /// Return the staged base directory. + pub fn path(&self) -> &Path { + &self.path + } +} + +impl Drop for StagedAssets { + fn drop(&mut self) { + let _ = fs::remove_dir_all(&self.path); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn stages_and_removes_embedded_files() { + let root_path; + { + let root = StagedAssets::create(&[EmbeddedFile { + path: "nested/value.txt", + contents: "value", + }]) + .expect("stage assets"); + root_path = root.path().to_path_buf(); + assert_eq!( + fs::read_to_string(root.path().join("nested/value.txt")).expect("read asset"), + "value" + ); + } + assert!(!root_path.exists()); + } +} diff --git a/crates/fabric-cli/src/examples.rs b/crates/fabric-cli/src/examples.rs new file mode 100644 index 000000000..69670c375 --- /dev/null +++ b/crates/fabric-cli/src/examples.rs @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Maintained runnable examples composed from presets and shared assets. + +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; + +use nemo_fabric_core::{FabricConfig, MetadataConfig, SkillConfig}; + +use crate::assets::{EmbeddedFile, StagedAssets}; +use crate::presets::{self, Preset}; + +pub(crate) const CODE_REVIEW_WORKSPACE: &str = + include_str!("../assets/examples/code-review/repo/calculator.py"); +pub(crate) const CODE_REVIEW_SKILL: &str = + include_str!("../assets/examples/code-review/skills/code-review.md"); + +const CODE_REVIEW_FILES: &[EmbeddedFile] = &[ + EmbeddedFile { + path: "repo/calculator.py", + contents: CODE_REVIEW_WORKSPACE, + }, + EmbeddedFile { + path: "skills/code-review.md", + contents: CODE_REVIEW_SKILL, + }, +]; +const CODE_REVIEW_VARIANTS: &[&str] = &["scripted", "hermes", "claude", "codex", "deepagents"]; + +/// Metadata and variant catalog for one maintained CLI example. +#[derive(Debug, Clone, Copy)] +pub struct Example { + /// Stable command-line name. + pub name: &'static str, + /// Short experimentation-oriented description. + pub description: &'static str, + /// Variant selected when `--variant` is omitted. + pub default_variant: &'static str, + /// Complete supported variant names. + pub variants: &'static [&'static str], +} + +impl Example { + fn preset(self, variant: Option<&str>) -> Result { + let variant = variant.unwrap_or(self.default_variant); + if !self.variants.contains(&variant) { + return Err(format!( + "unknown variant {variant:?} for example {:?}; available: {}", + self.name, + self.variants.join(", ") + )); + } + presets::find(variant).ok_or_else(|| { + format!( + "example {:?} references missing preset {variant:?}", + self.name + ) + }) + } + + /// Construct one complete example config without staging its assets. + pub fn config(self, variant: Option<&str>) -> Result { + self.preset(variant).map(code_review_config) + } + + /// Construct and stage one complete example variant. + pub fn select(self, variant: Option<&str>) -> Result { + let preset = self.preset(variant)?; + let files = self.embedded_files(preset); + let assets = StagedAssets::create(&files) + .map_err(|error| format!("failed to stage example {:?}: {error}", self.name))?; + Ok(SelectedExample { + example: self, + variant: preset, + config: code_review_config(preset), + assets, + }) + } + + pub(crate) fn embedded_files(self, preset: Preset) -> Vec { + let mut files = Vec::from(preset.embedded_files()); + files.extend_from_slice(CODE_REVIEW_FILES); + files + } +} + +/// Selected example variant and its staged installation-safe assets. +#[derive(Debug)] +pub struct SelectedExample { + /// Example metadata. + pub example: Example, + /// Preset reused by this complete example variant. + pub variant: Preset, + /// Complete typed example configuration. + pub config: FabricConfig, + assets: StagedAssets, +} + +impl SelectedExample { + /// Return the base directory for resolving this example. + pub fn base_dir(&self) -> &Path { + self.assets.path() + } +} + +/// Return the maintained example catalog. +pub fn all() -> &'static [Example] { + &EXAMPLES +} + +/// Find an example by its stable CLI name. +pub fn find(name: &str) -> Option { + EXAMPLES + .iter() + .copied() + .find(|example| example.name == name) +} + +const EXAMPLES: [Example; 1] = [Example { + name: "code-review", + description: "Review a small Python workspace using a maintained skill.", + default_variant: "scripted", + variants: CODE_REVIEW_VARIANTS, +}]; + +fn code_review_config(preset: Preset) -> FabricConfig { + let mut config = preset.config(); + config.metadata = MetadataConfig { + name: "code-review-agent".to_string(), + description: Some("Maintained example for reviewing a small Python workspace.".to_string()), + extensions: BTreeMap::new(), + }; + let environment = config + .environment + .as_mut() + .expect("CLI presets always define an execution environment"); + environment.workspace = Some(PathBuf::from("repo")); + config.skills = Some(SkillConfig { + paths: vec![PathBuf::from("skills/code-review.md")], + extensions: BTreeMap::new(), + }); + config +} + +#[cfg(test)] +mod tests { + use nemo_fabric_core::{ResolveContext, RunRequest, resolve_run_plan_from_config, run_plan}; + + use super::*; + + #[test] + fn variants_reuse_the_preset_catalog() { + let example = find("code-review").expect("code review example"); + for variant in example.variants { + assert!(presets::find(variant).is_some(), "missing preset {variant}"); + } + } + + #[test] + fn code_review_stages_one_shared_asset_tree_and_runs() { + let selected = find("code-review") + .expect("code review example") + .select(Some("scripted")) + .expect("select example"); + assert!(selected.base_dir().join("repo/calculator.py").is_file()); + assert!(selected.base_dir().join("skills/code-review.md").is_file()); + let plan = resolve_run_plan_from_config( + selected.config.clone(), + ResolveContext::new(selected.base_dir()), + ) + .expect("plan example"); + assert_eq!(plan.agent_name, "code-review-agent"); + assert_eq!( + plan.environment_plan + .as_ref() + .expect("environment") + .workspace + .as_ref(), + Some(&selected.base_dir().join("repo")) + ); + let result = + run_plan(&plan, RunRequest::text("review")).expect("run deterministic example variant"); + assert_eq!(result.output["response"], "review"); + } +} diff --git a/crates/fabric-cli/src/lib.rs b/crates/fabric-cli/src/lib.rs new file mode 100644 index 000000000..1ff9b6c8c --- /dev/null +++ b/crates/fabric-cli/src/lib.rs @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Catalog and asset staging for the NeMo Fabric experimentation CLI. + +pub mod app; +pub mod assets; +pub mod examples; +pub mod presets; +pub mod scaffold; diff --git a/crates/fabric-cli/src/main.rs b/crates/fabric-cli/src/main.rs new file mode 100644 index 000000000..4fc11efcd --- /dev/null +++ b/crates/fabric-cli/src/main.rs @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Standalone NeMo Fabric experimentation CLI binary. + +use std::process::ExitCode; + +fn main() -> ExitCode { + match nemo_fabric_cli::app::execute_from(std::env::args_os()) { + Ok(()) => ExitCode::SUCCESS, + Err(error) => { + eprintln!("error: {error}"); + ExitCode::FAILURE + } + } +} diff --git a/crates/fabric-cli/src/presets.rs b/crates/fabric-cli/src/presets.rs new file mode 100644 index 000000000..bf7d959de --- /dev/null +++ b/crates/fabric-cli/src/presets.rs @@ -0,0 +1,312 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Complete typed preset configurations maintained by the CLI. + +use std::collections::BTreeMap; + +use nemo_fabric_core::{ + ControlLocation, EnvironmentConfig, EnvironmentOwnership, FabricConfig, HarnessConfig, + MetadataConfig, ModelConfig, ResolutionStrategy, RuntimeConfig, +}; +use serde_json::{Map, Value, json}; + +use crate::assets::{EmbeddedFile, StagedAssets}; + +const SCRIPTED_DESCRIPTOR: &str = include_str!("../assets/adapters/scripted/fabric-adapter.json"); +const SCRIPTED_RUNNER: &str = include_str!("../assets/adapters/scripted/run.py"); +const HERMES_DESCRIPTOR: &str = include_str!("../../../adapters/hermes/fabric-adapter.json"); +const CLAUDE_DESCRIPTOR: &str = include_str!("../../../adapters/claude/fabric-adapter.json"); +const CODEX_DESCRIPTOR: &str = include_str!("../../../adapters/codex/fabric-adapter.json"); +const DEEPAGENTS_DESCRIPTOR: &str = + include_str!("../../../adapters/deepagents/fabric-adapter.json"); + +const SCRIPTED_ASSETS: &[EmbeddedFile] = &[ + EmbeddedFile { + path: "adapters/scripted/fabric-adapter.json", + contents: SCRIPTED_DESCRIPTOR, + }, + EmbeddedFile { + path: "adapters/scripted/run.py", + contents: SCRIPTED_RUNNER, + }, +]; +const HERMES_ASSETS: &[EmbeddedFile] = &[EmbeddedFile { + path: "adapters/hermes/fabric-adapter.json", + contents: HERMES_DESCRIPTOR, +}]; +const CLAUDE_ASSETS: &[EmbeddedFile] = &[EmbeddedFile { + path: "adapters/claude/fabric-adapter.json", + contents: CLAUDE_DESCRIPTOR, +}]; +const CODEX_ASSETS: &[EmbeddedFile] = &[EmbeddedFile { + path: "adapters/codex/fabric-adapter.json", + contents: CODEX_DESCRIPTOR, +}]; +const DEEPAGENTS_ASSETS: &[EmbeddedFile] = &[EmbeddedFile { + path: "adapters/deepagents/fabric-adapter.json", + contents: DEEPAGENTS_DESCRIPTOR, +}]; + +/// Metadata and constructor for one complete CLI preset. +#[derive(Debug, Clone, Copy)] +pub struct Preset { + /// Stable command-line name. + pub name: &'static str, + /// Short experimentation-oriented description. + pub description: &'static str, + /// Environment variables required by the harness/model combination. + pub required_env: &'static [&'static str], + build: fn() -> FabricConfig, + assets: &'static [EmbeddedFile], +} + +impl Preset { + /// Construct a fresh complete typed config. + pub fn config(self) -> FabricConfig { + (self.build)() + } + + /// Stage the adapter assets required to plan or run this preset. + pub fn stage(self) -> std::io::Result { + Ok(SelectedPreset { + preset: self, + config: self.config(), + assets: StagedAssets::create(self.assets)?, + }) + } + + pub(crate) fn embedded_files(self) -> &'static [EmbeddedFile] { + self.assets + } +} + +/// Selected preset and the staged base directory that must outlive its run. +#[derive(Debug)] +pub struct SelectedPreset { + /// Preset metadata. + pub preset: Preset, + /// Complete typed configuration. + pub config: FabricConfig, + assets: StagedAssets, +} + +impl SelectedPreset { + /// Return the base directory for resolving this preset. + pub fn base_dir(&self) -> &std::path::Path { + self.assets.path() + } +} + +/// Return the maintained preset catalog. +pub fn all() -> &'static [Preset] { + &PRESETS +} + +/// Find a preset by its stable CLI name. +pub fn find(name: &str) -> Option { + PRESETS.iter().copied().find(|preset| preset.name == name) +} + +const PRESETS: [Preset; 5] = [ + Preset { + name: "scripted", + description: "Credential-free deterministic smoke preset.", + required_env: &[], + build: scripted, + assets: SCRIPTED_ASSETS, + }, + Preset { + name: "hermes", + description: "Hermes Agent with an NVIDIA-hosted model.", + required_env: &["NVIDIA_API_KEY"], + build: hermes, + assets: HERMES_ASSETS, + }, + Preset { + name: "claude", + description: "Claude Code with an Anthropic model.", + required_env: &["ANTHROPIC_API_KEY"], + build: claude, + assets: CLAUDE_ASSETS, + }, + Preset { + name: "codex", + description: "Codex with an OpenAI model.", + required_env: &["OPENAI_API_KEY"], + build: codex, + assets: CODEX_ASSETS, + }, + Preset { + name: "deepagents", + description: "LangChain Deep Agents with an NVIDIA-hosted model.", + required_env: &["NVIDIA_API_KEY"], + build: deepagents, + assets: DEEPAGENTS_ASSETS, + }, +]; + +fn scripted() -> FabricConfig { + config( + "scripted-agent", + "Credential-free NeMo Fabric CLI smoke preset.", + "nvidia.fabric.scripted", + None, + Map::new(), + ) +} + +fn hermes() -> FabricConfig { + config( + "hermes-agent", + "NeMo Fabric Hermes CLI preset.", + "nvidia.fabric.hermes", + Some(model( + "nvidia", + "nvidia/nemotron-3-nano-30b-a3b", + Some("NVIDIA_API_KEY"), + )), + Map::from_iter([( + "base_url".to_string(), + json!("https://integrate.api.nvidia.com/v1"), + )]), + ) +} + +fn claude() -> FabricConfig { + config( + "claude-agent", + "NeMo Fabric Claude CLI preset.", + "nvidia.fabric.claude", + Some(model( + "anthropic", + "anthropic/claude-sonnet-4-5", + Some("ANTHROPIC_API_KEY"), + )), + Map::from_iter([("permission_mode".to_string(), json!("dontAsk"))]), + ) +} + +fn codex() -> FabricConfig { + config( + "codex-agent", + "NeMo Fabric Codex CLI preset.", + "nvidia.fabric.codex", + Some(model("openai", "openai/gpt-5.4", Some("OPENAI_API_KEY"))), + Map::from_iter([("sandbox".to_string(), json!("workspace-write"))]), + ) +} + +fn deepagents() -> FabricConfig { + config( + "deepagents-agent", + "NeMo Fabric Deep Agents CLI preset.", + "nvidia.fabric.langchain.deepagents", + Some(model( + "nvidia", + "nvidia/nemotron-3-nano-30b-a3b", + Some("NVIDIA_API_KEY"), + )), + Map::new(), + ) +} + +fn config( + name: &str, + description: &str, + adapter_id: &str, + default_model: Option, + settings: Map, +) -> FabricConfig { + FabricConfig { + schema_version: "fabric.agent/v1alpha1".to_string(), + metadata: MetadataConfig { + name: name.to_string(), + description: Some(description.to_string()), + extensions: BTreeMap::new(), + }, + harness: HarnessConfig { + adapter_id: adapter_id.to_string(), + resolution: Some(ResolutionStrategy::Preinstalled), + settings, + extensions: BTreeMap::new(), + }, + models: default_model + .map(|model| BTreeMap::from_iter([("default".to_string(), model)])) + .unwrap_or_default(), + runtime: RuntimeConfig { + input_schema: "text".to_string(), + output_schema: "message".to_string(), + artifacts: None, + extensions: BTreeMap::new(), + }, + environment: Some(EnvironmentConfig { + provider: "local".to_string(), + control_location: ControlLocation::InEnvControl, + ownership: EnvironmentOwnership::FabricOwned, + workspace: None, + artifacts: None, + connection: Map::new(), + metadata: Map::new(), + settings: Map::new(), + extensions: BTreeMap::new(), + }), + tools: None, + skills: None, + mcp: None, + telemetry: None, + relay: None, + extensions: BTreeMap::new(), + } +} + +fn model(provider: &str, name: &str, api_key_env: Option<&str>) -> ModelConfig { + ModelConfig { + provider: provider.to_string(), + model: name.to_string(), + temperature: None, + api_key_env: api_key_env.map(str::to_string), + settings: Map::new(), + extensions: BTreeMap::new(), + } +} + +#[cfg(test)] +mod tests { + use nemo_fabric_core::{ResolveContext, resolve_run_plan_from_config}; + + use super::*; + + #[test] + fn catalog_names_are_unique_and_configs_are_complete() { + let mut names = std::collections::BTreeSet::new(); + for preset in all() { + assert!(names.insert(preset.name)); + let config = preset.config(); + assert_eq!(config.metadata.name, format!("{}-agent", preset.name)); + assert!(!config.harness.adapter_id.is_empty()); + } + } + + #[test] + fn scripted_preset_plans_from_staged_assets() { + let selected = find("scripted") + .expect("scripted preset") + .stage() + .expect("stage preset"); + let plan = resolve_run_plan_from_config( + selected.config.clone(), + ResolveContext::new(selected.base_dir()), + ) + .expect("plan scripted preset"); + + assert_eq!(plan.agent_name, "scripted-agent"); + assert_eq!( + plan.adapter_descriptor + .expect("adapter") + .descriptor + .adapter_id, + "nvidia.fabric.scripted" + ); + } +} diff --git a/crates/fabric-cli/src/scaffold.rs b/crates/fabric-cli/src/scaffold.rs new file mode 100644 index 000000000..9bc5c651b --- /dev/null +++ b/crates/fabric-cli/src/scaffold.rs @@ -0,0 +1,301 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! Editable applications generated from the maintained example catalog. + +use std::fs; +use std::path::{Path, PathBuf}; + +use clap::ValueEnum; +use nemo_fabric_core::{FabricConfig, ModelConfig}; +use serde_json::Value; + +use crate::examples::Example; +use crate::presets; + +const PYTHON_MAIN: &str = include_str!("../templates/python/main.py.tmpl"); +const PYTHON_PROJECT: &str = include_str!("../templates/python/pyproject.toml.tmpl"); +const RUST_MAIN: &str = include_str!("../templates/rust/main.rs.tmpl"); +const RUST_PROJECT: &str = include_str!("../templates/rust/Cargo.toml.tmpl"); +const README: &str = include_str!("../templates/README.md.tmpl"); + +#[derive(Debug)] +struct ScaffoldFile { + path: String, + contents: String, +} + +/// Language API used by a generated editable application. +#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] +pub enum Language { + /// Generate a Python SDK application. + Python, + /// Generate a direct Rust core application. + Rust, +} + +impl Language { + /// Return the command-line spelling. + pub fn as_str(self) -> &'static str { + match self { + Self::Python => "python", + Self::Rust => "rust", + } + } +} + +/// Generate an ordinary editable application and return its destination. +pub fn init( + example: Example, + variant: Option<&str>, + language: Language, + destination: impl AsRef, +) -> Result { + let destination = destination.as_ref(); + if destination.exists() { + return Err(format!( + "destination already exists: {}", + destination.display() + )); + } + let variant = variant.unwrap_or(example.default_variant); + let preset = + presets::find(variant).ok_or_else(|| format!("unknown preset variant {variant:?}"))?; + let config = example.config(Some(variant))?; + let mut files = example + .embedded_files(preset) + .into_iter() + .map(|file| ScaffoldFile { + path: file.path.to_string(), + contents: file.contents.to_string(), + }) + .collect::>(); + files.extend(language_files(language, &config, example.name)); + files.push(ScaffoldFile { + path: "README.md".to_string(), + contents: README + .replace("{{EXAMPLE}}", example.name) + .replace("{{VARIANT}}", variant) + .replace("{{LANGUAGE}}", language.as_str()), + }); + write_files(destination, &files)?; + Ok(destination.to_path_buf()) +} + +fn language_files(language: Language, config: &FabricConfig, example: &str) -> Vec { + let (project_path, project, main_path, main) = match language { + Language::Python => ( + "pyproject.toml", + PYTHON_PROJECT.replace("{{PACKAGE}}", &package_name(example)), + "main.py", + render_python(config), + ), + Language::Rust => ( + "Cargo.toml", + RUST_PROJECT.replace("{{PACKAGE}}", &package_name(example)), + "src/main.rs", + render_rust(config), + ), + }; + vec![ + ScaffoldFile { + path: project_path.to_string(), + contents: project, + }, + ScaffoldFile { + path: main_path.to_string(), + contents: main, + }, + ] +} + +fn write_files(destination: &Path, files: &[ScaffoldFile]) -> Result<(), String> { + fs::create_dir(destination).map_err(|error| { + format!( + "failed to create destination {}: {error}", + destination.display() + ) + })?; + for file in files { + let path = destination.join(&file.path); + if let Some(parent) = path.parent() { + fs::create_dir_all(parent) + .map_err(|error| format!("failed to create {}: {error}", parent.display()))?; + } + fs::write(&path, &file.contents) + .map_err(|error| format!("failed to write {}: {error}", path.display()))?; + } + Ok(()) +} + +fn render_python(config: &FabricConfig) -> String { + PYTHON_MAIN + .replace("{{AGENT_NAME}}", &python_string(&config.metadata.name)) + .replace( + "{{DESCRIPTION}}", + &python_string(config.metadata.description.as_deref().unwrap_or("")), + ) + .replace("{{ADAPTER_ID}}", &python_string(&config.harness.adapter_id)) + .replace( + "{{HARNESS_SETTINGS}}", + &python_value(&Value::Object(config.harness.settings.clone())), + ) + .replace("{{MODELS}}", &python_models(config.models.get("default"))) +} + +fn python_models(model: Option<&ModelConfig>) -> String { + let Some(model) = model else { + return "{}".to_string(); + }; + format!( + "{{\"default\": ModelConfig(provider={}, model={}, api_key_env={})}}", + python_string(&model.provider), + python_string(&model.model), + model + .api_key_env + .as_deref() + .map(python_string) + .unwrap_or_else(|| "None".to_string()) + ) +} + +fn python_value(value: &Value) -> String { + match value { + Value::Null => "None".to_string(), + Value::Bool(value) => if *value { "True" } else { "False" }.to_string(), + Value::Number(value) => value.to_string(), + Value::String(value) => python_string(value), + Value::Array(values) => format!( + "[{}]", + values + .iter() + .map(python_value) + .collect::>() + .join(", ") + ), + Value::Object(values) => format!( + "{{{}}}", + values + .iter() + .map(|(key, value)| format!("{}: {}", python_string(key), python_value(value))) + .collect::>() + .join(", ") + ), + } +} + +fn python_string(value: &str) -> String { + serde_json::to_string(value).expect("strings serialize") +} + +fn render_rust(config: &FabricConfig) -> String { + RUST_MAIN + .replace("{{AGENT_NAME}}", &rust_string(&config.metadata.name)) + .replace( + "{{DESCRIPTION}}", + &rust_string(config.metadata.description.as_deref().unwrap_or("")), + ) + .replace("{{ADAPTER_ID}}", &rust_string(&config.harness.adapter_id)) + .replace( + "{{HARNESS_SETTINGS}}", + &rust_settings(&config.harness.settings), + ) + .replace("{{MODELS}}", &rust_models(config.models.get("default"))) +} + +fn rust_settings(settings: &serde_json::Map) -> String { + if settings.is_empty() { + return "Map::new()".to_string(); + } + format!( + "Map::from_iter([{}])", + settings + .iter() + .map(|(key, value)| format!("({}.to_string(), json!({}))", rust_string(key), value)) + .collect::>() + .join(", ") + ) +} + +fn rust_models(model: Option<&ModelConfig>) -> String { + let Some(model) = model else { + return "BTreeMap::new()".to_string(); + }; + let api_key = model + .api_key_env + .as_deref() + .map(|value| format!("Some({}.to_string())", rust_string(value))) + .unwrap_or_else(|| "None".to_string()); + format!( + "BTreeMap::from_iter([(\"default\".to_string(), ModelConfig {{ provider: {}.to_string(), model: {}.to_string(), temperature: None, api_key_env: {api_key}, settings: Map::new(), extensions: BTreeMap::new() }})])", + rust_string(&model.provider), + rust_string(&model.model), + ) +} + +fn rust_string(value: &str) -> String { + format!("{value:?}") +} + +fn package_name(example: &str) -> String { + example.replace('_', "-") +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::examples; + + fn destination(test: &str, language: Language) -> PathBuf { + std::env::temp_dir().join(format!( + "nemo-fabric-scaffold-test-{}-{test}-{}", + std::process::id(), + language.as_str() + )) + } + + #[test] + fn generates_both_languages_from_the_same_example_assets_and_config() { + let example = examples::find("code-review").expect("example"); + for language in [Language::Python, Language::Rust] { + let destination = destination("generate", language); + let _ = fs::remove_dir_all(&destination); + init(example, Some("hermes"), language, &destination).expect("generate scaffold"); + assert!(destination.join("repo/calculator.py").is_file()); + assert!(destination.join("skills/code-review.md").is_file()); + assert!( + destination + .join("adapters/hermes/fabric-adapter.json") + .is_file() + ); + assert_eq!( + fs::read_to_string(destination.join("repo/calculator.py")).expect("read workspace"), + crate::examples::CODE_REVIEW_WORKSPACE + ); + let launcher = match language { + Language::Python => destination.join("main.py"), + Language::Rust => destination.join("src/main.rs"), + }; + let source = fs::read_to_string(launcher).expect("read launcher"); + assert!(source.contains("nvidia.fabric.hermes")); + assert!(source.contains("nvidia/nemotron-3-nano-30b-a3b")); + fs::remove_dir_all(destination).expect("remove scaffold"); + } + } + + #[test] + fn refuses_to_overwrite_a_destination() { + let destination = destination("overwrite", Language::Python); + let _ = fs::remove_dir_all(&destination); + fs::create_dir(&destination).expect("create destination"); + let error = init( + examples::find("code-review").expect("example"), + None, + Language::Python, + &destination, + ) + .expect_err("must refuse overwrite"); + assert!(error.contains("already exists")); + fs::remove_dir_all(destination).expect("remove destination"); + } +} diff --git a/crates/fabric-cli/templates/README.md.tmpl b/crates/fabric-cli/templates/README.md.tmpl new file mode 100644 index 000000000..4918f2a84 --- /dev/null +++ b/crates/fabric-cli/templates/README.md.tmpl @@ -0,0 +1,11 @@ + + +# {{EXAMPLE}} + +Editable {{LANGUAGE}} application generated from the maintained `{{EXAMPLE}}` +example using its complete `{{VARIANT}}` variant. Edit the configuration in the +launcher and run this as its own application; it is not loaded by the central +`nemo-fabric` CLI. diff --git a/crates/fabric-cli/templates/python/main.py.tmpl b/crates/fabric-cli/templates/python/main.py.tmpl new file mode 100644 index 000000000..76c039d1b --- /dev/null +++ b/crates/fabric-cli/templates/python/main.py.tmpl @@ -0,0 +1,49 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Editable NeMo Fabric code-review application.""" + +import argparse +import asyncio +import json +from pathlib import Path + +from nemo_fabric import EnvironmentConfig +from nemo_fabric import Fabric +from nemo_fabric import FabricConfig +from nemo_fabric import HarnessConfig +from nemo_fabric import MetadataConfig +from nemo_fabric import ModelConfig +from nemo_fabric import RuntimeConfig + +BASE_DIR = Path(__file__).parent + + +def build_config() -> FabricConfig: + """Build the complete editable configuration.""" + + config = FabricConfig( + metadata=MetadataConfig(name={{AGENT_NAME}}, description={{DESCRIPTION}}), + harness=HarnessConfig( + adapter_id={{ADAPTER_ID}}, + resolution="preinstalled", + settings={{HARNESS_SETTINGS}}, + ), + models={{MODELS}}, + runtime=RuntimeConfig(input_schema="text", output_schema="message"), + environment=EnvironmentConfig(provider="local", workspace="repo"), + ) + config.add_skill_path("skills/code-review.md") + return config + + +async def main() -> None: + parser = argparse.ArgumentParser() + parser.add_argument("input", nargs="?", default="Review the workspace") + args = parser.parse_args() + result = await Fabric().run(build_config(), base_dir=BASE_DIR, input=args.input) + print(json.dumps(result.to_mapping(), indent=2)) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/__init__.py b/crates/fabric-cli/templates/python/pyproject.toml.tmpl similarity index 53% rename from python/src/nemo_fabric/_bundled/examples/code_review_agent/__init__.py rename to crates/fabric-cli/templates/python/pyproject.toml.tmpl index 2cdb92dfe..e96a93bfd 100644 --- a/python/src/nemo_fabric/_bundled/examples/code_review_agent/__init__.py +++ b/crates/fabric-cli/templates/python/pyproject.toml.tmpl @@ -1,8 +1,8 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -"""Editable code-review example scaffold.""" - -from .config import build_config - -__all__ = ["build_config"] +[project] +name = "{{PACKAGE}}-python" +version = "0.1.0" +requires-python = ">=3.11" +dependencies = ["nemo-fabric-runtime"] diff --git a/crates/fabric-cli/templates/rust/Cargo.toml.tmpl b/crates/fabric-cli/templates/rust/Cargo.toml.tmpl new file mode 100644 index 000000000..b515964e6 --- /dev/null +++ b/crates/fabric-cli/templates/rust/Cargo.toml.tmpl @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +[package] +name = "{{PACKAGE}}-rust" +version = "0.1.0" +edition = "2024" + +[dependencies] +nemo-fabric-core = "0.1.0" +serde_json = "1" diff --git a/crates/fabric-cli/templates/rust/main.rs.tmpl b/crates/fabric-cli/templates/rust/main.rs.tmpl new file mode 100644 index 000000000..565335881 --- /dev/null +++ b/crates/fabric-cli/templates/rust/main.rs.tmpl @@ -0,0 +1,67 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +use std::collections::BTreeMap; +use std::path::PathBuf; + +use nemo_fabric_core::{ + ControlLocation, EnvironmentConfig, EnvironmentOwnership, FabricConfig, HarnessConfig, + MetadataConfig, ModelConfig, ResolutionStrategy, ResolveContext, RunRequest, RuntimeConfig, + SkillConfig, resolve_run_plan_from_config, run_plan, +}; +use serde_json::{Map, json}; + +fn build_config() -> FabricConfig { + FabricConfig { + schema_version: "fabric.agent/v1alpha1".to_string(), + metadata: MetadataConfig { + name: {{AGENT_NAME}}.to_string(), + description: Some({{DESCRIPTION}}.to_string()), + extensions: BTreeMap::new(), + }, + harness: HarnessConfig { + adapter_id: {{ADAPTER_ID}}.to_string(), + resolution: Some(ResolutionStrategy::Preinstalled), + settings: {{HARNESS_SETTINGS}}, + extensions: BTreeMap::new(), + }, + models: {{MODELS}}, + runtime: RuntimeConfig { + input_schema: "text".to_string(), + output_schema: "message".to_string(), + artifacts: None, + extensions: BTreeMap::new(), + }, + environment: Some(EnvironmentConfig { + provider: "local".to_string(), + control_location: ControlLocation::InEnvControl, + ownership: EnvironmentOwnership::FabricOwned, + workspace: Some(PathBuf::from("repo")), + artifacts: None, + connection: Map::new(), + metadata: Map::new(), + settings: Map::new(), + extensions: BTreeMap::new(), + }), + skills: Some(SkillConfig { + paths: vec![PathBuf::from("skills/code-review.md")], + extensions: BTreeMap::new(), + }), + tools: None, + mcp: None, + telemetry: None, + relay: None, + extensions: BTreeMap::new(), + } +} + +fn main() -> Result<(), Box> { + let base_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let input = std::env::args() + .nth(1) + .unwrap_or_else(|| "Review the workspace".to_string()); + let plan = resolve_run_plan_from_config(build_config(), ResolveContext::new(base_dir))?; + let result = run_plan(&plan, RunRequest::text(input))?; + println!("{}", serde_json::to_string_pretty(&result)?); + Ok(()) +} diff --git a/crates/fabric-core/src/config.rs b/crates/fabric-core/src/config.rs index 03ca5bfd5..49b5592da 100644 --- a/crates/fabric-core/src/config.rs +++ b/crates/fabric-core/src/config.rs @@ -972,26 +972,6 @@ pub fn load_adapter_descriptor(path: impl AsRef) -> Result Result { - validate_config(&config)?; - let supplied_base_dir = context.base_dir; - let base_dir = std::path::absolute(&supplied_base_dir) - .map(normalize_path) - .map_err(|source| FabricError::ResolveBaseDirectory { - path: supplied_base_dir, - source, - })?; - Ok(EffectiveConfig { - agent_name: config.metadata.name.clone(), - base_dir, - config, - }) -} - fn validate_config(config: &FabricConfig) -> Result<()> { if config.harness.adapter_id.trim().is_empty() { return Err(FabricError::UnknownAdapter { @@ -1010,7 +990,15 @@ pub fn resolve_run_plan_from_config( config: FabricConfig, context: ResolveContext, ) -> Result { - resolve_run_plan_from_effective_config(resolve_effective_config_from_config(config, context)?) + validate_config(&config)?; + let supplied_base_dir = context.base_dir; + let base_dir = std::path::absolute(&supplied_base_dir) + .map(normalize_path) + .map_err(|source| FabricError::ResolveBaseDirectory { + path: supplied_base_dir, + source, + })?; + resolve_run_plan(config, base_dir) } fn read_json(path: &Path) -> Result @@ -1027,12 +1015,7 @@ where }) } -/// Resolve execution planning metadata from merged effective config. -pub fn resolve_run_plan_from_effective_config( - effective_config: EffectiveConfig, -) -> Result { - let config = effective_config.config.clone(); - let base_dir = effective_config.base_dir.clone(); +fn resolve_run_plan(config: FabricConfig, base_dir: PathBuf) -> Result { let adapter_descriptor = resolve_adapter_descriptor(&config, &base_dir)?; let descriptor = adapter_descriptor .as_ref() @@ -1044,16 +1027,15 @@ pub fn resolve_run_plan_from_effective_config( let capabilities = resolve_runtime_capabilities(&config, descriptor); let telemetry_plan = resolve_telemetry_plan(&config, descriptor)?; Ok(RunPlan { - agent_name: effective_config.agent_name.clone(), + agent_name: config.metadata.name.clone(), + base_dir, + config, adapter_descriptor, resolution, environment_plan, capability_plan, capabilities, telemetry_plan, - base_dir, - config, - effective_config, }) } @@ -1438,24 +1420,15 @@ fn normalize_path(path: PathBuf) -> PathBuf { .collect() } -/// Resolved Fabric config with explicit path context. +/// Resolved Fabric run plan. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] -pub struct EffectiveConfig { +pub struct RunPlan { /// Stable agent name. pub agent_name: String, /// Base directory used to resolve relative Fabric paths. pub base_dir: PathBuf, /// Complete typed Fabric config. pub config: FabricConfig, -} - -/// Resolved Fabric run plan. -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] -pub struct RunPlan { - /// Resolved typed configuration and path context used by this plan. - pub effective_config: EffectiveConfig, - /// Stable agent name. - pub agent_name: String, /// Adapter descriptor resolved for this plan, when configured. #[serde(default, skip_serializing_if = "Option::is_none")] pub adapter_descriptor: Option, @@ -1473,28 +1446,6 @@ pub struct RunPlan { /// Resolved telemetry pass-through plan. #[serde(default, skip_serializing_if = "Option::is_none")] pub telemetry_plan: Option, - /// Base directory used to resolve relative Fabric paths. - pub base_dir: PathBuf, - /// Complete typed Fabric config. - pub config: FabricConfig, -} - -impl RunPlan { - /// Reject conflicting duplicated state before a plan enters a lifecycle boundary. - pub fn validate_consistency(&self) -> Result<()> { - if self.agent_name != self.effective_config.agent_name { - return Err(FabricError::RunPlanMismatch { - field: "agent_name", - }); - } - if self.base_dir != self.effective_config.base_dir { - return Err(FabricError::RunPlanMismatch { field: "base_dir" }); - } - if self.config != self.effective_config.config { - return Err(FabricError::RunPlanMismatch { field: "config" }); - } - Ok(()) - } } /// Lifecycle behavior implemented by a resolved runtime path. @@ -1567,7 +1518,7 @@ pub struct CapabilityPlan { /// Capabilities that are configured but not executable by this Fabric build. #[serde(default)] pub unsupported: CapabilityTargetPlan, - /// Routing decisions made while resolving the effective config. + /// Routing decisions made while planning the configured capabilities. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub routes: Vec, } @@ -1704,7 +1655,6 @@ mod tests { assert_eq!(plan.agent_name, "typed-agent"); assert_eq!(plan.base_dir, base_dir); - assert_eq!(plan.effective_config.base_dir, plan.base_dir); assert_eq!( plan.capability_plan.skill_paths, vec![plan.base_dir.join("skills/review")] @@ -1739,33 +1689,18 @@ mod tests { ); } - #[test] - fn rejects_conflicting_duplicated_plan_state() { - let mut plan = resolve_run_plan_from_config( - typed_config("nvidia.fabric.hermes"), - ResolveContext::new(repository_root()), - ) - .expect("typed plan"); - plan.effective_config.base_dir = plan.base_dir.join("other"); - - assert!(matches!( - plan.validate_consistency(), - Err(FabricError::RunPlanMismatch { field: "base_dir" }) - )); - } - #[test] fn typed_config_round_trip_has_no_file_provenance() { - let effective = resolve_effective_config_from_config( + let plan = resolve_run_plan_from_config( typed_config("nvidia.fabric.hermes"), ResolveContext::new("/tmp/fabric-base"), ) - .expect("effective config"); - let value = serde_json::to_value(effective).expect("effective config JSON"); + .expect("run plan"); + let value = serde_json::to_value(plan).expect("run plan JSON"); assert_eq!(value["base_dir"], "/tmp/fabric-base"); assert_eq!(value["config"]["metadata"]["name"], "typed-agent"); - assert_eq!(value.as_object().map(serde_json::Map::len), Some(3)); + assert!(value.get("effective_config").is_none()); } #[test] diff --git a/crates/fabric-core/src/doctor.rs b/crates/fabric-core/src/doctor.rs index 11739eac9..10f3117d1 100644 --- a/crates/fabric-core/src/doctor.rs +++ b/crates/fabric-core/src/doctor.rs @@ -55,7 +55,6 @@ pub struct DoctorReport { /// Inspect a resolved run plan without mutating the environment. pub fn doctor_plan(plan: &RunPlan) -> DoctorReport { let mut checks = Vec::new(); - checks.push(check_plan_consistency(plan)); checks.push(check_adapter_descriptor(plan)); checks.push(check_resolution(plan)); checks.extend(check_runtime_execution_surface(plan)); @@ -72,17 +71,6 @@ pub fn doctor_plan(plan: &RunPlan) -> DoctorReport { } } -fn check_plan_consistency(plan: &RunPlan) -> DoctorCheck { - match plan.validate_consistency() { - Ok(()) => check( - "plan.consistency", - DoctorStatus::Pass, - "run plan configuration state is internally consistent", - ), - Err(error) => check("plan.consistency", DoctorStatus::Fail, error.to_string()), - } -} - fn check_adapter_descriptor(plan: &RunPlan) -> DoctorCheck { if let Some(adapter) = &plan.adapter_descriptor { let mut metadata = BTreeMap::new(); diff --git a/crates/fabric-core/src/error.rs b/crates/fabric-core/src/error.rs index 6908a7e64..411409598 100644 --- a/crates/fabric-core/src/error.rs +++ b/crates/fabric-core/src/error.rs @@ -104,14 +104,6 @@ pub enum FabricError { /// Runtime handle id. runtime_id: String, }, - /// A run plan contains conflicting copies of resolved configuration state. - #[error( - "run plan contains conflicting `{field}` values between its top-level and effective config" - )] - RunPlanMismatch { - /// Conflicting run-plan field. - field: &'static str, - }, /// An environment provider is not runnable for the selected adapter in this POC. #[error("environment provider `{provider}` is not implemented for adapter `{adapter_kind:?}`")] UnsupportedEnvironmentProvider { diff --git a/crates/fabric-core/src/lib.rs b/crates/fabric-core/src/lib.rs index 95f680237..2d55cc98d 100644 --- a/crates/fabric-core/src/lib.rs +++ b/crates/fabric-core/src/lib.rs @@ -12,13 +12,12 @@ pub mod schema; pub use config::{ ADAPTER_CONTRACT_VERSION, AdapterConfigSupport, AdapterDescriptor, AdapterDescriptorSource, AdapterKind, AdapterRequirements, AdapterTelemetryProviderSupport, AdapterTelemetrySupport, - CapabilityPlan, ControlLocation, EffectiveConfig, EnvironmentConfig, EnvironmentOwnership, - EnvironmentPlan, FabricConfig, HarnessConfig, McpConfig, McpExposure, McpServerPlan, - MetadataConfig, ModelConfig, ResolutionStrategy, ResolveContext, ResolvedAdapterDescriptor, - RunPlan, RuntimeCapabilities, RuntimeConfig, SkillConfig, TelemetryConfig, TelemetryPlan, + CapabilityPlan, ControlLocation, EnvironmentConfig, EnvironmentOwnership, EnvironmentPlan, + FabricConfig, HarnessConfig, McpConfig, McpExposure, McpServerPlan, MetadataConfig, + ModelConfig, ResolutionStrategy, ResolveContext, ResolvedAdapterDescriptor, RunPlan, + RuntimeCapabilities, RuntimeConfig, SkillConfig, TelemetryConfig, TelemetryPlan, TelemetryProvider, TelemetryProviderConfig, load_adapter_descriptor, - resolve_effective_config_from_config, resolve_run_plan_from_config, - resolve_run_plan_from_effective_config, + resolve_run_plan_from_config, }; pub use doctor::{DoctorCheck, DoctorReport, DoctorStatus, doctor_plan}; pub use error::{FabricError, Result}; diff --git a/crates/fabric-core/src/runtime.rs b/crates/fabric-core/src/runtime.rs index f0be45809..8c07fd406 100644 --- a/crates/fabric-core/src/runtime.rs +++ b/crates/fabric-core/src/runtime.rs @@ -19,7 +19,7 @@ use serde_json::{Map, Value}; use crate::config::{ AdapterKind, CapabilityKind, CapabilityPlan, CapabilityTarget, ControlLocation, - EffectiveConfig, EnvironmentOwnership, RunPlan, TelemetryPlan, + EnvironmentOwnership, FabricConfig, RunPlan, TelemetryPlan, }; use crate::error::{FabricError, Result}; @@ -309,8 +309,12 @@ pub struct RuntimeTelemetryContext { /// Adapter-facing invocation payload. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct AdapterInvocation { - /// Resolved typed configuration with explicit path context. - pub effective_config: EffectiveConfig, + /// Stable agent name. + pub agent_name: String, + /// Absolute base directory used to resolve relative Fabric paths. + pub base_dir: PathBuf, + /// Complete typed Fabric config. + pub config: FabricConfig, /// Per-runtime/per-invocation execution context. pub runtime_context: RuntimeContext, /// Per-invocation request. @@ -359,7 +363,6 @@ pub fn run_plan(plan: &RunPlan, request: RunRequest) -> Result { /// Resolve or attach to the execution environment context for a run plan. pub fn prepare_environment(plan: &RunPlan) -> Result { - plan.validate_consistency()?; let mut metadata = BTreeMap::new(); let mut connection = BTreeMap::new(); let ( @@ -443,7 +446,6 @@ pub fn invoke_runtime( runtime: &RuntimeHandle, request: RunRequest, ) -> Result { - plan.validate_consistency()?; validate_blocked_tools_support(plan)?; validate_runtime_handle(plan, runtime)?; match adapter_kind(plan) { @@ -470,7 +472,6 @@ fn validate_blocked_tools_support(plan: &RunPlan) -> Result<()> { /// Stop or detach from a harness runtime. pub fn stop_runtime(plan: &RunPlan, runtime: &RuntimeHandle) -> Result> { - plan.validate_consistency()?; validate_runtime_handle(plan, runtime)?; match runtime.adapter_kind { AdapterKind::Process => ProcessAdapter.stop(runtime), @@ -1319,10 +1320,10 @@ fn adapter_invocation( artifacts: &ArtifactManifest, relay_config: Option<&RelayRuntimeConfig>, ) -> Result { - let mut effective_config = plan.effective_config.clone(); - effective_config.base_dir = absolute_path(effective_config.base_dir)?; Ok(AdapterInvocation { - effective_config, + agent_name: plan.agent_name.clone(), + base_dir: absolute_path(plan.base_dir.clone())?, + config: plan.config.clone(), runtime_context: RuntimeContext { runtime_id: runtime.runtime_id.clone(), invocation_id: invocation.invocation_id.clone(), diff --git a/crates/fabric-core/src/schema.rs b/crates/fabric-core/src/schema.rs index e7811aeed..f248466f5 100644 --- a/crates/fabric-core/src/schema.rs +++ b/crates/fabric-core/src/schema.rs @@ -9,7 +9,7 @@ use std::path::{Path, PathBuf}; use schemars::schema_for; use serde_json::Value; -use crate::config::{AdapterDescriptor, EffectiveConfig, FabricConfig, RunPlan}; +use crate::config::{AdapterDescriptor, FabricConfig, RunPlan}; use crate::error::{FabricError, Result}; use crate::runtime::{ AdapterInvocation, ArtifactManifest, EnvironmentHandle, ErrorInfo, FabricEvent, @@ -23,8 +23,6 @@ pub enum SchemaName { Agent, /// Adapter descriptor schema. AdapterDescriptor, - /// Effective merged config schema. - EffectiveConfig, /// Resolved run plan schema. RunPlan, /// Adapter-facing invocation payload schema. @@ -51,10 +49,9 @@ pub enum SchemaName { impl SchemaName { /// All public schemas in stable output order. - pub const ALL: [Self; 14] = [ + pub const ALL: [Self; 13] = [ Self::Agent, Self::AdapterDescriptor, - Self::EffectiveConfig, Self::RunPlan, Self::AdapterInvocation, Self::RuntimeContext, @@ -73,7 +70,6 @@ impl SchemaName { match self { Self::Agent => "agent", Self::AdapterDescriptor => "adapter-descriptor", - Self::EffectiveConfig => "effective-config", Self::RunPlan => "run-plan", Self::AdapterInvocation => "adapter-invocation", Self::RuntimeContext => "runtime-context", @@ -98,7 +94,6 @@ impl SchemaName { match value { "agent" => Ok(Self::Agent), "adapter-descriptor" | "adapter_descriptor" => Ok(Self::AdapterDescriptor), - "effective-config" | "effective_config" => Ok(Self::EffectiveConfig), "run-plan" | "run_plan" => Ok(Self::RunPlan), "adapter-invocation" | "adapter_invocation" => Ok(Self::AdapterInvocation), "runtime-context" | "runtime_context" => Ok(Self::RuntimeContext), @@ -126,7 +121,6 @@ pub fn generate_schema(schema: SchemaName) -> Result { match schema { SchemaName::Agent => to_value(schema_for!(FabricConfig)), SchemaName::AdapterDescriptor => to_value(schema_for!(AdapterDescriptor)), - SchemaName::EffectiveConfig => to_value(schema_for!(EffectiveConfig)), SchemaName::RunPlan => to_value(schema_for!(RunPlan)), SchemaName::AdapterInvocation => to_value(schema_for!(AdapterInvocation)), SchemaName::RuntimeContext => to_value(schema_for!(RuntimeContext)), diff --git a/crates/fabric-python/Cargo.toml b/crates/fabric-python/Cargo.toml index 7d3defcf4..f4538ca1c 100644 --- a/crates/fabric-python/Cargo.toml +++ b/crates/fabric-python/Cargo.toml @@ -16,6 +16,7 @@ test = false doctest = false [dependencies] +nemo-fabric-cli = { path = "../fabric-cli", version = "0.1.0" } nemo-fabric-core.workspace = true pyo3 = { workspace = true, features = ["abi3", "abi3-py311", "macros"] } serde.workspace = true diff --git a/crates/fabric-python/src/lib.rs b/crates/fabric-python/src/lib.rs index a1e4d4f30..b421d525d 100644 --- a/crates/fabric-python/src/lib.rs +++ b/crates/fabric-python/src/lib.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use nemo_fabric_core::{ FabricConfig, ResolveContext, RunPlan, RunRequest, RuntimeHandle, doctor_plan, - resolve_effective_config_from_config, resolve_run_plan_from_config, run_plan, + resolve_run_plan_from_config, run_plan, }; use pyo3::exceptions::PyRuntimeError; use pyo3::prelude::*; @@ -18,14 +18,11 @@ fn version() -> PyResult { Ok(nemo_fabric_core::version().to_string()) } -/// Resolve typed config JSON into effective config and return JSON. +/// Execute the Rust experimentation CLI using arguments supplied by the console bridge. #[pyfunction] -#[pyo3(signature = (config_json, base_dir=None))] -fn resolve_config(config_json: String, base_dir: Option) -> PyResult { - let config = parse_config(config_json)?; - let effective = resolve_effective_config_from_config(config, resolve_context(base_dir)) - .map_err(to_py_error)?; - to_json(&effective) +fn cli_main(args: Vec) -> PyResult<()> { + nemo_fabric_cli::app::execute_from(std::iter::once("nemo-fabric".to_string()).chain(args)) + .map_err(PyRuntimeError::new_err) } /// Resolve typed config JSON into a runnable plan and return JSON. @@ -136,7 +133,7 @@ fn stop_runtime(py: Python<'_>, plan_json: String, runtime_json: String) -> PyRe #[pymodule] fn _native(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(version, m)?)?; - m.add_function(wrap_pyfunction!(resolve_config, m)?)?; + m.add_function(wrap_pyfunction!(cli_main, m)?)?; m.add_function(wrap_pyfunction!(plan_config, m)?)?; m.add_function(wrap_pyfunction!(doctor_config, m)?)?; m.add_function(wrap_pyfunction!(run_config, m)?)?; @@ -170,10 +167,7 @@ fn parse_run_request(contents: String) -> PyResult { } fn parse_run_plan(contents: String) -> PyResult { - let plan: RunPlan = serde_json::from_str(&contents) - .map_err(|error| PyRuntimeError::new_err(error.to_string()))?; - plan.validate_consistency().map_err(to_py_error)?; - Ok(plan) + serde_json::from_str(&contents).map_err(|error| PyRuntimeError::new_err(error.to_string())) } fn parse_runtime_handle(contents: String) -> PyResult { diff --git a/docs/reference/api/python-library-reference/index.md b/docs/reference/api/python-library-reference/index.md index e0720f831..8d580c39c 100644 --- a/docs/reference/api/python-library-reference/index.md +++ b/docs/reference/api/python-library-reference/index.md @@ -50,7 +50,6 @@ SPDX-License-Identifier: Apache-2.0 */} - [`types.ArtifactRef`](./nemo_fabric.types.md#class-artifactref): Reference to one artifact produced by a run. - [`types.DoctorCheck`](./nemo_fabric.types.md#class-doctorcheck): One diagnostic check in a ``DoctorReport``. - [`types.DoctorReport`](./nemo_fabric.types.md#class-doctorreport): Aggregate preflight diagnostics for a resolved run plan. -- [`types.EffectiveConfig`](./nemo_fabric.types.md#class-effectiveconfig): Immutable result of resolving a complete typed config. - [`types.ErrorInfo`](./nemo_fabric.types.md#class-errorinfo): Structured failure returned inside a normalized ``RunResult``. - [`types.FabricEvent`](./nemo_fabric.types.md#class-fabricevent): One normalized lifecycle or invocation event. - [`types.RunOutput`](./nemo_fabric.types.md#class-runoutput): Normalized adapter output. diff --git a/docs/reference/api/python-library-reference/nemo_fabric.client.md b/docs/reference/api/python-library-reference/nemo_fabric.client.md index 42f5f392b..f4cff7762 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.client.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.client.md @@ -87,7 +87,7 @@ Planning resolves the selected adapter and reports optional runtime capabilities **Returns:** - A ``RunPlan`` containing the effective config, adapter, and declared runtime capabilities. + A ``RunPlan`` containing the canonical config, path context, adapter, and declared runtime capabilities. @@ -99,41 +99,6 @@ Planning resolves the selected adapter and reports optional runtime capabilities --- -### method `resolve` - -```python -resolve( - config: 'FabricConfig', - base_dir: 'str | PathLike[str] | None' = None -) → EffectiveConfig -``` - -Resolve a complete typed configuration. - -Resolution validates and normalizes configuration but does not resolve an adapter or compute runtime capabilities. Use ``plan()`` when those execution details are required. - - - -**Args:** - - - `config`: Complete typed ``FabricConfig``. Raw mappings are not accepted; convert them with ``FabricConfig.from_mapping()``. - - `base_dir`: Base directory for resolving relative paths. - - - -**Returns:** - The normalized ``EffectiveConfig`` snapshot. - - - -**Raises:** - - - `FabricConfigError`: If the config is invalid. - - `FabricNativeUnavailableError`: If the native extension is not installed. - ---- - - ### method `run` ```python diff --git a/docs/reference/api/python-library-reference/nemo_fabric.types.md b/docs/reference/api/python-library-reference/nemo_fabric.types.md index b18d332fc..5de0e451d 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.types.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.types.md @@ -110,74 +110,6 @@ __init__(mapping: 'Mapping[str, Any]') → None ---- - -### property extra_fields - -Return an immutable view of preserved extension fields. - - - ---- - - -### classmethod `from_mapping` - -```python -from_mapping(mapping: 'Mapping[str, Any]') → 'FabricMapping' -``` - -Validate and copy a mapping into the requested typed model. - ---- - - -### method `to_dict` - -```python -to_dict() → dict[str, Any] -``` - -Return the same detached representation as ``to_mapping()``. - ---- - - -### method `to_mapping` - -```python -to_mapping() → dict[str, Any] -``` - -Return a detached, JSON-compatible mapping for serialization. - - ---- - - -## class `EffectiveConfig` -Immutable result of resolving a complete typed config. - - - -**Attributes:** - - - `agent_name`: Resolved agent name. - - `base_dir`: Base directory used to resolve relative paths. - - `config`: Fully resolved typed ``FabricConfig``. - - -### method `__init__` - -```python -__init__(mapping: 'Mapping[str, Any]') → None -``` - - - - - - --- ### property extra_fields @@ -230,8 +162,9 @@ Immutable execution plan produced before a runtime is started. **Attributes:** - - `effective_config`: Resolved configuration snapshot. - `agent_name`: Resolved agent name. + - `base_dir`: Base directory used to resolve relative paths. + - `config`: Typed configuration snapshot. - `adapter`: Resolved adapter identity. - `capabilities`: Operations declared by the resolved runtime. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx index 5b133e34d..b0d72b888 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx @@ -2,7 +2,7 @@ title: "Enum Capability Kind" sidebar-title: "CapabilityKind" description: "Capability kind." -position: 40 +position: 39 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx index d51db854e..2b82413a1 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx @@ -2,7 +2,7 @@ title: "Enum Capability Target" sidebar-title: "CapabilityTarget" description: "Capability routing target." -position: 41 +position: 40 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-environmentownership.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-environmentownership.mdx index 56f121751..962a0afdc 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-environmentownership.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-environmentownership.mdx @@ -2,7 +2,7 @@ title: "Enum Environment Ownership" sidebar-title: "EnvironmentOwnership" description: "Whether Fabric owns the underlying environment resource." -position: 13 +position: 12 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx index a75f60441..b06236033 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx @@ -2,7 +2,7 @@ title: "Enum McpExposure" sidebar-title: "McpExposure" description: "MCP exposure strategy." -position: 18 +position: 17 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx index 59e753c33..1b2589f2c 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atif Storage Config" sidebar-title: "RelayAtifStorageConfig" description: "Relay ATIF remote storage configuration." -position: 45 +position: 44 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx index a5623ec0d..e500f6aa1 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Endpoint Field Name Policy" sidebar-title: "RelayAtofEndpointFieldNamePolicy" description: "Relay ATOF endpoint field-name policy." -position: 46 +position: 45 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointtransport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointtransport.mdx index f1d86d780..089169e4e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointtransport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofendpointtransport.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Endpoint Transport" sidebar-title: "RelayAtofEndpointTransport" description: "Relay ATOF endpoint transport." -position: 47 +position: 46 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx index 53dc69eb4..010b88501 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Mode" sidebar-title: "RelayAtofMode" description: "Relay ATOF file mode." -position: 48 +position: 47 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx index bf25bac43..fae226e2b 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Otlp Transport" sidebar-title: "RelayOtlpTransport" description: "Relay OTLP transport." -position: 49 +position: 48 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx index 4031718ba..34db2ad99 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Unsupported Behavior" sidebar-title: "RelayUnsupportedBehavior" description: "Relay unsupported/unknown config handling." -position: 50 +position: 49 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx index a85905a89..86197cf30 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx @@ -2,7 +2,7 @@ title: "Enum Resolution Strategy" sidebar-title: "ResolutionStrategy" description: "Adapter install or availability strategy." -position: 22 +position: 21 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx index 9c578453a..9cab3af61 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx @@ -2,7 +2,7 @@ title: "Enum Telemetry Provider" sidebar-title: "TelemetryProvider" description: "Telemetry runtime provider." -position: 31 +position: 30 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx index 40e27412e..93dff2ba9 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx @@ -2,7 +2,7 @@ title: "Function load_adapter_descriptor" sidebar-title: "load_adapter_descriptor" description: "Load an adapter descriptor from JSON package metadata." -position: 33 +position: 32 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config.mdx deleted file mode 100644 index edf9c8ca6..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "Function resolve_effective_config_from_config" -sidebar-title: "resolve_effective_config_from_config" -description: "Resolve a typed Fabric config into an effective config." -position: 34 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
FabricConfig,\n    context: ResolveContext,\n) -> Result<EffectiveConfig>"}} />
- -Resolve a typed Fabric config into an effective config. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx index 3412e2fe7..830eb41f2 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx @@ -2,7 +2,7 @@ title: "Function resolve_run_plan_from_config" sidebar-title: "resolve_run_plan_from_config" description: "Resolve a typed Fabric config into a runnable plan." -position: 35 +position: 33 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx deleted file mode 100644 index 099b97497..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx +++ /dev/null @@ -1,14 +0,0 @@ ---- -title: "Function resolve_run_plan_from_effective_config" -sidebar-title: "resolve_run_plan_from_effective_config" -description: "Resolve execution planning metadata from merged effective config." -position: 36 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
EffectiveConfig,\n) -> Result<RunPlan>"}} />
- -Resolve execution planning metadata from merged effective config. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx index 2a3b78bae..4de4cef4d 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx @@ -2,7 +2,7 @@ title: "Module config" sidebar-title: "config" description: "Fabric config models and loading helpers." -position: 68 +position: 65 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -21,7 +21,6 @@ Fabric config models and loading helpers. - [CapabilityPlan](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityplan): Resolved capability configuration. - [CapabilityRoute](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityroute): One capability routing decision. - [CapabilityTargetPlan](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilitytargetplan): Capabilities routed to one target. -- [EffectiveConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig): Resolved Fabric config with explicit path context. - [EnvironmentConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentconfig): Execution environment configuration. - [EnvironmentPlan](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentplan): Resolved environment plan. - [FabricConfig](/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig): Versioned Fabric agent config. @@ -76,6 +75,4 @@ Fabric config models and loading helpers. ## Functions - [load_adapter_descriptor](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor): Load an adapter descriptor from JSON package metadata. -- [resolve_effective_config_from_config](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-effective-config-from-config): Resolve a typed Fabric config into an effective config. - [resolve_run_plan_from_config](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config): Resolve a typed Fabric config into a runnable plan. -- [resolve_run_plan_from_effective_config](/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-effective-config): Resolve execution planning metadata from merged effective config. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityplan.mdx index a3e63f791..8e98b7c73 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityplan.mdx @@ -45,7 +45,7 @@ Capabilities that are configured but not executable by this Fabric build. ### `routes: Vec` -Routing decisions made while resolving the effective config. +Routing decisions made while planning the configured capabilities. ## Trait Implementations diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx deleted file mode 100644 index 379aead3d..000000000 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-effectiveconfig.mdx +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: "Struct Effective Config" -sidebar-title: "EffectiveConfig" -description: "Resolved Fabric config with explicit path context." -position: 11 ---- -{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -SPDX-License-Identifier: Apache-2.0 */} - -Generated from `cargo doc --no-deps -p nemo-fabric-core`. - -
String,\n    pub base_dir: PathBuf,\n    pub config: FabricConfig,\n}"}} />
- -Resolved Fabric config with explicit path context. - -## Fields - -### `agent_name: String` - -Stable agent name. - -### `base_dir: PathBuf` - -Base directory used to resolve relative Fabric paths. - -### `config: FabricConfig` - -Complete typed Fabric config. - -## Trait Implementations - -### `impl Clone for EffectiveConfig` - -
Clone for EffectiveConfig"}} />
- -#### `clone` - -
clone(&self) -> EffectiveConfig"}} />
- -#### `clone_from` - -
clone_from(&mut self, source: &Self)"}} />
- -### `impl Debug for EffectiveConfig` - -
Debug for EffectiveConfig"}} />
- -#### `fmt` - -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
- -### `impl<'de> Deserialize<'de> for EffectiveConfig` - -
Deserialize<'de> for EffectiveConfig"}} />
- -#### `deserialize` - -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
- -### `impl JsonSchema for EffectiveConfig` - -
EffectiveConfig"}} />
- -#### `schema_name` - -
Cow<'static, str>"}} />
- -#### `schema_id` - -
Cow<'static, str>"}} />
- -#### `json_schema` - -
- -#### `inline_schema` - -
bool"}} />
- -### `impl PartialEq for EffectiveConfig` - -
PartialEq for EffectiveConfig"}} />
- -#### `eq` - -
eq(&self, other: &EffectiveConfig) -> bool"}} />
- -#### `ne` - -
ne(&self, other: &Rhs) -> bool"}} />
- -### `impl Serialize for EffectiveConfig` - -
Serialize for EffectiveConfig"}} />
- -#### `serialize` - -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
- -### `impl StructuralPartialEq for EffectiveConfig` - -
StructuralPartialEq for EffectiveConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentconfig.mdx index 4c0cc7b44..97e9e0d19 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Environment Config" sidebar-title: "EnvironmentConfig" description: "Execution environment configuration." -position: 12 +position: 11 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentplan.mdx index 906e78ccc..f0f664bc7 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentplan.mdx @@ -2,7 +2,7 @@ title: "Struct Environment Plan" sidebar-title: "EnvironmentPlan" description: "Resolved environment plan." -position: 14 +position: 13 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx index 5e4e357a0..dca40ab9c 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Fabric Config" sidebar-title: "FabricConfig" description: "Versioned Fabric agent config." -position: 15 +position: 14 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx index fd2177594..ce73c2a14 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Harness Config" sidebar-title: "HarnessConfig" description: "Harness selection." -position: 16 +position: 15 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx index 9c6b81189..12e5c64c5 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx @@ -2,7 +2,7 @@ title: "Struct McpConfig" sidebar-title: "McpConfig" description: "MCP capability configuration." -position: 17 +position: 16 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverconfig.mdx index 0d3eb1f09..a569574dd 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverconfig.mdx @@ -2,7 +2,7 @@ title: "Struct McpServer Config" sidebar-title: "McpServerConfig" description: "MCP server configuration." -position: 15 +position: 14 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx index 8ab22f57e..6902533b3 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx @@ -2,7 +2,7 @@ title: "Struct McpServer Plan" sidebar-title: "McpServerPlan" description: "Resolved MCP server exposure." -position: 19 +position: 18 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx index f83f18a1c..c9bfdec0d 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Metadata Config" sidebar-title: "MetadataConfig" description: "Human-readable metadata." -position: 20 +position: 19 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx index 579ebc62e..92033d79b 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Model Config" sidebar-title: "ModelConfig" description: "Model configuration." -position: 21 +position: 20 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx index 93aa8ea45..2beb9d723 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Atif Config" sidebar-title: "RelayAtifConfig" description: "Relay ATIF export configuration." -position: 19 +position: 18 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx index 01d50a87d..0d91c211a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Atof Config" sidebar-title: "RelayAtofConfig" description: "Relay ATOF export configuration." -position: 20 +position: 19 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofendpointconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofendpointconfig.mdx index eb203ad7e..74f698421 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofendpointconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofendpointconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Atof Endpoint Config" sidebar-title: "RelayAtofEndpointConfig" description: "Relay ATOF endpoint configuration." -position: 21 +position: 20 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx index 3d482e200..44df6f4e6 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Component Config" sidebar-title: "RelayComponentConfig" description: "Generic NeMo Relay plugin component configuration." -position: 22 +position: 21 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx index 4283ccfb7..852abee85 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Config" sidebar-title: "RelayConfig" description: "NeMo Relay integration configuration." -position: 23 +position: 22 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx index 74bfd08cb..66b07af70 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Config Policy" sidebar-title: "RelayConfigPolicy" description: "Relay validation policy." -position: 24 +position: 23 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx index 77ce17e4a..1fba378f6 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Observability Config" sidebar-title: "RelayObservabilityConfig" description: "NeMo Relay observability component configuration." -position: 25 +position: 24 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx index ad8fc12c2..0d9cfc977 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Otlp Config" sidebar-title: "RelayOtlpConfig" description: "Relay OpenTelemetry/OpenInference export configuration." -position: 26 +position: 25 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx index 60f1e8808..1fd20085e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx @@ -2,7 +2,7 @@ title: "Struct Resolve Context" sidebar-title: "ResolveContext" description: "Source context used when resolving an in-memory Fabric config." -position: 23 +position: 22 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx index 97724b25a..c1891a955 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx @@ -2,7 +2,7 @@ title: "Struct Resolved Adapter Descriptor" sidebar-title: "ResolvedAdapterDescriptor" description: "Adapter descriptor selected for a run plan." -position: 24 +position: 23 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx index 8b822f019..f042afb24 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx @@ -2,27 +2,31 @@ title: "Struct RunPlan" sidebar-title: "RunPlan" description: "Resolved Fabric run plan." -position: 25 +position: 24 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
EffectiveConfig,\n    pub agent_name: String,\n    pub adapter_descriptor: Option<ResolvedAdapterDescriptor>,\n    pub resolution: Option<ResolutionStrategy>,\n    pub environment_plan: Option<EnvironmentPlan>,\n    pub capability_plan: CapabilityPlan,\n    pub capabilities: RuntimeCapabilities,\n    pub telemetry_plan: Option<TelemetryPlan>,\n    pub base_dir: PathBuf,\n    pub config: FabricConfig,\n}"}} />
+
String,\n    pub base_dir: PathBuf,\n    pub config: FabricConfig,\n    pub adapter_descriptor: Option<ResolvedAdapterDescriptor>,\n    pub resolution: Option<ResolutionStrategy>,\n    pub environment_plan: Option<EnvironmentPlan>,\n    pub capability_plan: CapabilityPlan,\n    pub capabilities: RuntimeCapabilities,\n    pub telemetry_plan: Option<TelemetryPlan>,\n}"}} />
Resolved Fabric run plan. ## Fields -### `effective_config: EffectiveConfig` - -Resolved typed configuration and path context used by this plan. - ### `agent_name: String` Stable agent name. +### `base_dir: PathBuf` + +Base directory used to resolve relative Fabric paths. + +### `config: FabricConfig` + +Complete typed Fabric config. + ### `adapter_descriptor: Option` Adapter descriptor resolved for this plan, when configured. @@ -47,26 +51,6 @@ Lifecycle behavior implemented by the selected runtime path. Resolved telemetry pass-through plan. -### `base_dir: PathBuf` - -Base directory used to resolve relative Fabric paths. - -### `config: FabricConfig` - -Complete typed Fabric config. - -## Implementations - -### `impl RunPlan` - -
RunPlan"}} />
- -#### `validate_consistency` - -
Result<()>"}} />
- -Reject conflicting duplicated state before a plan enters a lifecycle boundary. - ## Trait Implementations ### `impl Clone for RunPlan` diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx index 639ef5796..7ef3c1696 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx @@ -2,7 +2,7 @@ title: "Struct Runtime Capabilities" sidebar-title: "RuntimeCapabilities" description: "Lifecycle behavior implemented by a resolved runtime path." -position: 26 +position: 25 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx index 022813485..86641ac9b 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Runtime Config" sidebar-title: "RuntimeConfig" description: "Runtime input/output contract." -position: 27 +position: 26 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx index 1b3c1e1fe..9572bbf71 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Skill Config" sidebar-title: "SkillConfig" description: "Skill capability configuration." -position: 28 +position: 27 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx index c35e2db57..5dd271a6f 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Telemetry Config" sidebar-title: "TelemetryConfig" description: "Telemetry configuration." -position: 29 +position: 28 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx index 767112630..04c79e8c9 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx @@ -2,7 +2,7 @@ title: "Struct Telemetry Plan" sidebar-title: "TelemetryPlan" description: "Resolved telemetry plan." -position: 30 +position: 29 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx index afa21e960..21e9291ba 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Telemetry Provider Config" sidebar-title: "TelemetryProviderConfig" description: "Provider-specific telemetry configuration." -position: 32 +position: 31 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx index a3e932752..ccedcf918 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Tools Config" sidebar-title: "ToolsConfig" description: "Harness-neutral tool capability configuration." -position: 36 +position: 35 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx index f46908102..948ab6ecf 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx @@ -2,7 +2,7 @@ title: "Struct Tools Plan" sidebar-title: "ToolsPlan" description: "Normalized tool policy for a run." -position: 37 +position: 36 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx index 50a55eccd..0d424ce77 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx @@ -2,7 +2,7 @@ title: "Enum Doctor Status" sidebar-title: "DoctorStatus" description: "Diagnostic status." -position: 39 +position: 36 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx index 8ddbe6d24..c25887030 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx @@ -2,7 +2,7 @@ title: "Function doctor_plan" sidebar-title: "doctor_plan" description: "Inspect a resolved run plan without mutating the environment." -position: 40 +position: 37 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx index b33235362..5dff6b0b1 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx @@ -2,7 +2,7 @@ title: "Module doctor" sidebar-title: "doctor" description: "Plan diagnostics for Fabric." -position: 69 +position: 66 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx index d3d3d0190..75af40335 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx @@ -2,7 +2,7 @@ title: "Struct Doctor Check" sidebar-title: "DoctorCheck" description: "Diagnostic check result." -position: 37 +position: 34 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx index 5f71661de..e7b635eb3 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx @@ -2,7 +2,7 @@ title: "Struct Doctor Report" sidebar-title: "DoctorReport" description: "Diagnostic report for a resolved run plan." -position: 38 +position: 35 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx index a773136b0..4ff429c42 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx @@ -2,14 +2,14 @@ title: "Enum Fabric Error" sidebar-title: "FabricError" description: "Errors raised by Fabric config loading and validation." -position: 41 +position: 38 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
PathBuf,\n        source: Error,\n    },\n    PathNotFound(PathBuf),\n    UnknownAdapter {\n        adapter_id: String,\n        available: Vec<String>,\n    },\n    AdapterDescriptorMismatch {\n        path: PathBuf,\n        field: &'static str,\n        expected: String,\n        actual: String,\n    },\n    AdapterDescriptorUnsupported {\n        adapter_id: String,\n        field: &'static str,\n        value: String,\n    },\n    InvalidAdapterDescriptor {\n        path: PathBuf,\n        message: String,\n    },\n    UnknownSchema {\n        schema: String,\n        available: Vec<String>,\n    },\n    UnsupportedRuntimeAdapter {\n        harness: String,\n        adapter_kind: AdapterKind,\n    },\n    UnsupportedToolsPolicy {\n        harness: String,\n        reason: String,\n    },\n    RuntimeHandleMismatch {\n        field: &'static str,\n        expected: String,\n        actual: String,\n        runtime_id: String,\n    },\n    RunPlanMismatch {\n        field: &'static str,\n    },\n    UnsupportedEnvironmentProvider {\n        provider: String,\n        adapter_kind: AdapterKind,\n    },\n    InvalidProcessSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    InvalidPythonSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    PythonInterpreterUnavailable {\n        path: PathBuf,\n        origin: String,\n        reason: String,\n    },\n    ProcessRunner {\n        command: String,\n        source: Error,\n    },\n    SerializeJson(Error),\n    Read {\n        path: PathBuf,\n        source: Error,\n    },\n    Write {\n        path: PathBuf,\n        source: Error,\n    },\n    ParseJson {\n        path: PathBuf,\n        source: Error,\n    },\n}"}} />
+
PathBuf,\n        source: Error,\n    },\n    PathNotFound(PathBuf),\n    UnknownAdapter {\n        adapter_id: String,\n        available: Vec<String>,\n    },\n    AdapterDescriptorMismatch {\n        path: PathBuf,\n        field: &'static str,\n        expected: String,\n        actual: String,\n    },\n    AdapterDescriptorUnsupported {\n        adapter_id: String,\n        field: &'static str,\n        value: String,\n    },\n    InvalidAdapterDescriptor {\n        path: PathBuf,\n        message: String,\n    },\n    UnknownSchema {\n        schema: String,\n        available: Vec<String>,\n    },\n    UnsupportedRuntimeAdapter {\n        harness: String,\n        adapter_kind: AdapterKind,\n    },\n    UnsupportedToolsPolicy {\n        harness: String,\n        reason: String,\n    },\n    RuntimeHandleMismatch {\n        field: &'static str,\n        expected: String,\n        actual: String,\n        runtime_id: String,\n    },\n    UnsupportedEnvironmentProvider {\n        provider: String,\n        adapter_kind: AdapterKind,\n    },\n    InvalidProcessSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    InvalidPythonSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    PythonInterpreterUnavailable {\n        path: PathBuf,\n        origin: String,\n        reason: String,\n    },\n    ProcessRunner {\n        command: String,\n        source: Error,\n    },\n    SerializeJson(Error),\n    Read {\n        path: PathBuf,\n        source: Error,\n    },\n    Write {\n        path: PathBuf,\n        source: Error,\n    },\n    ParseJson {\n        path: PathBuf,\n        source: Error,\n    },\n}"}} />
Errors raised by Fabric config loading and validation. @@ -185,18 +185,6 @@ Actual value from the runtime handle. Runtime handle id. -### `RunPlanMismatch` - -
- -A run plan contains conflicting copies of resolved configuration state. - -#### Fields - -### `field: &'static str` - -Conflicting run-plan field. - ### `UnsupportedEnvironmentProvider`
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx index d8ddd4843..bc0a4a451 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx @@ -2,7 +2,7 @@ title: "Module error" sidebar-title: "error" description: "Error types for Fabric core." -position: 70 +position: 67 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx index 618862e5d..011b5b223 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx @@ -2,7 +2,7 @@ title: "Type Alias Result" sidebar-title: "Result" description: "Core Fabric result type." -position: 42 +position: 39 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx index a3206a15a..b9f5b49fb 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx @@ -2,7 +2,7 @@ title: "Function version" sidebar-title: "version" description: "Returns the crate version compiled into this build." -position: 73 +position: 70 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx index 2d35ab987..0f9030b11 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx @@ -23,7 +23,6 @@ Core config and runtime contract for NeMo Fabric. - `pub use config::AdapterTelemetrySupport;` - `pub use config::CapabilityPlan;` - `pub use config::ControlLocation;` -- `pub use config::EffectiveConfig;` - `pub use config::EnvironmentConfig;` - `pub use config::EnvironmentOwnership;` - `pub use config::EnvironmentPlan;` @@ -46,9 +45,7 @@ Core config and runtime contract for NeMo Fabric. - `pub use config::TelemetryProvider;` - `pub use config::TelemetryProviderConfig;` - `pub use config::load_adapter_descriptor;` -- `pub use config::resolve_effective_config_from_config;` - `pub use config::resolve_run_plan_from_config;` -- `pub use config::resolve_run_plan_from_effective_config;` - `pub use doctor::DoctorCheck;` - `pub use doctor::DoctorReport;` - `pub use doctor::DoctorStatus;` diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx index daca55dec..05f895e78 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx @@ -2,7 +2,7 @@ title: "Module runtime" sidebar-title: "runtime" description: "Runtime invocation helpers." -position: 71 +position: 68 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx index 6d73bbd0b..24052b9de 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx @@ -9,15 +9,23 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
EffectiveConfig,\n    pub runtime_context: RuntimeContext,\n    pub request: RunRequest,\n    pub capability_plan: CapabilityPlan,\n    pub telemetry_plan: Option<TelemetryPlan>,\n}"}} />
+
String,\n    pub base_dir: PathBuf,\n    pub config: FabricConfig,\n    pub runtime_context: RuntimeContext,\n    pub request: RunRequest,\n    pub capability_plan: CapabilityPlan,\n    pub telemetry_plan: Option<TelemetryPlan>,\n}"}} />
Adapter-facing invocation payload. ## Fields -### `effective_config: EffectiveConfig` +### `agent_name: String` -Resolved typed configuration with explicit path context. +Stable agent name. + +### `base_dir: PathBuf` + +Absolute base directory used to resolve relative Fabric paths. + +### `config: FabricConfig` + +Complete typed Fabric config. ### `runtime_context: RuntimeContext` diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx index ab644e11e..b7cc4cbfb 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx @@ -11,9 +11,8 @@ Generated from `cargo doc --no-deps -p nemo-fabric-core`. ```rust pub enum SchemaName { -Show 14 variants Agent, +Show 13 variants Agent, AdapterDescriptor, - EffectiveConfig, RunPlan, AdapterInvocation, RuntimeContext, @@ -44,12 +43,6 @@ Complete typed Fabric config schema. Adapter descriptor schema. -### `EffectiveConfig` - -
- -Effective merged config schema. - ### `RunPlan`
@@ -124,7 +117,7 @@ Fabric lifecycle event schema. #### `ALL` -
14]"}} />
+
13]"}} />
All public schemas in stable output order. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx index 42b126261..a3475f92e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx @@ -2,7 +2,7 @@ title: "Module schema" sidebar-title: "schema" description: "JSON Schema generation for the public Fabric contract." -position: 72 +position: 69 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/sdk/python.mdx b/docs/sdk/python.mdx index 509864050..b9f370b22 100644 --- a/docs/sdk/python.mdx +++ b/docs/sdk/python.mdx @@ -96,7 +96,7 @@ Most application code works with four objects: | Concept | What It Represents | How Consumers Use It | | --- | --- | --- | | `FabricConfig` | Typed configuration for the harness, models, runtime, and capabilities. | Construct it from application config before planning or running. | -| `RunPlan` | The resolved config, selected adapter, and declared capabilities. | Inspect it before starting work when adapter selection or feature support matters. | +| `RunPlan` | The canonical config and path context, selected adapter, and declared capabilities. | Inspect it before starting work when adapter selection or feature support matters. | | `Runtime` | The Python object for one logical, stateful harness execution. | Use it to send ordered invocations and stop the execution. Use it as an async context manager for cleanup. | | `RunResult` | The normalized outcome of one invocation. | Read its status, output, error, events, artifacts, and correlation IDs. | @@ -204,7 +204,6 @@ multiple independent runtimes. | API | Async | Use When | Notes | | --- | --- | --- | --- | -| `Fabric.resolve(config, base_dir=...)` | No | You need the normalized effective config without resolving an adapter. | Does not start a runtime. | | `Fabric.plan(config, base_dir=...)` | No | You need to inspect the selected adapter, capability mapping, and runtime capabilities before running. | Does not start a runtime. | | `Fabric.doctor(config, base_dir=...)` | Yes | You need preflight diagnostics for adapter availability, config support, and environment assumptions. | Checks may touch runtime dependencies. | | `Fabric.run(config, input=...)` | Yes | You need one complete start, invoke, result, stop lifecycle. | Pass a `RunRequest` instead when the invocation needs IDs, context, or overrides. | diff --git a/examples/notebooks/01_quickstart.ipynb b/examples/notebooks/01_quickstart.ipynb index 29af40c2b..ec207bf81 100644 --- a/examples/notebooks/01_quickstart.ipynb +++ b/examples/notebooks/01_quickstart.ipynb @@ -384,8 +384,8 @@ "\n", "print(\"adapter :\", plan.adapter.adapter_id)\n", "print(\"harness :\", plan.adapter.harness)\n", - "print(\"in/out :\", plan.effective_config.config.runtime.input_schema,\n", - " \"/\", plan.effective_config.config.runtime.output_schema)\n", + "print(\"in/out :\", plan.config.runtime.input_schema,\n", + " \"/\", plan.config.runtime.output_schema)\n", "print(\"caps : streaming=%s updates=%s cancellation=%s\" % (\n", " plan.capabilities.streaming,\n", " plan.capabilities.updates,\n", diff --git a/examples/notebooks/02_variations.ipynb b/examples/notebooks/02_variations.ipynb index c81e97155..68f6a8832 100644 --- a/examples/notebooks/02_variations.ipynb +++ b/examples/notebooks/02_variations.ipynb @@ -245,8 +245,8 @@ "for harness in HARNESSES:\n", " cfg = for_harness(harness)\n", " plan = fabric.plan(cfg, base_dir=BASE_DIR)\n", - " rc = plan.effective_config.config.runtime\n", - " model = plan.effective_config.config.models[\"default\"][\"model\"]\n", + " rc = plan.config.runtime\n", + " model = plan.config.models[\"default\"][\"model\"]\n", " print(f\"### {harness['name']}\")\n", " print(f\" adapter = {plan.adapter.adapter_id}\")\n", " print(f\" model = {model} input schema = {rc.input_schema}\")\n", diff --git a/python/src/nemo_fabric/__init__.py b/python/src/nemo_fabric/__init__.py index c4f847324..df761ccf0 100644 --- a/python/src/nemo_fabric/__init__.py +++ b/python/src/nemo_fabric/__init__.py @@ -41,7 +41,6 @@ from nemo_fabric.types import ArtifactRef from nemo_fabric.types import DoctorCheck from nemo_fabric.types import DoctorReport -from nemo_fabric.types import EffectiveConfig from nemo_fabric.types import ErrorInfo from nemo_fabric.types import FabricEvent from nemo_fabric.types import RunOutput @@ -57,7 +56,6 @@ "ArtifactRef", "DoctorCheck", "DoctorReport", - "EffectiveConfig", "EnvironmentConfig", "ErrorInfo", "Fabric", diff --git a/python/src/nemo_fabric/_bundled/adapters/claude/fabric-adapter.json b/python/src/nemo_fabric/_bundled/adapters/claude/fabric-adapter.json deleted file mode 100644 index 876e1620e..000000000 --- a/python/src/nemo_fabric/_bundled/adapters/claude/fabric-adapter.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "nvidia.fabric.claude", - "harness": "claude", - "adapter_kind": "python", - "runner": {"module": "nemo_fabric_adapters.claude.adapter", "callable": "run"}, - "config": {"accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"]}, - "telemetry": {"providers": {"relay": {"outputs": ["atif", "otel", "openinference"], "integration_modes": ["hooks", "gateway"]}}} -} diff --git a/python/src/nemo_fabric/_bundled/adapters/codex/fabric-adapter.json b/python/src/nemo_fabric/_bundled/adapters/codex/fabric-adapter.json deleted file mode 100644 index 5cb09f538..000000000 --- a/python/src/nemo_fabric/_bundled/adapters/codex/fabric-adapter.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "nvidia.fabric.codex", - "harness": "codex", - "adapter_kind": "python", - "runner": {"module": "nemo_fabric_adapters.codex.adapter", "callable": "run"}, - "config": {"accepts": ["models", "telemetry"]}, - "telemetry": {"providers": {"relay": {"outputs": ["atif", "otel", "openinference"], "integration_modes": ["hooks", "gateway"]}, "native": {"outputs": ["otel"]}}} -} diff --git a/python/src/nemo_fabric/_bundled/adapters/deepagents/fabric-adapter.json b/python/src/nemo_fabric/_bundled/adapters/deepagents/fabric-adapter.json deleted file mode 100644 index 314fbad22..000000000 --- a/python/src/nemo_fabric/_bundled/adapters/deepagents/fabric-adapter.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "nvidia.fabric.langchain.deepagents", - "harness": "deepagents", - "adapter_kind": "python", - "runner": {"module": "nemo_fabric_adapters.deepagents.adapter", "callable": "run"}, - "requirements": {}, - "config": {"accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"]}, - "telemetry": {"providers": {"relay": {"outputs": ["atif", "otel", "openinference"]}, "native": {"outputs": ["otel", "openinference"]}}} -} diff --git a/python/src/nemo_fabric/_bundled/adapters/hermes/fabric-adapter.json b/python/src/nemo_fabric/_bundled/adapters/hermes/fabric-adapter.json deleted file mode 100644 index 35ab6e5de..000000000 --- a/python/src/nemo_fabric/_bundled/adapters/hermes/fabric-adapter.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "nvidia.fabric.hermes", - "harness": "hermes", - "adapter_kind": "python", - "runner": {"module": "nemo_fabric_adapters.hermes.adapter", "callable": "run"}, - "requirements": {"env": ["NVIDIA_API_KEY"]}, - "config": {"accepts": ["tools", "tools.blocked", "mcp", "skills", "telemetry"]}, - "telemetry": {"providers": {"relay": {"outputs": ["atif", "otel", "openinference"]}}} -} diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md b/python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md deleted file mode 100644 index 728b08498..000000000 --- a/python/src/nemo_fabric/_bundled/examples/code_review_agent/README.md +++ /dev/null @@ -1,20 +0,0 @@ - - -# Editable Code Review Example - -This directory is ordinary Python source copied by `nemo-fabric example init`. -Edit `config.py` to choose a real harness, model, tools, or telemetry. The -credential-free default uses the bundled scripted adapter so the scaffold can -be validated immediately. - -From the directory containing this package, run: - -```bash -nemo-fabric run \ - --factory my_agent.config:build_config \ - --base-dir my_agent \ - --input "Review the workspace" -``` diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/fabric-adapter.json b/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/fabric-adapter.json deleted file mode 100644 index 298b1e4e3..000000000 --- a/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/fabric-adapter.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "contract_version": "fabric.adapter/v1alpha1", - "adapter_id": "nvidia.fabric.scripted", - "harness": "scripted", - "adapter_kind": "process", - "runner": { - "command": "python3", - "script": "run.py", - "stdin_payload": "fabric_request" - } -} diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/run.py b/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/run.py deleted file mode 100644 index 308555541..000000000 --- a/python/src/nemo_fabric/_bundled/examples/code_review_agent/adapters/scripted/run.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -"""Deterministic process adapter included with the editable scaffold.""" - -import json -import sys - - -payload = json.load(sys.stdin) -request = payload["request"] -print(json.dumps({"response": request.get("input"), "request_id": request["request_id"]})) diff --git a/python/src/nemo_fabric/_bundled/examples/code_review_agent/config.py b/python/src/nemo_fabric/_bundled/examples/code_review_agent/config.py deleted file mode 100644 index 74304ea9f..000000000 --- a/python/src/nemo_fabric/_bundled/examples/code_review_agent/config.py +++ /dev/null @@ -1,29 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -"""Complete, editable Fabric configuration for this example.""" - -from nemo_fabric import EnvironmentConfig -from nemo_fabric import FabricConfig -from nemo_fabric import HarnessConfig -from nemo_fabric import MetadataConfig -from nemo_fabric import RuntimeConfig - - -def build_config() -> FabricConfig: - """Return a credential-free starting point for experimentation.""" - - config = FabricConfig( - metadata=MetadataConfig( - name="code-review-agent", - description="Editable SDK example for reviewing a small Python workspace.", - ), - harness=HarnessConfig( - adapter_id="nvidia.fabric.scripted", - resolution="preinstalled", - ), - runtime=RuntimeConfig(input_schema="text", output_schema="message"), - environment=EnvironmentConfig(provider="local", workspace="repo"), - ) - config.add_skill_path("skills/code-review.md") - return config diff --git a/python/src/nemo_fabric/_native.pyi b/python/src/nemo_fabric/_native.pyi index 588831dcd..0ff233863 100644 --- a/python/src/nemo_fabric/_native.pyi +++ b/python/src/nemo_fabric/_native.pyi @@ -2,10 +2,7 @@ # SPDX-License-Identifier: Apache-2.0 def version() -> str: ... -def resolve_config( - config_json: str, - base_dir: str | None = None, -) -> str: ... +def cli_main(args: list[str]) -> None: ... def plan_config( config_json: str, base_dir: str | None = None, diff --git a/python/src/nemo_fabric/cli.py b/python/src/nemo_fabric/cli.py index 5514aac09..5f4d54f32 100644 --- a/python/src/nemo_fabric/cli.py +++ b/python/src/nemo_fabric/cli.py @@ -1,316 +1,23 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -"""Developer experimentation interface backed by the NeMo Fabric SDK.""" +"""Console-script bridge to the Rust experimentation CLI.""" from __future__ import annotations -import argparse -import asyncio -import importlib.metadata -import json -import shutil import sys -from dataclasses import dataclass -from pathlib import Path -from typing import Any -from nemo_fabric.client import Fabric -from nemo_fabric.errors import FabricError -from nemo_fabric.factories import load_config_factory -from nemo_fabric.models import FabricConfig, RunRequest -from nemo_fabric.presets import EXAMPLES, PRESETS - - -class SourceError(ValueError): - """Invalid CLI source selector.""" - - -@dataclass(frozen=True) -class SelectedSource: - """A complete typed config and its path-resolution base.""" - - config: FabricConfig - base_dir: Path - label: str - - -def load_factory(spec: str) -> FabricConfig: - """Import and invoke a ``module:callable`` Fabric config factory.""" - - try: - return load_config_factory(spec) - except Exception as error: - raise SourceError(str(error)) from error - - -def _invoke_factory(label: str, factory: Any) -> FabricConfig: - """Invoke a bundled config factory with CLI-friendly failures.""" - - try: - return factory() - except (FabricError, SourceError, OSError, ValueError): - raise - except Exception as error: - raise SourceError(f"failed to build {label}: {error}") from error - - -def select_source(args: argparse.Namespace) -> SelectedSource: - """Resolve one mutually exclusive CLI selector.""" - - override = Path(args.base_dir).resolve() if args.base_dir else None - if args.variant and not args.example: - raise SourceError("--variant requires --example") - if args.preset: - try: - preset = PRESETS[args.preset] - except KeyError as error: - raise SourceError(_unknown("preset", args.preset, PRESETS)) from error - label = f"preset:{preset.name}" - return SelectedSource(_invoke_factory(label, preset.factory), override or preset.base_dir, label) - if args.example: - try: - example = EXAMPLES[args.example] - except KeyError as error: - raise SourceError(_unknown("example", args.example, EXAMPLES)) from error - variant = args.variant or example.default_variant - try: - factory = example.variants[variant] - except KeyError as error: - choices = ", ".join(sorted(example.variants)) - raise SourceError(f"unknown variant {variant!r} for {example.name}; available: {choices}") from error - label = f"example:{example.name}:{variant}" - return SelectedSource(_invoke_factory(label, factory), override or example.base_dir, label) - if args.factory: - return SelectedSource(load_factory(args.factory), override or Path.cwd(), f"factory:{args.factory}") - raise SourceError("select exactly one of --preset, --example, or --factory") - - -def _unknown(kind: str, name: str, registry: dict[str, Any]) -> str: - return f"unknown {kind} {name!r}; available: {', '.join(sorted(registry))}" - - -def _add_selector(parser: argparse.ArgumentParser) -> None: - sources = parser.add_mutually_exclusive_group(required=True) - sources.add_argument("--preset", help="Use a maintained, complete built-in config.") - sources.add_argument("--example", help="Use an installed example config.") - sources.add_argument("--factory", help="Import a custom module:callable returning FabricConfig.") - parser.add_argument("--variant", help="Variant for --example (defaults to the example default).") - parser.add_argument("--base-dir", help="Override the source base directory for relative paths.") - - -def build_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser( - prog="nemo-fabric", - description=__doc__, - epilog="For stable application integration, construct FabricConfig and use the SDK directly.", - ) - subcommands = parser.add_subparsers(dest="command", required=True) - - for name in ("plan", "doctor", "run", "chat"): - command = subcommands.add_parser(name) - _add_selector(command) - if name in {"run", "chat"}: - inputs = command.add_mutually_exclusive_group() - inputs.add_argument( - "--input", - default=None, - help="Invocation text; defaults to an empty string.", - ) - inputs.add_argument( - "--input-file", - type=Path, - help="Read invocation text from this path, or use - for stdin.", - ) - if name == "run": - command.add_argument("--request-json", help="JSON object or @path containing a RunRequest.") - command.add_argument("--output", type=Path, help="Write JSON output to this path instead of stdout.") - - preset = subcommands.add_parser("preset", help="Discover built-in presets.") - preset_commands = preset.add_subparsers(dest="preset_command", required=True) - preset_commands.add_parser("list") - preset_show = preset_commands.add_parser("show") - preset_show.add_argument("name") - - example = subcommands.add_parser("example", help="Discover installed examples.") - example_commands = example.add_subparsers(dest="example_command", required=True) - example_commands.add_parser("list") - example_show = example_commands.add_parser("show") - example_show.add_argument("name") - example_init = example_commands.add_parser("init") - example_init.add_argument("name") - example_init.add_argument("destination", type=Path) - - subcommands.add_parser("version", help="Show the installed Fabric runtime version.") - return parser - - -def _input(args: argparse.Namespace) -> str: - if args.input_file is None: - return args.input or "" - if args.input_file == Path("-"): - return sys.stdin.read() - return args.input_file.read_text(encoding="utf-8") - - -def _request(args: argparse.Namespace) -> RunRequest: - if args.request_json and (args.input is not None or args.input_file is not None): - raise SourceError("--input/--input-file and --request-json are mutually exclusive") - if not args.request_json: - return RunRequest(input=_input(args)) - raw = args.request_json - if raw.startswith("@"): - raw = Path(raw[1:]).read_text(encoding="utf-8") - try: - value = json.loads(raw) - except json.JSONDecodeError as error: - raise SourceError(f"invalid request JSON: {error}") from error - if not isinstance(value, dict): - raise SourceError("request JSON must be an object") - return RunRequest.from_mapping(value) - - -def _emit(value: Any, output: Path | None) -> None: - mapping = value.to_mapping() if hasattr(value, "to_mapping") else value - rendered = json.dumps(mapping, indent=2) - if output is None: - print(rendered) - return - output.parent.mkdir(parents=True, exist_ok=True) - output.write_text(rendered + "\n", encoding="utf-8") - - -async def _execute(args: argparse.Namespace) -> int: - selected = select_source(args) - fabric = Fabric() - if args.command == "plan": - result = fabric.plan(selected.config, base_dir=selected.base_dir) - elif args.command == "doctor": - result = await fabric.doctor(selected.config, base_dir=selected.base_dir) - elif args.command == "run": - result = await fabric.run(selected.config, base_dir=selected.base_dir, request=_request(args)) - else: - return await _chat(fabric, selected, args) - _emit(result, args.output) - return 1 if getattr(result, "status", None) in {"fail", "failed"} else 0 - - -async def _chat(fabric: Fabric, selected: SelectedSource, args: argparse.Namespace) -> int: - transcript: list[dict[str, Any]] = [] - async with await fabric.start_runtime(selected.config, base_dir=selected.base_dir) as runtime: - pending = _input(args) if args.input is not None or args.input_file is not None else None - while True: - if pending is None: - try: - pending = await asyncio.to_thread(input, "you> ") - except EOFError: - break - if pending.strip().lower() in {"/exit", "/quit"}: - break - result = await runtime.invoke(input=pending) - transcript.append(result.to_mapping()) - response = getattr(result.output, "response", None) - print(f"agent> {response if response is not None else result.status}") - pending = None - if args.output is not None: - _emit(transcript, args.output) - return 0 - - -def _discovery(args: argparse.Namespace) -> int | None: - if args.command == "version": - print(_runtime_version()) - return 0 - if args.command == "preset": - if args.preset_command == "list": - for name in sorted(PRESETS): - print(name) - else: - try: - item = PRESETS[args.name] - except KeyError as error: - raise SourceError(_unknown("preset", args.name, PRESETS)) from error - _emit(item.discovery(), None) - return 0 - if args.command == "example": - if args.example_command == "list": - for name in sorted(EXAMPLES): - print(name) - elif args.example_command == "show": - try: - item = EXAMPLES[args.name] - except KeyError as error: - raise SourceError(_unknown("example", args.name, EXAMPLES)) from error - _emit( - { - "name": item.name, - "description": item.description, - "default_variant": item.default_variant, - "variants": sorted(item.variants), - "init_supported": item.template_dir is not None, - }, - None, - ) - else: - _init_example(args.name, args.destination) - return 0 - return None - - -def _runtime_version() -> str: - try: - return importlib.metadata.version("nemo-fabric-runtime") - except importlib.metadata.PackageNotFoundError: - from nemo_fabric import _native - - return _native.version() - - -def _init_example(name: str, destination: Path) -> None: - try: - item = EXAMPLES[name] - except KeyError as error: - raise SourceError(_unknown("example", name, EXAMPLES)) from error - if item.template_dir is None: - raise SourceError(f"example {name!r} cannot be copied") - destination = destination.resolve() - if not destination.name.isidentifier(): - raise SourceError("destination name must be a valid Python package name") - if destination.exists(): - raise SourceError(f"destination already exists: {destination}") - shutil.copytree(item.template_dir, destination) - print(f"Created {name} in {destination}") - print( - f"From {destination.parent}, run: nemo-fabric run " - f"--factory {destination.name}.config:build_config " - f"--base-dir {destination} --input 'Review the workspace'" - ) - - -def cli(argv: list[str] | None = None) -> int: - """Run the CLI and return a process exit code.""" - - arguments = list(argv) if argv is not None else sys.argv[1:] - parser = build_parser() - if not arguments: - parser.print_help() - return 0 - args = parser.parse_args(arguments) - try: - discovery = _discovery(args) - return discovery if discovery is not None else asyncio.run(_execute(args)) - except KeyboardInterrupt: - print("nemo-fabric: interrupted", file=sys.stderr) - return 130 - except (FabricError, SourceError, OSError, ValueError) as error: - print(f"nemo-fabric: {error}", file=sys.stderr) - return 2 +from nemo_fabric import _native def main() -> None: - """Console-script entrypoint.""" + """Run the single Rust/Clap CLI implementation.""" - raise SystemExit(cli()) + try: + _native.cli_main(sys.argv[1:]) + except RuntimeError as error: + print(f"error: {error}", file=sys.stderr) + raise SystemExit(1) from None if __name__ == "__main__": diff --git a/python/src/nemo_fabric/client.py b/python/src/nemo_fabric/client.py index fda860f6c..1285e8729 100644 --- a/python/src/nemo_fabric/client.py +++ b/python/src/nemo_fabric/client.py @@ -27,7 +27,6 @@ ) from nemo_fabric.types import ( DoctorReport, - EffectiveConfig, RunPlan, RunResult, ) @@ -55,45 +54,6 @@ class Fabric: multi-turn examples. """ - def resolve( - self, - config: FabricConfig, - *, - base_dir: str | os.PathLike[str] | None = None, - ) -> EffectiveConfig: - """Resolve a complete typed configuration. - - Resolution validates and normalizes configuration but does not resolve - an adapter or compute runtime capabilities. Use ``plan()`` when those - execution details are required. - - Args: - config: Complete typed ``FabricConfig``. Raw mappings are not - accepted; convert them with - ``FabricConfig.from_mapping()``. - base_dir: Base directory for resolving relative paths. - - Returns: - The normalized ``EffectiveConfig`` snapshot. - - Raises: - FabricConfigError: If the config is invalid. - FabricNativeUnavailableError: If the native extension is not - installed. - """ - - native = self._require_native_module("resolve") - try: - raw = native.resolve_config( - _config_json(config), - _base_dir_arg(base_dir), - ) - return EffectiveConfig.from_mapping(json.loads(raw)) - except FabricError: - raise - except Exception as error: - raise FabricConfigError(str(error)) from error - def plan( self, config: FabricConfig, @@ -112,8 +72,8 @@ def plan( base_dir: Base directory for resolving relative paths. Returns: - A ``RunPlan`` containing the effective config, adapter, and - declared runtime capabilities. + A ``RunPlan`` containing the canonical config, path context, + adapter, and declared runtime capabilities. Raises: FabricConfigError: If the config or adapter resolution is invalid. diff --git a/python/src/nemo_fabric/factories.py b/python/src/nemo_fabric/factories.py deleted file mode 100644 index 943fac2bb..000000000 --- a/python/src/nemo_fabric/factories.py +++ /dev/null @@ -1,36 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -"""Import helpers for code-defined Fabric configurations.""" - -from __future__ import annotations - -import importlib - -from nemo_fabric.errors import FabricConfigError -from nemo_fabric.models import FabricConfig - - -def load_config_factory(spec: str) -> FabricConfig: - """Import and invoke a ``module:callable`` returning ``FabricConfig``.""" - - module_name, separator, attribute = spec.partition(":") - if not separator or not module_name or not attribute: - raise FabricConfigError("factory must use module:callable syntax") - try: - module = importlib.import_module(module_name) - except Exception as error: - raise FabricConfigError(f"could not import factory module {module_name!r}: {error}") from error - try: - factory = getattr(module, attribute) - except AttributeError as error: - raise FabricConfigError(f"factory module {module_name!r} has no attribute {attribute!r}") from error - if not callable(factory): - raise FabricConfigError(f"factory target {spec!r} is not callable") - try: - config = factory() - except Exception as error: - raise FabricConfigError(f"factory {spec!r} failed: {error}") from error - if not isinstance(config, FabricConfig): - raise FabricConfigError(f"factory {spec!r} returned {type(config).__name__}; expected FabricConfig") - return config diff --git a/python/src/nemo_fabric/presets.py b/python/src/nemo_fabric/presets.py deleted file mode 100644 index 0749bee4b..000000000 --- a/python/src/nemo_fabric/presets.py +++ /dev/null @@ -1,218 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -"""Built-in, code-defined Fabric CLI presets and examples.""" - -from __future__ import annotations - -import importlib.util -import os -from collections.abc import Callable -from dataclasses import dataclass -from pathlib import Path - -from nemo_fabric.models import EnvironmentConfig -from nemo_fabric.models import FabricConfig -from nemo_fabric.models import HarnessConfig -from nemo_fabric.models import MetadataConfig -from nemo_fabric.models import ModelConfig -from nemo_fabric.models import RuntimeConfig - -ConfigFactory = Callable[[], FabricConfig] -BUNDLED_BASE_DIR = Path(__file__).resolve().parent / "_bundled" - - -@dataclass(frozen=True) -class Preset: - """One maintained, complete Fabric configuration.""" - - name: str - description: str - factory: ConfigFactory - base_dir: Path = BUNDLED_BASE_DIR - install_extra: str | None = None - required_env: tuple[str, ...] = () - probe_module: str | None = None - - @property - def available(self) -> bool: - """Return whether the preset's adapter implementation is importable.""" - - if self.probe_module is None: - return True - try: - return importlib.util.find_spec(self.probe_module) is not None - except (ImportError, ModuleNotFoundError, ValueError): - return False - - def discovery(self) -> dict[str, object]: - """Return user-facing preset requirements without serializing config.""" - - install = "pip install nemo-fabric" - if self.install_extra is not None: - install = f"pip install 'nemo-fabric[{self.install_extra}]'" - return { - "name": self.name, - "description": self.description, - "available": self.available, - "install": install, - "required_env": list(self.required_env), - "missing_env": [name for name in self.required_env if not os.environ.get(name)], - } - - -@dataclass(frozen=True) -class Example: - """One installed example with one or more complete variants.""" - - name: str - description: str - variants: dict[str, ConfigFactory] - default_variant: str - base_dir: Path - template_dir: Path | None = None - - -def _preset( - *, - name: str, - adapter_id: str, - provider: str, - model: str, - settings: dict[str, object] | None = None, -) -> FabricConfig: - return FabricConfig( - metadata=MetadataConfig(name=f"{name}-agent", description=f"NeMo Fabric {name} CLI preset."), - harness=HarnessConfig(adapter_id=adapter_id, resolution="preinstalled", settings=settings or {}), - models={"default": ModelConfig(provider=provider, model=model)}, - environment=EnvironmentConfig(provider="local"), - ) - - -def scripted() -> FabricConfig: - """Return a deterministic credential-free experimentation preset.""" - - return FabricConfig( - metadata=MetadataConfig( - name="scripted-agent", - description="Credential-free NeMo Fabric CLI smoke preset.", - ), - harness=HarnessConfig( - adapter_id="nvidia.fabric.scripted", - resolution="preinstalled", - ), - runtime=RuntimeConfig(input_schema="text", output_schema="message"), - environment=EnvironmentConfig(provider="local"), - ) - - -def hermes() -> FabricConfig: - """Return a fresh Hermes preset.""" - - return _preset( - name="hermes", - adapter_id="nvidia.fabric.hermes", - provider="nvidia", - model="nvidia/nemotron-3-nano-30b-a3b", - settings={"base_url": "https://integrate.api.nvidia.com/v1"}, - ) - - -def claude() -> FabricConfig: - """Return a fresh Claude preset.""" - - return _preset( - name="claude", - adapter_id="nvidia.fabric.claude", - provider="anthropic", - model="anthropic/claude-sonnet-4-5", - settings={"permission_mode": "dontAsk"}, - ) - - -def codex() -> FabricConfig: - """Return a fresh Codex preset.""" - - return _preset( - name="codex", - adapter_id="nvidia.fabric.codex", - provider="openai", - model="openai/gpt-5.4", - settings={"sandbox": "workspace-write"}, - ) - - -def deepagents() -> FabricConfig: - """Return a fresh Deep Agents preset.""" - - return _preset( - name="deepagents", - adapter_id="nvidia.fabric.langchain.deepagents", - provider="nvidia", - model="nvidia/nemotron-3-nano-30b-a3b", - ) - - -def _code_review(variant: str) -> FabricConfig: - config = PRESETS[variant].factory() - config.metadata = MetadataConfig( - name="code-review-agent", - description="Reviews a small Python workspace for correctness risks.", - ) - config.environment = EnvironmentConfig(provider="local", workspace="examples/code_review_agent/repo") - config.add_skill_path("examples/code_review_agent/skills/code-review.md") - return config - - -PRESETS: dict[str, Preset] = { - "scripted": Preset( - "scripted", - "Credential-free deterministic smoke preset.", - scripted, - ), - "hermes": Preset( - "hermes", - "Hermes Agent with an NVIDIA-hosted model.", - hermes, - install_extra="hermes", - required_env=("NVIDIA_API_KEY",), - probe_module="nemo_fabric_adapters.hermes.adapter", - ), - "claude": Preset( - "claude", - "Claude Code with an Anthropic model.", - claude, - install_extra="claude", - required_env=("ANTHROPIC_API_KEY",), - probe_module="nemo_fabric_adapters.claude.adapter", - ), - "codex": Preset( - "codex", - "Codex with an OpenAI model.", - codex, - install_extra="codex", - required_env=("OPENAI_API_KEY",), - probe_module="nemo_fabric_adapters.codex.adapter", - ), - "deepagents": Preset( - "deepagents", - "LangChain Deep Agents with an NVIDIA-hosted model.", - deepagents, - install_extra="deepagents", - required_env=("NVIDIA_API_KEY",), - probe_module="nemo_fabric_adapters.deepagents.adapter", - ), -} - -EXAMPLE_VARIANTS = ("hermes", "claude", "codex", "deepagents") - -EXAMPLES: dict[str, Example] = { - "examples.code_review_agent": Example( - name="examples.code_review_agent", - description="Installed, editable-in-Python code review example.", - variants={name: (lambda name=name: _code_review(name)) for name in EXAMPLE_VARIANTS}, - default_variant="hermes", - base_dir=BUNDLED_BASE_DIR, - template_dir=BUNDLED_BASE_DIR / "examples" / "code_review_agent", - ) -} diff --git a/python/src/nemo_fabric/types.py b/python/src/nemo_fabric/types.py index 9cf47ad94..f9a4cb1e0 100644 --- a/python/src/nemo_fabric/types.py +++ b/python/src/nemo_fabric/types.py @@ -541,11 +541,11 @@ def _provider(cls, provider: str) -> str: return value -class _ResolvedFabricConfig(_ConfigMapping): - """Mutable typed representation of a Fabric agent configuration. +class _FabricConfigSnapshot(_ConfigMapping): + """Typed snapshot of the Fabric configuration stored in a run plan. - It is mutable while callers compose a job, then copied into immutable - resolution and plan snapshots. Unknown fields survive round trips through + It is reconstructed from the native plan payload and exposed through the + immutable ``RunPlan`` mapping. Unknown fields survive round trips through ``extra_fields``. Attributes: @@ -629,7 +629,7 @@ def __init__( super().__init__(values, extra_fields=extra_fields) @classmethod - def from_mapping(cls, value: Mapping[str, Any]) -> "_ResolvedFabricConfig": + def from_mapping(cls, value: Mapping[str, Any]) -> "_FabricConfigSnapshot": """Build a typed agent config from a mapping.""" data = _mapping(value, "FabricConfig") @@ -715,7 +715,7 @@ def add_mcp_server( url: str, exposure: str = "harness_native", extra_fields: Mapping[str, Any] | None = None, - ) -> "_ResolvedFabricConfig": + ) -> "_FabricConfigSnapshot": """Add or replace a named MCP server and return this config.""" self.mcp.add_server( @@ -727,13 +727,13 @@ def add_mcp_server( ) return self - def add_skill_path(self, path: str | Path) -> "_ResolvedFabricConfig": + def add_skill_path(self, path: str | Path) -> "_FabricConfigSnapshot": """Add a skill path and return this config.""" self.skills.add_path(path) return self - def block_tools(self, *tools: str) -> _ResolvedFabricConfig: + def block_tools(self, *tools: str) -> _FabricConfigSnapshot: """Block adapter-native tool names or toolsets and return this config.""" self.tools.block(*tools) @@ -747,7 +747,7 @@ def enable_relay( observability: Mapping[str, Any] | None = None, components: Sequence[Mapping[str, Any]] | None = None, policy: Mapping[str, Any] | None = None, - ) -> "_ResolvedFabricConfig": + ) -> "_FabricConfigSnapshot": """Enable NeMo Relay telemetry and return this config.""" self.telemetry.enable_relay() @@ -934,51 +934,33 @@ def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: return data -class EffectiveConfig(FabricMapping): - """Immutable result of resolving a complete typed config. - - Attributes: - agent_name: Resolved agent name. - base_dir: Base directory used to resolve relative paths. - config: Fully resolved typed ``FabricConfig``. - """ - - agent_name: str - base_dir: Path - config: _ResolvedFabricConfig - _fields = frozenset({"agent_name", "base_dir", "config"}) - - @classmethod - def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: - if "base_dir" not in data: - raise FabricConfigError("EffectiveConfig base_dir is required") - data["base_dir"] = Path(data["base_dir"]) - data["config"] = _ResolvedFabricConfig.from_mapping(data.get("config", {})) - return data - - class RunPlan(FabricMapping): """Immutable execution plan produced before a runtime is started. Attributes: - effective_config: Resolved configuration snapshot. agent_name: Resolved agent name. + base_dir: Base directory used to resolve relative paths. + config: Typed configuration snapshot. adapter: Resolved adapter identity. capabilities: Operations declared by the resolved runtime. """ - effective_config: EffectiveConfig agent_name: str + base_dir: Path + config: _FabricConfigSnapshot adapter: AdapterInfo capabilities: RuntimeCapabilities - _fields = frozenset({"effective_config", "agent_name", "adapter", "capabilities"}) + _fields = frozenset({"agent_name", "base_dir", "config", "adapter", "capabilities"}) @classmethod def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: descriptor = data.get("adapter") if descriptor is None: descriptor = (data.get("adapter_descriptor") or {}).get("descriptor", {}) - data["effective_config"] = EffectiveConfig.from_mapping(data["effective_config"]) + if "base_dir" not in data: + raise FabricConfigError("RunPlan base_dir is required") + data["base_dir"] = Path(data["base_dir"]) + data["config"] = _FabricConfigSnapshot.from_mapping(data.get("config", {})) data["adapter"] = AdapterInfo.from_mapping(descriptor) data["capabilities"] = RuntimeCapabilities.from_mapping(data.get("capabilities", {})) return data diff --git a/schemas/SCHEMA.md b/schemas/SCHEMA.md index 4856bfedf..bd534e92e 100644 --- a/schemas/SCHEMA.md +++ b/schemas/SCHEMA.md @@ -23,8 +23,8 @@ The core schema generator exports the current public typed contract. - `adapter-descriptor`: minimal adapter descriptor consumed by Fabric. Each descriptor declares a `contract_version`; Fabric rejects descriptors for unsupported adapter contracts during planning. -- `effective-config`: validated config plus its explicit base directory. -- `run-plan`: executable plan derived from effective config. +- `run-plan`: executable plan containing the canonical typed config, absolute + base directory, selected adapter, and derived execution metadata. ### Adapter Invocation diff --git a/schemas/adapter-invocation.schema.json b/schemas/adapter-invocation.schema.json index dc6992596..44162719f 100644 --- a/schemas/adapter-invocation.schema.json +++ b/schemas/adapter-invocation.schema.json @@ -95,7 +95,7 @@ "description": "Capabilities mapped into the harness-native surface." }, "routes": { - "description": "Routing decisions made while resolving the effective config.", + "description": "Routing decisions made while planning the configured capabilities.", "items": { "$ref": "#/$defs/CapabilityRoute" }, @@ -216,29 +216,6 @@ } ] }, - "EffectiveConfig": { - "description": "Resolved Fabric config with explicit path context.", - "properties": { - "agent_name": { - "description": "Stable agent name.", - "type": "string" - }, - "base_dir": { - "description": "Base directory used to resolve relative Fabric paths.", - "type": "string" - }, - "config": { - "$ref": "#/$defs/FabricConfig", - "description": "Complete typed Fabric config." - } - }, - "required": [ - "agent_name", - "base_dir", - "config" - ], - "type": "object" - }, "EnvironmentConfig": { "additionalProperties": true, "description": "Execution environment configuration.", @@ -1495,6 +1472,14 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "description": "Adapter-facing invocation payload.", "properties": { + "agent_name": { + "description": "Stable agent name.", + "type": "string" + }, + "base_dir": { + "description": "Absolute base directory used to resolve relative Fabric paths.", + "type": "string" + }, "capability_plan": { "$ref": "#/$defs/CapabilityPlan", "default": { @@ -1512,9 +1497,9 @@ }, "description": "Derived capability routing plan for the selected adapter." }, - "effective_config": { - "$ref": "#/$defs/EffectiveConfig", - "description": "Resolved typed configuration with explicit path context." + "config": { + "$ref": "#/$defs/FabricConfig", + "description": "Complete typed Fabric config." }, "request": { "$ref": "#/$defs/RunRequest", @@ -1537,7 +1522,9 @@ } }, "required": [ - "effective_config", + "agent_name", + "base_dir", + "config", "runtime_context", "request" ], diff --git a/schemas/effective-config.schema.json b/schemas/effective-config.schema.json deleted file mode 100644 index e83b16490..000000000 --- a/schemas/effective-config.schema.json +++ /dev/null @@ -1,1042 +0,0 @@ -{ - "$defs": { - "ControlLocation": { - "description": "Where Fabric control code runs relative to the environment.", - "oneOf": [ - { - "const": "external_control", - "description": "Fabric runs on the host/control plane and starts or connects to the harness in the environment.", - "type": "string" - }, - { - "const": "in_env_control", - "description": "Fabric runs inside the prepared environment with the harness.", - "type": "string" - } - ] - }, - "EnvironmentConfig": { - "additionalProperties": true, - "description": "Execution environment configuration.", - "properties": { - "artifacts": { - "description": "Artifact path inside or outside the provider.", - "type": [ - "string", - "null" - ] - }, - "connection": { - "additionalProperties": true, - "description": "Provider connection metadata, such as server URL, credential reference, or namespace.", - "type": "object" - }, - "control_location": { - "$ref": "#/$defs/ControlLocation", - "default": "in_env_control", - "description": "Where Fabric control code runs relative to the environment." - }, - "metadata": { - "additionalProperties": true, - "description": "Consumer-provided environment metadata.", - "type": "object" - }, - "ownership": { - "$ref": "#/$defs/EnvironmentOwnership", - "default": "caller_owned", - "description": "Whether Fabric owns the environment resource." - }, - "provider": { - "description": "Environment provider, for example `local`, `docker`, `opensandbox`, or `k8s`.", - "type": "string" - }, - "settings": { - "additionalProperties": true, - "description": "Provider-specific settings.", - "type": "object" - }, - "workspace": { - "description": "Workspace path inside or outside the provider.", - "type": [ - "string", - "null" - ] - } - }, - "required": [ - "provider" - ], - "type": "object" - }, - "EnvironmentOwnership": { - "description": "Whether Fabric owns the underlying environment resource.", - "oneOf": [ - { - "const": "caller_owned", - "description": "The caller or a surrounding system owns the environment resource.", - "type": "string" - }, - { - "const": "fabric_owned", - "description": "Fabric created or leased the environment resource and may release it.", - "type": "string" - } - ] - }, - "FabricConfig": { - "additionalProperties": true, - "description": "Versioned Fabric agent config.", - "properties": { - "environment": { - "anyOf": [ - { - "$ref": "#/$defs/EnvironmentConfig" - }, - { - "type": "null" - } - ], - "description": "Environment where the harness or its tools execute." - }, - "harness": { - "$ref": "#/$defs/HarnessConfig", - "description": "Harness selection and harness-specific settings." - }, - "mcp": { - "anyOf": [ - { - "$ref": "#/$defs/McpConfig" - }, - { - "type": "null" - } - ], - "description": "MCP capability configuration." - }, - "metadata": { - "$ref": "#/$defs/MetadataConfig", - "description": "Human-readable metadata." - }, - "models": { - "additionalProperties": { - "$ref": "#/$defs/ModelConfig" - }, - "description": "Model aliases.", - "type": "object" - }, - "relay": { - "anyOf": [ - { - "$ref": "#/$defs/RelayConfig" - }, - { - "type": "null" - } - ], - "description": "First-class NeMo Relay integration configuration." - }, - "runtime": { - "$ref": "#/$defs/RuntimeConfig", - "description": "Runtime input/output contract." - }, - "schema_version": { - "description": "Config schema version.", - "type": "string" - }, - "skills": { - "anyOf": [ - { - "$ref": "#/$defs/SkillConfig" - }, - { - "type": "null" - } - ], - "description": "Skill capability configuration." - }, - "telemetry": { - "anyOf": [ - { - "$ref": "#/$defs/TelemetryConfig" - }, - { - "type": "null" - } - ], - "description": "Telemetry configuration." - }, - "tools": { - "anyOf": [ - { - "$ref": "#/$defs/ToolsConfig" - }, - { - "type": "null" - } - ], - "description": "Tool capability configuration." - } - }, - "required": [ - "schema_version", - "metadata", - "harness", - "runtime" - ], - "type": "object" - }, - "HarnessConfig": { - "additionalProperties": true, - "description": "Harness selection.", - "properties": { - "adapter_id": { - "description": "Adapter implementation id.", - "type": "string" - }, - "resolution": { - "anyOf": [ - { - "$ref": "#/$defs/ResolutionStrategy" - }, - { - "type": "null" - } - ], - "description": "Selected install or availability strategy." - }, - "settings": { - "additionalProperties": true, - "description": "Harness-specific settings.", - "type": "object" - } - }, - "required": [ - "adapter_id" - ], - "type": "object" - }, - "McpConfig": { - "additionalProperties": true, - "description": "MCP capability configuration.", - "properties": { - "servers": { - "additionalProperties": { - "$ref": "#/$defs/McpServerConfig" - }, - "description": "Named MCP servers.", - "type": "object" - } - }, - "type": "object" - }, - "McpExposure": { - "description": "MCP exposure strategy.", - "oneOf": [ - { - "const": "harness_native", - "description": "Map into harness-native MCP config through the selected adapter.", - "type": "string" - }, - { - "const": "fabric_managed", - "description": "Fabric manages MCP and exposes basic tools/actions.", - "type": "string" - } - ] - }, - "McpServerConfig": { - "additionalProperties": true, - "description": "MCP server configuration.", - "properties": { - "exposure": { - "$ref": "#/$defs/McpExposure", - "description": "How Fabric exposes the MCP capability to the harness." - }, - "transport": { - "description": "MCP transport.", - "type": "string" - }, - "url": { - "description": "MCP server URL or process command, depending on transport.", - "type": "string" - } - }, - "required": [ - "transport", - "url", - "exposure" - ], - "type": "object" - }, - "MetadataConfig": { - "additionalProperties": true, - "description": "Human-readable metadata.", - "properties": { - "description": { - "description": "Optional description.", - "type": [ - "string", - "null" - ] - }, - "name": { - "description": "Agent/config name.", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "ModelConfig": { - "additionalProperties": true, - "description": "Model configuration.", - "properties": { - "api_key_env": { - "description": "Optional environment variable containing an API key.", - "type": [ - "string", - "null" - ] - }, - "model": { - "description": "Provider model identifier.", - "type": "string" - }, - "provider": { - "description": "Model provider name.", - "type": "string" - }, - "settings": { - "additionalProperties": true, - "description": "Provider-specific settings.", - "type": "object" - }, - "temperature": { - "description": "Optional temperature.", - "format": "double", - "type": [ - "number", - "null" - ] - } - }, - "required": [ - "provider", - "model" - ], - "type": "object" - }, - "RelayAtifConfig": { - "additionalProperties": true, - "description": "Relay ATIF export configuration.", - "properties": { - "agent_name": { - "default": "NeMo Relay", - "description": "Agent name written into ATIF.", - "type": "string" - }, - "agent_version": { - "description": "Agent version written into ATIF.", - "type": [ - "string", - "null" - ] - }, - "enabled": { - "default": false, - "description": "Whether ATIF export is enabled.", - "type": "boolean" - }, - "extra": { - "description": "Extra ATIF metadata." - }, - "filename_template": { - "default": "nemo-relay-atif-{session_id}.json", - "description": "ATIF file name template.", - "type": "string" - }, - "model_name": { - "default": "unknown", - "description": "Model name written into ATIF.", - "type": "string" - }, - "output_directory": { - "description": "Directory used for ATIF files.", - "type": [ - "string", - "null" - ] - }, - "storage": { - "description": "Optional ATIF remote storage.", - "items": { - "$ref": "#/$defs/RelayAtifStorageConfig" - }, - "type": [ - "array", - "null" - ] - }, - "tool_definitions": { - "description": "Tool definitions written into ATIF.", - "items": true, - "type": [ - "array", - "null" - ] - } - }, - "type": "object" - }, - "RelayAtifStorageConfig": { - "description": "Relay ATIF remote storage configuration.", - "oneOf": [ - { - "additionalProperties": true, - "description": "Upload ATIF artifacts to HTTP storage.", - "properties": { - "endpoint": { - "default": "", - "description": "HTTP storage endpoint.", - "type": "string" - }, - "header_env": { - "additionalProperties": { - "type": "string" - }, - "description": "Environment-variable-backed HTTP headers.", - "type": "object" - }, - "headers": { - "additionalProperties": { - "type": "string" - }, - "description": "Static HTTP headers.", - "type": "object" - }, - "timeout_millis": { - "default": 3000, - "description": "Request timeout in milliseconds.", - "format": "uint64", - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "http", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "additionalProperties": true, - "description": "Upload ATIF artifacts to S3-compatible storage.", - "properties": { - "access_key_id": { - "description": "AWS access key id.", - "type": [ - "string", - "null" - ] - }, - "allow_http": { - "description": "Allow HTTP endpoints for S3-compatible storage.", - "type": [ - "boolean", - "null" - ] - }, - "bucket": { - "default": "", - "description": "S3 bucket name.", - "type": "string" - }, - "endpoint_url": { - "description": "S3-compatible endpoint URL.", - "type": [ - "string", - "null" - ] - }, - "key_prefix": { - "description": "Optional S3 object key prefix.", - "type": [ - "string", - "null" - ] - }, - "region": { - "description": "AWS region.", - "type": [ - "string", - "null" - ] - }, - "secret_access_key_var": { - "description": "Environment variable containing the AWS secret access key.", - "type": [ - "string", - "null" - ] - }, - "session_token_var": { - "description": "Environment variable containing the AWS session token.", - "type": [ - "string", - "null" - ] - }, - "type": { - "const": "s3", - "type": "string" - } - }, - "required": [ - "type" - ], - "type": "object" - } - ] - }, - "RelayAtofConfig": { - "additionalProperties": true, - "description": "Relay ATOF export configuration.", - "properties": { - "enabled": { - "default": false, - "description": "Whether ATOF export is enabled.", - "type": "boolean" - }, - "endpoints": { - "description": "Optional remote ATOF endpoints.", - "items": { - "$ref": "#/$defs/RelayAtofEndpointConfig" - }, - "type": "array" - }, - "filename": { - "description": "ATOF file name.", - "type": [ - "string", - "null" - ] - }, - "mode": { - "$ref": "#/$defs/RelayAtofMode", - "default": "append", - "description": "File write mode." - }, - "output_directory": { - "description": "Directory used for ATOF files.", - "type": [ - "string", - "null" - ] - } - }, - "type": "object" - }, - "RelayAtofEndpointConfig": { - "additionalProperties": true, - "description": "Relay ATOF endpoint configuration.", - "properties": { - "field_name_policy": { - "$ref": "#/$defs/RelayAtofEndpointFieldNamePolicy", - "default": "preserve", - "description": "Field-name handling policy." - }, - "headers": { - "additionalProperties": { - "type": "string" - }, - "description": "Endpoint headers.", - "type": "object" - }, - "timeout_millis": { - "default": 3000, - "description": "Request timeout in milliseconds.", - "format": "uint64", - "minimum": 0, - "type": "integer" - }, - "transport": { - "$ref": "#/$defs/RelayAtofEndpointTransport", - "default": "http_post", - "description": "Endpoint transport." - }, - "url": { - "description": "Endpoint URL.", - "type": "string" - } - }, - "required": [ - "url" - ], - "type": "object" - }, - "RelayAtofEndpointFieldNamePolicy": { - "description": "Relay ATOF endpoint field-name policy.", - "oneOf": [ - { - "const": "preserve", - "description": "Preserve field names.", - "type": "string" - }, - { - "const": "replace_dots", - "description": "Replace dots in field names.", - "type": "string" - } - ] - }, - "RelayAtofEndpointTransport": { - "description": "Relay ATOF endpoint transport.", - "oneOf": [ - { - "const": "http_post", - "description": "HTTP POST transport.", - "type": "string" - }, - { - "const": "websocket", - "description": "WebSocket transport.", - "type": "string" - }, - { - "const": "ndjson", - "description": "NDJSON transport.", - "type": "string" - } - ] - }, - "RelayAtofMode": { - "description": "Relay ATOF file mode.", - "oneOf": [ - { - "const": "append", - "description": "Append to an existing ATOF file.", - "type": "string" - }, - { - "const": "overwrite", - "description": "Overwrite an existing ATOF file.", - "type": "string" - } - ] - }, - "RelayComponentConfig": { - "additionalProperties": true, - "description": "Generic NeMo Relay plugin component configuration.", - "properties": { - "config": { - "additionalProperties": true, - "description": "Component-local Relay plugin config.", - "type": "object" - }, - "enabled": { - "default": true, - "description": "Whether this Relay component should be activated.", - "type": "boolean" - }, - "kind": { - "description": "Registered Relay plugin kind.", - "type": "string" - } - }, - "required": [ - "kind" - ], - "type": "object" - }, - "RelayConfig": { - "additionalProperties": true, - "description": "NeMo Relay integration configuration.", - "properties": { - "components": { - "description": "Additional Relay plugin components.", - "items": { - "$ref": "#/$defs/RelayComponentConfig" - }, - "type": "array" - }, - "observability": { - "anyOf": [ - { - "$ref": "#/$defs/RelayObservabilityConfig" - }, - { - "type": "null" - } - ], - "description": "Relay observability component configuration." - }, - "output_dir": { - "description": "Optional Relay output directory.", - "type": [ - "string", - "null" - ] - }, - "policy": { - "anyOf": [ - { - "$ref": "#/$defs/RelayConfigPolicy" - }, - { - "type": "null" - } - ], - "description": "Relay plugin validation policy." - }, - "project": { - "description": "Optional project name for Relay backends.", - "type": [ - "string", - "null" - ] - } - }, - "type": "object" - }, - "RelayConfigPolicy": { - "description": "Relay validation policy.", - "properties": { - "unknown_component": { - "$ref": "#/$defs/RelayUnsupportedBehavior", - "default": "warn", - "description": "Policy for unknown components." - }, - "unknown_field": { - "$ref": "#/$defs/RelayUnsupportedBehavior", - "default": "warn", - "description": "Policy for unknown fields." - }, - "unsupported_value": { - "$ref": "#/$defs/RelayUnsupportedBehavior", - "default": "error", - "description": "Policy for unsupported values." - } - }, - "type": "object" - }, - "RelayObservabilityConfig": { - "additionalProperties": true, - "description": "NeMo Relay observability component configuration.", - "properties": { - "atif": { - "anyOf": [ - { - "$ref": "#/$defs/RelayAtifConfig" - }, - { - "type": "null" - } - ], - "description": "ATIF export configuration." - }, - "atof": { - "anyOf": [ - { - "$ref": "#/$defs/RelayAtofConfig" - }, - { - "type": "null" - } - ], - "description": "ATOF export configuration." - }, - "openinference": { - "anyOf": [ - { - "$ref": "#/$defs/RelayOtlpConfig" - }, - { - "type": "null" - } - ], - "description": "OpenInference export configuration." - }, - "opentelemetry": { - "anyOf": [ - { - "$ref": "#/$defs/RelayOtlpConfig" - }, - { - "type": "null" - } - ], - "description": "OpenTelemetry export configuration." - }, - "policy": { - "anyOf": [ - { - "$ref": "#/$defs/RelayConfigPolicy" - }, - { - "type": "null" - } - ], - "description": "Relay config validation policy." - }, - "version": { - "default": 1, - "description": "Relay observability config version.", - "format": "uint32", - "minimum": 0, - "type": "integer" - } - }, - "type": "object" - }, - "RelayOtlpConfig": { - "additionalProperties": true, - "description": "Relay OpenTelemetry/OpenInference export configuration.", - "properties": { - "enabled": { - "default": false, - "description": "Whether OTLP export is enabled.", - "type": "boolean" - }, - "endpoint": { - "description": "OTLP endpoint.", - "type": [ - "string", - "null" - ] - }, - "headers": { - "additionalProperties": { - "type": "string" - }, - "description": "OTLP headers.", - "type": "object" - }, - "instrumentation_scope": { - "description": "OTLP instrumentation scope.", - "type": [ - "string", - "null" - ] - }, - "resource_attributes": { - "additionalProperties": { - "type": "string" - }, - "description": "OTLP resource attributes.", - "type": "object" - }, - "service_name": { - "default": "nemo-relay", - "description": "OTLP service name.", - "type": "string" - }, - "service_namespace": { - "description": "OTLP service namespace.", - "type": [ - "string", - "null" - ] - }, - "service_version": { - "description": "OTLP service version.", - "type": [ - "string", - "null" - ] - }, - "timeout_millis": { - "default": 3000, - "description": "Request timeout in milliseconds.", - "format": "uint64", - "minimum": 0, - "type": "integer" - }, - "transport": { - "$ref": "#/$defs/RelayOtlpTransport", - "default": "http_binary", - "description": "OTLP transport." - } - }, - "type": "object" - }, - "RelayOtlpTransport": { - "description": "Relay OTLP transport.", - "oneOf": [ - { - "const": "http_binary", - "description": "OTLP HTTP binary transport.", - "type": "string" - }, - { - "const": "grpc", - "description": "OTLP gRPC transport.", - "type": "string" - } - ] - }, - "RelayUnsupportedBehavior": { - "description": "Relay unsupported/unknown config handling.", - "oneOf": [ - { - "const": "ignore", - "description": "Ignore the unsupported or unknown value.", - "type": "string" - }, - { - "const": "warn", - "description": "Warn on the unsupported or unknown value.", - "type": "string" - }, - { - "const": "error", - "description": "Error on the unsupported or unknown value.", - "type": "string" - } - ] - }, - "ResolutionStrategy": { - "description": "Adapter install or availability strategy.", - "oneOf": [ - { - "const": "preinstalled", - "description": "Harness is already available in the prepared environment.", - "type": "string" - }, - { - "const": "image_provided", - "description": "Environment image already contains the harness and dependencies.", - "type": "string" - }, - { - "const": "pip_uv", - "description": "Adapter may install a Python package with pip or uv.", - "type": "string" - }, - { - "const": "npm", - "description": "Adapter may install a Node package.", - "type": "string" - }, - { - "const": "source", - "description": "Adapter may install from source.", - "type": "string" - }, - { - "const": "service", - "description": "Adapter connects to an already-running service.", - "type": "string" - }, - { - "const": "native_plugin", - "description": "Adapter is installed through a harness-native plugin manager.", - "type": "string" - } - ] - }, - "RuntimeConfig": { - "additionalProperties": true, - "description": "Runtime input/output contract.", - "properties": { - "artifacts": { - "description": "Artifact directory.", - "type": [ - "string", - "null" - ] - }, - "input_schema": { - "default": "text", - "description": "Input schema label.", - "type": "string" - }, - "output_schema": { - "default": "text", - "description": "Output schema label.", - "type": "string" - } - }, - "type": "object" - }, - "SkillConfig": { - "additionalProperties": true, - "description": "Skill capability configuration.", - "properties": { - "paths": { - "description": "Skill paths resolved relative to the config base directory.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, - "TelemetryConfig": { - "additionalProperties": true, - "description": "Telemetry configuration.", - "properties": { - "providers": { - "additionalProperties": { - "$ref": "#/$defs/TelemetryProviderConfig" - }, - "description": "Telemetry providers enabled for this run.", - "type": "object" - } - }, - "type": "object" - }, - "TelemetryProviderConfig": { - "additionalProperties": true, - "description": "Provider-specific telemetry configuration.", - "properties": { - "config": { - "description": "Provider-specific pass-through config." - } - }, - "type": "object" - }, - "ToolsConfig": { - "additionalProperties": true, - "description": "Harness-neutral tool capability configuration.", - "properties": { - "blocked": { - "description": "Adapter-native tool names or toolset names to block.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - } - }, - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "Resolved Fabric config with explicit path context.", - "properties": { - "agent_name": { - "description": "Stable agent name.", - "type": "string" - }, - "base_dir": { - "description": "Base directory used to resolve relative Fabric paths.", - "type": "string" - }, - "config": { - "$ref": "#/$defs/FabricConfig", - "description": "Complete typed Fabric config." - } - }, - "required": [ - "agent_name", - "base_dir", - "config" - ], - "title": "EffectiveConfig", - "type": "object" -} \ No newline at end of file diff --git a/schemas/run-plan.schema.json b/schemas/run-plan.schema.json index f28ee2bc8..110290d0f 100644 --- a/schemas/run-plan.schema.json +++ b/schemas/run-plan.schema.json @@ -245,7 +245,7 @@ "description": "Capabilities mapped into the harness-native surface." }, "routes": { - "description": "Routing decisions made while resolving the effective config.", + "description": "Routing decisions made while planning the configured capabilities.", "items": { "$ref": "#/$defs/CapabilityRoute" }, @@ -366,29 +366,6 @@ } ] }, - "EffectiveConfig": { - "description": "Resolved Fabric config with explicit path context.", - "properties": { - "agent_name": { - "description": "Stable agent name.", - "type": "string" - }, - "base_dir": { - "description": "Base directory used to resolve relative Fabric paths.", - "type": "string" - }, - "config": { - "$ref": "#/$defs/FabricConfig", - "description": "Complete typed Fabric config." - } - }, - "required": [ - "agent_name", - "base_dir", - "config" - ], - "type": "object" - }, "EnvironmentConfig": { "additionalProperties": true, "description": "Execution environment configuration.", @@ -1647,10 +1624,6 @@ "$ref": "#/$defs/FabricConfig", "description": "Complete typed Fabric config." }, - "effective_config": { - "$ref": "#/$defs/EffectiveConfig", - "description": "Resolved typed configuration and path context used by this plan." - }, "environment_plan": { "anyOf": [ { @@ -1686,11 +1659,10 @@ } }, "required": [ - "effective_config", "agent_name", - "capabilities", "base_dir", - "config" + "config", + "capabilities" ], "title": "RunPlan", "type": "object" diff --git a/skills/nemo-fabric-integrate/SKILL.md b/skills/nemo-fabric-integrate/SKILL.md index a85cd088a..110d5a21c 100644 --- a/skills/nemo-fabric-integrate/SKILL.md +++ b/skills/nemo-fabric-integrate/SKILL.md @@ -175,8 +175,6 @@ print(plan.adapter.adapter_id, report.status) contents of `harness.settings`: an unknown or misspelled adapter setting still passes and is silently ignored unless the adapter reads it, so validate settings against the adapter's own docs and your integration tests. -- Use `resolve(...)` when only the normalized effective config is needed, with - no adapter resolution. ## Consume Results And Handle Errors diff --git a/skills/nemo-fabric-integrate/references/sdk-api-inventory.md b/skills/nemo-fabric-integrate/references/sdk-api-inventory.md index ef9498b67..99f6b1ad2 100644 --- a/skills/nemo-fabric-integrate/references/sdk-api-inventory.md +++ b/skills/nemo-fabric-integrate/references/sdk-api-inventory.md @@ -20,7 +20,6 @@ The following table lists the `Fabric` methods and when to use each. | Method | Async | Use When | Returns | | --- | --- | --- | --- | -| `resolve(config, *, base_dir=...)` | No | You need the normalized effective config, without resolving an adapter. | `EffectiveConfig` | | `plan(config, *, base_dir=...)` | No | You need the selected adapter, capability routing, and runtime capabilities before running. | `RunPlan` | | `doctor(config, *, base_dir=...)` | Yes | You need preflight diagnostics for adapter availability, config support, and environment assumptions. | `DoctorReport` | | `run(config, *, base_dir=..., input=... \| request=...)` | Yes | You need one complete start, invoke, result, and stop cycle. | `RunResult` | diff --git a/tests/_utils/utils.py b/tests/_utils/utils.py index f0c05848f..6b3c10ec0 100644 --- a/tests/_utils/utils.py +++ b/tests/_utils/utils.py @@ -2,17 +2,10 @@ # SPDX-License-Identifier: Apache-2.0 import json -import os -import subprocess -import sys from collections.abc import Mapping from pathlib import Path from typing import Any -REPO_ROOT = Path(__file__).resolve().parents[2] -FABRIC_COMMAND = (sys.executable, "-m", "nemo_fabric.cli") - - def _relay_event_total_tokens(event: dict) -> int: profile = event.get("category_profile") or {} annotated = profile.get("annotated_response") or {} @@ -64,31 +57,6 @@ def assert_semantic_relay_artifacts( assert any( expected_response.lower() in message.lower() for message in agent_messages ), agent_messages - - -def run_fabric_cli( - *args: object, - stdin: str | None = None, - timeout: float = 60.0, -) -> subprocess.CompletedProcess[str]: - """Run the Fabric CLI from the repository root and capture its output.""" - env = os.environ.copy() - python_path = [str(REPO_ROOT / "python" / "src"), str(REPO_ROOT / "tests")] - if env.get("PYTHONPATH"): - python_path.append(env["PYTHONPATH"]) - env["PYTHONPATH"] = os.pathsep.join(python_path) - return subprocess.run( - [*FABRIC_COMMAND, *(str(arg) for arg in args)], - cwd=REPO_ROOT, - env=env, - input=stdin, - text=True, - capture_output=True, - check=False, - timeout=timeout, - ) - - def assert_relay_disabled_native_observability(result: dict): """Assert telemetry-off runs still surface native harness evidence.""" diff --git a/tests/adapters/test_adapaters_common_utils.py b/tests/adapters/test_adapaters_common_utils.py index 42b7d0831..e24afb614 100644 --- a/tests/adapters/test_adapaters_common_utils.py +++ b/tests/adapters/test_adapaters_common_utils.py @@ -140,18 +140,16 @@ def test_selected_model_config( if selected_model is not None: settings["model"] = selected_model payload = { - "effective_config": { - "config": { - "harness": {"settings": settings}, - "models": models, - } + "config": { + "harness": {"settings": settings}, + "models": models, } } assert common_utils.selected_model_config(payload) == expected -def test_payload_accessors_prefer_effective_config(): +def test_payload_accessors_use_canonical_plan_fields(): payload = { "agent_name": "outer-agent", "base_dir": "/outer", @@ -163,21 +161,16 @@ def test_payload_accessors_prefer_effective_config(): "runtime_context": { "environment": {"workspace": "/runtime-workspace"}, }, - "effective_config": { - "agent_name": "effective-agent", - "base_dir": "/effective", - "config": { - "harness": {"settings": {"inner": True}}, - "models": {"inner": {"model": "inner-model"}}, - }, + "config": { + "harness": {"settings": {"inner": True}}, + "models": {"inner": {"model": "inner-model"}}, }, "capability_plan": {"native": {"skill_paths": ["skills"]}}, } - assert common_utils.effective_config(payload) == payload["effective_config"] - assert common_utils.fabric_config(payload) == payload["effective_config"]["config"] - assert common_utils.agent_name(payload) == "effective-agent" - assert common_utils.base_dir(payload) == "/effective" + assert common_utils.fabric_config(payload) == payload["config"] + assert common_utils.agent_name(payload) == "outer-agent" + assert common_utils.base_dir(payload) == "/outer" assert common_utils.runtime_context(payload) == payload["runtime_context"] assert common_utils.environment_payload(payload) == {"workspace": "/runtime-workspace"} assert common_utils.settings_payload(payload) == {"inner": True} @@ -305,13 +298,11 @@ def test_load_relay_plugin_config_wraps_and_normalizes_bare_observability_config "{}", encoding="utf-8" ) payload = { - "effective_config": { - "agent_name": "review-agent", - "base_dir": str(tmp_path), - "config": { - "harness": {"settings": {"model": "review"}}, - "models": {"review": {"model": "nvidia/review-model"}}, - }, + "agent_name": "review-agent", + "base_dir": str(tmp_path), + "config": { + "harness": {"settings": {"model": "review"}}, + "models": {"review": {"model": "nvidia/review-model"}}, }, "runtime_context": {"runtime_id": "runtime-current"}, } diff --git a/tests/adapters/test_claude_adapter.py b/tests/adapters/test_claude_adapter.py index 96d10f1d9..a2e231e93 100644 --- a/tests/adapters/test_claude_adapter.py +++ b/tests/adapters/test_claude_adapter.py @@ -81,10 +81,9 @@ def claude_payload_fixture(tmp_path) -> dict[str, Any]: skill_path.mkdir(parents=True) (skill_path / "SKILL.md").write_text("# Review\n", encoding="utf-8") return { - "effective_config": { - "agent_name": "claude-test", - "base_dir": str(tmp_path), - "config": { + "agent_name": "claude-test", + "base_dir": str(tmp_path), + "config": { "harness": { "adapter_id": "nvidia.fabric.claude", "settings": { @@ -106,7 +105,6 @@ def claude_payload_fixture(tmp_path) -> dict[str, Any]: } }, "tools": {"blocked": ["Bash"]}, - }, }, "runtime_context": { "runtime_id": "runtime-claude-1", @@ -332,7 +330,7 @@ def test_build_options_does_not_enable_skills_for_relay_plugin_alone( def test_build_options_maps_blocked_tools_to_disallowed_tools(claude_payload): - claude_payload["effective_config"]["config"]["tools"] = { + claude_payload["config"]["tools"] = { "blocked": ["Bash", "WebFetch"] } @@ -356,7 +354,7 @@ def test_build_options_maps_blocked_tools_to_disallowed_tools(claude_payload): def test_build_options_rejects_normalized_capabilities_in_harness_settings( claude_payload, name, normalized_field ): - claude_payload["effective_config"]["config"]["harness"]["settings"][name] = [] + claude_payload["config"]["harness"]["settings"][name] = [] with pytest.raises( adapter.AdapterConfigError, match=normalized_field.replace(".", r"\.") @@ -373,7 +371,7 @@ def test_build_options_rejects_skill_path_without_skill_manifest(claude_payload) def test_selected_model_rejects_unsupported_provider(claude_payload): - model = claude_payload["effective_config"]["config"]["models"]["default"] + model = claude_payload["config"]["models"]["default"] model["provider"] = "nvidia" with pytest.raises(adapter.AdapterConfigError, match="provider must be anthropic"): @@ -842,9 +840,9 @@ def test_run_reports_relay_start_failure_without_raw_diagnostic( def test_build_options_forwards_anthropic_auth_environment( claude_payload, auth_environment ): - model = claude_payload["effective_config"]["config"]["models"]["default"] + model = claude_payload["config"]["models"]["default"] model.pop("api_key_env") - settings = claude_payload["effective_config"]["config"]["harness"]["settings"] + settings = claude_payload["config"]["harness"]["settings"] settings.pop("env") for name in ANTHROPIC_AUTH_ENV_NAMES: os.environ.pop(name, None) diff --git a/tests/adapters/test_codex_adapter.py b/tests/adapters/test_codex_adapter.py index 118a9f1c7..1e70af43e 100644 --- a/tests/adapters/test_codex_adapter.py +++ b/tests/adapters/test_codex_adapter.py @@ -20,10 +20,9 @@ def codex_payload_fixture(tmp_path): workspace = tmp_path / "workspace" workspace.mkdir() return { - "effective_config": { - "agent_name": "codex-test", - "base_dir": str(tmp_path), - "config": { + "agent_name": "codex-test", + "base_dir": str(tmp_path), + "config": { "harness": { "adapter_id": "nvidia.fabric.codex", "settings": { @@ -41,7 +40,6 @@ def codex_payload_fixture(tmp_path): } }, "runtime": {}, - }, }, "runtime_context": { "runtime_id": "runtime-1", @@ -149,7 +147,7 @@ def test_sdk_oneshot_uses_native_thread_and_turn_contract( os.environ["CODEX_HOME"] = str(tmp_path / "codex-home") os.environ["CODEX_INTERNAL_ORIGINATOR_OVERRIDE"] = "parent-codex" os.environ["FABRIC_UNRELATED_SECRET"] = "do-not-forward" - codex_payload["effective_config"]["config"]["harness"]["settings"]["env"] = { + codex_payload["config"]["harness"]["settings"]["env"] = { "CODEX_EXPLICIT": "forward-me" } @@ -196,7 +194,7 @@ def test_sdk_can_use_an_explicit_codex_runtime(codex_payload, mock_codex, tmp_pa codex_bin = tmp_path / "bin" / "codex" codex_bin.parent.mkdir() codex_bin.touch() - codex_payload["effective_config"]["config"]["harness"]["settings"][ + codex_payload["config"]["harness"]["settings"][ "codex_bin" ] = str(codex_bin) @@ -210,19 +208,19 @@ def test_sdk_can_use_an_explicit_codex_runtime(codex_payload, mock_codex, tmp_pa def test_sdk_resolves_relative_codex_runtime_from_base_dir( codex_payload, codex_bin ): - codex_payload["effective_config"]["config"]["harness"]["settings"][ + codex_payload["config"]["harness"]["settings"][ "codex_bin" ] = codex_bin config = adapter.sdk_config(codex_payload, relay=None) - base_dir = Path(codex_payload["effective_config"]["base_dir"]) + base_dir = Path(codex_payload["base_dir"]) assert config.codex_bin == str((base_dir / codex_bin).resolve()) def test_sdk_keeps_absolute_codex_runtime_path(codex_payload, tmp_path): codex_bin = tmp_path / "bin" / ".." / "codex" - codex_payload["effective_config"]["config"]["harness"]["settings"][ + codex_payload["config"]["harness"]["settings"][ "codex_bin" ] = str(codex_bin) @@ -294,7 +292,7 @@ def test_incomplete_sdk_turn_is_failed_without_persisting_thread( def test_selected_model_rejects_unsupported_provider(codex_payload, mock_codex): - model = codex_payload["effective_config"]["config"]["models"]["default"] + model = codex_payload["config"]["models"]["default"] model["provider"] = "nvidia" output = adapter.run(codex_payload) @@ -415,7 +413,7 @@ def test_prepare_relay_reuses_one_resolved_executable( assert relay.gateway.executable == executable assert relay.gateway.url == "http://127.0.0.1:43210" resolve.assert_called_once_with( - Path(codex_payload["effective_config"]["base_dir"]).resolve(), + Path(codex_payload["base_dir"]).resolve(), "nemo-relay", ) contract.assert_called_once_with(executable) @@ -462,7 +460,7 @@ def test_relay_cleanup_failure_changes_success_to_failure( def test_native_sdk_controls_and_telemetry_are_request_scoped( codex_payload, mock_codex ): - settings = codex_payload["effective_config"]["config"]["harness"]["settings"] + settings = codex_payload["config"]["harness"]["settings"] settings.update( { "personality": "pragmatic", @@ -529,7 +527,7 @@ async def block(): mock_blocking_thread.handle.run.side_effect = block mock_codex.next_thread = mock_blocking_thread - codex_payload["effective_config"]["config"]["harness"]["settings"][ + codex_payload["config"]["harness"]["settings"][ "timeout_seconds" ] = 0.01 @@ -545,7 +543,7 @@ async def block(): "setting", ["codex_command", "codex_args", "codex_profile", "skip_git_repo_check"] ) def test_cli_only_settings_are_rejected(codex_payload, setting): - codex_payload["effective_config"]["config"]["harness"]["settings"][setting] = ( + codex_payload["config"]["harness"]["settings"][setting] = ( "legacy" ) @@ -587,8 +585,8 @@ def test_codex_config_resolves_sdk_adapter(): assert plan.adapter.adapter_id == "nvidia.fabric.codex" assert plan.adapter.harness == "codex" - assert plan.effective_config.config.runtime.input_schema == "text" - assert plan.effective_config.config.harness.settings["reasoning_effort"] == "high" + assert plan.config.runtime.input_schema == "text" + assert plan.config.harness.settings["reasoning_effort"] == "high" unsupported = plan["capability_plan"]["unsupported"] assert not unsupported.get("skill_paths") assert not unsupported.get("mcp_servers") @@ -611,7 +609,7 @@ def test_environment_preserves_runtime_telemetry_env(codex_payload): "CODEX_EXPLICIT": "telemetry", } } - codex_payload["effective_config"]["config"]["harness"]["settings"]["env"] = { + codex_payload["config"]["harness"]["settings"]["env"] = { "CODEX_EXPLICIT": "configured" } os.environ["FABRIC_RELAY_CONFIG_PATH"] = "/tmp/parent-relay.json" diff --git a/tests/adapters/test_deepagents.py b/tests/adapters/test_deepagents.py index 98bd125ff..bffa8c10a 100644 --- a/tests/adapters/test_deepagents.py +++ b/tests/adapters/test_deepagents.py @@ -127,9 +127,8 @@ def make_payload_fixture(): def make(tmp_path: Path, *, runtime_id: str = "run-1") -> dict[str, Any]: return { - "effective_config": { - "base_dir": str(tmp_path), - "config": { + "base_dir": str(tmp_path), + "config": { "harness": {"settings": {"system_prompt": "be concise"}}, "models": { "default": { @@ -138,7 +137,6 @@ def make(tmp_path: Path, *, runtime_id: str = "run-1") -> dict[str, Any]: "api_key_env": "NVIDIA_API_KEY", } }, - }, }, "runtime_context": { "runtime_id": runtime_id, @@ -608,7 +606,7 @@ def build(**kwargs): async def test_openai_provider_keeps_openai_endpoint(tmp_path, make_payload, monkeypatch, fake_sdks): monkeypatch.setenv("OPENAI_API_KEY", "sk-test") payload = make_payload(tmp_path) - payload["effective_config"]["config"]["models"]["default"] = { + payload["config"]["models"]["default"] = { "provider": "openai", "model": "gpt-4o", "api_key_env": "OPENAI_API_KEY", @@ -722,12 +720,12 @@ async def test_subagents_are_gated_by_blocked_tools(tmp_path, make_payload): from langchain_core.messages import ToolMessage payload = make_payload(tmp_path) - payload["effective_config"]["config"]["tools"] = {"blocked": ["write_file"]} - payload["effective_config"]["config"]["harness"]["settings"]["deepagents"] = { + payload["config"]["tools"] = {"blocked": ["write_file"]} + payload["config"]["harness"]["settings"]["deepagents"] = { "subagents": [{"name": "researcher", "prompt": "research"}] } - settings = payload["effective_config"]["config"]["harness"]["settings"] + settings = payload["config"]["harness"]["settings"] create_kwargs = await adapter.build_agent_kwargs(payload, MagicMock(), settings) assert create_kwargs["middleware"], "main agent blocked-tools middleware not attached" subagents = create_kwargs["subagents"] @@ -753,9 +751,9 @@ def request(name: str) -> types.SimpleNamespace: @pytest.mark.usefixtures("use_real_langgraph") async def test_default_subagent_is_gated_by_blocked_tools(tmp_path, make_payload): payload = make_payload(tmp_path) - payload["effective_config"]["config"]["tools"] = {"blocked": ["write_file"]} + payload["config"]["tools"] = {"blocked": ["write_file"]} - settings = payload["effective_config"]["config"]["harness"]["settings"] + settings = payload["config"]["harness"]["settings"] create_kwargs = await adapter.build_agent_kwargs(payload, MagicMock(), settings) assert [subagent["name"] for subagent in create_kwargs["subagents"]] == ["general-purpose"] @@ -765,12 +763,12 @@ async def test_default_subagent_is_gated_by_blocked_tools(tmp_path, make_payload @pytest.mark.parametrize("unsupported", [{"graph_id": "remote"}, {"runnable": "compiled"}]) async def test_blocked_tools_reject_unenforceable_subagents(tmp_path, make_payload, unsupported): payload = make_payload(tmp_path) - payload["effective_config"]["config"]["tools"] = {"blocked": ["write_file"]} - payload["effective_config"]["config"]["harness"]["settings"]["deepagents"] = { + payload["config"]["tools"] = {"blocked": ["write_file"]} + payload["config"]["harness"]["settings"]["deepagents"] = { "subagents": [{"name": "worker", **unsupported}] } - settings = payload["effective_config"]["config"]["harness"]["settings"] + settings = payload["config"]["harness"]["settings"] with pytest.raises(adapter.AdapterConfigError, match="cannot be enforced"): await adapter.build_agent_kwargs(payload, MagicMock(), settings) @@ -798,7 +796,7 @@ def test_gated_subagents_reject_invalid_configuration(subagents, message): async def test_deepagents_passthrough_forwards_supported_options(tmp_path, make_payload, fake_sdks): # Documented JSON-serializable options reach create_deep_agent unchanged. payload = make_payload(tmp_path) - payload["effective_config"]["config"]["harness"]["settings"]["deepagents"] = {"interrupt_on": {"write_file": True}} + payload["config"]["harness"]["settings"]["deepagents"] = {"interrupt_on": {"write_file": True}} await adapter.run_deepagents(payload) @@ -809,7 +807,7 @@ async def test_deepagents_passthrough_cannot_override_fabric_owned_keys(tmp_path # Overriding a Fabric-owned key (here backend) would defeat workspace confinement; # it must fail loudly rather than silently replacing the derived value. payload = make_payload(tmp_path) - payload["effective_config"]["config"]["harness"]["settings"]["deepagents"] = {"backend": {"root_dir": "/etc"}} + payload["config"]["harness"]["settings"]["deepagents"] = {"backend": {"root_dir": "/etc"}} output = await adapter.run_deepagents(payload) @@ -820,7 +818,7 @@ async def test_deepagents_passthrough_cannot_override_fabric_owned_keys(tmp_path async def test_deepagents_passthrough_rejects_unknown_option(tmp_path, make_payload): # A typo or unsupported option must fail clearly instead of being silently dropped. payload = make_payload(tmp_path) - payload["effective_config"]["config"]["harness"]["settings"]["deepagents"] = { + payload["config"]["harness"]["settings"]["deepagents"] = { "interupt_on": {} # note the typo } @@ -901,7 +899,7 @@ async def test_unknown_provider_requires_api_key_env(tmp_path, make_payload, mon # defaulting to NVIDIA_API_KEY and sending the wrong key to the endpoint. monkeypatch.setenv("ANTHROPIC_API_KEY", "sk-x") payload = make_payload(tmp_path) - payload["effective_config"]["config"]["models"]["default"] = { + payload["config"]["models"]["default"] = { "provider": "anthropic", "model": "claude-x", } @@ -918,7 +916,7 @@ async def test_openai_provider_defaults_to_openai_key(tmp_path, make_payload, mo monkeypatch.delenv("NVIDIA_API_KEY", raising=False) monkeypatch.setenv("OPENAI_API_KEY", "sk-test") payload = make_payload(tmp_path) - payload["effective_config"]["config"]["models"]["default"] = { + payload["config"]["models"]["default"] = { "provider": "openai", "model": "gpt-4o", } @@ -934,7 +932,7 @@ async def test_openai_compatible_provider_requires_api_key_env(tmp_path, make_pa # openai-compatible uses ChatOpenAI but has no default credential var, so it must # set api_key_env explicitly rather than silently falling back to NVIDIA_API_KEY. payload = make_payload(tmp_path) - payload["effective_config"]["config"]["models"]["default"] = { + payload["config"]["models"]["default"] = { "provider": "openai-compatible", "model": "some/model", } diff --git a/tests/adapters/test_hermes_adapter.py b/tests/adapters/test_hermes_adapter.py index fdd9b0f95..0124ab8f8 100644 --- a/tests/adapters/test_hermes_adapter.py +++ b/tests/adapters/test_hermes_adapter.py @@ -69,8 +69,7 @@ def test_build_hermes_config_maps_fabric_config_to_hermes_config(): }, } }, - "effective_config": { - "config": { + "config": { "harness": { "settings": { "model": "review", @@ -91,7 +90,6 @@ def test_build_hermes_config_maps_fabric_config_to_hermes_config(): "settings": {"base_url": "https://model.example/v1"}, } }, - } }, } @@ -144,11 +142,9 @@ def test_build_hermes_config_omits_max_turns_when_max_iterations_unset(): # When max_iterations is unset the config layer must leave agent.max_turns # absent so Hermes applies its own default rather than a starving override. payload = { - "effective_config": { - "config": { + "config": { "harness": {"settings": {}}, "models": {"default": {"provider": "nvidia", "model": "nvidia/test-model"}}, - } } } @@ -161,11 +157,9 @@ def test_build_hermes_config_omits_max_turns_when_max_iterations_null(): # An explicit null max_iterations is treated like unset: agent.max_turns is # omitted so Hermes applies its own default instead of a starving override. payload = { - "effective_config": { - "config": { + "config": { "harness": {"settings": {"max_iterations": None}}, "models": {"default": {"provider": "nvidia", "model": "nvidia/test-model"}}, - } } } @@ -219,10 +213,9 @@ def test_hermes_config_variation_matrix_surfaces_supported_capabilities( }, } }, - "effective_config": { - "agent_name": "matrix-agent", - "base_dir": str(tmp_path), - "config": { + "agent_name": "matrix-agent", + "base_dir": str(tmp_path), + "config": { "harness": { "settings": { "model": "review", @@ -237,7 +230,6 @@ def test_hermes_config_variation_matrix_surfaces_supported_capabilities( "model": "nvidia/review-model", } }, - }, }, } @@ -274,11 +266,9 @@ def test_hermes_config_variation_matrix_surfaces_supported_capabilities( def test_write_hermes_config_writes_file(tmp_path: Path): payload = { - "effective_config": { - "config": { + "config": { "harness": {"settings": {}}, "models": {"default": {"provider": "nvidia", "model": "nvidia/test-model"}}, - } } } @@ -407,10 +397,9 @@ async def test_fabric_runtime_id_drives_hermes_session_id_and_db_history( os.environ["TEST_API_KEY"] = "secret" payload = { - "effective_config": { - "agent_name": "demo", - "base_dir": str(tmp_path), - "config": { + "agent_name": "demo", + "base_dir": str(tmp_path), + "config": { "harness": { "settings": { "hermes_home": "./hermes-home", @@ -427,7 +416,6 @@ async def test_fabric_runtime_id_drives_hermes_session_id_and_db_history( "api_key_env": "TEST_API_KEY", } }, - }, }, "runtime_context": { "runtime_id": "runtime-fabric-123", diff --git a/tests/e2e/test_cli.py b/tests/e2e/test_cli.py index 9a48d914c..f94e9554b 100644 --- a/tests/e2e/test_cli.py +++ b/tests/e2e/test_cli.py @@ -1,224 +1,80 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -"""End-to-end coverage for the SDK-backed ``nemo-fabric`` CLI.""" +"""Installed-package bridge coverage for the Rust experimentation CLI.""" from __future__ import annotations import json -import os import subprocess import sys from pathlib import Path -from typing import Any -from unittest.mock import AsyncMock, MagicMock -import nemo_fabric.cli as cli_module -import pytest -ROOT = Path(__file__).resolve().parents[2] +def run(*args: str) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, "-m", "nemo_fabric.cli", *args], + text=True, + capture_output=True, + check=False, + timeout=30, + ) -def test_discovery_commands(): +def test_package_bridge_lists_rust_catalogs(): presets = run("preset", "list") - examples = run("example", "list") + examples = run("example", "show", "code-review") assert presets.returncode == 0, presets.stderr - assert {"scripted", "hermes", "claude", "codex", "deepagents"}.issubset( - presets.stdout.splitlines() - ) + assert "scripted" in presets.stdout + assert "hermes" in presets.stdout assert examples.returncode == 0, examples.stderr - assert "examples.code_review_agent" in examples.stdout.splitlines() - - shown = call_json("example", "show", "examples.code_review_agent") - assert shown["default_variant"] == "hermes" - assert set(shown["variants"]) == {"hermes", "claude", "codex", "deepagents"} - assert shown["init_supported"] is True - - scripted = call_json("preset", "show", "scripted") - assert scripted["available"] is True - assert scripted["required_env"] == [] - assert scripted["missing_env"] == [] - assert scripted["install"] == "pip install nemo-fabric" - - -def test_scripted_preset_runs_without_credentials(tmp_path: Path): - input_file = tmp_path / "input.txt" - input_file.write_text("credential-free smoke", encoding="utf-8") - - result = call_json("run", "--preset", "scripted", "--input-file", input_file) - - assert result["status"] == "succeeded" - assert result["adapter_id"] == "nvidia.fabric.scripted" - assert result["output"]["response"] == "credential-free smoke" - - -def test_help_and_version_are_available(): - help_result = run() - version = run("version") - - assert help_result.returncode == 0 - assert "experimentation interface" in help_result.stdout - assert version.returncode == 0 - assert version.stdout.strip() + assert "variants: scripted, hermes, claude, codex, deepagents" in examples.stdout -def test_example_can_be_copied_and_run_as_a_factory(tmp_path: Path): - destination = tmp_path / "my_agent" - initialized = run("example", "init", "examples.code_review_agent", destination) +def test_package_bridge_plans_preset_and_example(): + preset = run("plan", "--preset", "scripted") + example = run("plan", "--example", "code-review", "--variant", "hermes") - assert initialized.returncode == 0, initialized.stderr - assert (destination / "config.py").is_file() - assert (destination / "adapters" / "scripted" / "fabric-adapter.json").is_file() - - result = call_json_from( - tmp_path, - "run", - "--factory", - "my_agent.config:build_config", - "--base-dir", - destination, - "--input", - "review this", - ) - assert result["status"] == "succeeded" - assert result["output"]["response"] == "review this" + assert preset.returncode == 0, preset.stderr + assert json.loads(preset.stdout)["agent_name"] == "scripted-agent" + assert example.returncode == 0, example.stderr + plan = json.loads(example.stdout) + assert plan["agent_name"] == "code-review-agent" + assert plan["config"]["harness"]["adapter_id"] == "nvidia.fabric.hermes" -def test_preset_and_example_reach_the_same_sdk_plan_path(): - preset = call_json("plan", "--preset", "hermes") - example = call_json( - "plan", - "--example", - "examples.code_review_agent", - "--variant", - "hermes", - ) - - assert preset["adapter_descriptor"]["descriptor"]["adapter_id"] == "nvidia.fabric.hermes" - assert example["adapter_descriptor"]["descriptor"]["adapter_id"] == "nvidia.fabric.hermes" - expected_base = (ROOT / "python" / "src" / "nemo_fabric" / "_bundled").resolve() - assert Path(preset["effective_config"]["base_dir"]).resolve() == expected_base - assert example["capability_plan"]["native"]["skill_paths"] - - -def test_factory_plan_doctor_and_run(hermes_shim_agent_dir: Path): - selector = ( - "--factory", - "_utils.configs:hermes_shim_config", - "--base-dir", - hermes_shim_agent_dir, - ) - plan = call_json("plan", *selector) - doctor = call_json("doctor", *selector) - result = call_json("run", *selector, "--input", "hello factory") - - assert plan["agent_name"] == "hermes-shim-agent" - assert plan["adapter_descriptor"]["descriptor"]["adapter_id"] == "test.fabric.hermes_shim" - assert doctor["agent_name"] == "hermes-shim-agent" - assert doctor["checks"] - assert result["status"] == "succeeded" - assert result["output"]["received"] == "hello factory" - - -def test_factory_request_json_and_output_file(hermes_shim_agent_dir: Path, tmp_path: Path): - output = tmp_path / "result.json" - request = json.dumps( - { - "input": "hello request", - "request_id": "cli-request-1", - "context": {"source": "test"}, - } - ) - completed = run( - "run", - "--factory", - "_utils.configs:hermes_shim_config", - "--base-dir", - hermes_shim_agent_dir, - "--request-json", - request, - "--output", - output, - ) +def test_package_bridge_runs_the_deterministic_preset(): + completed = run("run", "--preset", "scripted", "--input", "package-smoke") assert completed.returncode == 0, completed.stderr - assert completed.stdout == "" - result = json.loads(output.read_text(encoding="utf-8")) - assert result["request_id"] == "cli-request-1" - assert result["output"]["received"] == "hello request" - - -def test_selector_errors_are_actionable(): - unknown = run("plan", "--preset", "missing") - malformed = run("plan", "--factory", "missing-factory") - conflict = run("plan", "--preset", "hermes", "--factory", "mod:factory") - misplaced_variant = run("plan", "--preset", "scripted", "--variant", "hermes") - - assert unknown.returncode == 2 - assert "available:" in unknown.stderr - assert malformed.returncode == 2 - assert "module:callable" in malformed.stderr - assert conflict.returncode == 2 - assert "not allowed with argument" in conflict.stderr - assert misplaced_variant.returncode == 2 - assert "--variant requires --example" in misplaced_variant.stderr - - -async def test_failed_run_returns_nonzero(monkeypatch): - result = MagicMock() - result.status = "failed" - result.to_mapping.return_value = {"status": "failed"} - fabric = MagicMock() - fabric.run = AsyncMock(return_value=result) - monkeypatch.setattr(cli_module, "Fabric", MagicMock(return_value=fabric)) - args = cli_module.build_parser().parse_args( - ["run", "--preset", "scripted", "--input", "fail"] - ) - - assert await cli_module._execute(args) == 1 - - -def test_bundled_factory_errors_are_normalized(monkeypatch): - preset = MagicMock() - preset.name = "broken" - preset.base_dir = ROOT - preset.factory.side_effect = RuntimeError("builder exploded") - monkeypatch.setitem(cli_module.PRESETS, "broken", preset) - args = cli_module.build_parser().parse_args(["plan", "--preset", "broken"]) - - with pytest.raises(cli_module.SourceError, match="failed to build preset:broken"): - cli_module.select_source(args) - - -def call_json(*args: object) -> dict[str, Any]: - return call_json_from(ROOT, *args) - - -def call_json_from(cwd: Path, *args: object) -> dict[str, Any]: - completed = run_from(cwd, *args) - assert completed.returncode == 0, completed.stderr - value = json.loads(completed.stdout) - assert isinstance(value, dict) - return value - - -def run(*args: object) -> subprocess.CompletedProcess[str]: - return run_from(ROOT, *args) - - -def run_from(cwd: Path, *args: object) -> subprocess.CompletedProcess[str]: - env = os.environ.copy() - python_path = [str(ROOT / "python" / "src"), str(ROOT / "tests")] - if env.get("PYTHONPATH"): - python_path.append(env["PYTHONPATH"]) - env["PYTHONPATH"] = os.pathsep.join(python_path) - return subprocess.run( - [sys.executable, "-m", "nemo_fabric.cli", *(str(arg) for arg in args)], - cwd=cwd, - env=env, - text=True, - capture_output=True, - check=False, - timeout=30, - ) + result = json.loads(completed.stdout) + assert result["status"] == "succeeded" + assert result["output"]["response"] == "package-smoke" + + +def test_package_bridge_generates_editable_languages(tmp_path: Path): + for language in ("python", "rust"): + destination = tmp_path / language + completed = run( + "example", + "init", + "code-review", + str(destination), + "--language", + language, + "--variant", + "hermes", + ) + assert completed.returncode == 0, completed.stderr + assert (destination / "repo" / "calculator.py").is_file() + assert (destination / "skills" / "code-review.md").is_file() + launcher = destination / ("main.py" if language == "python" else "src/main.rs") + assert "nvidia.fabric.hermes" in launcher.read_text(encoding="utf-8") + + +def test_cli_rejects_removed_configuration_sources(): + for removed in ("--config", "--profile", "--factory"): + completed = run("plan", removed, "value") + assert completed.returncode != 0 + assert "unexpected argument" in completed.stderr diff --git a/tests/e2e/test_harbor_swebench_task.py b/tests/e2e/test_harbor_swebench_task.py index c23be7f67..5687161c7 100644 --- a/tests/e2e/test_harbor_swebench_task.py +++ b/tests/e2e/test_harbor_swebench_task.py @@ -17,7 +17,8 @@ import pytest -from _utils.utils import run_fabric_cli +from _utils.configs import harbor_swebench_config +from nemo_fabric import Fabric, RunRequest ROOT = Path(__file__).resolve().parents[2] HARBOR_ROOT = ROOT.parent / "harbor" @@ -29,7 +30,7 @@ VERIFY_ENV = "RUN_FABRIC_HARBOR_SWEBENCH_VERIFY" @pytest.mark.usefixtures("requires_harbor") -def test_harbor_swebench_task(hermes_shim_agent_dir: Path): +async def test_harbor_swebench_task(hermes_shim_agent_dir: Path): if os.environ.get(RUN_ENV) != "1": pytest.skip(f"set {RUN_ENV}=1 to run the Docker-backed SWE-Bench test") @@ -44,20 +45,13 @@ def test_harbor_swebench_task(hermes_shim_agent_dir: Path): copy_testbed_from_image(workspace) assert_clean_workspace(workspace) - request_file = hermes_shim_agent_dir / "django-13741.request.json" - request_file.write_text( - json.dumps(build_request(task_dir), indent=2), encoding="utf-8" - ) - - result = call_json( - "run", - "--factory", - "_utils.configs:harbor_swebench_config", - "--base-dir", - hermes_shim_agent_dir, - "--request-json", - f"@{request_file}", - ) + result = ( + await Fabric().run( + harbor_swebench_config(), + base_dir=hermes_shim_agent_dir, + request=RunRequest.from_mapping(build_request(task_dir)), + ) + ).to_mapping() assert result["status"] == "succeeded", result assert result["output"]["task"]["instance_id"] == "django__django-13741" @@ -171,17 +165,6 @@ def verify_with_harbor_task(task_dir: Path, workspace: Path, logs: Path) -> None assert reward == "1", (logs / "verifier" / "report.json").read_text( encoding="utf-8" ) - - -def call_json(*args: object) -> dict: - completed = run_fabric_cli(*args) - if completed.returncode != 0: - raise AssertionError( - f"command failed: {completed.args}\nstdout:\n{completed.stdout}\nstderr:\n{completed.stderr}" - ) - return json.loads(completed.stdout) - - def run(*command: object, cwd: Path | None = None) -> subprocess.CompletedProcess[str]: completed = subprocess.run( [str(part) for part in command], diff --git a/tests/e2e/test_local_env_e2e.py b/tests/e2e/test_local_env_e2e.py index 4c6965eca..bcb6f52f1 100644 --- a/tests/e2e/test_local_env_e2e.py +++ b/tests/e2e/test_local_env_e2e.py @@ -5,36 +5,31 @@ from __future__ import annotations -import json from pathlib import Path -from _utils.utils import run_fabric_cli +from _utils.configs import hermes_shim_config +from nemo_fabric import Fabric, RunRequest -def test_local_env_e2e(hermes_shim_agent_dir: Path): - selector = ( - "--factory", - "_utils.configs:hermes_shim_config", - "--base-dir", - hermes_shim_agent_dir, - ) - plan = call_json("plan", *selector) +async def test_local_env_e2e(hermes_shim_agent_dir: Path): + fabric = Fabric() + config = hermes_shim_config() + plan = fabric.plan(config, base_dir=hermes_shim_agent_dir).to_mapping() assert plan["environment_plan"]["provider"] == "local" assert plan["environment_plan"]["workspace"].endswith("repos/my-service") assert plan["adapter_descriptor"]["source"] == "local" - result = call_json( - "run", - *selector, - "--request-json", - json.dumps( - { - "request_id": "local-env-e2e", - "input": "review local workspace", - "context": {"source": "local-e2e"}, - } - ), - ) + result = ( + await fabric.run( + config, + base_dir=hermes_shim_agent_dir, + request=RunRequest( + request_id="local-env-e2e", + input="review local workspace", + context={"source": "local-e2e"}, + ), + ) + ).to_mapping() assert result["status"] == "succeeded" assert result["request_id"] == "local-env-e2e" @@ -49,17 +44,6 @@ def test_local_env_e2e(hermes_shim_agent_dir: Path): and event["metadata"]["environment_provider"] == "local" for event in result["events"] ) - - -def call_json(*args: object) -> dict: - completed = run_fabric_cli(*args) - if completed.returncode != 0: - raise AssertionError( - f"command failed: {completed.args}\nstdout:\n{completed.stdout}\nstderr:\n{completed.stderr}" - ) - return json.loads(completed.stdout) - - def read_artifact(result: dict, name: str) -> str: matches = [ artifact diff --git a/tests/e2e/test_swebench_style.py b/tests/e2e/test_swebench_style.py index c6a86fffb..006f7ae53 100644 --- a/tests/e2e/test_swebench_style.py +++ b/tests/e2e/test_swebench_style.py @@ -5,27 +5,25 @@ from __future__ import annotations -import json import subprocess from pathlib import Path -from _utils.utils import run_fabric_cli +from _utils.configs import swebench_shim_config +from nemo_fabric import Fabric -def test_swebench_style(hermes_shim_agent_dir: Path): +async def test_swebench_style(hermes_shim_agent_dir: Path): workspace = hermes_shim_agent_dir / "repos" / "my-service" run_command(workspace, "git", "init", "-q") run_command(workspace, "git", "add", "calculator.py") - result = call_json( - "run", - "--factory", - "_utils.configs:swebench_shim_config", - "--base-dir", - hermes_shim_agent_dir, - "--input", - "Fix the bug so answer() returns 42.", - ) + result = ( + await Fabric().run( + swebench_shim_config(), + base_dir=hermes_shim_agent_dir, + input="Fix the bug so answer() returns 42.", + ) + ).to_mapping() assert result["status"] == "succeeded", result assert result["adapter_kind"] == "python" @@ -48,22 +46,6 @@ def test_swebench_style(hermes_shim_agent_dir: Path): ] assert len(status_artifacts) == 1 assert "calculator.py" in Path(status_artifacts[0]["path"]).read_text() - - -def call_json(*args: object) -> dict: - completed = run(*args) - return json.loads(completed.stdout) - - -def run(*args: object) -> subprocess.CompletedProcess[str]: - completed = run_fabric_cli(*args) - if completed.returncode != 0: - raise AssertionError( - f"command failed: {completed.args}\nstdout:\n{completed.stdout}\nstderr:\n{completed.stderr}" - ) - return completed - - def run_command(cwd: Path, *command: str) -> None: completed = subprocess.run( command, diff --git a/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/src/nemo_fabric_test_adapters/hermes_shim/adapter.py b/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/src/nemo_fabric_test_adapters/hermes_shim/adapter.py index 8b472694a..8aee6c9bf 100644 --- a/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/src/nemo_fabric_test_adapters/hermes_shim/adapter.py +++ b/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/src/nemo_fabric_test_adapters/hermes_shim/adapter.py @@ -26,12 +26,8 @@ def run(payload: dict[str, Any]) -> dict[str, Any]: return run_selected_mode(payload) -def effective_config(payload: dict[str, Any]) -> dict[str, Any]: - return payload.get("effective_config") or {} - - def fabric_config(payload: dict[str, Any]) -> dict[str, Any]: - return effective_config(payload).get("config") or {} + return payload.get("config") or {} def runtime_context(payload: dict[str, Any]) -> dict[str, Any]: diff --git a/tests/integrations/test_harbor_runner.py b/tests/integrations/test_harbor_runner.py index 0b1ffbc17..44b786538 100644 --- a/tests/integrations/test_harbor_runner.py +++ b/tests/integrations/test_harbor_runner.py @@ -204,9 +204,8 @@ def test_codex_adapter_maps_fabric_request_to_sdk(tmp_path): adapter = load_codex_adapter() payload = { - "effective_config": { - "config_root": str(tmp_path), - "config": { + "base_dir": str(tmp_path), + "config": { "harness": { "settings": { "sandbox": "workspace-write", @@ -220,7 +219,6 @@ def test_codex_adapter_maps_fabric_request_to_sdk(tmp_path): } }, "runtime": {}, - }, }, "runtime_context": { "runtime_id": "harbor-test", diff --git a/tests/python/test_code_review_example.py b/tests/python/test_code_review_example.py index 61c624122..1237ab834 100644 --- a/tests/python/test_code_review_example.py +++ b/tests/python/test_code_review_example.py @@ -88,7 +88,7 @@ def test_variants_plan_from_complete_configs(): for config in (hermes_config(), codex_config(), claude_config()): plan = client.plan(config, base_dir=BASE_DIR) - assert plan.effective_config.base_dir == BASE_DIR + assert plan.base_dir == BASE_DIR assert plan.agent_name == "code-review-agent" assert plan.adapter.adapter_id == config.harness.adapter_id diff --git a/tests/python/test_native_sdk.py b/tests/python/test_native_sdk.py index 497d812b2..ab9576edd 100644 --- a/tests/python/test_native_sdk.py +++ b/tests/python/test_native_sdk.py @@ -70,13 +70,10 @@ def test_native_run_rejects_multiple_request_sources(hermes_shim_agent_dir: Path async def smoke(client: Fabric, fixture_agent: Path) -> None: example_config = base_config() - inspected = client.resolve(example_config, base_dir=BASE_DIR) - assert inspected["agent_name"] == "code-review-agent" - assert inspected.base_dir == BASE_DIR - assert inspected["config"]["metadata"]["name"] == "code-review-agent" - plan = client.plan(example_config, base_dir=BASE_DIR) assert plan["agent_name"] == "code-review-agent" + assert plan.base_dir == BASE_DIR + assert plan.config.metadata.name == "code-review-agent" assert ( plan["adapter_descriptor"]["descriptor"]["adapter_id"] == "nvidia.fabric.hermes" @@ -90,9 +87,9 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: "harness": {"adapter_id": "nvidia.fabric.hermes"}, } ) - minimal_resolved = client.resolve(minimal) - assert minimal_resolved.config.runtime.input_schema == "text" - assert minimal_resolved.config.runtime.output_schema == "text" + minimal_plan = client.plan(minimal) + assert minimal_plan.config.runtime.input_schema == "text" + assert minimal_plan.config.runtime.output_schema == "text" typed_config = FabricConfig.from_mapping( { @@ -141,19 +138,14 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: }, } ) - typed_config_resolved = client.resolve( - typed_config, - base_dir=fixture_agent, - ) typed_plan = client.plan( typed_config, base_dir=fixture_agent, ) - assert typed_config_resolved.agent_name == "typed-hermes-shim-agent" assert typed_plan["agent_name"] == "typed-hermes-shim-agent" assert typed_plan["adapter_descriptor"]["source"] == "local" assert typed_plan["telemetry_plan"]["relay_enabled"] is True - resolved_config = typed_config_resolved.config.to_mapping() + resolved_config = typed_plan.config.to_mapping() assert resolved_config["harness"]["adapter_id"] == "test.fabric.hermes_shim" assert resolved_config["harness"]["settings"]["workspace"] == "./repos/my-service" assert resolved_config["harness"]["settings"]["timeout_seconds"] == 30 diff --git a/tests/python/test_runtime.py b/tests/python/test_runtime.py index b26947091..582c351d8 100644 --- a/tests/python/test_runtime.py +++ b/tests/python/test_runtime.py @@ -40,11 +40,7 @@ def _plan() -> dict[str, Any]: } return { "agent_name": "demo", - "effective_config": { - "agent_name": "demo", - "base_dir": ".", - "config": config, - }, + "base_dir": ".", "config": config, "adapter_descriptor": { "descriptor": { diff --git a/tests/python/test_sdk_contract.py b/tests/python/test_sdk_contract.py index 9939009dc..2b44272c5 100644 --- a/tests/python/test_sdk_contract.py +++ b/tests/python/test_sdk_contract.py @@ -16,7 +16,6 @@ import pytest from nemo_fabric import AdapterInfo from nemo_fabric import DoctorReport -from nemo_fabric import EffectiveConfig from nemo_fabric import EnvironmentConfig from nemo_fabric import Fabric from nemo_fabric import FabricCapabilityError @@ -45,7 +44,7 @@ from nemo_fabric import SkillConfig from nemo_fabric import TelemetryConfig from nemo_fabric import ToolsConfig -from nemo_fabric.types import _ResolvedFabricConfig +from nemo_fabric.types import _FabricConfigSnapshot from nemo_fabric.types import _ToolsConfig from pydantic import ValidationError @@ -59,7 +58,8 @@ def test_public_contract_has_no_unreleased_aliases(): for name in ("plan_config", "run_config", "doctor_config", "start", "start_config"): assert not hasattr(Fabric, name) - for name in ("resolve", "plan", "doctor", "run", "start_runtime"): + assert not hasattr(Fabric, "resolve") + for name in ("plan", "doctor", "run", "start_runtime"): assert not get_overloads(getattr(Fabric, name)), name assert not hasattr(fabric_errors, "FabricCliError") @@ -200,7 +200,7 @@ def test_typed_tools_config_serializes_blocked_policy(): def test_run_plan_config_block_tools_emits_canonical_shape(): - config = _ResolvedFabricConfig.from_mapping(_plan()["config"]) + config = _FabricConfigSnapshot.from_mapping(_plan()["config"]) config.block_tools("browser", "shell", "browser") @@ -383,14 +383,11 @@ def test_inspection_models_are_typed_read_only_mappings(): plan = RunPlan.from_mapping( { "agent_name": "demo", - "effective_config": { - "agent_name": "demo", - "base_dir": ".", - "config": { - "metadata": {"name": "demo"}, - "harness": {"adapter_id": "test.fabric.shim"}, - "runtime": {"input_schema": "chat"}, - }, + "base_dir": ".", + "config": { + "metadata": {"name": "demo"}, + "harness": {"adapter_id": "test.fabric.shim"}, + "runtime": {"input_schema": "chat"}, }, "adapter_descriptor": { "descriptor": { @@ -410,24 +407,23 @@ def test_inspection_models_are_typed_read_only_mappings(): } ) - assert isinstance(plan.effective_config, EffectiveConfig) assert isinstance(plan.adapter, AdapterInfo) assert isinstance(plan.capabilities, RuntimeCapabilities) - assert plan.effective_config.base_dir == Path(".") + assert plan.base_dir == Path(".") assert plan.adapter.harness == "hermes" assert "harness_type" not in plan.adapter assert plan.adapter.extra_fields["future"] == "value" assert plan.capabilities.extra_fields["future_capability"] == "declared" resolved = plan.to_mapping() - plan.effective_config.config.metadata.name = "mutated" + plan.config.metadata.name = "mutated" assert plan.to_mapping() == resolved with pytest.raises(TypeError): plan["agent_name"] = "mutated" # type: ignore[index] -def test_resolved_config_rejects_removed_profiles_and_missing_base_dir(): +def test_run_plan_config_rejects_removed_profiles_and_missing_base_dir(): with pytest.raises(FabricConfigError, match="profiles are no longer supported"): - _ResolvedFabricConfig.from_mapping( + _FabricConfigSnapshot.from_mapping( { "metadata": {"name": "demo"}, "harness": {"adapter_id": "test.fabric.shim"}, @@ -436,13 +432,19 @@ def test_resolved_config_rejects_removed_profiles_and_missing_base_dir(): ) with pytest.raises(FabricConfigError, match="base_dir is required"): - EffectiveConfig.from_mapping( + RunPlan.from_mapping( { "agent_name": "demo", "config": { "metadata": {"name": "demo"}, "harness": {"adapter_id": "test.fabric.shim"}, }, + "adapter": { + "adapter_id": "test.fabric.shim", + "harness": "shim", + "adapter_kind": "python", + }, + "capabilities": {}, } ) @@ -489,7 +491,7 @@ def test_runtime_handle_requires_native_contract_fields(field): def test_run_plan_config_enable_relay_preserves_existing_relay_fields(): - config = _ResolvedFabricConfig.from_mapping(_plan()["config"]) + config = _FabricConfigSnapshot.from_mapping(_plan()["config"]) config.enable_relay( output_dir="./artifacts/relay", @@ -505,7 +507,7 @@ def test_run_plan_config_enable_relay_preserves_existing_relay_fields(): def test_run_plan_config_enable_native_preserves_existing_native_config(): - config = _ResolvedFabricConfig.from_mapping(_plan()["config"]) + config = _FabricConfigSnapshot.from_mapping(_plan()["config"]) config.telemetry.enable_native(config={"components": [{"kind": "observability"}]}) config.telemetry.enable_native() @@ -560,11 +562,7 @@ def _plan() -> dict[str, Any]: } return { "agent_name": "demo", - "effective_config": { - "agent_name": "demo", - "base_dir": ".", - "config": config, - }, + "base_dir": ".", "config": config, "adapter_descriptor": { "descriptor": { @@ -632,15 +630,6 @@ def __init__(self) -> None: self.stopped = 0 self.fail_invoke = False - def resolve_config( - self, - config_json: str, - base_dir: str | None = None, - ) -> str: - assert json.loads(config_json)["metadata"]["name"] == "demo" - self.config_base_dir_calls.append(base_dir) - return json.dumps(_plan()["effective_config"]) - def plan_config( self, config_json: str, @@ -1178,14 +1167,6 @@ def test_fabric_config_constructors_emit_schema_shaped_mappings(): client.plan(config) -def test_resolve_accepts_a_complete_fabric_config(): - client = NativeClient(NativeRecorder()) - - typed_config = client.resolve(_fabric_config()) - - assert typed_config["config"]["runtime"]["input_schema"] == "chat" - - async def test_start_runtime_returns_runtime_with_typed_handle(): runtime = await NativeClient(NativeRecorder()).start_runtime(_fabric_config()) diff --git a/tests/python/test_sdk_runtimes.py b/tests/python/test_sdk_runtimes.py index 2622a961f..a57bc431f 100644 --- a/tests/python/test_sdk_runtimes.py +++ b/tests/python/test_sdk_runtimes.py @@ -22,11 +22,7 @@ def _plan() -> dict[str, Any]: } return { "agent_name": "demo", - "effective_config": { - "agent_name": "demo", - "base_dir": ".", - "config": config, - }, + "base_dir": ".", "config": config, "adapter_descriptor": { "descriptor": { From 8c97985fbb7bb4ae618d66df23f2b849c90d3082 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Fri, 17 Jul 2026 15:18:57 -0700 Subject: [PATCH 07/20] docs: move CLI guide under experimentation Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- CLI.md | 56 -------------------------- README.md | 7 ++-- docs/about-nemo-fabric/overview.mdx | 15 +++---- docs/experimentation/cli.mdx | 62 +++++++++++++++++++++++++++++ docs/index.yml | 4 ++ 5 files changed, 78 insertions(+), 66 deletions(-) delete mode 100644 CLI.md create mode 100644 docs/experimentation/cli.mdx diff --git a/CLI.md b/CLI.md deleted file mode 100644 index f06941a38..000000000 --- a/CLI.md +++ /dev/null @@ -1,56 +0,0 @@ - - -# NeMo Fabric Experimentation CLI - -`nemo-fabric` is a small interface for trying harnesses, inspecting plans, and -starting from maintained examples. It is intentionally for experimentation; -applications that need a stable integration should use a language API directly. - -The command implementation and catalogs live in the Rust `fabric-cli` crate. -The `nemo-fabric-runtime` Python package exposes the same Rust implementation -through a thin console-script bridge, so package and standalone installs do not -maintain separate CLIs. - -## Variations - -Use a preset for the shortest complete probe: - -```bash -nemo-fabric run --preset hermes --input "Say hello" -``` - -Use a maintained example to exercise a complete workflow and variant: - -```bash -nemo-fabric run --example code-review --variant hermes \ - --input "Review the workspace" -``` - -Generate ordinary editable application code when the catalog is no longer -enough: - -```bash -nemo-fabric example init code-review my-agent --language python --variant hermes -nemo-fabric example init code-review my-agent-rs --language rust --variant hermes -``` - -The Python scaffold calls the Python SDK; the Rust scaffold calls -`fabric-core`. Neither is loaded back into the central CLI. - -## Boundaries - -- Every preset and example variant constructs a complete typed `FabricConfig`. -- Examples reuse preset defaults and one shared workspace/skill asset tree. -- Variants do not inherit, merge, or behave like profiles. -- There is no `--config`, `--profile`, or `--factory` input. -- Fabric does not discover or persist YAML, TOML, or JSON agent configuration. -- JSON request payloads and harness-generated files remain valid; they are not - Fabric configuration sources. -- The CLI is not a scheduler, evaluation framework, or production deployment - interface. - -Current lifecycle commands are `plan`, `doctor`, and `run`. Catalog discovery -is available through `preset list/show` and `example list/show`. diff --git a/README.md b/README.md index 68726e6cf..24570f505 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,8 @@ Copy an editable Python starting point with: nemo-fabric example init code-review ./my-agent --language python ``` -See [CLI.md](CLI.md) for the CLI's intent and boundaries. +See the [experimentation CLI guide](docs/experimentation/cli.mdx) for the CLI's +intent and boundaries. ## Quick Start: Hermes Agent @@ -200,8 +201,8 @@ the [Python SDK guide](docs/sdk/python.mdx). Exact signatures are in the - [Python SDK guide](docs/sdk/python.mdx): typed configuration, planning, diagnostics, requests, multi-turn runtimes, parallelism, results, and errors. -- [Experimentation CLI](CLI.md): presets, maintained examples, editable - application scaffolds, and explicit non-goals. +- [Experimentation CLI](docs/experimentation/cli.mdx): presets, maintained + examples, editable application scaffolds, and explicit non-goals. - [Consumer integration skills](skills/README.md): repository-local coding-agent skills for integrating Fabric into an application through the Python SDK. - [Getting Started overview](docs/about-nemo-fabric/overview.mdx): interface diff --git a/docs/about-nemo-fabric/overview.mdx b/docs/about-nemo-fabric/overview.mdx index b805b8e5b..33d371557 100644 --- a/docs/about-nemo-fabric/overview.mdx +++ b/docs/about-nemo-fabric/overview.mdx @@ -54,10 +54,12 @@ Hermes | Codex SDK | custom harness RunResult + artifacts + events + telemetry references ``` -The experimentation CLI is a thin client of the Python SDK. Presets, examples, -and user factories all produce a complete `FabricConfig` before using the same -resolved config and run-plan contracts. Adapters own harness-specific -preparation and invocation; consumers own the request and returned evidence. +The experimentation CLI and its catalogs are implemented once in the Rust +`fabric-cli` crate. The Python package exposes the same implementation as a +console command. Built-in presets and maintained examples produce a complete +`FabricConfig` before using the same run-plan contract. Adapters own +harness-specific preparation and invocation; consumers own the request and +returned evidence. ## Quick Start @@ -126,12 +128,11 @@ requests, and multi-turn runtime examples. | --- | --- | --- | | Python SDK | Your application owns job config, runtime lifecycle, or multi-turn state | [Client API](/reference/api/python-library-reference/client) | | Runtime API | You need multiple ordered turns over one live harness runtime | [Runtime](/reference/api/python-library-reference/runtime) | -| `nemo-fabric` CLI | You are experimenting with harnesses, running examples, or troubleshooting configs | `preset`, `example`, `plan`, `doctor`, `run`, and `chat` | +| `nemo-fabric` CLI | You are experimenting with harnesses, running maintained examples, or troubleshooting configs | [Experimentation CLI](/experimentation/cli) | | JSON Schema | You are building editors, validation, code generation, or another language binding | Committed schemas in the [repository](https://github.com/NVIDIA/NeMo-Fabric/tree/main/schemas) | Use `FabricConfig` as the canonical configuration contract. CLI selectors -obtain complete typed configs from maintained presets, examples, or user-owned -Python factories. +obtain complete typed configs from built-in presets or maintained examples. ## Core Workflow diff --git a/docs/experimentation/cli.mdx b/docs/experimentation/cli.mdx new file mode 100644 index 000000000..f0eca7a50 --- /dev/null +++ b/docs/experimentation/cli.mdx @@ -0,0 +1,62 @@ +--- +title: "Experimentation CLI" +description: "Use presets, maintained examples, and editable scaffolds to experiment with NeMo Fabric." +--- +{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 */} + + + The `nemo-fabric` CLI is an experimentation surface. Use it for quick harness + probes, maintained examples, planning, and diagnostics. Applications and + services should construct `FabricConfig` through a language API directly. + + +The command implementation and catalogs live in the Rust `fabric-cli` crate. +The `nemo-fabric-runtime` Python package exposes that same implementation +through a thin console-script bridge. Package and standalone installs do not +maintain separate parsers, presets, or example catalogs. + +## Run a Preset + +Use a preset for the shortest complete probe: + +```bash +nemo-fabric run --preset hermes --input "Say hello" +``` + +## Run a Maintained Example + +Use an example to exercise a complete workflow and harness variant: + +```bash +nemo-fabric run --example code-review --variant hermes \ + --input "Review the workspace" +``` + +## Generate an Editable Application + +Generate ordinary application code when you want to customize an example: + +```bash +nemo-fabric example init code-review my-agent --language python --variant hermes +nemo-fabric example init code-review my-agent-rs --language rust --variant hermes +``` + +The Python scaffold calls the Python SDK. The Rust scaffold calls +`fabric-core`. Neither scaffold is loaded back into the CLI. + +## Boundaries + +- Every preset and example variant constructs a complete typed `FabricConfig`. +- Examples reuse preset constructors and one shared workspace and skill asset + tree; Python and Rust launchers do not duplicate those definitions. +- Variants do not inherit, merge, or behave like profiles. +- There is no `--config`, `--profile`, or `--factory` input. +- Fabric does not discover or persist YAML, TOML, or JSON agent configuration. +- JSON request payloads and harness-generated files remain valid; they are not + Fabric configuration sources. +- The CLI is not an application API, scheduler, evaluation framework, or + production deployment interface. + +Current lifecycle commands are `plan`, `doctor`, and `run`. Catalog discovery +is available through `preset list/show` and `example list/show`. diff --git a/docs/index.yml b/docs/index.yml index 025d6bb2f..5db031d2e 100644 --- a/docs/index.yml +++ b/docs/index.yml @@ -14,6 +14,10 @@ navigation: path: ./getting-started/install.mdx - page: Quickstart path: ./getting-started/quickstart.mdx + - section: Experimentation + contents: + - page: CLI + path: ./experimentation/cli.mdx - section: SDK contents: - page: Python SDK From fc440db4a3f86615892d4bc1e32ef46ace769b1e Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Sun, 19 Jul 2026 11:14:29 -0700 Subject: [PATCH 08/20] docs: track adapter metadata migration Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- TODO.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/TODO.md b/TODO.md index 25ed111bb..1eddbbdc4 100644 --- a/TODO.md +++ b/TODO.md @@ -9,6 +9,30 @@ Track repository workarounds that must be removed after an upstream dependency ships. Each entry must identify an upstream reference, removal condition, and cleanup validation. +## Installed Adapter Descriptor Metadata + +- **Status:** Waiting for adapter wheel metadata support +- **Added:** July 19, 2026 +- **Affected implementation:** `crates/fabric-core/src/config.rs`, + `crates/fabric-cli/src/presets.rs`, and the Python binding +- **Reason:** CLI presets currently embed adapter descriptors from repository + paths at compile time and stage them under a temporary `adapters/` directory. + Core also probes a compile-time repository path. Built wheels can run without + a checkout, but the build and resolution paths still assume the monorepo + layout. +- **Upstream resolution:** NeMo Fabric adapter distributions advertise their + descriptors through installed wheel metadata (internal packaging milestone; + no external dependency). +- **Removal condition:** Python can discover installed adapter descriptors from + wheel metadata and pass them to core through an explicit, typed adapter + registry. +- **Cleanup:** Add adapter registrations to `ResolveContext`; remove implicit + repository and `base_dir/adapters` discovery from core; have the Python + binding register wheel-owned descriptors; have the CLI register embedded + descriptors directly; retain temporary staging only for descriptor-local + assets such as the scripted runner; and validate both installed-wheel and + standalone Rust CLI behavior before removing this entry. + ## NeMo Relay 0.6.x Request Decoding Release - **Status:** Waiting for an upstream release From 9ac244a2533834d4be9d110d8961c3336eba4e5e Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Sun, 19 Jul 2026 11:34:21 -0700 Subject: [PATCH 09/20] docs: structure CLI experimentation workflows Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- docs/experimentation/cli.mdx | 102 ++++++++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/docs/experimentation/cli.mdx b/docs/experimentation/cli.mdx index f0eca7a50..fc11e8ec5 100644 --- a/docs/experimentation/cli.mdx +++ b/docs/experimentation/cli.mdx @@ -1,5 +1,5 @@ --- -title: "Experimentation CLI" +title: "NVIDIA NeMo Fabric Experimentation CLI" description: "Use presets, maintained examples, and editable scaffolds to experiment with NeMo Fabric." --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. @@ -16,36 +16,114 @@ The `nemo-fabric-runtime` Python package exposes that same implementation through a thin console-script bridge. Package and standalone installs do not maintain separate parsers, presets, or example catalogs. -## Run a Preset +## Experiment With Presets -Use a preset for the shortest complete probe: +List the complete configurations maintained by the CLI: + +```bash +nemo-fabric preset list +``` + +Inspect a preset's purpose and required environment variables: + +```bash +nemo-fabric preset show hermes +``` + +Resolve the preset to inspect its complete run plan, including its typed +configuration and adapter descriptor: + +```bash +nemo-fabric plan --preset hermes +``` + +To display only the authored `FabricConfig`, filter the plan with `jq`: + +```bash +nemo-fabric plan --preset hermes | jq '.config' +``` + +Diagnose adapter availability, credentials, and other requirements before +running the preset: + +```bash +nemo-fabric doctor --preset hermes +``` + +Run the preset with an input: ```bash nemo-fabric run --preset hermes --input "Say hello" ``` -## Run a Maintained Example +## Experiment With Examples -Use an example to exercise a complete workflow and harness variant: +List the maintained workflows and inspect an example's available variants: + +```bash +nemo-fabric example list +nemo-fabric example show code-review +``` + +Run an example with its default variant: + +```bash +nemo-fabric run --example code-review --input "Review the workspace" +``` + +Select a different maintained harness variant when you want to compare its +behavior: ```bash nemo-fabric run --example code-review --variant hermes \ --input "Review the workspace" ``` -## Generate an Editable Application +The `plan` and `doctor` commands accept the same `--example` and `--variant` +selectors. + +## Scaffold Examples + +Generate ordinary application code when you want to customize an example. The +default `scripted` variant is credential-free: + +```bash +nemo-fabric example init code-review my-agent --language python +nemo-fabric example init code-review my-agent-rs --language rust +``` + +Add a selector such as `--variant hermes` to scaffold a non-default variant. A +non-default variant requires its adapter package and credentials. + +### Run a Python Scaffold + +Create and activate a virtual environment, install the generated application, +and run its launcher: + +```bash +cd my-agent +python -m venv .venv +source .venv/bin/activate +python -m pip install -e . +python main.py "Review the workspace" +``` + +The Python scaffold constructs `FabricConfig` and calls the Python SDK +directly. + +### Run a Rust Scaffold -Generate ordinary application code when you want to customize an example: +Build and run the generated Rust application: ```bash -nemo-fabric example init code-review my-agent --language python --variant hermes -nemo-fabric example init code-review my-agent-rs --language rust --variant hermes +cd my-agent-rs +cargo run -- "Review the workspace" ``` -The Python scaffold calls the Python SDK. The Rust scaffold calls -`fabric-core`. Neither scaffold is loaded back into the CLI. +The Rust scaffold constructs `FabricConfig` and calls `fabric-core` directly. +Neither scaffold is loaded back into the central CLI. -## Boundaries +The CLI has the following boundaries: - Every preset and example variant constructs a complete typed `FabricConfig`. - Examples reuse preset constructors and one shared workspace and skill asset From 0483efdd7f79801e1ec90530f3fa8be24bdae28f Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Sun, 19 Jul 2026 11:45:50 -0700 Subject: [PATCH 10/20] refactor: distribute the experimentation CLI separately Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- Cargo.lock | 1 - README.md | 10 +-- crates/fabric-python/Cargo.toml | 1 - crates/fabric-python/src/lib.rs | 8 -- docs/about-nemo-fabric/overview.mdx | 14 ++-- docs/experimentation/cli.mdx | 37 ++++++++- .../nemo_fabric.client.md | 2 +- pypi.md | 9 +-- python/pyproject.toml | 3 - python/src/nemo_fabric/_native.pyi | 1 - python/src/nemo_fabric/cli.py | 24 ------ python/src/nemo_fabric/client.py | 3 +- tests/e2e/test_cli.py | 80 ------------------- 13 files changed, 51 insertions(+), 142 deletions(-) delete mode 100644 python/src/nemo_fabric/cli.py delete mode 100644 tests/e2e/test_cli.py diff --git a/Cargo.lock b/Cargo.lock index 541b2c1e0..afa7716d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,7 +57,6 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" name = "fabric-python" version = "0.1.0" dependencies = [ - "nemo-fabric-cli", "nemo-fabric-core", "pyo3", "serde", diff --git a/README.md b/README.md index 24570f505..fd280f56a 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Fabric provides: - a versioned, typed `FabricConfig` contract constructed through the SDK; - ordinary Python composition for harness and experiment variants; - adapter descriptors for harness-specific launch and control; -- a Rust core, Python SDK, and SDK-backed experimentation CLI; +- a Rust core, Python SDK, and standalone experimentation CLI; - JSON Schema snapshots for the public config and runtime contract; - normalized run results, artifact manifests, and telemetry references. @@ -53,12 +53,12 @@ quick harness experiments, smoke tests, examples, planning, and diagnostics. Its command contract can evolve as experiments mature. The Python SDK remains the stable application-facing contract. -Build and install the local wheels, then run the credential-free preset: +Install the Rust CLI from this source checkout, verify it, and run the +credential-free preset: ```bash -just wheels -python -m pip install --find-links dist nemo-fabric - +cargo install --path crates/fabric-cli --locked +nemo-fabric --version nemo-fabric preset show scripted nemo-fabric run --preset scripted --input "fabric works" ``` diff --git a/crates/fabric-python/Cargo.toml b/crates/fabric-python/Cargo.toml index f4538ca1c..7d3defcf4 100644 --- a/crates/fabric-python/Cargo.toml +++ b/crates/fabric-python/Cargo.toml @@ -16,7 +16,6 @@ test = false doctest = false [dependencies] -nemo-fabric-cli = { path = "../fabric-cli", version = "0.1.0" } nemo-fabric-core.workspace = true pyo3 = { workspace = true, features = ["abi3", "abi3-py311", "macros"] } serde.workspace = true diff --git a/crates/fabric-python/src/lib.rs b/crates/fabric-python/src/lib.rs index b421d525d..1362f4803 100644 --- a/crates/fabric-python/src/lib.rs +++ b/crates/fabric-python/src/lib.rs @@ -18,13 +18,6 @@ fn version() -> PyResult { Ok(nemo_fabric_core::version().to_string()) } -/// Execute the Rust experimentation CLI using arguments supplied by the console bridge. -#[pyfunction] -fn cli_main(args: Vec) -> PyResult<()> { - nemo_fabric_cli::app::execute_from(std::iter::once("nemo-fabric".to_string()).chain(args)) - .map_err(PyRuntimeError::new_err) -} - /// Resolve typed config JSON into a runnable plan and return JSON. #[pyfunction] #[pyo3(signature = (config_json, base_dir=None))] @@ -133,7 +126,6 @@ fn stop_runtime(py: Python<'_>, plan_json: String, runtime_json: String) -> PyRe #[pymodule] fn _native(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(version, m)?)?; - m.add_function(wrap_pyfunction!(cli_main, m)?)?; m.add_function(wrap_pyfunction!(plan_config, m)?)?; m.add_function(wrap_pyfunction!(doctor_config, m)?)?; m.add_function(wrap_pyfunction!(run_config, m)?)?; diff --git a/docs/about-nemo-fabric/overview.mdx b/docs/about-nemo-fabric/overview.mdx index 33d371557..a54e6a549 100644 --- a/docs/about-nemo-fabric/overview.mdx +++ b/docs/about-nemo-fabric/overview.mdx @@ -54,12 +54,11 @@ Hermes | Codex SDK | custom harness RunResult + artifacts + events + telemetry references ``` -The experimentation CLI and its catalogs are implemented once in the Rust -`fabric-cli` crate. The Python package exposes the same implementation as a -console command. Built-in presets and maintained examples produce a complete -`FabricConfig` before using the same run-plan contract. Adapters own -harness-specific preparation and invocation; consumers own the request and -returned evidence. +The experimentation CLI and its catalogs live in the Rust `fabric-cli` crate +and are installed separately from the Python SDK. Built-in presets and +maintained examples produce a complete `FabricConfig` before using the same +run-plan contract. Adapters own harness-specific preparation and invocation; +consumers own the request and returned evidence. ## Quick Start @@ -85,8 +84,7 @@ uv venv -p 3.13 --seed .venv source .venv/bin/activate ``` -Install the Python SDK with its native binding and the CLI from a source -checkout: +Build the Python SDK and install the Rust CLI from a source checkout: ```bash just build-all diff --git a/docs/experimentation/cli.mdx b/docs/experimentation/cli.mdx index fc11e8ec5..c447fc91c 100644 --- a/docs/experimentation/cli.mdx +++ b/docs/experimentation/cli.mdx @@ -12,9 +12,40 @@ SPDX-License-Identifier: Apache-2.0 */} The command implementation and catalogs live in the Rust `fabric-cli` crate. -The `nemo-fabric-runtime` Python package exposes that same implementation -through a thin console-script bridge. Package and standalone installs do not -maintain separate parsers, presets, or example catalogs. +The CLI is installed separately from the `nemo-fabric-runtime` Python SDK so +each interface can evolve around its own audience without maintaining a bridge +between them. + +## Install the CLI + +The CLI currently builds from a NeMo Fabric source checkout. Install it with +Cargo: + +```bash +cargo install --path crates/fabric-cli --locked +``` + +Verify that the executable is available: + +```bash +nemo-fabric --version +``` + +If the command is not found, add Cargo's binary directory to your `PATH`: + +```bash +export PATH="$HOME/.cargo/bin:$PATH" +``` + +When developing the CLI, run the workspace binary without installing it: + +```bash +cargo run -p nemo-fabric-cli -- preset list +``` + +Installing the Python SDK does not install the CLI. Adapter packages and +credentials are also separate requirements for presets that launch an external +harness. Use the credential-free `scripted` preset to verify the CLI by itself. ## Experiment With Presets diff --git a/docs/reference/api/python-library-reference/nemo_fabric.client.md b/docs/reference/api/python-library-reference/nemo_fabric.client.md index f4cff7762..206e407e4 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.client.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.client.md @@ -19,7 +19,7 @@ Primary Python entrypoint for NeMo Fabric. Every lifecycle method accepts a complete, typed ``FabricConfig`` plus an optional ``base_dir`` used to resolve relative paths. Compose variants in Python before calling the SDK. All inspection and execution APIs return typed, read-only mapping models. -``Fabric`` is native-only. The ``nemo-fabric`` CLI is backed by this public SDK; SDK calls raise ``FabricNativeUnavailableError`` when the native extension is not installed. +``Fabric`` uses the native Rust extension. SDK calls raise ``FabricNativeUnavailableError`` when the native extension is not installed. See the Getting Started overview for runnable one-shot, typed-config, and multi-turn examples. diff --git a/pypi.md b/pypi.md index 1297acb9e..b760799bf 100644 --- a/pypi.md +++ b/pypi.md @@ -14,7 +14,7 @@ collect artifacts from agent harnesses. It provides: - a versioned, typed configuration contract; - ordinary Python composition for experiment variants; - adapter integrations for harness-specific launch and control; -- a Python SDK, experimentation CLI, and Rust core; +- a Python SDK backed by the Rust core; - normalized run results, artifact manifests, and telemetry references. ## Install @@ -60,15 +60,14 @@ NeMo Fabric supports Python versions 3.11-3.14, however some of the integrations - **Typed configuration:** Construct a complete `FabricConfig` in Python and use ordinary functions to create experiment variants. -- **Experimentation CLI:** Run curated presets, copy editable examples, or - invoke a user-owned Python factory through the SDK-backed `nemo-fabric` CLI. - **Adapters:** Select harness-specific integrations with `harness.adapter_id`. - **Artifacts:** Receive normalized output, logs, patches, and telemetry references through an `ArtifactManifest`. -The CLI selects complete typed configs from presets, examples, or Python -factories. Applications that need a stable integration surface should use the +The experimental `nemo-fabric` CLI is distributed separately from the Python +package. It selects complete typed configs from built-in presets and maintained +examples. Applications that need a stable integration surface should use the Python SDK. ## Learn More diff --git a/python/pyproject.toml b/python/pyproject.toml index 041e9db09..4e952d051 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -27,9 +27,6 @@ dependencies = [ ] dynamic = ["version"] -[project.scripts] -nemo-fabric = "nemo_fabric.cli:main" - [project.urls] Repository = "https://github.com/NVIDIA/nemo-fabric/" Documentation = "https://nvidia-nemo-fabric.docs.buildwithfern.com/nemo/fabric" diff --git a/python/src/nemo_fabric/_native.pyi b/python/src/nemo_fabric/_native.pyi index 0ff233863..8df4b4bc0 100644 --- a/python/src/nemo_fabric/_native.pyi +++ b/python/src/nemo_fabric/_native.pyi @@ -2,7 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 def version() -> str: ... -def cli_main(args: list[str]) -> None: ... def plan_config( config_json: str, base_dir: str | None = None, diff --git a/python/src/nemo_fabric/cli.py b/python/src/nemo_fabric/cli.py deleted file mode 100644 index 5f4d54f32..000000000 --- a/python/src/nemo_fabric/cli.py +++ /dev/null @@ -1,24 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -"""Console-script bridge to the Rust experimentation CLI.""" - -from __future__ import annotations - -import sys - -from nemo_fabric import _native - - -def main() -> None: - """Run the single Rust/Clap CLI implementation.""" - - try: - _native.cli_main(sys.argv[1:]) - except RuntimeError as error: - print(f"error: {error}", file=sys.stderr) - raise SystemExit(1) from None - - -if __name__ == "__main__": - main() diff --git a/python/src/nemo_fabric/client.py b/python/src/nemo_fabric/client.py index 1285e8729..af33ba3c8 100644 --- a/python/src/nemo_fabric/client.py +++ b/python/src/nemo_fabric/client.py @@ -45,8 +45,7 @@ class Fabric: Python before calling the SDK. All inspection and execution APIs return typed, read-only mapping models. - ``Fabric`` is native-only. The ``nemo-fabric`` CLI is backed by this public - SDK; SDK calls raise + ``Fabric`` uses the native Rust extension. SDK calls raise ``FabricNativeUnavailableError`` when the native extension is not installed. diff --git a/tests/e2e/test_cli.py b/tests/e2e/test_cli.py deleted file mode 100644 index f94e9554b..000000000 --- a/tests/e2e/test_cli.py +++ /dev/null @@ -1,80 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -"""Installed-package bridge coverage for the Rust experimentation CLI.""" - -from __future__ import annotations - -import json -import subprocess -import sys -from pathlib import Path - - -def run(*args: str) -> subprocess.CompletedProcess[str]: - return subprocess.run( - [sys.executable, "-m", "nemo_fabric.cli", *args], - text=True, - capture_output=True, - check=False, - timeout=30, - ) - - -def test_package_bridge_lists_rust_catalogs(): - presets = run("preset", "list") - examples = run("example", "show", "code-review") - - assert presets.returncode == 0, presets.stderr - assert "scripted" in presets.stdout - assert "hermes" in presets.stdout - assert examples.returncode == 0, examples.stderr - assert "variants: scripted, hermes, claude, codex, deepagents" in examples.stdout - - -def test_package_bridge_plans_preset_and_example(): - preset = run("plan", "--preset", "scripted") - example = run("plan", "--example", "code-review", "--variant", "hermes") - - assert preset.returncode == 0, preset.stderr - assert json.loads(preset.stdout)["agent_name"] == "scripted-agent" - assert example.returncode == 0, example.stderr - plan = json.loads(example.stdout) - assert plan["agent_name"] == "code-review-agent" - assert plan["config"]["harness"]["adapter_id"] == "nvidia.fabric.hermes" - - -def test_package_bridge_runs_the_deterministic_preset(): - completed = run("run", "--preset", "scripted", "--input", "package-smoke") - - assert completed.returncode == 0, completed.stderr - result = json.loads(completed.stdout) - assert result["status"] == "succeeded" - assert result["output"]["response"] == "package-smoke" - - -def test_package_bridge_generates_editable_languages(tmp_path: Path): - for language in ("python", "rust"): - destination = tmp_path / language - completed = run( - "example", - "init", - "code-review", - str(destination), - "--language", - language, - "--variant", - "hermes", - ) - assert completed.returncode == 0, completed.stderr - assert (destination / "repo" / "calculator.py").is_file() - assert (destination / "skills" / "code-review.md").is_file() - launcher = destination / ("main.py" if language == "python" else "src/main.rs") - assert "nvidia.fabric.hermes" in launcher.read_text(encoding="utf-8") - - -def test_cli_rejects_removed_configuration_sources(): - for removed in ("--config", "--profile", "--factory"): - completed = run("plan", removed, "value") - assert completed.returncode != 0 - assert "unexpected argument" in completed.stderr From 376d9aa623884a230679d9f6fed1b3b633dee1d2 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Sun, 19 Jul 2026 11:56:21 -0700 Subject: [PATCH 11/20] feat: allow preset model overrides Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- crates/fabric-cli/src/app.rs | 110 ++++++++++++++++++++++++++++++++++- docs/experimentation/cli.mdx | 13 +++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/crates/fabric-cli/src/app.rs b/crates/fabric-cli/src/app.rs index bbe4545c0..9dd1f1506 100644 --- a/crates/fabric-cli/src/app.rs +++ b/crates/fabric-cli/src/app.rs @@ -108,6 +108,32 @@ struct Selector { conflicts_with = "preset" )] variant: Option, + /// Override the preset's default model identifier. + #[arg( + long, + value_name = "MODEL", + conflicts_with = "example", + value_parser = clap::builder::NonEmptyStringValueParser::new() + )] + model: Option, + /// Override the preset's default model temperature. + #[arg( + long, + value_name = "FLOAT", + conflicts_with = "example", + value_parser = parse_temperature + )] + temperature: Option, +} + +fn parse_temperature(value: &str) -> Result { + let temperature = value + .parse::() + .map_err(|error| format!("invalid temperature: {error}"))?; + if !temperature.is_finite() { + return Err("temperature must be finite".to_string()); + } + Ok(temperature) } enum SelectedSource { @@ -238,7 +264,9 @@ fn run(cli: Cli) -> Result<(), Box> { fn select_source(selector: &Selector) -> Result> { if let Some(name) = &selector.preset { - return Ok(SelectedSource::Preset(find_preset(name)?.stage()?)); + let mut selected = find_preset(name)?.stage()?; + apply_model_overrides(&mut selected.config, selector)?; + return Ok(SelectedSource::Preset(selected)); } if let Some(name) = &selector.example { return Ok(SelectedSource::Example( @@ -248,6 +276,26 @@ fn select_source(selector: &Selector) -> Result Result<(), String> { + if selector.model.is_none() && selector.temperature.is_none() { + return Ok(()); + } + let default_model = config + .models + .get_mut("default") + .ok_or_else(|| "the selected preset does not define a default model".to_string())?; + if let Some(model) = &selector.model { + default_model.model.clone_from(model); + } + if let Some(temperature) = selector.temperature { + default_model.temperature = Some(temperature); + } + Ok(()) +} + fn find_preset(name: &str) -> Result { presets::find(name).ok_or_else(|| { format!( @@ -287,6 +335,66 @@ mod tests { assert!(matches!(cli.command, Command::Plan(_))); } + #[test] + fn preset_model_overrides_preserve_provider_settings() { + let selector = Selector { + preset: Some("hermes".to_string()), + example: None, + variant: None, + model: Some("meta/llama-3.3-70b-instruct".to_string()), + temperature: Some(0.2), + }; + let selected = select_source(&selector).expect("select preset"); + let model = selected.config().models.get("default").expect("model"); + + assert_eq!(model.provider, "nvidia"); + assert_eq!(model.model, "meta/llama-3.3-70b-instruct"); + assert_eq!(model.temperature, Some(0.2)); + assert_eq!(model.api_key_env.as_deref(), Some("NVIDIA_API_KEY")); + } + + #[test] + fn model_overrides_require_a_preset_with_a_default_model() { + assert!( + Cli::try_parse_from([ + "nemo-fabric", + "plan", + "--example", + "code-review", + "--model", + "other-model", + ]) + .is_err() + ); + + let selector = Selector { + preset: Some("scripted".to_string()), + example: None, + variant: None, + model: Some("other-model".to_string()), + temperature: None, + }; + assert_eq!( + select_source(&selector) + .err() + .expect("scripted has no default model") + .to_string(), + "the selected preset does not define a default model" + ); + + assert!( + Cli::try_parse_from([ + "nemo-fabric", + "plan", + "--preset", + "hermes", + "--temperature", + "NaN", + ]) + .is_err() + ); + } + #[test] fn parses_example_variant() { let cli = Cli::try_parse_from([ diff --git a/docs/experimentation/cli.mdx b/docs/experimentation/cli.mdx index c447fc91c..9b12fce60 100644 --- a/docs/experimentation/cli.mdx +++ b/docs/experimentation/cli.mdx @@ -87,6 +87,19 @@ Run the preset with an input: nemo-fabric run --preset hermes --input "Say hello" ``` +Override the preset's default model and temperature for a quick experiment: + +```bash +nemo-fabric run --preset hermes \ + --model meta/llama-3.3-70b-instruct \ + --temperature 0.2 \ + --input "Say hello" +``` + +These flags preserve the preset's provider, credential environment variable, +endpoint, and harness settings. Use a model that is available from the preset's +provider. The `plan` and `doctor` commands accept the same overrides. + ## Experiment With Examples List the maintained workflows and inspect an example's available variants: From 08e1650bba09c39c1ad3eb0d2142b0433cbddf85 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Sun, 19 Jul 2026 12:26:20 -0700 Subject: [PATCH 12/20] feat: support NVIDIA models in Claude adapter Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- .../nemo_fabric_adapters/claude/adapter.py | 43 ++++++++++++- tests/adapters/test_claude_adapter.py | 63 ++++++++++++++++++- 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py b/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py index 16815969d..3ea25bb7e 100644 --- a/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py +++ b/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py @@ -36,6 +36,8 @@ LOGGER = logging.getLogger(__name__) +NVIDIA_ANTHROPIC_BASE_URL = "https://integrate.api.nvidia.com" + PERMISSION_MODES = { "default", "acceptEdits", @@ -232,16 +234,50 @@ def selected_model(payload: dict[str, Any]) -> str | None: value = model_config.get("model") if value is None: return None - if model_config.get("provider") != "anthropic": + provider = model_config.get("provider") + if provider not in {"anthropic", "nvidia"}: raise AdapterConfigError( "claude_invalid_configuration", - "models.default.provider must be anthropic for the Claude adapter", + "models.default.provider must be anthropic or nvidia for the Claude adapter", ) if not isinstance(value, str) or not value: raise AdapterConfigError( "claude_invalid_configuration", "model must be a non-empty string" ) - return value.removeprefix("anthropic/") + return value.removeprefix("anthropic/") if provider == "anthropic" else value + + +def _nvidia_environment(payload: dict[str, Any]) -> dict[str, str]: + model = _selected_model_config(payload) + if model.get("provider") != "nvidia": + return {} + api_key_env = model.get("api_key_env") or "NVIDIA_API_KEY" + if not isinstance(api_key_env, str) or not api_key_env: + raise AdapterConfigError( + "claude_invalid_configuration", + "models.default.api_key_env must be a non-empty string", + ) + api_key = os.environ.get(api_key_env) + if not api_key: + raise AdapterConfigError( + "claude_invalid_configuration", + f"{api_key_env} is required for the NVIDIA model provider", + ) + settings = _settings(payload) + model_settings = _mapping(model.get("settings"), name="models.default.settings") + base_url = settings.get("base_url") or model_settings.get("base_url") + if base_url is None: + base_url = NVIDIA_ANTHROPIC_BASE_URL + if not isinstance(base_url, str) or not base_url: + raise AdapterConfigError( + "claude_invalid_configuration", + "the NVIDIA model provider base URL must be a non-empty string", + ) + return { + "ANTHROPIC_API_KEY": "", + "ANTHROPIC_AUTH_TOKEN": api_key, + "ANTHROPIC_BASE_URL": base_url.rstrip("/"), + } def _mcp_servers(payload: dict[str, Any]) -> dict[str, Any]: @@ -705,6 +741,7 @@ def child_environment( "claude_invalid_configuration", "harness.settings.env must contain strings" ) values.update(configured) + values.update(_nvidia_environment(payload)) if relay_gateway_url is not None: values["NEMO_RELAY_GATEWAY_URL"] = relay_gateway_url values["ANTHROPIC_BASE_URL"] = relay_gateway_url diff --git a/tests/adapters/test_claude_adapter.py b/tests/adapters/test_claude_adapter.py index a2e231e93..d67798eef 100644 --- a/tests/adapters/test_claude_adapter.py +++ b/tests/adapters/test_claude_adapter.py @@ -370,11 +370,70 @@ def test_build_options_rejects_skill_path_without_skill_manifest(claude_payload) adapter.build_options(claude_payload, resume=None) +def test_build_options_maps_nvidia_provider_to_claude_gateway_environment( + claude_payload, +): + model = claude_payload["config"]["models"]["default"] + model.update( + { + "provider": "nvidia", + "model": "aws/anthropic/claude-opus-4-5", + "api_key_env": "NVIDIA_API_KEY", + "settings": {"base_url": "https://nvidia.example/"}, + } + ) + os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + + options = adapter.build_options(claude_payload, resume=None) + + assert options.model == "aws/anthropic/claude-opus-4-5" + assert options.env["ANTHROPIC_BASE_URL"] == "https://nvidia.example" + assert options.env["ANTHROPIC_AUTH_TOKEN"] == "nvidia-secret" + assert options.env["ANTHROPIC_API_KEY"] == "" + + +def test_build_options_defaults_nvidia_provider_endpoint_and_credential( + claude_payload, +): + model = claude_payload["config"]["models"]["default"] + model.update( + { + "provider": "nvidia", + "model": "aws/anthropic/claude-opus-4-5", + } + ) + model.pop("api_key_env") + claude_payload["config"]["harness"]["settings"].pop("env") + os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + + options = adapter.build_options(claude_payload, resume=None) + + assert options.env["ANTHROPIC_BASE_URL"] == adapter.NVIDIA_ANTHROPIC_BASE_URL + assert options.env["ANTHROPIC_AUTH_TOKEN"] == "nvidia-secret" + + +def test_build_options_requires_nvidia_provider_credential(claude_payload): + model = claude_payload["config"]["models"]["default"] + model.update( + { + "provider": "nvidia", + "model": "aws/anthropic/claude-opus-4-5", + "api_key_env": "NVIDIA_API_KEY", + } + ) + os.environ.pop("NVIDIA_API_KEY", None) + + with pytest.raises(adapter.AdapterConfigError, match="NVIDIA_API_KEY is required"): + adapter.build_options(claude_payload, resume=None) + + def test_selected_model_rejects_unsupported_provider(claude_payload): model = claude_payload["config"]["models"]["default"] - model["provider"] = "nvidia" + model["provider"] = "openai" - with pytest.raises(adapter.AdapterConfigError, match="provider must be anthropic"): + with pytest.raises( + adapter.AdapterConfigError, match="provider must be anthropic or nvidia" + ): adapter.selected_model(claude_payload) From b593f8215adfc69785fb16db3ca6457882b6a116 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Sun, 19 Jul 2026 13:06:19 -0700 Subject: [PATCH 13/20] feat: route CLI presets through NVIDIA inference Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- README.md | 5 +- adapters/claude/README.md | 11 ++- .../nemo_fabric_adapters/claude/adapter.py | 20 +++-- adapters/codex/README.md | 11 ++- .../src/nemo_fabric_adapters/codex/adapter.py | 58 +++++++++++++- crates/fabric-cli/src/presets.rs | 75 ++++++++++++++----- crates/fabric-cli/src/scaffold.rs | 2 +- docs/experimentation/cli.mdx | 44 ++++++++++- tests/adapters/test_claude_adapter.py | 12 +-- tests/adapters/test_codex_adapter.py | 56 +++++++++++++- 10 files changed, 248 insertions(+), 46 deletions(-) diff --git a/README.md b/README.md index fd280f56a..92a002d5f 100644 --- a/README.md +++ b/README.md @@ -160,8 +160,9 @@ authentication, and execution details. ## Core Concepts - **Typed config:** callers construct a complete `FabricConfig` in Python. - Start with `examples/code_review_agent/config.py` for the application-facing - Pydantic pattern. + Start with the [Python SDK guide](docs/sdk/python.mdx), and refer to + [`examples/code_review_agent/config.py`](examples/code_review_agent/config.py) + for a complete application example. - **Variants:** ordinary Python functions copy and modify complete configs to vary the harness, model, MCP, tools, skills, telemetry, or environment. - **Experimentation CLI:** presets provide quick probes, examples provide diff --git a/adapters/claude/README.md b/adapters/claude/README.md index a0d159936..f2bc13dc9 100644 --- a/adapters/claude/README.md +++ b/adapters/claude/README.md @@ -35,6 +35,11 @@ bearer credential, `ANTHROPIC_API_KEY` for a static API credential, or Anthropic Workload Identity Federation (WIF) for production and CI workloads that should not store a long-lived API key. +When `models.default.provider` is `nvidia`, the adapter reads the selected +model's credential from `api_key_env` (default: `NVIDIA_API_KEY`) and translates +the configured NVIDIA `/v1` endpoint into the host URL expected by Claude Code. +This request-scoped mapping does not change the parent environment. + The adapter forwards the Anthropic profile and federation environment variables that Claude Code and the Claude Agent SDK consume. This includes `ANTHROPIC_CONFIG_DIR`, `ANTHROPIC_PROFILE`, the direct federation identifiers, @@ -74,9 +79,9 @@ stays resident. Configure portable capabilities through the normalized `FabricConfig` fields: -- `models` selects the Claude model. A configured model must use - `provider="anthropic"`; normalized hosted/custom provider resolution is - tracked in [FABRIC-64](https://linear.app/nvidia/issue/FABRIC-64/add-normalized-model-provider-resolution-and-harness-compatibility). +- `models` selects the Claude model. The adapter accepts the native `anthropic` + provider and NVIDIA-hosted Anthropic Messages-compatible models through the + `nvidia` provider. - `environment.workspace` sets the Claude working directory. - `tools.blocked` maps to Claude `disallowed_tools` using Claude-native tool names. diff --git a/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py b/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py index 3ea25bb7e..e33d4a51a 100644 --- a/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py +++ b/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py @@ -36,7 +36,7 @@ LOGGER = logging.getLogger(__name__) -NVIDIA_ANTHROPIC_BASE_URL = "https://integrate.api.nvidia.com" +NVIDIA_FRONTIER_BASE_URL = "https://inference-api.nvidia.com/v1" PERMISSION_MODES = { "default", @@ -265,18 +265,24 @@ def _nvidia_environment(payload: dict[str, Any]) -> dict[str, str]: ) settings = _settings(payload) model_settings = _mapping(model.get("settings"), name="models.default.settings") - base_url = settings.get("base_url") or model_settings.get("base_url") - if base_url is None: - base_url = NVIDIA_ANTHROPIC_BASE_URL + base_url = ( + settings.get("base_url") + or model_settings.get("base_url") + or os.environ.get("NVIDIA_FRONTIER_BASE_URL") + or NVIDIA_FRONTIER_BASE_URL + ) if not isinstance(base_url, str) or not base_url: raise AdapterConfigError( "claude_invalid_configuration", "the NVIDIA model provider base URL must be a non-empty string", ) + # Claude Code appends the Anthropic API version path itself, while Fabric's + # shared NVIDIA endpoint includes it for OpenAI-compatible clients. + claude_base_url = base_url.rstrip("/").removesuffix("/v1") return { - "ANTHROPIC_API_KEY": "", - "ANTHROPIC_AUTH_TOKEN": api_key, - "ANTHROPIC_BASE_URL": base_url.rstrip("/"), + "ANTHROPIC_API_KEY": api_key, + "ANTHROPIC_AUTH_TOKEN": "", + "ANTHROPIC_BASE_URL": claude_base_url, } diff --git a/adapters/codex/README.md b/adapters/codex/README.md index a311dd401..ffe7dada8 100644 --- a/adapters/codex/README.md +++ b/adapters/codex/README.md @@ -49,6 +49,12 @@ the SDK runtime. The current real-agent acceptance path validates an existing Codex login; it does not yet claim a raw environment variable as a complete login flow. +When `models.default.provider` is `nvidia`, the adapter defines a request-scoped +Codex model provider for the configured NVIDIA Responses endpoint. It reads the +credential from `api_key_env` (default: `NVIDIA_API_KEY`) and isolates Codex +state under the Fabric artifact root, so the invocation does not depend on or +modify a user's Codex login. + The dependency graph includes `openai-codex-cli-bin`. The Codex SDK owns this pinned app-server distribution; Fabric does not treat it as a user-installed command or an adapter descriptor requirement. @@ -76,8 +82,9 @@ return codes, stdout, or stderr. Use normalized `FabricConfig` fields for portable configuration: -- `models` selects the Codex model. The adapter requires and explicitly selects - the built-in `openai` provider. +- `models` selects the Codex model. The adapter supports the built-in `openai` + provider and NVIDIA-hosted Responses-compatible models through the `nvidia` + provider. - `environment.workspace` sets the working directory. - `telemetry` enables native OpenTelemetry or NeMo Relay observability. diff --git a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py index 73b960b30..8d414d1c7 100644 --- a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py +++ b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py @@ -196,16 +196,61 @@ def selected_model(payload: dict[str, Any]) -> str | None: value = model_config.get("model") if value is None: return None - if model_config.get("provider") != "openai": + provider = model_config.get("provider") + if provider not in {"openai", "nvidia"}: raise AdapterConfigError( "codex_invalid_configuration", - "selected model provider must be openai for the Codex adapter", + "selected model provider must be openai or nvidia for the Codex adapter", ) if not isinstance(value, str) or not value: raise AdapterConfigError( "codex_invalid_configuration", "model must be a non-empty string" ) - return value.removeprefix("openai/") + return value.removeprefix("openai/") if provider == "openai" else value + + +def selected_model_provider(payload: dict[str, Any]) -> str: + return str(_selected_model_config(payload).get("provider") or "openai") + + +def nvidia_model_provider_config(payload: dict[str, Any]) -> dict[str, Any]: + model_config = _selected_model_config(payload) + if model_config.get("provider") != "nvidia": + return {} + api_key_env = model_config.get("api_key_env") or "NVIDIA_API_KEY" + if not isinstance(api_key_env, str) or not api_key_env: + raise AdapterConfigError( + "codex_invalid_configuration", + "models.default.api_key_env must be a non-empty string", + ) + if not os.environ.get(api_key_env): + raise AdapterConfigError( + "codex_invalid_configuration", + f"{api_key_env} is required for the NVIDIA model provider", + ) + model_settings = _mapping( + model_config.get("settings"), name="selected model settings" + ) + base_url = ( + model_settings.get("base_url") + or os.environ.get("NVIDIA_FRONTIER_BASE_URL") + or "https://inference-api.nvidia.com/v1" + ) + if not isinstance(base_url, str) or not base_url: + raise AdapterConfigError( + "codex_invalid_configuration", + "the NVIDIA model provider base URL must be a non-empty string", + ) + return { + "model_providers": { + "nvidia": { + "name": "NVIDIA", + "base_url": base_url.rstrip("/"), + "env_key": api_key_env, + "wire_api": "responses", + } + } + } def sandbox(payload: dict[str, Any]) -> Sandbox: @@ -296,6 +341,10 @@ def child_environment( "harness.settings.env must contain strings", ) values.update(configured) + if selected_model_provider(payload) == "nvidia": + codex_home = state_dir(payload) / "nvidia-home" + codex_home.mkdir(parents=True, exist_ok=True) + values["CODEX_HOME"] = str(codex_home) # The SDK overlays this mapping on the parent environment. An empty # originator is still treated as an override by Codex and produces invalid # initialize metadata ("/"). Use the official SDK client identity @@ -520,6 +569,7 @@ def thread_config( """Build request-scoped Codex config without writing a user profile.""" config = native_codex_telemetry_config(payload) + _merge_config(config, nvidia_model_provider_config(payload)) overrides = _mapping( _settings(payload).get("config_overrides"), name="harness.settings.config_overrides", @@ -780,7 +830,7 @@ async def invoke_codex_sdk( settings, "developer_instructions" ), "model": selected_model(payload), - "model_provider": "openai", + "model_provider": selected_model_provider(payload), "personality": _personality(payload), "sandbox": sandbox(payload), "service_tier": _optional_string(settings, "service_tier"), diff --git a/crates/fabric-cli/src/presets.rs b/crates/fabric-cli/src/presets.rs index bf7d959de..0f3331d69 100644 --- a/crates/fabric-cli/src/presets.rs +++ b/crates/fabric-cli/src/presets.rs @@ -20,6 +20,7 @@ const CLAUDE_DESCRIPTOR: &str = include_str!("../../../adapters/claude/fabric-ad const CODEX_DESCRIPTOR: &str = include_str!("../../../adapters/codex/fabric-adapter.json"); const DEEPAGENTS_DESCRIPTOR: &str = include_str!("../../../adapters/deepagents/fabric-adapter.json"); +const NVIDIA_FRONTIER_BASE_URL: &str = "https://inference-api.nvidia.com/v1"; const SCRIPTED_ASSETS: &[EmbeddedFile] = &[ EmbeddedFile { @@ -125,15 +126,15 @@ const PRESETS: [Preset; 5] = [ }, Preset { name: "claude", - description: "Claude Code with an Anthropic model.", - required_env: &["ANTHROPIC_API_KEY"], + description: "Claude Code with an NVIDIA-hosted Claude model.", + required_env: &["NVIDIA_API_KEY"], build: claude, assets: CLAUDE_ASSETS, }, Preset { name: "codex", - description: "Codex with an OpenAI model.", - required_env: &["OPENAI_API_KEY"], + description: "Codex with an NVIDIA-hosted OpenAI model.", + required_env: &["NVIDIA_API_KEY"], build: codex, assets: CODEX_ASSETS, }, @@ -163,13 +164,11 @@ fn hermes() -> FabricConfig { "nvidia.fabric.hermes", Some(model( "nvidia", - "nvidia/nemotron-3-nano-30b-a3b", + "nvidia/nvidia/Nemotron-3-Nano-30B-A3B", Some("NVIDIA_API_KEY"), + Some(NVIDIA_FRONTIER_BASE_URL), )), - Map::from_iter([( - "base_url".to_string(), - json!("https://integrate.api.nvidia.com/v1"), - )]), + Map::new(), ) } @@ -179,9 +178,10 @@ fn claude() -> FabricConfig { "NeMo Fabric Claude CLI preset.", "nvidia.fabric.claude", Some(model( - "anthropic", - "anthropic/claude-sonnet-4-5", - Some("ANTHROPIC_API_KEY"), + "nvidia", + "aws/anthropic/claude-opus-4-5", + Some("NVIDIA_API_KEY"), + Some(NVIDIA_FRONTIER_BASE_URL), )), Map::from_iter([("permission_mode".to_string(), json!("dontAsk"))]), ) @@ -192,8 +192,24 @@ fn codex() -> FabricConfig { "codex-agent", "NeMo Fabric Codex CLI preset.", "nvidia.fabric.codex", - Some(model("openai", "openai/gpt-5.4", Some("OPENAI_API_KEY"))), - Map::from_iter([("sandbox".to_string(), json!("workspace-write"))]), + Some(model( + "nvidia", + "azure/openai/gpt-5.4", + Some("NVIDIA_API_KEY"), + Some(NVIDIA_FRONTIER_BASE_URL), + )), + Map::from_iter([ + ("sandbox".to_string(), json!("workspace-write")), + ( + "config_overrides".to_string(), + json!({ + "features.apps": false, + "features.multi_agent": false, + "features.plugins": false, + "web_search": "disabled", + }), + ), + ]), ) } @@ -204,8 +220,9 @@ fn deepagents() -> FabricConfig { "nvidia.fabric.langchain.deepagents", Some(model( "nvidia", - "nvidia/nemotron-3-nano-30b-a3b", + "nvidia/nvidia/Nemotron-3-Nano-30B-A3B", Some("NVIDIA_API_KEY"), + Some(NVIDIA_FRONTIER_BASE_URL), )), Map::new(), ) @@ -260,13 +277,20 @@ fn config( } } -fn model(provider: &str, name: &str, api_key_env: Option<&str>) -> ModelConfig { +fn model( + provider: &str, + name: &str, + api_key_env: Option<&str>, + base_url: Option<&str>, +) -> ModelConfig { ModelConfig { provider: provider.to_string(), model: name.to_string(), temperature: None, api_key_env: api_key_env.map(str::to_string), - settings: Map::new(), + settings: base_url + .map(|value| Map::from_iter([("base_url".to_string(), json!(value))])) + .unwrap_or_default(), extensions: BTreeMap::new(), } } @@ -309,4 +333,21 @@ mod tests { "nvidia.fabric.scripted" ); } + + #[test] + fn adapter_presets_use_nvidia_credentials() { + for name in ["hermes", "claude", "codex", "deepagents"] { + let preset = find(name).expect("adapter preset"); + let config = preset.config(); + let model = config.models.get("default").expect("default model"); + + assert_eq!(model.provider, "nvidia"); + assert_eq!(model.api_key_env.as_deref(), Some("NVIDIA_API_KEY")); + assert_eq!( + model.settings.get("base_url").and_then(Value::as_str), + Some(NVIDIA_FRONTIER_BASE_URL) + ); + assert!(preset.required_env.contains(&"NVIDIA_API_KEY")); + } + } } diff --git a/crates/fabric-cli/src/scaffold.rs b/crates/fabric-cli/src/scaffold.rs index 9bc5c651b..638d24ee7 100644 --- a/crates/fabric-cli/src/scaffold.rs +++ b/crates/fabric-cli/src/scaffold.rs @@ -278,7 +278,7 @@ mod tests { }; let source = fs::read_to_string(launcher).expect("read launcher"); assert!(source.contains("nvidia.fabric.hermes")); - assert!(source.contains("nvidia/nemotron-3-nano-30b-a3b")); + assert!(source.contains("nvidia/nvidia/Nemotron-3-Nano-30B-A3B")); fs::remove_dir_all(destination).expect("remove scaffold"); } } diff --git a/docs/experimentation/cli.mdx b/docs/experimentation/cli.mdx index 9b12fce60..251c8973d 100644 --- a/docs/experimentation/cli.mdx +++ b/docs/experimentation/cli.mdx @@ -25,6 +25,18 @@ Cargo: cargo install --path crates/fabric-cli --locked ``` +Set up the checkout's Python environment when you want to run presets backed +by Python adapters: + +```bash +uv sync --all-groups --all-extras +export ADAPTER_PYTHON="$PWD/.venv/bin/python" +``` + +`ADAPTER_PYTHON` tells the Rust runtime which interpreter contains the selected +adapter and harness. Use an absolute path if you run `nemo-fabric` outside the +checkout. + Verify that the executable is available: ```bash @@ -49,6 +61,29 @@ harness. Use the credential-free `scripted` preset to verify the CLI by itself. ## Experiment With Presets +Presets are complete, embedded `FabricConfig` values intended for quick +experiments: + +| Preset | Harness | Default model | Credential | +| --- | --- | --- | --- | +| `scripted` | Deterministic test adapter | None | None | +| `hermes` | Hermes Agent | `nvidia/nvidia/Nemotron-3-Nano-30B-A3B` | `NVIDIA_API_KEY` | +| `claude` | Claude Code | `aws/anthropic/claude-opus-4-5` | `NVIDIA_API_KEY` | +| `codex` | Codex | `azure/openai/gpt-5.4` | `NVIDIA_API_KEY` | +| `deepagents` | LangChain Deep Agents | `nvidia/nvidia/Nemotron-3-Nano-30B-A3B` | `NVIDIA_API_KEY` | + +The `scripted` preset does not call a model. It returns a deterministic response +through the same Fabric runtime and adapter contract, which makes it useful for +checking CLI installation, request flow, and result formatting without network +access or credentials. + +The four external-harness presets use the NVIDIA inference endpoint. Export one +credential before running them: + +```bash +export NVIDIA_API_KEY="..." +``` + List the complete configurations maintained by the CLI: ```bash @@ -91,14 +126,17 @@ Override the preset's default model and temperature for a quick experiment: ```bash nemo-fabric run --preset hermes \ - --model meta/llama-3.3-70b-instruct \ + --model nvidia/meta/llama-3.3-70b-instruct \ --temperature 0.2 \ --input "Say hello" ``` These flags preserve the preset's provider, credential environment variable, endpoint, and harness settings. Use a model that is available from the preset's -provider. The `plan` and `doctor` commands accept the same overrides. +provider and compatible with the selected harness. For example, a Claude preset +still requires an Anthropic Messages-compatible model, while a Codex preset +requires a Responses-compatible model. The `plan` and `doctor` commands accept +the same overrides. ## Experiment With Examples @@ -167,6 +205,8 @@ cargo run -- "Review the workspace" The Rust scaffold constructs `FabricConfig` and calls `fabric-core` directly. Neither scaffold is loaded back into the central CLI. +## CLI Boundaries + The CLI has the following boundaries: - Every preset and example variant constructs a complete typed `FabricConfig`. diff --git a/tests/adapters/test_claude_adapter.py b/tests/adapters/test_claude_adapter.py index d67798eef..fd2279ca8 100644 --- a/tests/adapters/test_claude_adapter.py +++ b/tests/adapters/test_claude_adapter.py @@ -379,7 +379,7 @@ def test_build_options_maps_nvidia_provider_to_claude_gateway_environment( "provider": "nvidia", "model": "aws/anthropic/claude-opus-4-5", "api_key_env": "NVIDIA_API_KEY", - "settings": {"base_url": "https://nvidia.example/"}, + "settings": {"base_url": "https://nvidia.example/v1/"}, } ) os.environ["NVIDIA_API_KEY"] = "nvidia-secret" @@ -388,11 +388,11 @@ def test_build_options_maps_nvidia_provider_to_claude_gateway_environment( assert options.model == "aws/anthropic/claude-opus-4-5" assert options.env["ANTHROPIC_BASE_URL"] == "https://nvidia.example" - assert options.env["ANTHROPIC_AUTH_TOKEN"] == "nvidia-secret" - assert options.env["ANTHROPIC_API_KEY"] == "" + assert options.env["ANTHROPIC_API_KEY"] == "nvidia-secret" + assert options.env["ANTHROPIC_AUTH_TOKEN"] == "" -def test_build_options_defaults_nvidia_provider_endpoint_and_credential( +def test_build_options_uses_nvidia_provider_endpoint_and_default_credential( claude_payload, ): model = claude_payload["config"]["models"]["default"] @@ -408,8 +408,8 @@ def test_build_options_defaults_nvidia_provider_endpoint_and_credential( options = adapter.build_options(claude_payload, resume=None) - assert options.env["ANTHROPIC_BASE_URL"] == adapter.NVIDIA_ANTHROPIC_BASE_URL - assert options.env["ANTHROPIC_AUTH_TOKEN"] == "nvidia-secret" + assert options.env["ANTHROPIC_BASE_URL"] == "https://inference-api.nvidia.com" + assert options.env["ANTHROPIC_API_KEY"] == "nvidia-secret" def test_build_options_requires_nvidia_provider_credential(claude_payload): diff --git a/tests/adapters/test_codex_adapter.py b/tests/adapters/test_codex_adapter.py index 1e70af43e..d55cf9f23 100644 --- a/tests/adapters/test_codex_adapter.py +++ b/tests/adapters/test_codex_adapter.py @@ -293,12 +293,64 @@ def test_incomplete_sdk_turn_is_failed_without_persisting_thread( def test_selected_model_rejects_unsupported_provider(codex_payload, mock_codex): model = codex_payload["config"]["models"]["default"] - model["provider"] = "nvidia" + model["provider"] = "anthropic" output = adapter.run(codex_payload) assert output["error"]["code"] == "codex_invalid_configuration" - assert "provider must be openai" in output["error"]["message"] + assert "provider must be openai or nvidia" in output["error"]["message"] + mock_codex.assert_not_called() + + +def test_nvidia_provider_uses_responses_api_and_nvidia_credential( + codex_payload, mock_codex +): + model = codex_payload["config"]["models"]["default"] + model.update( + { + "provider": "nvidia", + "model": "openai/gpt-oss-120b", + "api_key_env": "NVIDIA_API_KEY", + "settings": {"base_url": "https://nvidia.example/v1/"}, + } + ) + os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + + output = adapter.run(codex_payload) + + assert output["completed"] is True + client = mock_codex.instances[0] + assert client.config.env["NVIDIA_API_KEY"] == "nvidia-secret" + start = client.thread_start.await_args.kwargs + assert start["model"] == "openai/gpt-oss-120b" + assert start["model_provider"] == "nvidia" + assert client.config.env["CODEX_HOME"].endswith("/.fabric/codex/nvidia-home") + assert start["config"]["features"] == {"web_search": False} + assert start["config"]["model_providers"] == { + "nvidia": { + "name": "NVIDIA", + "base_url": "https://nvidia.example/v1", + "env_key": "NVIDIA_API_KEY", + "wire_api": "responses", + } + } + + +def test_nvidia_provider_requires_credential(codex_payload, mock_codex): + model = codex_payload["config"]["models"]["default"] + model.update( + { + "provider": "nvidia", + "model": "openai/gpt-oss-120b", + "api_key_env": "NVIDIA_API_KEY", + } + ) + os.environ.pop("NVIDIA_API_KEY", None) + + output = adapter.run(codex_payload) + + assert output["error"]["code"] == "codex_invalid_configuration" + assert "NVIDIA_API_KEY is required" in output["error"]["message"] mock_codex.assert_not_called() From 7a3aa0d97e0cdc608054f684c6122dbbefc0e8b8 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Mon, 20 Jul 2026 09:39:19 -0700 Subject: [PATCH 14/20] docs: clarify CLI examples and scaffolding Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- TODO.md | 19 +++++++++++++++++++ docs/experimentation/cli.mdx | 19 +++++++++---------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/TODO.md b/TODO.md index 1eddbbdc4..86a7863ab 100644 --- a/TODO.md +++ b/TODO.md @@ -66,3 +66,22 @@ cleanup validation. PyPI in a version the skill can target. - **Cleanup:** Replace the source and wheel install steps in the skill with the published `pip install "nemo-fabric[...]"` instructions and remove this entry. + +# Planned Work + +## Example Authoring Skill + +- **Status:** Not started +- **Purpose:** Add a guided workflow for users who need help designing a new + example, selecting the closest preset or maintained example, customizing the + generated code, and adding documentation and tests. +- **Boundary:** Keep `nemo-fabric example init` as the deterministic operation + for users who already know which example and variant they want to customize. + The skill provides authoring judgment and validation around that operation. +- **Implementation:** Have the skill invoke or reuse `example init` as its + starting point, then modify and validate the generated application. Do not + create a separate template collection or scaffolding implementation in the + skill. +- **Completion condition:** The skill can guide a user from an example idea to + a runnable, documented, and tested Python or Rust example while keeping the + CLI scaffold as the single source for generated starter code. diff --git a/docs/experimentation/cli.mdx b/docs/experimentation/cli.mdx index 251c8973d..d91438e6b 100644 --- a/docs/experimentation/cli.mdx +++ b/docs/experimentation/cli.mdx @@ -11,10 +11,10 @@ SPDX-License-Identifier: Apache-2.0 */} services should construct `FabricConfig` through a language API directly. -The command implementation and catalogs live in the Rust `fabric-cli` crate. -The CLI is installed separately from the `nemo-fabric-runtime` Python SDK so -each interface can evolve around its own audience without maintaining a bridge -between them. +The command implementation, built-in presets, and maintained example +definitions live in the Rust `fabric-cli` crate. +The `nemo-fabric` CLI is installed separately from the `nemo-fabric-runtime` +Python SDK. Installing the Python SDK does not install the CLI. ## Install the CLI @@ -212,13 +212,12 @@ The CLI has the following boundaries: - Every preset and example variant constructs a complete typed `FabricConfig`. - Examples reuse preset constructors and one shared workspace and skill asset tree; Python and Rust launchers do not duplicate those definitions. -- Variants do not inherit, merge, or behave like profiles. -- There is no `--config`, `--profile`, or `--factory` input. - Fabric does not discover or persist YAML, TOML, or JSON agent configuration. -- JSON request payloads and harness-generated files remain valid; they are not - Fabric configuration sources. +- JSON request payloads and harness-generated files are runtime inputs and + outputs, not Fabric configuration sources. - The CLI is not an application API, scheduler, evaluation framework, or production deployment interface. -Current lifecycle commands are `plan`, `doctor`, and `run`. Catalog discovery -is available through `preset list/show` and `example list/show`. +Current lifecycle commands are `plan`, `doctor`, and `run`. Use `preset list` +and `preset show` to discover presets. Use `example list` and `example show` to +discover examples. From 8353acebe83478d9ba961d9c6487bfeb09d2d676 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Mon, 20 Jul 2026 12:54:16 -0700 Subject: [PATCH 15/20] fix: address CLI review findings Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- ATTRIBUTIONS-Rust.md | 180 ++++++++++++++---- README.md | 2 +- adapters/claude/README.md | 4 +- .../nemo_fabric_adapters/claude/adapter.py | 6 +- adapters/codex/README.md | 4 +- .../src/nemo_fabric_adapters/codex/adapter.py | 10 +- .../src/nemo_fabric_adapters/common/utils.py | 7 +- crates/fabric-cli/README.md | 2 +- .../adapters/claude/fabric-adapter.json | 21 ++ .../assets/adapters/codex/fabric-adapter.json | 24 +++ .../adapters/deepagents/fabric-adapter.json | 24 +++ .../adapters/hermes/fabric-adapter.json | 36 ++++ crates/fabric-cli/src/app.rs | 14 +- crates/fabric-cli/src/presets.rs | 65 +++++-- crates/fabric-cli/src/scaffold.rs | 53 +++++- .../fabric-cli/templates/rust/Cargo.toml.tmpl | 2 +- docs/experimentation/cli.mdx | 25 ++- .../nemo_fabric.client.md | 2 +- docs/sdk/python.mdx | 4 +- python/src/nemo_fabric/client.py | 5 +- scripts/licensing/attributions_lockfile_md.py | 16 +- .../references/config-mapping.md | 6 +- tests/adapters/test_adapaters_common_utils.py | 6 + tests/adapters/test_claude_adapter.py | 20 +- tests/adapters/test_codex_adapter.py | 22 +++ 25 files changed, 464 insertions(+), 96 deletions(-) create mode 100644 crates/fabric-cli/assets/adapters/claude/fabric-adapter.json create mode 100644 crates/fabric-cli/assets/adapters/codex/fabric-adapter.json create mode 100644 crates/fabric-cli/assets/adapters/deepagents/fabric-adapter.json create mode 100644 crates/fabric-cli/assets/adapters/hermes/fabric-adapter.json diff --git a/ATTRIBUTIONS-Rust.md b/ATTRIBUTIONS-Rust.md index 7a8ac6ab7..d71db631c 100644 --- a/ATTRIBUTIONS-Rust.md +++ b/ATTRIBUTIONS-Rust.md @@ -10,10 +10,13 @@ This project uses the following third-party libraries. Each library is open-sour This file is automatically generated. Please do not edit it directly. Regenerate with `./scripts/generate_attributions.sh rust`. ## anstyle - 1.0.14 + **Repository URL**: https://github.com/rust-cli/anstyle.git **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -220,10 +223,13 @@ This file is automatically generated. Please do not edit it directly. Regenerate ``` ## clap - 4.6.2 + **Repository URL**: https://github.com/clap-rs/clap **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -430,10 +436,13 @@ This file is automatically generated. Please do not edit it directly. Regenerate ``` ## clap_builder - 4.6.2 + **Repository URL**: https://github.com/clap-rs/clap **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -640,10 +649,13 @@ This file is automatically generated. Please do not edit it directly. Regenerate ``` ## clap_derive - 4.6.1 + **Repository URL**: https://github.com/clap-rs/clap **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -850,10 +862,13 @@ This file is automatically generated. Please do not edit it directly. Regenerate ``` ## clap_lex - 1.1.0 + **Repository URL**: https://github.com/clap-rs/clap **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -1060,10 +1075,13 @@ This file is automatically generated. Please do not edit it directly. Regenerate ``` ## dyn-clone - 1.0.20 + **Repository URL**: https://github.com/dtolnay/dyn-clone **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -1141,10 +1159,13 @@ limitations under the License. ``` ## heck - 0.5.0 + **Repository URL**: https://github.com/withoutboats/heck **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -1350,10 +1371,13 @@ limitations under the License. ``` ## itoa - 1.0.18 + **Repository URL**: https://github.com/dtolnay/itoa **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -1431,10 +1455,13 @@ limitations under the License. ``` ## libc - 0.2.186 + **Repository URL**: https://github.com/rust-lang/libc **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -1512,10 +1539,13 @@ limitations under the License. ``` ## memchr - 2.8.1 + **Repository URL**: https://github.com/BurntSushi/memchr **License Type(s)**: MIT + ### License: https://spdx.org/licenses/MIT.html -``` + +```text The MIT License (MIT) Copyright (c) 2015 Andrew Gallant @@ -1541,10 +1571,13 @@ THE SOFTWARE. ``` ## once_cell - 1.21.4 + **Repository URL**: https://github.com/matklad/once_cell **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -1750,10 +1783,13 @@ limitations under the License. ``` ## portable-atomic - 1.13.1 + **Repository URL**: https://github.com/taiki-e/portable-atomic **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -1831,10 +1867,13 @@ limitations under the License. ``` ## proc-macro2 - 1.0.106 + **Repository URL**: https://github.com/dtolnay/proc-macro2 **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -1912,10 +1951,13 @@ limitations under the License. ``` ## pyo3 - 0.28.3 + **Repository URL**: https://github.com/pyo3/pyo3 **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -1993,10 +2035,13 @@ limitations under the License. ``` ## pyo3-build-config - 0.28.3 + **Repository URL**: https://github.com/pyo3/pyo3 **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2074,10 +2119,13 @@ limitations under the License. ``` ## pyo3-ffi - 0.28.3 + **Repository URL**: https://github.com/pyo3/pyo3 **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2155,10 +2203,13 @@ limitations under the License. ``` ## pyo3-macros - 0.28.3 + **Repository URL**: https://github.com/pyo3/pyo3 **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2236,10 +2287,13 @@ limitations under the License. ``` ## pyo3-macros-backend - 0.28.3 + **Repository URL**: https://github.com/pyo3/pyo3 **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2317,10 +2371,13 @@ limitations under the License. ``` ## quote - 1.0.45 + **Repository URL**: https://github.com/dtolnay/quote **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2398,10 +2455,13 @@ limitations under the License. ``` ## ref-cast - 1.0.25 + **Repository URL**: https://github.com/dtolnay/ref-cast **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2479,10 +2539,13 @@ limitations under the License. ``` ## ref-cast-impl - 1.0.25 + **Repository URL**: https://github.com/dtolnay/ref-cast **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2560,10 +2623,13 @@ limitations under the License. ``` ## schemars - 1.2.1 + **Repository URL**: https://github.com/GREsau/schemars **License Type(s)**: MIT + ### License: https://spdx.org/licenses/MIT.html -``` + +```text MIT License Copyright (c) 2019 Graham Esau @@ -2589,10 +2655,13 @@ SOFTWARE. ``` ## schemars_derive - 1.2.1 + **Repository URL**: https://github.com/GREsau/schemars **License Type(s)**: MIT + ### License: https://spdx.org/licenses/MIT.html -``` + +```text MIT License Copyright (c) 2019 Graham Esau @@ -2618,10 +2687,13 @@ SOFTWARE. ``` ## serde - 1.0.228 + **Repository URL**: https://github.com/serde-rs/serde **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2699,10 +2771,13 @@ limitations under the License. ``` ## serde_core - 1.0.228 + **Repository URL**: https://github.com/serde-rs/serde **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2780,10 +2855,13 @@ limitations under the License. ``` ## serde_derive - 1.0.228 + **Repository URL**: https://github.com/serde-rs/serde **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2861,10 +2939,13 @@ limitations under the License. ``` ## serde_derive_internals - 0.29.1 + **Repository URL**: https://github.com/serde-rs/serde **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -2942,10 +3023,13 @@ limitations under the License. ``` ## serde_json - 1.0.150 + **Repository URL**: https://github.com/serde-rs/json **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -3023,10 +3107,13 @@ limitations under the License. ``` ## strsim - 0.11.1 + **Repository URL**: https://github.com/rapidfuzz/strsim-rs **License Type(s)**: MIT + ### License: https://spdx.org/licenses/MIT.html -``` + +```text The MIT License (MIT) Copyright (c) 2015 Danny Guo @@ -3054,10 +3141,13 @@ SOFTWARE. ``` ## syn - 2.0.117 + **Repository URL**: https://github.com/dtolnay/syn **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -3135,10 +3225,13 @@ limitations under the License. ``` ## target-lexicon - 0.13.5 + **Repository URL**: https://github.com/bytecodealliance/target-lexicon **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 @@ -3363,10 +3456,13 @@ Software. ``` ## thiserror - 2.0.18 + **Repository URL**: https://github.com/dtolnay/thiserror **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -3444,10 +3540,13 @@ limitations under the License. ``` ## thiserror-impl - 2.0.18 + **Repository URL**: https://github.com/dtolnay/thiserror **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -3525,10 +3624,13 @@ limitations under the License. ``` ## unicode-ident - 1.0.24 + **Repository URL**: https://github.com/dtolnay/unicode-ident **License Type(s)**: Apache-2.0 + ### License: https://spdx.org/licenses/Apache-2.0.html -``` + +```text Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -3606,10 +3708,13 @@ limitations under the License. ``` ## unicode-ident - 1.0.24 + **Repository URL**: https://github.com/dtolnay/unicode-ident **License Type(s)**: Unicode-3.0 + ### License: https://spdx.org/licenses/Unicode-3.0.html -``` + +```text UNICODE LICENSE V3 COPYRIGHT AND PERMISSION NOTICE @@ -3653,10 +3758,13 @@ authorization of the copyright holder. ``` ## zmij - 1.0.21 + **Repository URL**: https://github.com/dtolnay/zmij **License Type(s)**: MIT + ### License: https://spdx.org/licenses/MIT.html -``` + +```text Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the diff --git a/README.md b/README.md index 92a002d5f..b279ef14b 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ Install Fabric, Hermes Agent, and the Hermes adapter into an environment: ```bash # Use any Python 3.11-3.13 interpreter for Hermes. python3 -m venv .tmp/hermes-venv -.tmp/hermes-venv/bin/python -m pip install --find-links dist "nemo-fabric[adapters-hermes]" +.tmp/hermes-venv/bin/python -m pip install --find-links dist "nemo-fabric[hermes]" ``` If you are working from a local Hermes checkout, replace the final install line diff --git a/adapters/claude/README.md b/adapters/claude/README.md index f2bc13dc9..776521424 100644 --- a/adapters/claude/README.md +++ b/adapters/claude/README.md @@ -38,7 +38,9 @@ not store a long-lived API key. When `models.default.provider` is `nvidia`, the adapter reads the selected model's credential from `api_key_env` (default: `NVIDIA_API_KEY`) and translates the configured NVIDIA `/v1` endpoint into the host URL expected by Claude Code. -This request-scoped mapping does not change the parent environment. +Set the endpoint in `models.default.settings.base_url` or +`NVIDIA_FRONTIER_BASE_URL`; the adapter does not assume a default frontier +endpoint. This request-scoped mapping does not change the parent environment. The adapter forwards the Anthropic profile and federation environment variables that Claude Code and the Claude Agent SDK consume. This includes diff --git a/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py b/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py index e33d4a51a..b76c0672c 100644 --- a/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py +++ b/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py @@ -36,8 +36,6 @@ LOGGER = logging.getLogger(__name__) -NVIDIA_FRONTIER_BASE_URL = "https://inference-api.nvidia.com/v1" - PERMISSION_MODES = { "default", "acceptEdits", @@ -269,12 +267,12 @@ def _nvidia_environment(payload: dict[str, Any]) -> dict[str, str]: settings.get("base_url") or model_settings.get("base_url") or os.environ.get("NVIDIA_FRONTIER_BASE_URL") - or NVIDIA_FRONTIER_BASE_URL ) if not isinstance(base_url, str) or not base_url: raise AdapterConfigError( "claude_invalid_configuration", - "the NVIDIA model provider base URL must be a non-empty string", + "models.default.settings.base_url or NVIDIA_FRONTIER_BASE_URL is required " + "for the NVIDIA model provider", ) # Claude Code appends the Anthropic API version path itself, while Fabric's # shared NVIDIA endpoint includes it for OpenAI-compatible clients. diff --git a/adapters/codex/README.md b/adapters/codex/README.md index ffe7dada8..bf678406b 100644 --- a/adapters/codex/README.md +++ b/adapters/codex/README.md @@ -53,7 +53,9 @@ When `models.default.provider` is `nvidia`, the adapter defines a request-scoped Codex model provider for the configured NVIDIA Responses endpoint. It reads the credential from `api_key_env` (default: `NVIDIA_API_KEY`) and isolates Codex state under the Fabric artifact root, so the invocation does not depend on or -modify a user's Codex login. +modify a user's Codex login. Set the endpoint in +`models.default.settings.base_url` or `NVIDIA_FRONTIER_BASE_URL`; the adapter +does not assume a default frontier endpoint. The dependency graph includes `openai-codex-cli-bin`. The Codex SDK owns this pinned app-server distribution; Fabric does not treat it as a user-installed diff --git a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py index 8d414d1c7..22a503e9c 100644 --- a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py +++ b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py @@ -234,12 +234,12 @@ def nvidia_model_provider_config(payload: dict[str, Any]) -> dict[str, Any]: base_url = ( model_settings.get("base_url") or os.environ.get("NVIDIA_FRONTIER_BASE_URL") - or "https://inference-api.nvidia.com/v1" ) if not isinstance(base_url, str) or not base_url: raise AdapterConfigError( "codex_invalid_configuration", - "the NVIDIA model provider base URL must be a non-empty string", + "models.default.settings.base_url or NVIDIA_FRONTIER_BASE_URL is required " + "for the NVIDIA model provider", ) return { "model_providers": { @@ -343,7 +343,6 @@ def child_environment( values.update(configured) if selected_model_provider(payload) == "nvidia": codex_home = state_dir(payload) / "nvidia-home" - codex_home.mkdir(parents=True, exist_ok=True) values["CODEX_HOME"] = str(codex_home) # The SDK overlays this mapping on the parent environment. An empty # originator is still treated as an override by Codex and produces invalid @@ -815,7 +814,10 @@ async def invoke_codex_sdk( settings = _settings(payload) config = thread_config(payload, relay) - codex = AsyncCodex(config=sdk_config(payload, relay)) + client_config = sdk_config(payload, relay) + if selected_model_provider(payload) == "nvidia": + Path(client_config.env["CODEX_HOME"]).mkdir(parents=True, exist_ok=True) + codex = AsyncCodex(config=client_config) handle = None output: dict[str, Any] thread_id: str | None = None diff --git a/adapters/common/src/nemo_fabric_adapters/common/utils.py b/adapters/common/src/nemo_fabric_adapters/common/utils.py index 968fd348a..9697690c2 100644 --- a/adapters/common/src/nemo_fabric_adapters/common/utils.py +++ b/adapters/common/src/nemo_fabric_adapters/common/utils.py @@ -59,7 +59,12 @@ def fabric_config(payload: dict[str, Any]) -> dict[str, Any]: def base_dir(payload: dict[str, Any]) -> str: - return payload.get("base_dir") or "." + value = payload.get("base_dir") + if not isinstance(value, str) or not value: + raise ValueError("base_dir is required") + if not Path(value).is_absolute(): + raise ValueError("base_dir must be an absolute path") + return value def agent_name(payload: dict[str, Any]) -> str: diff --git a/crates/fabric-cli/README.md b/crates/fabric-cli/README.md index cc5f043ef..b70d44ce5 100644 --- a/crates/fabric-cli/README.md +++ b/crates/fabric-cli/README.md @@ -3,7 +3,7 @@ SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All SPDX-License-Identifier: Apache-2.0 --> -# NeMo Fabric CLI +# NVIDIA NeMo Fabric CLI `nemo-fabric` is a small experimentation interface for presets and maintained examples. Applications should construct `FabricConfig` through a language API. diff --git a/crates/fabric-cli/assets/adapters/claude/fabric-adapter.json b/crates/fabric-cli/assets/adapters/claude/fabric-adapter.json new file mode 100644 index 000000000..ed36b146a --- /dev/null +++ b/crates/fabric-cli/assets/adapters/claude/fabric-adapter.json @@ -0,0 +1,21 @@ +{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "nvidia.fabric.claude", + "harness": "claude", + "adapter_kind": "python", + "runner": { + "module": "nemo_fabric_adapters.claude.adapter", + "callable": "run" + }, + "config": { + "accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"] + }, + "telemetry": { + "providers": { + "relay": { + "outputs": ["atif", "otel", "openinference"], + "integration_modes": ["hooks", "gateway"] + } + } + } +} diff --git a/crates/fabric-cli/assets/adapters/codex/fabric-adapter.json b/crates/fabric-cli/assets/adapters/codex/fabric-adapter.json new file mode 100644 index 000000000..9cbc7f147 --- /dev/null +++ b/crates/fabric-cli/assets/adapters/codex/fabric-adapter.json @@ -0,0 +1,24 @@ +{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "nvidia.fabric.codex", + "harness": "codex", + "adapter_kind": "python", + "runner": { + "module": "nemo_fabric_adapters.codex.adapter", + "callable": "run" + }, + "config": { + "accepts": ["models", "telemetry"] + }, + "telemetry": { + "providers": { + "relay": { + "outputs": ["atif", "otel", "openinference"], + "integration_modes": ["hooks", "gateway"] + }, + "native": { + "outputs": ["otel"] + } + } + } +} diff --git a/crates/fabric-cli/assets/adapters/deepagents/fabric-adapter.json b/crates/fabric-cli/assets/adapters/deepagents/fabric-adapter.json new file mode 100644 index 000000000..78a09fe35 --- /dev/null +++ b/crates/fabric-cli/assets/adapters/deepagents/fabric-adapter.json @@ -0,0 +1,24 @@ +{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "nvidia.fabric.langchain.deepagents", + "harness": "deepagents", + "adapter_kind": "python", + "runner": { + "module": "nemo_fabric_adapters.deepagents.adapter", + "callable": "run" + }, + "requirements": {}, + "config": { + "accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"] + }, + "telemetry": { + "providers": { + "relay": { + "outputs": ["atif", "otel", "openinference"] + }, + "native": { + "outputs": ["otel", "openinference"] + } + } + } +} diff --git a/crates/fabric-cli/assets/adapters/hermes/fabric-adapter.json b/crates/fabric-cli/assets/adapters/hermes/fabric-adapter.json new file mode 100644 index 000000000..f28574707 --- /dev/null +++ b/crates/fabric-cli/assets/adapters/hermes/fabric-adapter.json @@ -0,0 +1,36 @@ +{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "nvidia.fabric.hermes", + "harness": "hermes", + "adapter_kind": "python", + "runner": { + "module": "nemo_fabric_adapters.hermes.adapter", + "callable": "run" + }, + "requirements": { + "env": [ + "NVIDIA_API_KEY" + ] + }, + "config": { + "accepts": [ + "models", + "tools", + "tools.blocked", + "mcp", + "skills", + "telemetry" + ] + }, + "telemetry": { + "providers": { + "relay": { + "outputs": [ + "atif", + "otel", + "openinference" + ] + } + } + } +} diff --git a/crates/fabric-cli/src/app.rs b/crates/fabric-cli/src/app.rs index 9dd1f1506..14db82fbd 100644 --- a/crates/fabric-cli/src/app.rs +++ b/crates/fabric-cli/src/app.rs @@ -168,9 +168,7 @@ where Err(error) if matches!( error.kind(), - ErrorKind::DisplayHelp - | ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand - | ErrorKind::DisplayVersion + ErrorKind::DisplayHelp | ErrorKind::DisplayVersion ) => { error.print().map_err(|error| error.to_string())?; @@ -328,6 +326,16 @@ mod tests { use super::*; + #[test] + fn missing_subcommand_is_an_error() { + assert!(execute_from(["nemo-fabric"]).is_err()); + } + + #[test] + fn explicit_help_is_successful() { + assert!(execute_from(["nemo-fabric", "--help"]).is_ok()); + } + #[test] fn parses_preset_plan() { let cli = Cli::try_parse_from(["nemo-fabric", "plan", "--preset", "scripted"]) diff --git a/crates/fabric-cli/src/presets.rs b/crates/fabric-cli/src/presets.rs index 0f3331d69..4eabcfccf 100644 --- a/crates/fabric-cli/src/presets.rs +++ b/crates/fabric-cli/src/presets.rs @@ -15,12 +15,12 @@ use crate::assets::{EmbeddedFile, StagedAssets}; const SCRIPTED_DESCRIPTOR: &str = include_str!("../assets/adapters/scripted/fabric-adapter.json"); const SCRIPTED_RUNNER: &str = include_str!("../assets/adapters/scripted/run.py"); -const HERMES_DESCRIPTOR: &str = include_str!("../../../adapters/hermes/fabric-adapter.json"); -const CLAUDE_DESCRIPTOR: &str = include_str!("../../../adapters/claude/fabric-adapter.json"); -const CODEX_DESCRIPTOR: &str = include_str!("../../../adapters/codex/fabric-adapter.json"); +const HERMES_DESCRIPTOR: &str = include_str!("../assets/adapters/hermes/fabric-adapter.json"); +const CLAUDE_DESCRIPTOR: &str = include_str!("../assets/adapters/claude/fabric-adapter.json"); +const CODEX_DESCRIPTOR: &str = include_str!("../assets/adapters/codex/fabric-adapter.json"); const DEEPAGENTS_DESCRIPTOR: &str = - include_str!("../../../adapters/deepagents/fabric-adapter.json"); -const NVIDIA_FRONTIER_BASE_URL: &str = "https://inference-api.nvidia.com/v1"; + include_str!("../assets/adapters/deepagents/fabric-adapter.json"); +const NVIDIA_API_CATALOG_BASE_URL: &str = "https://integrate.api.nvidia.com/v1"; const SCRIPTED_ASSETS: &[EmbeddedFile] = &[ EmbeddedFile { @@ -127,14 +127,14 @@ const PRESETS: [Preset; 5] = [ Preset { name: "claude", description: "Claude Code with an NVIDIA-hosted Claude model.", - required_env: &["NVIDIA_API_KEY"], + required_env: &["NVIDIA_API_KEY", "NVIDIA_FRONTIER_BASE_URL"], build: claude, assets: CLAUDE_ASSETS, }, Preset { name: "codex", description: "Codex with an NVIDIA-hosted OpenAI model.", - required_env: &["NVIDIA_API_KEY"], + required_env: &["NVIDIA_API_KEY", "NVIDIA_FRONTIER_BASE_URL"], build: codex, assets: CODEX_ASSETS, }, @@ -164,9 +164,9 @@ fn hermes() -> FabricConfig { "nvidia.fabric.hermes", Some(model( "nvidia", - "nvidia/nvidia/Nemotron-3-Nano-30B-A3B", + "nvidia/nemotron-3-nano-30b-a3b", Some("NVIDIA_API_KEY"), - Some(NVIDIA_FRONTIER_BASE_URL), + Some(NVIDIA_API_CATALOG_BASE_URL), )), Map::new(), ) @@ -181,7 +181,7 @@ fn claude() -> FabricConfig { "nvidia", "aws/anthropic/claude-opus-4-5", Some("NVIDIA_API_KEY"), - Some(NVIDIA_FRONTIER_BASE_URL), + std::env::var("NVIDIA_FRONTIER_BASE_URL").ok().as_deref(), )), Map::from_iter([("permission_mode".to_string(), json!("dontAsk"))]), ) @@ -196,7 +196,7 @@ fn codex() -> FabricConfig { "nvidia", "azure/openai/gpt-5.4", Some("NVIDIA_API_KEY"), - Some(NVIDIA_FRONTIER_BASE_URL), + std::env::var("NVIDIA_FRONTIER_BASE_URL").ok().as_deref(), )), Map::from_iter([ ("sandbox".to_string(), json!("workspace-write")), @@ -220,9 +220,9 @@ fn deepagents() -> FabricConfig { "nvidia.fabric.langchain.deepagents", Some(model( "nvidia", - "nvidia/nvidia/Nemotron-3-Nano-30B-A3B", + "nvidia/nemotron-3-nano-30b-a3b", Some("NVIDIA_API_KEY"), - Some(NVIDIA_FRONTIER_BASE_URL), + Some(NVIDIA_API_CATALOG_BASE_URL), )), Map::new(), ) @@ -343,11 +343,46 @@ mod tests { assert_eq!(model.provider, "nvidia"); assert_eq!(model.api_key_env.as_deref(), Some("NVIDIA_API_KEY")); + assert!(preset.required_env.contains(&"NVIDIA_API_KEY")); + } + + for name in ["hermes", "deepagents"] { + let config = find(name).expect("catalog preset").config(); + let model = config.models.get("default").expect("default model"); assert_eq!( model.settings.get("base_url").and_then(Value::as_str), - Some(NVIDIA_FRONTIER_BASE_URL) + Some(NVIDIA_API_CATALOG_BASE_URL) ); - assert!(preset.required_env.contains(&"NVIDIA_API_KEY")); + } + + for name in ["claude", "codex"] { + assert!( + find(name) + .expect("frontier preset") + .required_env + .contains(&"NVIDIA_FRONTIER_BASE_URL") + ); + } + } + + #[test] + fn packaged_descriptors_match_repository_adapters() { + let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")); + let repository_adapters = manifest_dir.join("../../adapters"); + if !repository_adapters.is_dir() { + return; + } + + for (name, embedded) in [ + ("hermes", HERMES_DESCRIPTOR), + ("claude", CLAUDE_DESCRIPTOR), + ("codex", CODEX_DESCRIPTOR), + ("deepagents", DEEPAGENTS_DESCRIPTOR), + ] { + let canonical = + std::fs::read_to_string(repository_adapters.join(name).join("fabric-adapter.json")) + .expect("read repository adapter descriptor"); + assert_eq!(embedded, canonical, "{name} descriptor drifted"); } } } diff --git a/crates/fabric-cli/src/scaffold.rs b/crates/fabric-cli/src/scaffold.rs index 638d24ee7..ed017a3ba 100644 --- a/crates/fabric-cli/src/scaffold.rs +++ b/crates/fabric-cli/src/scaffold.rs @@ -92,7 +92,9 @@ fn language_files(language: Language, config: &FabricConfig, example: &str) -> V ), Language::Rust => ( "Cargo.toml", - RUST_PROJECT.replace("{{PACKAGE}}", &package_name(example)), + RUST_PROJECT + .replace("{{PACKAGE}}", &package_name(example)) + .replace("{{NEMO_FABRIC_VERSION}}", env!("CARGO_PKG_VERSION")), "src/main.rs", render_rust(config), ), @@ -147,15 +149,20 @@ fn python_models(model: Option<&ModelConfig>) -> String { let Some(model) = model else { return "{}".to_string(); }; + let temperature = model + .temperature + .map(|value| value.to_string()) + .unwrap_or_else(|| "None".to_string()); format!( - "{{\"default\": ModelConfig(provider={}, model={}, api_key_env={})}}", + "{{\"default\": ModelConfig(provider={}, model={}, temperature={temperature}, api_key_env={}, settings={})}}", python_string(&model.provider), python_string(&model.model), model .api_key_env .as_deref() .map(python_string) - .unwrap_or_else(|| "None".to_string()) + .unwrap_or_else(|| "None".to_string()), + python_value(&Value::Object(model.settings.clone())), ) } @@ -226,10 +233,15 @@ fn rust_models(model: Option<&ModelConfig>) -> String { .as_deref() .map(|value| format!("Some({}.to_string())", rust_string(value))) .unwrap_or_else(|| "None".to_string()); + let temperature = model + .temperature + .map(|value| format!("Some({value})")) + .unwrap_or_else(|| "None".to_string()); format!( - "BTreeMap::from_iter([(\"default\".to_string(), ModelConfig {{ provider: {}.to_string(), model: {}.to_string(), temperature: None, api_key_env: {api_key}, settings: Map::new(), extensions: BTreeMap::new() }})])", + "BTreeMap::from_iter([(\"default\".to_string(), ModelConfig {{ provider: {}.to_string(), model: {}.to_string(), temperature: {temperature}, api_key_env: {api_key}, settings: {}, extensions: BTreeMap::new() }})])", rust_string(&model.provider), rust_string(&model.model), + rust_settings(&model.settings), ) } @@ -278,11 +290,42 @@ mod tests { }; let source = fs::read_to_string(launcher).expect("read launcher"); assert!(source.contains("nvidia.fabric.hermes")); - assert!(source.contains("nvidia/nvidia/Nemotron-3-Nano-30B-A3B")); + assert!(source.contains("nvidia/nemotron-3-nano-30b-a3b")); + assert!(source.contains("https://integrate.api.nvidia.com/v1")); + if language == Language::Rust { + let manifest = + fs::read_to_string(destination.join("Cargo.toml")).expect("read manifest"); + assert!(manifest.contains(&format!( + "nemo-fabric-core = \"{}\"", + env!("CARGO_PKG_VERSION") + ))); + } fs::remove_dir_all(destination).expect("remove scaffold"); } } + #[test] + fn renderers_preserve_model_settings_and_temperature() { + let mut config = presets::find("hermes").expect("hermes preset").config(); + config + .models + .get_mut("default") + .expect("default model") + .temperature = Some(0.2); + + let python = render_python(&config); + assert!(python.contains("temperature=0.2")); + assert!( + python.contains("settings={\"base_url\": \"https://integrate.api.nvidia.com/v1\"}") + ); + + let rust = render_rust(&config); + assert!(rust.contains("temperature: Some(0.2)")); + assert!(rust.contains( + "(\"base_url\".to_string(), json!(\"https://integrate.api.nvidia.com/v1\"))" + )); + } + #[test] fn refuses_to_overwrite_a_destination() { let destination = destination("overwrite", Language::Python); diff --git a/crates/fabric-cli/templates/rust/Cargo.toml.tmpl b/crates/fabric-cli/templates/rust/Cargo.toml.tmpl index b515964e6..e9391eb14 100644 --- a/crates/fabric-cli/templates/rust/Cargo.toml.tmpl +++ b/crates/fabric-cli/templates/rust/Cargo.toml.tmpl @@ -7,5 +7,5 @@ version = "0.1.0" edition = "2024" [dependencies] -nemo-fabric-core = "0.1.0" +nemo-fabric-core = "{{NEMO_FABRIC_VERSION}}" serde_json = "1" diff --git a/docs/experimentation/cli.mdx b/docs/experimentation/cli.mdx index d91438e6b..de151623d 100644 --- a/docs/experimentation/cli.mdx +++ b/docs/experimentation/cli.mdx @@ -64,26 +64,37 @@ harness. Use the credential-free `scripted` preset to verify the CLI by itself. Presets are complete, embedded `FabricConfig` values intended for quick experiments: -| Preset | Harness | Default model | Credential | +| Preset | Harness | Default model | Endpoint | | --- | --- | --- | --- | | `scripted` | Deterministic test adapter | None | None | -| `hermes` | Hermes Agent | `nvidia/nvidia/Nemotron-3-Nano-30B-A3B` | `NVIDIA_API_KEY` | -| `claude` | Claude Code | `aws/anthropic/claude-opus-4-5` | `NVIDIA_API_KEY` | -| `codex` | Codex | `azure/openai/gpt-5.4` | `NVIDIA_API_KEY` | -| `deepagents` | LangChain Deep Agents | `nvidia/nvidia/Nemotron-3-Nano-30B-A3B` | `NVIDIA_API_KEY` | +| `hermes` | Hermes Agent | `nvidia/nemotron-3-nano-30b-a3b` | NVIDIA API Catalog | +| `claude` | Claude Code | `aws/anthropic/claude-opus-4-5` | `NVIDIA_FRONTIER_BASE_URL` | +| `codex` | Codex | `azure/openai/gpt-5.4` | `NVIDIA_FRONTIER_BASE_URL` | +| `deepagents` | LangChain Deep Agents | `nvidia/nemotron-3-nano-30b-a3b` | NVIDIA API Catalog | The `scripted` preset does not call a model. It returns a deterministic response through the same Fabric runtime and adapter contract, which makes it useful for checking CLI installation, request flow, and result formatting without network access or credentials. -The four external-harness presets use the NVIDIA inference endpoint. Export one -credential before running them: +The four external-harness presets use `NVIDIA_API_KEY`. Hermes and Deep Agents +target the public NVIDIA API Catalog at `https://integrate.api.nvidia.com/v1`. +Export the credential before running them: ```bash export NVIDIA_API_KEY="..." ``` +Claude and Codex require the base URL for an NVIDIA endpoint that serves the +selected frontier model and supports the harness protocol. Set it explicitly: + +```bash +export NVIDIA_FRONTIER_BASE_URL="https://your-frontier-endpoint.example/v1" +``` + +Fabric does not provide a default frontier URL because the correct endpoint +depends on the model and the user's access. + List the complete configurations maintained by the CLI: ```bash diff --git a/docs/reference/api/python-library-reference/nemo_fabric.client.md b/docs/reference/api/python-library-reference/nemo_fabric.client.md index 206e407e4..59d911069 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.client.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.client.md @@ -17,7 +17,7 @@ Native Python client for resolving and running NeMo Fabric agents. ## class `Fabric` Primary Python entrypoint for NeMo Fabric. -Every lifecycle method accepts a complete, typed ``FabricConfig`` plus an optional ``base_dir`` used to resolve relative paths. Compose variants in Python before calling the SDK. All inspection and execution APIs return typed, read-only mapping models. +Every lifecycle method accepts a complete, typed ``FabricConfig`` plus an optional ``base_dir`` used to resolve relative paths. Compose variants in Python before calling the SDK. The ``doctor()``, ``plan()``, and ``run()`` results are typed, read-only mapping models. ``start_runtime()`` returns an active ``Runtime`` handle. ``Fabric`` uses the native Rust extension. SDK calls raise ``FabricNativeUnavailableError`` when the native extension is not installed. diff --git a/docs/sdk/python.mdx b/docs/sdk/python.mdx index cf0647fba..48256de68 100644 --- a/docs/sdk/python.mdx +++ b/docs/sdk/python.mdx @@ -191,8 +191,8 @@ The repository's uses this pattern for complete Hermes, Codex SDK, Deep Agents, environment, MCP, and telemetry variants. -If a config contains relative paths, pass a `base_dir` to `resolve(...)`, -`plan(...)`, `doctor(...)`, `run(...)`, or `start_runtime(...)`. The base +If a config contains relative paths, pass a `base_dir` to `plan(...)`, +`doctor(...)`, `run(...)`, or `start_runtime(...)`. The base directory anchors skills, workspaces, artifacts, and other relative paths to the caller's package or job layout. diff --git a/python/src/nemo_fabric/client.py b/python/src/nemo_fabric/client.py index af33ba3c8..64dc088af 100644 --- a/python/src/nemo_fabric/client.py +++ b/python/src/nemo_fabric/client.py @@ -42,8 +42,9 @@ class Fabric: Every lifecycle method accepts a complete, typed ``FabricConfig`` plus an optional ``base_dir`` used to resolve relative paths. Compose variants in - Python before calling the SDK. All inspection and execution APIs return - typed, read-only mapping models. + Python before calling the SDK. The ``doctor()``, ``plan()``, and ``run()`` + results are typed, read-only mapping models. ``start_runtime()`` returns an + active ``Runtime`` handle. ``Fabric`` uses the native Rust extension. SDK calls raise ``FabricNativeUnavailableError`` when the native extension is not diff --git a/scripts/licensing/attributions_lockfile_md.py b/scripts/licensing/attributions_lockfile_md.py index 35e582cdd..4e5f494b7 100755 --- a/scripts/licensing/attributions_lockfile_md.py +++ b/scripts/licensing/attributions_lockfile_md.py @@ -27,6 +27,7 @@ ROOT = Path(__file__).resolve().parents[2] MARKDOWN_CODE_FENCE = "```\n" +MARKDOWN_TEXT_CODE_FENCE = "\n```text\n" MARKDOWN_CODE_BLOCK_END = "```\n\n" @@ -362,20 +363,20 @@ def _render_rust_metadata_fallback_attribution(crate: dict[str, Any]) -> tuple[s license_name = _normalize_license_name(str(crate.get("license") or "UNKNOWN")) parts = [ - f"## {name} - {version}\n", + f"## {name} - {version}\n\n", f"**Repository URL**: {repo}\n", f"**License Type(s)**: {license_name}\n", - f"### License: {spdx_url(license_name, fallback='https://spdx.org/licenses/')}\n", + f"\n### License: {spdx_url(license_name, fallback='https://spdx.org/licenses/')}\n\n", ] license_files = _rust_license_files(crate) if license_files: for label, text in license_files: parts.append(f"### License File: {label}\n") - parts.append(MARKDOWN_CODE_FENCE) + parts.append(MARKDOWN_TEXT_CODE_FENCE) parts.append(text if text.endswith("\n") else text + "\n") parts.append(MARKDOWN_CODE_BLOCK_END) else: - parts.append(MARKDOWN_CODE_FENCE) + parts.append(MARKDOWN_TEXT_CODE_FENCE) parts.append(f"License expression from Cargo metadata: {license_name}\n") parts.append("No package license file was found in the downloaded crate archive.\n") parts.append(MARKDOWN_CODE_BLOCK_END) @@ -411,11 +412,11 @@ def _render_rust_crate_attribution( rendered = "".join( [ - f"## {name} - {version}\n", + f"## {name} - {version}\n\n", f"**Repository URL**: {repo}\n", f"**License Type(s)**: {license_id}\n", - f"### License: https://spdx.org/licenses/{license_id}.html\n", - MARKDOWN_CODE_FENCE, + f"\n### License: https://spdx.org/licenses/{license_id}.html\n", + MARKDOWN_TEXT_CODE_FENCE, license_text, "\n", MARKDOWN_CODE_BLOCK_END, @@ -898,4 +899,3 @@ def main() -> int: if __name__ == "__main__": raise SystemExit(main()) - diff --git a/skills/nemo-fabric-integrate/references/config-mapping.md b/skills/nemo-fabric-integrate/references/config-mapping.md index 20578f517..0e6de9df5 100644 --- a/skills/nemo-fabric-integrate/references/config-mapping.md +++ b/skills/nemo-fabric-integrate/references/config-mapping.md @@ -68,7 +68,7 @@ ordinary Python. ## Relative Paths If the config uses relative paths for skills, workspaces, or artifacts, pass -`base_dir=...` to `resolve(...)`, `plan(...)`, `doctor(...)`, `run(...)`, or +`base_dir=...` to `plan(...)`, `doctor(...)`, `run(...)`, or `start_runtime(...)`. The base directory anchors those paths to the consumer's package or job layout, so nothing depends on the process working directory. @@ -93,7 +93,9 @@ package or job layout, so nothing depends on the process working directory. Do not surface these mechanics in the consumer-facing integration: - Replacing direct SDK calls with a private serialized transport - (`to_mapping()` is for inspection, logging, and internal process boundaries). + (`to_mapping()` is for inspection and private process boundaries; redact + credentials, headers, metadata, and other sensitive or user-provided fields + before logging, and never emit an unredacted mapping to logs). - Importing `nemo_fabric._native` or adapter-internal modules. - Reimplementing harness start, invoke, or stop logic, or managing adapter threads, sessions, or processes. diff --git a/tests/adapters/test_adapaters_common_utils.py b/tests/adapters/test_adapaters_common_utils.py index e24afb614..906025224 100644 --- a/tests/adapters/test_adapaters_common_utils.py +++ b/tests/adapters/test_adapaters_common_utils.py @@ -178,6 +178,12 @@ def test_payload_accessors_use_canonical_plan_fields(): assert common_utils.capability_plan(payload) == {"native": {"skill_paths": ["skills"]}} +@pytest.mark.parametrize("value", [None, "", "relative/path"]) +def test_base_dir_requires_an_absolute_path(value): + with pytest.raises(ValueError, match="base_dir"): + common_utils.base_dir({"base_dir": value}) + + def test_load_payload_reads_fabric_invocation(tmp_path: Path): invocation_path = tmp_path / "invocation.json" invocation_path.write_text( diff --git a/tests/adapters/test_claude_adapter.py b/tests/adapters/test_claude_adapter.py index fd2279ca8..22d388f62 100644 --- a/tests/adapters/test_claude_adapter.py +++ b/tests/adapters/test_claude_adapter.py @@ -405,13 +405,31 @@ def test_build_options_uses_nvidia_provider_endpoint_and_default_credential( model.pop("api_key_env") claude_payload["config"]["harness"]["settings"].pop("env") os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + os.environ["NVIDIA_FRONTIER_BASE_URL"] = "https://frontier.example/v1" options = adapter.build_options(claude_payload, resume=None) - assert options.env["ANTHROPIC_BASE_URL"] == "https://inference-api.nvidia.com" + assert options.env["ANTHROPIC_BASE_URL"] == "https://frontier.example" assert options.env["ANTHROPIC_API_KEY"] == "nvidia-secret" +def test_build_options_requires_nvidia_provider_endpoint(claude_payload): + model = claude_payload["config"]["models"]["default"] + model.update( + { + "provider": "nvidia", + "model": "aws/anthropic/claude-opus-4-5", + "api_key_env": "NVIDIA_API_KEY", + } + ) + model.pop("settings", None) + os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + os.environ.pop("NVIDIA_FRONTIER_BASE_URL", None) + + with pytest.raises(adapter.AdapterConfigError, match="NVIDIA_FRONTIER_BASE_URL"): + adapter.build_options(claude_payload, resume=None) + + def test_build_options_requires_nvidia_provider_credential(claude_payload): model = claude_payload["config"]["models"]["default"] model.update( diff --git a/tests/adapters/test_codex_adapter.py b/tests/adapters/test_codex_adapter.py index d55cf9f23..dd1705c2f 100644 --- a/tests/adapters/test_codex_adapter.py +++ b/tests/adapters/test_codex_adapter.py @@ -351,6 +351,28 @@ def test_nvidia_provider_requires_credential(codex_payload, mock_codex): assert output["error"]["code"] == "codex_invalid_configuration" assert "NVIDIA_API_KEY is required" in output["error"]["message"] + assert not (adapter.state_dir(codex_payload) / "nvidia-home").exists() + mock_codex.assert_not_called() + + +def test_nvidia_provider_requires_endpoint(codex_payload, mock_codex): + model = codex_payload["config"]["models"]["default"] + model.update( + { + "provider": "nvidia", + "model": "openai/gpt-oss-120b", + "api_key_env": "NVIDIA_API_KEY", + } + ) + model.pop("settings", None) + os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + os.environ.pop("NVIDIA_FRONTIER_BASE_URL", None) + + output = adapter.run(codex_payload) + + assert output["error"]["code"] == "codex_invalid_configuration" + assert "NVIDIA_FRONTIER_BASE_URL" in output["error"]["message"] + assert not (adapter.state_dir(codex_payload) / "nvidia-home").exists() mock_codex.assert_not_called() From 787b602745575be0164b3788a519266cdbbab23f Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Mon, 20 Jul 2026 13:19:58 -0700 Subject: [PATCH 16/20] fix: address follow-up CLI review findings Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- TODO.md | 23 +++++++++++-------- .../src/nemo_fabric_adapters/codex/adapter.py | 6 ++++- docs/sdk/python.mdx | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/TODO.md b/TODO.md index 86a7863ab..9bd12bc19 100644 --- a/TODO.md +++ b/TODO.md @@ -14,12 +14,14 @@ cleanup validation. - **Status:** Waiting for adapter wheel metadata support - **Added:** July 19, 2026 - **Affected implementation:** `crates/fabric-core/src/config.rs`, - `crates/fabric-cli/src/presets.rs`, and the Python binding -- **Reason:** CLI presets currently embed adapter descriptors from repository - paths at compile time and stage them under a temporary `adapters/` directory. - Core also probes a compile-time repository path. Built wheels can run without - a checkout, but the build and resolution paths still assume the monorepo - layout. + `crates/fabric-cli/src/presets.rs`, `crates/fabric-cli/assets/adapters/`, and + the Python binding +- **Reason:** The published CLI crate currently carries package-local copies of + adapter descriptors because Cargo cannot package descriptors from the + repository-level adapter directories. A drift test keeps those copies aligned + with the canonical descriptors, but the duplication is a packaging + workaround. CLI presets stage the embedded descriptors under a temporary + `adapters/` directory, and core also probes a compile-time repository path. - **Upstream resolution:** NeMo Fabric adapter distributions advertise their descriptors through installed wheel metadata (internal packaging milestone; no external dependency). @@ -28,10 +30,11 @@ cleanup validation. registry. - **Cleanup:** Add adapter registrations to `ResolveContext`; remove implicit repository and `base_dir/adapters` discovery from core; have the Python - binding register wheel-owned descriptors; have the CLI register embedded - descriptors directly; retain temporary staging only for descriptor-local - assets such as the scripted runner; and validate both installed-wheel and - standalone Rust CLI behavior before removing this entry. + binding register wheel-owned descriptors; revisit and remove the duplicated + CLI descriptor assets once wheel metadata is consumable; retain embedded or + staged files only for standalone CLI assets such as the scripted runner; and + validate both installed-wheel and standalone Rust CLI behavior before + removing this entry. ## NeMo Relay 0.6.x Request Decoding Release diff --git a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py index 22a503e9c..361fbfa69 100644 --- a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py +++ b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py @@ -816,7 +816,11 @@ async def invoke_codex_sdk( config = thread_config(payload, relay) client_config = sdk_config(payload, relay) if selected_model_provider(payload) == "nvidia": - Path(client_config.env["CODEX_HOME"]).mkdir(parents=True, exist_ok=True) + await asyncio.to_thread( + Path(client_config.env["CODEX_HOME"]).mkdir, + parents=True, + exist_ok=True, + ) codex = AsyncCodex(config=client_config) handle = None output: dict[str, Any] diff --git a/docs/sdk/python.mdx b/docs/sdk/python.mdx index 48256de68..8c7bfaa0e 100644 --- a/docs/sdk/python.mdx +++ b/docs/sdk/python.mdx @@ -206,7 +206,7 @@ multiple independent runtimes. | --- | --- | --- | --- | | `Fabric.plan(config, base_dir=...)` | No | You need to inspect the selected adapter, capability mapping, and runtime capabilities before running. | Does not start a runtime. | | `Fabric.doctor(config, base_dir=...)` | Yes | You need preflight diagnostics for adapter availability, config support, and environment assumptions. | Checks may touch runtime dependencies. | -| `Fabric.run(config, input=...)` | Yes | You need one complete start, invoke, result, stop lifecycle. | Pass a `RunRequest` instead when the invocation needs IDs, context, or overrides. | +| `Fabric.run(config, base_dir=..., input=...)` | Yes | You need one complete start, invoke, result, stop lifecycle. | `base_dir` is optional. Pass a `RunRequest` instead when the invocation needs IDs, context, or overrides. | | `Fabric.start_runtime(config, ...)` | Yes | You need state across multiple ordered invocations. | Returns a `Runtime`. Use it as an async context manager. | | `Runtime.invoke(...)` | Yes | You need one turn on an existing runtime. | A runtime permits one active invocation at a time. | | `Runtime.stop()` | Yes | You need to stop or detach from the runtime. | Called automatically when using `async with`. | From d2130976af3b1cf7c57ef954f32122aa836c062d Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:04:34 -0700 Subject: [PATCH 17/20] fix: address CLI scaffold review feedback Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- adapters/codex/README.md | 7 ++ .../src/nemo_fabric_adapters/codex/adapter.py | 36 +++--- crates/fabric-cli/src/app.rs | 24 +++- crates/fabric-cli/src/scaffold.rs | 24 +++- .../templates/python/pyproject.toml.tmpl | 7 ++ .../fabric-cli/templates/rust/Cargo.toml.tmpl | 2 +- docs/integrations/codex.mdx | 5 + tests/adapters/test_codex_adapter.py | 50 ++++++++ tests/e2e/test_cli_scaffold.py | 108 ++++++++++++++++++ 9 files changed, 239 insertions(+), 24 deletions(-) create mode 100644 tests/e2e/test_cli_scaffold.py diff --git a/adapters/codex/README.md b/adapters/codex/README.md index bf678406b..28a2c07e0 100644 --- a/adapters/codex/README.md +++ b/adapters/codex/README.md @@ -135,6 +135,13 @@ capture Responses traffic. Fabric does not spoof the Codex CLI identity or fall back to CLI execution. Relay routes and observes requests; it does not provide OpenAI credentials or change the selected Codex authentication mode. +Relay-enabled Codex runs require `models.default.provider: openai`. The custom +`nvidia` provider is not supported with Relay because its configured NVIDIA +Responses base URL bypasses the built-in `openai_base_url` redirect. Run the +`nvidia` provider without Relay and supply its credential through `api_key_env` +(default: `NVIDIA_API_KEY`) and its endpoint through +`models.default.settings.base_url` or `NVIDIA_FRONTIER_BASE_URL`. + Relay-enabled runs require the external `nemo-relay` CLI in addition to the Python package dependencies. Fabric accepts CLI versions `>=0.6.0,<0.7.0`. Until the request-decoding fix is released, install the tested PR revision: diff --git a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py index 361fbfa69..ca1e606b9 100644 --- a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py +++ b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py @@ -673,6 +673,14 @@ def validate_payload(payload: dict[str, Any]) -> str: _personality(payload) _reasoning_effort(payload) _output_schema(payload) + if ( + common_utils.relay_enabled(payload) + and selected_model_provider(payload) != "openai" + ): + raise AdapterConfigError( + "codex_invalid_configuration", + "NeMo Relay requires the built-in openai model provider", + ) child_environment(payload) thread_config(payload, None) return fabric_runtime_id @@ -815,17 +823,18 @@ async def invoke_codex_sdk( settings = _settings(payload) config = thread_config(payload, relay) client_config = sdk_config(payload, relay) - if selected_model_provider(payload) == "nvidia": - await asyncio.to_thread( - Path(client_config.env["CODEX_HOME"]).mkdir, - parents=True, - exist_ok=True, - ) - codex = AsyncCodex(config=client_config) + codex: AsyncCodex | None = None handle = None output: dict[str, Any] thread_id: str | None = None try: + if selected_model_provider(payload) == "nvidia": + await asyncio.to_thread( + Path(client_config.env["CODEX_HOME"]).mkdir, + parents=True, + exist_ok=True, + ) + codex = AsyncCodex(config=client_config) async with asyncio.timeout(timeout_seconds(payload)): common = { "approval_mode": approval_mode(payload), @@ -869,12 +878,13 @@ async def invoke_codex_sdk( except (CodexError, RuntimeError, OSError) as error: output = sdk_failure(error) finally: - try: - await codex.close() - except Exception: - output = _failure( - "codex_sdk_stop_failed", "Codex SDK runtime failed to stop" - ) + if codex is not None: + try: + await codex.close() + except Exception: + output = _failure( + "codex_sdk_stop_failed", "Codex SDK runtime failed to stop" + ) return output, thread_id diff --git a/crates/fabric-cli/src/app.rs b/crates/fabric-cli/src/app.rs index 14db82fbd..6093f356c 100644 --- a/crates/fabric-cli/src/app.rs +++ b/crates/fabric-cli/src/app.rs @@ -6,7 +6,7 @@ use clap::error::ErrorKind; use clap::{ArgGroup, Args, Parser, Subcommand}; use nemo_fabric_core::{ - ResolveContext, RunRequest, doctor_plan, resolve_run_plan_from_config, run_plan, + ResolveContext, RunRequest, RunStatus, doctor_plan, resolve_run_plan_from_config, run_plan, }; use crate::examples; @@ -250,16 +250,23 @@ fn run(cli: Cli) -> Result<(), Box> { selected.config().clone(), ResolveContext::new(selected.base_dir()), )?; - println!( - "{}", - serde_json::to_string_pretty(&run_plan(&plan, RunRequest::text(input))?)? - ); + let result = run_plan(&plan, RunRequest::text(input))?; + println!("{}", serde_json::to_string_pretty(&result)?); + require_successful_run(result.status)?; } Command::Version => println!("{}", nemo_fabric_core::version()), } Ok(()) } +fn require_successful_run(status: RunStatus) -> Result<(), String> { + match status { + RunStatus::Succeeded => Ok(()), + RunStatus::Failed => Err("run completed with status failed".to_string()), + RunStatus::Cancelled => Err("run completed with status cancelled".to_string()), + } +} + fn select_source(selector: &Selector) -> Result> { if let Some(name) = &selector.preset { let mut selected = find_preset(name)?.stage()?; @@ -336,6 +343,13 @@ mod tests { assert!(execute_from(["nemo-fabric", "--help"]).is_ok()); } + #[test] + fn failed_and_cancelled_runs_are_errors() { + assert!(require_successful_run(RunStatus::Succeeded).is_ok()); + assert!(require_successful_run(RunStatus::Failed).is_err()); + assert!(require_successful_run(RunStatus::Cancelled).is_err()); + } + #[test] fn parses_preset_plan() { let cli = Cli::try_parse_from(["nemo-fabric", "plan", "--preset", "scripted"]) diff --git a/crates/fabric-cli/src/scaffold.rs b/crates/fabric-cli/src/scaffold.rs index ed017a3ba..ac2cdb864 100644 --- a/crates/fabric-cli/src/scaffold.rs +++ b/crates/fabric-cli/src/scaffold.rs @@ -94,7 +94,7 @@ fn language_files(language: Language, config: &FabricConfig, example: &str) -> V "Cargo.toml", RUST_PROJECT .replace("{{PACKAGE}}", &package_name(example)) - .replace("{{NEMO_FABRIC_VERSION}}", env!("CARGO_PKG_VERSION")), + .replace("{{NEMO_FABRIC_CORE_DEPENDENCY}}", &rust_core_dependency()), "src/main.rs", render_rust(config), ), @@ -111,6 +111,22 @@ fn language_files(language: Language, config: &FabricConfig, example: &str) -> V ] } +fn rust_core_dependency() -> String { + let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); + let workspace_core = manifest_dir + .parent() + .expect("CLI crate has a parent directory") + .join("fabric-core"); + if workspace_core.join("Cargo.toml").is_file() { + return format!( + "{{ path = {}, version = {} }}", + rust_string(&workspace_core.display().to_string()), + rust_string(env!("CARGO_PKG_VERSION")), + ); + } + rust_string(env!("CARGO_PKG_VERSION")) +} + fn write_files(destination: &Path, files: &[ScaffoldFile]) -> Result<(), String> { fs::create_dir(destination).map_err(|error| { format!( @@ -295,10 +311,8 @@ mod tests { if language == Language::Rust { let manifest = fs::read_to_string(destination.join("Cargo.toml")).expect("read manifest"); - assert!(manifest.contains(&format!( - "nemo-fabric-core = \"{}\"", - env!("CARGO_PKG_VERSION") - ))); + assert!(manifest.contains("nemo-fabric-core = { path = ")); + assert!(manifest.contains(&format!("version = \"{}\"", env!("CARGO_PKG_VERSION")))); } fs::remove_dir_all(destination).expect("remove scaffold"); } diff --git a/crates/fabric-cli/templates/python/pyproject.toml.tmpl b/crates/fabric-cli/templates/python/pyproject.toml.tmpl index e96a93bfd..3030ead7e 100644 --- a/crates/fabric-cli/templates/python/pyproject.toml.tmpl +++ b/crates/fabric-cli/templates/python/pyproject.toml.tmpl @@ -1,8 +1,15 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 +[build-system] +requires = ["setuptools>=77"] +build-backend = "setuptools.build_meta" + [project] name = "{{PACKAGE}}-python" version = "0.1.0" requires-python = ">=3.11" dependencies = ["nemo-fabric-runtime"] + +[tool.setuptools] +py-modules = ["main"] diff --git a/crates/fabric-cli/templates/rust/Cargo.toml.tmpl b/crates/fabric-cli/templates/rust/Cargo.toml.tmpl index e9391eb14..ecd8ea477 100644 --- a/crates/fabric-cli/templates/rust/Cargo.toml.tmpl +++ b/crates/fabric-cli/templates/rust/Cargo.toml.tmpl @@ -7,5 +7,5 @@ version = "0.1.0" edition = "2024" [dependencies] -nemo-fabric-core = "{{NEMO_FABRIC_VERSION}}" +nemo-fabric-core = {{NEMO_FABRIC_CORE_DEPENDENCY}} serde_json = "1" diff --git a/docs/integrations/codex.mdx b/docs/integrations/codex.mdx index 30a5f9dcb..9162c7e48 100644 --- a/docs/integrations/codex.mdx +++ b/docs/integrations/codex.mdx @@ -75,6 +75,11 @@ starts the Relay gateway as a supervised sidecar and directs the Codex SDK's built-in OpenAI provider through that gateway. The SDK still obtains credentials from the selected Codex authentication mode. +Relay-enabled Codex runs require `models.default.provider` to be `openai`. +The custom `nvidia` provider uses its configured NVIDIA Responses endpoint and +does not support the built-in provider redirect that Relay requires. Use the +`nvidia` provider without Relay. + NeMo Fabric supplies Relay configuration to the SDK for only the current request. It does not copy the Codex credential store into Relay configuration or persist credentials in Relay artifacts. diff --git a/tests/adapters/test_codex_adapter.py b/tests/adapters/test_codex_adapter.py index dd1705c2f..aa804644c 100644 --- a/tests/adapters/test_codex_adapter.py +++ b/tests/adapters/test_codex_adapter.py @@ -336,6 +336,31 @@ def test_nvidia_provider_uses_responses_api_and_nvidia_credential( } +def test_nvidia_provider_normalizes_codex_home_creation_failure( + codex_payload, mock_codex, monkeypatch +): + model = codex_payload["config"]["models"]["default"] + model.update( + { + "provider": "nvidia", + "model": "openai/gpt-oss-120b", + "api_key_env": "NVIDIA_API_KEY", + "settings": {"base_url": "https://nvidia.example/v1"}, + } + ) + os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + + async def fail_to_create_home(*_args, **_kwargs): + raise OSError("read-only filesystem") + + monkeypatch.setattr(adapter.asyncio, "to_thread", fail_to_create_home) + + output = adapter.run(codex_payload) + + assert output["error"]["code"] == "codex_runtime_unavailable" + mock_codex.assert_not_called() + + def test_nvidia_provider_requires_credential(codex_payload, mock_codex): model = codex_payload["config"]["models"]["default"] model.update( @@ -452,6 +477,31 @@ def test_relay_uses_gateway_and_request_scoped_sdk_config( stop_gateway.assert_called_once_with(process) +def test_relay_rejects_nvidia_provider(codex_payload, mock_codex): + model = codex_payload["config"]["models"]["default"] + model.update( + { + "provider": "nvidia", + "model": "openai/gpt-oss-120b", + "api_key_env": "NVIDIA_API_KEY", + "settings": {"base_url": "https://nvidia.example/v1"}, + } + ) + codex_payload["telemetry_plan"] = { + "providers": ["relay"], + "relay_enabled": True, + } + os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + + output = adapter.run(codex_payload) + + assert output["error"]["code"] == "codex_invalid_configuration" + assert output["error"]["message"] == ( + "NeMo Relay requires the built-in openai model provider" + ) + mock_codex.assert_not_called() + + def test_prepare_relay_reuses_one_resolved_executable( codex_payload, monkeypatch, tmp_path ): diff --git a/tests/e2e/test_cli_scaffold.py b/tests/e2e/test_cli_scaffold.py new file mode 100644 index 000000000..31b8fd384 --- /dev/null +++ b/tests/e2e/test_cli_scaffold.py @@ -0,0 +1,108 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import json +import os +from pathlib import Path +import subprocess +import sys + + +ROOT = Path(__file__).parents[2] + + +def generate_scaffold(destination: Path, language: str) -> None: + subprocess.run( + [ + "cargo", + "run", + "--quiet", + "-p", + "nemo-fabric-cli", + "--", + "example", + "init", + "code-review", + str(destination), + "--language", + language, + ], + cwd=ROOT, + check=True, + capture_output=True, + text=True, + ) + + +def test_generated_python_scaffold_installs_editable(tmp_path: Path): + destination = tmp_path / "python-agent" + generate_scaffold(destination, "python") + venv = tmp_path / "venv" + subprocess.run( + ["uv", "venv", "--seed", "--python", sys.executable, str(venv)], + check=True, + capture_output=True, + text=True, + ) + python = venv / ("Scripts/python.exe" if os.name == "nt" else "bin/python") + environment = os.environ.copy() + environment["PIP_NO_BUILD_ISOLATION"] = "1" + environment["PIP_NO_DEPS"] = "1" + + subprocess.run( + [str(python), "-m", "pip", "install", "-e", "."], + cwd=destination, + env=environment, + check=True, + capture_output=True, + text=True, + ) + + +def test_generated_rust_scaffold_builds(tmp_path: Path): + destination = tmp_path / "rust-agent" + generate_scaffold(destination, "rust") + environment = os.environ.copy() + environment["CARGO_TARGET_DIR"] = str(ROOT / "target") + + subprocess.run( + [ + "cargo", + "build", + "--offline", + "--manifest-path", + str(destination / "Cargo.toml"), + ], + env=environment, + check=True, + capture_output=True, + text=True, + ) + + +def test_failed_cli_run_returns_nonzero_status(): + environment = os.environ.copy() + environment.pop("NVIDIA_API_KEY", None) + result = subprocess.run( + [ + "cargo", + "run", + "--quiet", + "-p", + "nemo-fabric-cli", + "--", + "run", + "--preset", + "hermes", + "--input", + "Say hello", + ], + cwd=ROOT, + env=environment, + check=False, + capture_output=True, + text=True, + ) + + assert result.returncode != 0 + assert json.loads(result.stdout)["status"] == "failed" From 00f8fd1a1140f59505fa6f6c7dede85d96268274 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:12:50 -0700 Subject: [PATCH 18/20] test: allow fresh scaffold dependency resolution Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- tests/e2e/test_cli_scaffold.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/e2e/test_cli_scaffold.py b/tests/e2e/test_cli_scaffold.py index 31b8fd384..5366c628f 100644 --- a/tests/e2e/test_cli_scaffold.py +++ b/tests/e2e/test_cli_scaffold.py @@ -69,7 +69,6 @@ def test_generated_rust_scaffold_builds(tmp_path: Path): [ "cargo", "build", - "--offline", "--manifest-path", str(destination / "Cargo.toml"), ], From 982b06973003ba0058721ea70bef8dcfb3977af7 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:17:46 -0700 Subject: [PATCH 19/20] fix: keep Rust scaffolds independent of checkout Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- crates/fabric-cli/src/scaffold.rs | 18 ++++-------------- tests/e2e/test_cli_scaffold.py | 4 ++++ 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/crates/fabric-cli/src/scaffold.rs b/crates/fabric-cli/src/scaffold.rs index ac2cdb864..10c66b557 100644 --- a/crates/fabric-cli/src/scaffold.rs +++ b/crates/fabric-cli/src/scaffold.rs @@ -112,18 +112,6 @@ fn language_files(language: Language, config: &FabricConfig, example: &str) -> V } fn rust_core_dependency() -> String { - let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); - let workspace_core = manifest_dir - .parent() - .expect("CLI crate has a parent directory") - .join("fabric-core"); - if workspace_core.join("Cargo.toml").is_file() { - return format!( - "{{ path = {}, version = {} }}", - rust_string(&workspace_core.display().to_string()), - rust_string(env!("CARGO_PKG_VERSION")), - ); - } rust_string(env!("CARGO_PKG_VERSION")) } @@ -311,8 +299,10 @@ mod tests { if language == Language::Rust { let manifest = fs::read_to_string(destination.join("Cargo.toml")).expect("read manifest"); - assert!(manifest.contains("nemo-fabric-core = { path = ")); - assert!(manifest.contains(&format!("version = \"{}\"", env!("CARGO_PKG_VERSION")))); + assert!( + manifest.contains(&format!("nemo-fabric-core = {}", rust_core_dependency())) + ); + assert!(!manifest.contains("path =")); } fs::remove_dir_all(destination).expect("remove scaffold"); } diff --git a/tests/e2e/test_cli_scaffold.py b/tests/e2e/test_cli_scaffold.py index 5366c628f..f36521ffc 100644 --- a/tests/e2e/test_cli_scaffold.py +++ b/tests/e2e/test_cli_scaffold.py @@ -62,13 +62,17 @@ def test_generated_python_scaffold_installs_editable(tmp_path: Path): def test_generated_rust_scaffold_builds(tmp_path: Path): destination = tmp_path / "rust-agent" generate_scaffold(destination, "rust") + assert "path =" not in (destination / "Cargo.toml").read_text() environment = os.environ.copy() environment["CARGO_TARGET_DIR"] = str(ROOT / "target") + core_path = json.dumps(str(ROOT / "crates/fabric-core")) subprocess.run( [ "cargo", "build", + "--config", + f"patch.crates-io.nemo-fabric-core.path={core_path}", "--manifest-path", str(destination / "Cargo.toml"), ], From 01be3adbaed8d156911d613201a5e8ef839257c8 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Mon, 20 Jul 2026 14:22:57 -0700 Subject: [PATCH 20/20] fix: keep generated Rust variants warning-free Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- crates/fabric-cli/src/scaffold.rs | 12 +++++++++--- crates/fabric-cli/templates/rust/main.rs.tmpl | 6 +++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/fabric-cli/src/scaffold.rs b/crates/fabric-cli/src/scaffold.rs index 10c66b557..2d5cfbcef 100644 --- a/crates/fabric-cli/src/scaffold.rs +++ b/crates/fabric-cli/src/scaffold.rs @@ -222,7 +222,13 @@ fn rust_settings(settings: &serde_json::Map) -> String { "Map::from_iter([{}])", settings .iter() - .map(|(key, value)| format!("({}.to_string(), json!({}))", rust_string(key), value)) + .map(|(key, value)| { + format!( + "({}.to_string(), serde_json::json!({}))", + rust_string(key), + value + ) + }) .collect::>() .join(", ") ) @@ -242,7 +248,7 @@ fn rust_models(model: Option<&ModelConfig>) -> String { .map(|value| format!("Some({value})")) .unwrap_or_else(|| "None".to_string()); format!( - "BTreeMap::from_iter([(\"default\".to_string(), ModelConfig {{ provider: {}.to_string(), model: {}.to_string(), temperature: {temperature}, api_key_env: {api_key}, settings: {}, extensions: BTreeMap::new() }})])", + "BTreeMap::from_iter([(\"default\".to_string(), nemo_fabric_core::ModelConfig {{ provider: {}.to_string(), model: {}.to_string(), temperature: {temperature}, api_key_env: {api_key}, settings: {}, extensions: BTreeMap::new() }})])", rust_string(&model.provider), rust_string(&model.model), rust_settings(&model.settings), @@ -326,7 +332,7 @@ mod tests { let rust = render_rust(&config); assert!(rust.contains("temperature: Some(0.2)")); assert!(rust.contains( - "(\"base_url\".to_string(), json!(\"https://integrate.api.nvidia.com/v1\"))" + "(\"base_url\".to_string(), serde_json::json!(\"https://integrate.api.nvidia.com/v1\"))" )); } diff --git a/crates/fabric-cli/templates/rust/main.rs.tmpl b/crates/fabric-cli/templates/rust/main.rs.tmpl index 565335881..edbffadb4 100644 --- a/crates/fabric-cli/templates/rust/main.rs.tmpl +++ b/crates/fabric-cli/templates/rust/main.rs.tmpl @@ -6,10 +6,10 @@ use std::path::PathBuf; use nemo_fabric_core::{ ControlLocation, EnvironmentConfig, EnvironmentOwnership, FabricConfig, HarnessConfig, - MetadataConfig, ModelConfig, ResolutionStrategy, ResolveContext, RunRequest, RuntimeConfig, - SkillConfig, resolve_run_plan_from_config, run_plan, + MetadataConfig, ResolutionStrategy, ResolveContext, RunRequest, RuntimeConfig, SkillConfig, + resolve_run_plan_from_config, run_plan, }; -use serde_json::{Map, json}; +use serde_json::Map; fn build_config() -> FabricConfig { FabricConfig {