nemo-platform-plugin 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nemo_platform/__init__.py +117 -0
- nemo_platform/_base_client.py +2146 -0
- nemo_platform/_client.py +1200 -0
- nemo_platform/_compat.py +241 -0
- nemo_platform/_constants.py +29 -0
- nemo_platform/_decoders/jsonl.py +138 -0
- nemo_platform/_exceptions.py +123 -0
- nemo_platform/_files.py +188 -0
- nemo_platform/_models.py +967 -0
- nemo_platform/_qs.py +164 -0
- nemo_platform/_resource.py +58 -0
- nemo_platform/_response.py +872 -0
- nemo_platform/_streaming.py +391 -0
- nemo_platform/_types.py +288 -0
- nemo_platform/_utils/__init__.py +79 -0
- nemo_platform/_utils/_compat.py +60 -0
- nemo_platform/_utils/_datetime_parse.py +151 -0
- nemo_platform/_utils/_json.py +50 -0
- nemo_platform/_utils/_logs.py +40 -0
- nemo_platform/_utils/_path.py +142 -0
- nemo_platform/_utils/_proxy.py +80 -0
- nemo_platform/_utils/_reflection.py +57 -0
- nemo_platform/_utils/_resources_proxy.py +39 -0
- nemo_platform/_utils/_streams.py +27 -0
- nemo_platform/_utils/_sync.py +73 -0
- nemo_platform/_utils/_transform.py +472 -0
- nemo_platform/_utils/_typing.py +171 -0
- nemo_platform/_utils/_utils.py +448 -0
- nemo_platform/_version.py +21 -0
- nemo_platform/auth/__init__.py +15 -0
- nemo_platform/auth/device_flow.py +303 -0
- nemo_platform/auth/helpers.py +211 -0
- nemo_platform/auth/token_provider.py +239 -0
- nemo_platform/beta/evaluator/__init__.py +120 -0
- nemo_platform/beta/evaluator/agent_inference.py +360 -0
- nemo_platform/beta/evaluator/constants.py +4 -0
- nemo_platform/beta/evaluator/dataset_schemas/common.py +177 -0
- nemo_platform/beta/evaluator/dataset_schemas/compatibility.py +353 -0
- nemo_platform/beta/evaluator/dataset_schemas/templates.py +389 -0
- nemo_platform/beta/evaluator/datasets/__init__.py +28 -0
- nemo_platform/beta/evaluator/datasets/loader.py +415 -0
- nemo_platform/beta/evaluator/enums.py +64 -0
- nemo_platform/beta/evaluator/execution/_protocols.py +20 -0
- nemo_platform/beta/evaluator/execution/backends/base.py +86 -0
- nemo_platform/beta/evaluator/execution/backends/local/backend.py +132 -0
- nemo_platform/beta/evaluator/execution/benchmark_execution.py +666 -0
- nemo_platform/beta/evaluator/execution/config.py +64 -0
- nemo_platform/beta/evaluator/execution/evaluator.py +289 -0
- nemo_platform/beta/evaluator/execution/job_poll.py +98 -0
- nemo_platform/beta/evaluator/execution/metric_execution.py +878 -0
- nemo_platform/beta/evaluator/execution/pipeline.py +64 -0
- nemo_platform/beta/evaluator/execution/runs.py +143 -0
- nemo_platform/beta/evaluator/execution/scoring.py +224 -0
- nemo_platform/beta/evaluator/execution/utils.py +116 -0
- nemo_platform/beta/evaluator/execution/values.py +53 -0
- nemo_platform/beta/evaluator/inference.py +456 -0
- nemo_platform/beta/evaluator/metrics/aggregation.py +451 -0
- nemo_platform/beta/evaluator/metrics/bleu.py +94 -0
- nemo_platform/beta/evaluator/metrics/exact_match.py +52 -0
- nemo_platform/beta/evaluator/metrics/f1.py +50 -0
- nemo_platform/beta/evaluator/metrics/hooks.py +41 -0
- nemo_platform/beta/evaluator/metrics/llm_judge.py +392 -0
- nemo_platform/beta/evaluator/metrics/number_check.py +95 -0
- nemo_platform/beta/evaluator/metrics/protocol.py +241 -0
- nemo_platform/beta/evaluator/metrics/ragas/__init__.py +41 -0
- nemo_platform/beta/evaluator/metrics/ragas/base.py +521 -0
- nemo_platform/beta/evaluator/metrics/ragas/git_patch.py +36 -0
- nemo_platform/beta/evaluator/metrics/ragas/imports.py +192 -0
- nemo_platform/beta/evaluator/metrics/ragas/metrics.py +183 -0
- nemo_platform/beta/evaluator/metrics/remote.py +250 -0
- nemo_platform/beta/evaluator/metrics/rouge.py +71 -0
- nemo_platform/beta/evaluator/metrics/string_check.py +69 -0
- nemo_platform/beta/evaluator/metrics/template_rendering.py +141 -0
- nemo_platform/beta/evaluator/metrics/tool_calling.py +173 -0
- nemo_platform/beta/evaluator/metrics/types.py +62 -0
- nemo_platform/beta/evaluator/metrics/utils.py +54 -0
- nemo_platform/beta/evaluator/resilience/api.py +142 -0
- nemo_platform/beta/evaluator/resilience/classifier.py +142 -0
- nemo_platform/beta/evaluator/resilience/config.py +69 -0
- nemo_platform/beta/evaluator/resilience/errors.py +117 -0
- nemo_platform/beta/evaluator/resilience/policy.py +50 -0
- nemo_platform/beta/evaluator/resilience/scheduler.py +459 -0
- nemo_platform/beta/evaluator/resilience/types.py +93 -0
- nemo_platform/beta/evaluator/structured_output.py +184 -0
- nemo_platform/beta/evaluator/templates.py +115 -0
- nemo_platform/beta/evaluator/values/__init__.py +162 -0
- nemo_platform/beta/evaluator/values/agents.py +84 -0
- nemo_platform/beta/evaluator/values/common.py +28 -0
- nemo_platform/beta/evaluator/values/dataset_schemas.py +112 -0
- nemo_platform/beta/evaluator/values/datasets.py +28 -0
- nemo_platform/beta/evaluator/values/metrics.py +587 -0
- nemo_platform/beta/evaluator/values/models.py +157 -0
- nemo_platform/beta/evaluator/values/multi_metric_results.py +359 -0
- nemo_platform/beta/evaluator/values/params.py +109 -0
- nemo_platform/beta/evaluator/values/results.py +699 -0
- nemo_platform/beta/evaluator/values/scores.py +300 -0
- nemo_platform/beta/safe_synthesizer/__init__.py +4 -0
- nemo_platform/beta/safe_synthesizer/config.py +29 -0
- nemo_platform/beta/safe_synthesizer/job.py +309 -0
- nemo_platform/beta/safe_synthesizer/job_builder.py +355 -0
- nemo_platform/cli/__init__.py +4 -0
- nemo_platform/cli/app.py +321 -0
- nemo_platform/cli/commands/__init__.py +15 -0
- nemo_platform/cli/commands/api/__init__.py +114 -0
- nemo_platform/cli/commands/api/adapters.py +384 -0
- nemo_platform/cli/commands/api/files/__init__.py +237 -0
- nemo_platform/cli/commands/api/files/filesets.py +386 -0
- nemo_platform/cli/commands/api/files/otlp/__init__.py +12 -0
- nemo_platform/cli/commands/api/files/otlp/logs.py +116 -0
- nemo_platform/cli/commands/api/guardrail/__init__.py +270 -0
- nemo_platform/cli/commands/api/guardrail/configs.py +311 -0
- nemo_platform/cli/commands/api/iam/__init__.py +12 -0
- nemo_platform/cli/commands/api/iam/role_bindings.py +264 -0
- nemo_platform/cli/commands/api/inference/__init__.py +76 -0
- nemo_platform/cli/commands/api/inference/deployment_configs/__init__.py +370 -0
- nemo_platform/cli/commands/api/inference/deployment_configs/versions.py +132 -0
- nemo_platform/cli/commands/api/inference/deployments/__init__.py +565 -0
- nemo_platform/cli/commands/api/inference/deployments/versions.py +141 -0
- nemo_platform/cli/commands/api/inference/gateway/__init__.py +14 -0
- nemo_platform/cli/commands/api/inference/gateway/model.py +324 -0
- nemo_platform/cli/commands/api/inference/gateway/openai/__init__.py +12 -0
- nemo_platform/cli/commands/api/inference/gateway/openai/v1/__init__.py +12 -0
- nemo_platform/cli/commands/api/inference/gateway/openai/v1/models.py +106 -0
- nemo_platform/cli/commands/api/inference/gateway/provider.py +327 -0
- nemo_platform/cli/commands/api/inference/models.py +106 -0
- nemo_platform/cli/commands/api/inference/providers.py +612 -0
- nemo_platform/cli/commands/api/inference/virtual_models.py +412 -0
- nemo_platform/cli/commands/api/intake/__init__.py +18 -0
- nemo_platform/cli/commands/api/intake/apps/__init__.py +326 -0
- nemo_platform/cli/commands/api/intake/apps/tasks.py +340 -0
- nemo_platform/cli/commands/api/intake/entries/__init__.py +445 -0
- nemo_platform/cli/commands/api/intake/entries/events.py +118 -0
- nemo_platform/cli/commands/api/intake/evaluator_results.py +267 -0
- nemo_platform/cli/commands/api/intake/exports/__init__.py +63 -0
- nemo_platform/cli/commands/api/intake/exports/jobs.py +245 -0
- nemo_platform/cli/commands/api/intake/ingest/__init__.py +14 -0
- nemo_platform/cli/commands/api/intake/ingest/atif.py +115 -0
- nemo_platform/cli/commands/api/intake/ingest/chat_completions.py +125 -0
- nemo_platform/cli/commands/api/intake/ingest/otlp/__init__.py +12 -0
- nemo_platform/cli/commands/api/intake/ingest/otlp/v1/__init__.py +12 -0
- nemo_platform/cli/commands/api/intake/ingest/otlp/v1/traces.py +72 -0
- nemo_platform/cli/commands/api/intake/spans/__init__.py +202 -0
- nemo_platform/cli/commands/api/intake/spans/evaluator_results.py +65 -0
- nemo_platform/cli/commands/api/intake/traces.py +183 -0
- nemo_platform/cli/commands/api/jobs/__init__.py +576 -0
- nemo_platform/cli/commands/api/jobs/results.py +211 -0
- nemo_platform/cli/commands/api/jobs/steps.py +243 -0
- nemo_platform/cli/commands/api/jobs/tasks.py +210 -0
- nemo_platform/cli/commands/api/members.py +304 -0
- nemo_platform/cli/commands/api/models/__init__.py +555 -0
- nemo_platform/cli/commands/api/models/adapters.py +261 -0
- nemo_platform/cli/commands/api/projects.py +332 -0
- nemo_platform/cli/commands/api/safe_synthesizer/__init__.py +12 -0
- nemo_platform/cli/commands/api/safe_synthesizer/jobs/__init__.py +417 -0
- nemo_platform/cli/commands/api/safe_synthesizer/jobs/results.py +211 -0
- nemo_platform/cli/commands/api/secrets/__init__.py +315 -0
- nemo_platform/cli/commands/api/secrets/admin.py +44 -0
- nemo_platform/cli/commands/api/workspaces.py +339 -0
- nemo_platform/cli/commands/auth.py +803 -0
- nemo_platform/cli/commands/config.py +308 -0
- nemo_platform/cli/commands/docs.py +277 -0
- nemo_platform/cli/commands/manifest_registry.py +180 -0
- nemo_platform/cli/commands/plugins.py +87 -0
- nemo_platform/cli/commands/quickstart/__init__.py +15 -0
- nemo_platform/cli/commands/quickstart/cli.py +1178 -0
- nemo_platform/cli/commands/services/_process.py +472 -0
- nemo_platform/cli/commands/services/cli.py +694 -0
- nemo_platform/cli/commands/setup.py +1947 -0
- nemo_platform/cli/commands/skills/agents/claude.py +29 -0
- nemo_platform/cli/commands/skills/agents/codex.py +32 -0
- nemo_platform/cli/commands/skills/agents/cursor.py +18 -0
- nemo_platform/cli/commands/skills/agents/opencode.py +20 -0
- nemo_platform/cli/commands/skills/base.py +53 -0
- nemo_platform/cli/commands/skills/cli.py +313 -0
- nemo_platform/cli/commands/skills/installer.py +51 -0
- nemo_platform/cli/commands/skills/registry.py +396 -0
- nemo_platform/cli/commands/use_cases/__init__.py +15 -0
- nemo_platform/cli/commands/use_cases/agent.py +245 -0
- nemo_platform/cli/commands/use_cases/chat.py +965 -0
- nemo_platform/cli/commands/use_cases/wait.py +151 -0
- nemo_platform/cli/core/__init__.py +8 -0
- nemo_platform/cli/core/agent_helpers.py +84 -0
- nemo_platform/cli/core/api.py +160 -0
- nemo_platform/cli/core/autocomplete.py +63 -0
- nemo_platform/cli/core/code_generator.py +306 -0
- nemo_platform/cli/core/context.py +179 -0
- nemo_platform/cli/core/errors.py +349 -0
- nemo_platform/cli/core/formatters.py +718 -0
- nemo_platform/cli/core/help_formatter.py +868 -0
- nemo_platform/cli/core/lazy_load.py +187 -0
- nemo_platform/cli/core/logging.py +24 -0
- nemo_platform/cli/core/pagination.py +255 -0
- nemo_platform/cli/core/stdin_utils.py +233 -0
- nemo_platform/cli/core/streaming.py +30 -0
- nemo_platform/cli/core/table_config.py +302 -0
- nemo_platform/cli/core/timestamp_formatter.py +127 -0
- nemo_platform/cli/core/types.py +112 -0
- nemo_platform/cli/core/waiters.py +339 -0
- nemo_platform/cli/manifest.py +108 -0
- nemo_platform/client/__init__.py +15 -0
- nemo_platform/client/factory.py +597 -0
- nemo_platform/config/README.md +168 -0
- nemo_platform/config/__init__.py +23 -0
- nemo_platform/config/config.py +538 -0
- nemo_platform/config/models.py +377 -0
- nemo_platform/config/types.py +7 -0
- nemo_platform/filesets/__init__.py +19 -0
- nemo_platform/filesets/filesystem/callbacks.py +229 -0
- nemo_platform/filesets/filesystem/filesystem.py +940 -0
- nemo_platform/filesets/resources.py +1120 -0
- nemo_platform/lib/.keep +4 -0
- nemo_platform/models/__init__.py +14 -0
- nemo_platform/models/resources.py +836 -0
- nemo_platform/pagination.py +149 -0
- nemo_platform/py.typed +0 -0
- nemo_platform/quickstart/__init__.py +99 -0
- nemo_platform/quickstart/cluster.py +198 -0
- nemo_platform/quickstart/config.py +402 -0
- nemo_platform/quickstart/container.py +758 -0
- nemo_platform/quickstart/gpu_config.py +111 -0
- nemo_platform/quickstart/platform_config.py +82 -0
- nemo_platform/quickstart/preflight.py +451 -0
- nemo_platform/quickstart/prompts.py +426 -0
- nemo_platform/quickstart/storage.py +90 -0
- nemo_platform/quickstart/validators.py +268 -0
- nemo_platform/resources/__init__.py +16 -0
- nemo_platform/resources/adapters/__init__.py +34 -0
- nemo_platform/resources/adapters/adapters.py +704 -0
- nemo_platform/resources/adapters/api.md +15 -0
- nemo_platform/resources/audit/__init__.py +16 -0
- nemo_platform/resources/entities/__init__.py +34 -0
- nemo_platform/resources/entities/api.md +16 -0
- nemo_platform/resources/entities/entities.py +1014 -0
- nemo_platform/resources/evaluation/__init__.py +118 -0
- nemo_platform/resources/evaluation/api.md +303 -0
- nemo_platform/resources/evaluation/benchmark_job_results.py +541 -0
- nemo_platform/resources/evaluation/benchmark_jobs/__init__.py +48 -0
- nemo_platform/resources/evaluation/benchmark_jobs/benchmark_jobs.py +900 -0
- nemo_platform/resources/evaluation/benchmark_jobs/results/__init__.py +76 -0
- nemo_platform/resources/evaluation/benchmark_jobs/results/aggregate_scores.py +197 -0
- nemo_platform/resources/evaluation/benchmark_jobs/results/artifacts.py +206 -0
- nemo_platform/resources/evaluation/benchmark_jobs/results/results.py +516 -0
- nemo_platform/resources/evaluation/benchmark_jobs/results/row_scores.py +213 -0
- nemo_platform/resources/evaluation/benchmarks.py +690 -0
- nemo_platform/resources/evaluation/evaluation.py +277 -0
- nemo_platform/resources/evaluation/metric_job_results.py +533 -0
- nemo_platform/resources/evaluation/metric_jobs/__init__.py +48 -0
- nemo_platform/resources/evaluation/metric_jobs/metric_jobs.py +888 -0
- nemo_platform/resources/evaluation/metric_jobs/results/__init__.py +76 -0
- nemo_platform/resources/evaluation/metric_jobs/results/aggregate_scores.py +197 -0
- nemo_platform/resources/evaluation/metric_jobs/results/artifacts.py +206 -0
- nemo_platform/resources/evaluation/metric_jobs/results/results.py +512 -0
- nemo_platform/resources/evaluation/metric_jobs/results/row_scores.py +213 -0
- nemo_platform/resources/evaluation/metrics.py +3495 -0
- nemo_platform/resources/files/__init__.py +62 -0
- nemo_platform/resources/files/api.md +65 -0
- nemo_platform/resources/files/files.py +683 -0
- nemo_platform/resources/files/filesets.py +783 -0
- nemo_platform/resources/files/otlp/__init__.py +48 -0
- nemo_platform/resources/files/otlp/logs.py +337 -0
- nemo_platform/resources/files/otlp/otlp.py +117 -0
- nemo_platform/resources/guardrail/__init__.py +48 -0
- nemo_platform/resources/guardrail/api.md +116 -0
- nemo_platform/resources/guardrail/configs.py +679 -0
- nemo_platform/resources/guardrail/guardrail.py +463 -0
- nemo_platform/resources/iam/__init__.py +48 -0
- nemo_platform/resources/iam/api.md +22 -0
- nemo_platform/resources/iam/iam.py +117 -0
- nemo_platform/resources/iam/role_bindings.py +537 -0
- nemo_platform/resources/inference/__init__.py +118 -0
- nemo_platform/resources/inference/api.md +224 -0
- nemo_platform/resources/inference/deployment_configs/__init__.py +48 -0
- nemo_platform/resources/inference/deployment_configs/deployment_configs.py +756 -0
- nemo_platform/resources/inference/deployment_configs/versions.py +418 -0
- nemo_platform/resources/inference/deployments/__init__.py +48 -0
- nemo_platform/resources/inference/deployments/deployments.py +1017 -0
- nemo_platform/resources/inference/deployments/versions.py +434 -0
- nemo_platform/resources/inference/gateway/__init__.py +76 -0
- nemo_platform/resources/inference/gateway/gateway.py +181 -0
- nemo_platform/resources/inference/gateway/model.py +708 -0
- nemo_platform/resources/inference/gateway/openai/__init__.py +48 -0
- nemo_platform/resources/inference/gateway/openai/openai.py +700 -0
- nemo_platform/resources/inference/gateway/openai/v1/__init__.py +48 -0
- nemo_platform/resources/inference/gateway/openai/v1/models.py +290 -0
- nemo_platform/resources/inference/gateway/openai/v1/v1.py +117 -0
- nemo_platform/resources/inference/gateway/provider.py +749 -0
- nemo_platform/resources/inference/inference.py +277 -0
- nemo_platform/resources/inference/models.py +290 -0
- nemo_platform/resources/inference/providers.py +1049 -0
- nemo_platform/resources/inference/virtual_models.py +846 -0
- nemo_platform/resources/intake/__init__.py +132 -0
- nemo_platform/resources/intake/api.md +268 -0
- nemo_platform/resources/intake/apps/__init__.py +48 -0
- nemo_platform/resources/intake/apps/apps.py +718 -0
- nemo_platform/resources/intake/apps/tasks.py +731 -0
- nemo_platform/resources/intake/entries/__init__.py +48 -0
- nemo_platform/resources/intake/entries/entries.py +855 -0
- nemo_platform/resources/intake/entries/events.py +320 -0
- nemo_platform/resources/intake/evaluator_results.py +512 -0
- nemo_platform/resources/intake/exports/__init__.py +48 -0
- nemo_platform/resources/intake/exports/exports.py +229 -0
- nemo_platform/resources/intake/exports/jobs.py +464 -0
- nemo_platform/resources/intake/ingest/__init__.py +76 -0
- nemo_platform/resources/intake/ingest/atif.py +242 -0
- nemo_platform/resources/intake/ingest/chat_completions.py +270 -0
- nemo_platform/resources/intake/ingest/ingest.py +181 -0
- nemo_platform/resources/intake/ingest/otlp/__init__.py +48 -0
- nemo_platform/resources/intake/ingest/otlp/otlp.py +117 -0
- nemo_platform/resources/intake/ingest/otlp/v1/__init__.py +48 -0
- nemo_platform/resources/intake/ingest/otlp/v1/traces.py +183 -0
- nemo_platform/resources/intake/ingest/otlp/v1/v1.py +117 -0
- nemo_platform/resources/intake/intake.py +309 -0
- nemo_platform/resources/intake/spans/__init__.py +48 -0
- nemo_platform/resources/intake/spans/evaluator_results.py +197 -0
- nemo_platform/resources/intake/spans/spans.py +367 -0
- nemo_platform/resources/intake/traces.py +351 -0
- nemo_platform/resources/jobs/__init__.py +76 -0
- nemo_platform/resources/jobs/api.md +118 -0
- nemo_platform/resources/jobs/jobs.py +1300 -0
- nemo_platform/resources/jobs/results.py +555 -0
- nemo_platform/resources/jobs/steps.py +491 -0
- nemo_platform/resources/jobs/tasks.py +470 -0
- nemo_platform/resources/members/__init__.py +34 -0
- nemo_platform/resources/members/api.md +19 -0
- nemo_platform/resources/members/members.py +634 -0
- nemo_platform/resources/models/__init__.py +48 -0
- nemo_platform/resources/models/adapters.py +514 -0
- nemo_platform/resources/models/api.md +44 -0
- nemo_platform/resources/models/models.py +981 -0
- nemo_platform/resources/projects/__init__.py +34 -0
- nemo_platform/resources/projects/api.md +21 -0
- nemo_platform/resources/projects/projects.py +736 -0
- nemo_platform/resources/safe_synthesizer/__init__.py +48 -0
- nemo_platform/resources/safe_synthesizer/api.md +62 -0
- nemo_platform/resources/safe_synthesizer/jobs/__init__.py +48 -0
- nemo_platform/resources/safe_synthesizer/jobs/jobs.py +895 -0
- nemo_platform/resources/safe_synthesizer/jobs/results.py +819 -0
- nemo_platform/resources/safe_synthesizer/safe_synthesizer.py +117 -0
- nemo_platform/resources/secrets/__init__.py +48 -0
- nemo_platform/resources/secrets/admin.py +150 -0
- nemo_platform/resources/secrets/api.md +34 -0
- nemo_platform/resources/secrets/secrets.py +756 -0
- nemo_platform/resources/workspaces/__init__.py +34 -0
- nemo_platform/resources/workspaces/api.md +20 -0
- nemo_platform/resources/workspaces/workspaces.py +734 -0
- nemo_platform/skills/__init__.py +28 -0
- nemo_platform/skills/inference/SKILL.md +564 -0
- nemo_platform/skills/nemo-build-agent/SKILL.md +221 -0
- nemo_platform/skills/nemo-build-agent/references/templates/data-designer-config.py +80 -0
- nemo_platform/skills/nemo-evaluator/SKILL.md +140 -0
- nemo_platform/skills/nemo-explore/SKILL.md +70 -0
- nemo_platform/skills/nemo-files/SKILL.md +81 -0
- nemo_platform/skills/nemo-fine-tune/SKILL.md +43 -0
- nemo_platform/skills/nemo-guardrails/SKILL.md +519 -0
- nemo_platform/skills/nemo-secrets/SKILL.md +60 -0
- nemo_platform/skills/nemo-skill-selection/SKILL.md +146 -0
- nemo_platform/skills/nemo-spec/SKILL.md +76 -0
- nemo_platform/skills/nemo-spec/references/templates/agent-spec.md +68 -0
- nemo_platform/skills/nemo-status/SKILL.md +115 -0
- nemo_platform/skills/nemo-teardown/SKILL.md +185 -0
- nemo_platform/skills/nemo-try-agent/SKILL.md +102 -0
- nemo_platform/types/__init__.py +54 -0
- nemo_platform/types/adapters/__init__.py +24 -0
- nemo_platform/types/adapters/adapter_create_params.py +63 -0
- nemo_platform/types/adapters/adapter_entity_filter_param.py +56 -0
- nemo_platform/types/adapters/adapter_list_params.py +46 -0
- nemo_platform/types/adapters/adapter_patch_params.py +35 -0
- nemo_platform/types/adapters/adapters_page.py +37 -0
- nemo_platform/types/audit/__init__.py +18 -0
- nemo_platform/types/audit/jobs/__init__.py +18 -0
- nemo_platform/types/entities/__init__.py +26 -0
- nemo_platform/types/entities/entities_page.py +37 -0
- nemo_platform/types/entities/entity.py +63 -0
- nemo_platform/types/entities/entity_create_params.py +46 -0
- nemo_platform/types/entities/entity_delete_entity_by_name_params.py +31 -0
- nemo_platform/types/entities/entity_get_entity_by_name_params.py +31 -0
- nemo_platform/types/entities/entity_list_params.py +47 -0
- nemo_platform/types/entities/entity_update_entity_by_name_params.py +52 -0
- nemo_platform/types/evaluation/__init__.py +242 -0
- nemo_platform/types/evaluation/agent.py +68 -0
- nemo_platform/types/evaluation/agent_goal_accuracy_metric.py +92 -0
- nemo_platform/types/evaluation/agent_goal_accuracy_metric_param.py +69 -0
- nemo_platform/types/evaluation/agent_goal_accuracy_metric_param_param.py +70 -0
- nemo_platform/types/evaluation/agent_goal_accuracy_metric_response.py +88 -0
- nemo_platform/types/evaluation/agent_param.py +69 -0
- nemo_platform/types/evaluation/aggregate_range_score.py +65 -0
- nemo_platform/types/evaluation/aggregate_rubric_score.py +64 -0
- nemo_platform/types/evaluation/aggregated_metric_result.py +34 -0
- nemo_platform/types/evaluation/answer_accuracy_metric.py +89 -0
- nemo_platform/types/evaluation/answer_accuracy_metric_param.py +66 -0
- nemo_platform/types/evaluation/answer_accuracy_metric_param_param.py +67 -0
- nemo_platform/types/evaluation/answer_accuracy_metric_response.py +85 -0
- nemo_platform/types/evaluation/benchmark.py +81 -0
- nemo_platform/types/evaluation/benchmark_create_params.py +64 -0
- nemo_platform/types/evaluation/benchmark_create_response.py +26 -0
- nemo_platform/types/evaluation/benchmark_evaluation_job.py +75 -0
- nemo_platform/types/evaluation/benchmark_evaluation_jobs_list_filter_param.py +49 -0
- nemo_platform/types/evaluation/benchmark_evaluation_jobs_page.py +37 -0
- nemo_platform/types/evaluation/benchmark_evaluation_jobs_sort_field.py +22 -0
- nemo_platform/types/evaluation/benchmark_job_create_params.py +58 -0
- nemo_platform/types/evaluation/benchmark_job_get_logs_params.py +30 -0
- nemo_platform/types/evaluation/benchmark_job_list_params.py +44 -0
- nemo_platform/types/evaluation/benchmark_job_result.py +93 -0
- nemo_platform/types/evaluation/benchmark_job_result_list_params.py +116 -0
- nemo_platform/types/evaluation/benchmark_job_result_retrieve_params.py +50 -0
- nemo_platform/types/evaluation/benchmark_job_results_list_response.py +37 -0
- nemo_platform/types/evaluation/benchmark_jobs/__init__.py +18 -0
- nemo_platform/types/evaluation/benchmark_jobs/results/__init__.py +22 -0
- nemo_platform/types/evaluation/benchmark_jobs/results/benchmark_evaluation_result.py +30 -0
- nemo_platform/types/evaluation/benchmark_jobs/results/benchmark_metric_result.py +44 -0
- nemo_platform/types/evaluation/benchmark_jobs/results/row_score_download_params.py +28 -0
- nemo_platform/types/evaluation/benchmark_list_params.py +84 -0
- nemo_platform/types/evaluation/benchmark_offline_job.py +43 -0
- nemo_platform/types/evaluation/benchmark_offline_job_param.py +44 -0
- nemo_platform/types/evaluation/benchmark_online_agent_job.py +71 -0
- nemo_platform/types/evaluation/benchmark_online_agent_job_param.py +74 -0
- nemo_platform/types/evaluation/benchmark_online_job.py +66 -0
- nemo_platform/types/evaluation/benchmark_online_job_param.py +68 -0
- nemo_platform/types/evaluation/benchmark_ref.py +22 -0
- nemo_platform/types/evaluation/benchmark_retrieve_params.py +29 -0
- nemo_platform/types/evaluation/benchmark_retrieve_response.py +27 -0
- nemo_platform/types/evaluation/benchmarks_list_response.py +42 -0
- nemo_platform/types/evaluation/bleu_metric.py +76 -0
- nemo_platform/types/evaluation/bleu_metric_param.py +50 -0
- nemo_platform/types/evaluation/bleu_metric_param_param.py +52 -0
- nemo_platform/types/evaluation/bleu_metric_response.py +69 -0
- nemo_platform/types/evaluation/built_in_dataset.py +46 -0
- nemo_platform/types/evaluation/context_entity_recall_metric.py +89 -0
- nemo_platform/types/evaluation/context_entity_recall_metric_param.py +66 -0
- nemo_platform/types/evaluation/context_entity_recall_metric_param_param.py +67 -0
- nemo_platform/types/evaluation/context_entity_recall_metric_response.py +85 -0
- nemo_platform/types/evaluation/context_precision_metric.py +89 -0
- nemo_platform/types/evaluation/context_precision_metric_param.py +66 -0
- nemo_platform/types/evaluation/context_precision_metric_param_param.py +67 -0
- nemo_platform/types/evaluation/context_precision_metric_response.py +85 -0
- nemo_platform/types/evaluation/context_recall_metric.py +89 -0
- nemo_platform/types/evaluation/context_recall_metric_param.py +66 -0
- nemo_platform/types/evaluation/context_recall_metric_param_param.py +67 -0
- nemo_platform/types/evaluation/context_recall_metric_response.py +85 -0
- nemo_platform/types/evaluation/context_relevance_metric.py +89 -0
- nemo_platform/types/evaluation/context_relevance_metric_param.py +66 -0
- nemo_platform/types/evaluation/context_relevance_metric_param_param.py +67 -0
- nemo_platform/types/evaluation/context_relevance_metric_response.py +85 -0
- nemo_platform/types/evaluation/dataset_rows.py +35 -0
- nemo_platform/types/evaluation/dataset_rows_param.py +36 -0
- nemo_platform/types/evaluation/evaluate_dataset_rows_param.py +33 -0
- nemo_platform/types/evaluation/exact_match_metric.py +79 -0
- nemo_platform/types/evaluation/exact_match_metric_param.py +53 -0
- nemo_platform/types/evaluation/exact_match_metric_param_param.py +53 -0
- nemo_platform/types/evaluation/exact_match_metric_response.py +72 -0
- nemo_platform/types/evaluation/extended_benchmark.py +130 -0
- nemo_platform/types/evaluation/f1_metric.py +76 -0
- nemo_platform/types/evaluation/f1_metric_param.py +50 -0
- nemo_platform/types/evaluation/f1_metric_param_param.py +50 -0
- nemo_platform/types/evaluation/f1_metric_response.py +69 -0
- nemo_platform/types/evaluation/faithfulness_metric.py +89 -0
- nemo_platform/types/evaluation/faithfulness_metric_param.py +66 -0
- nemo_platform/types/evaluation/faithfulness_metric_param_param.py +67 -0
- nemo_platform/types/evaluation/faithfulness_metric_response.py +85 -0
- nemo_platform/types/evaluation/field_mapping.py +56 -0
- nemo_platform/types/evaluation/field_mapping_param.py +57 -0
- nemo_platform/types/evaluation/fileset.py +48 -0
- nemo_platform/types/evaluation/fileset_param.py +49 -0
- nemo_platform/types/evaluation/fileset_ref.py +22 -0
- nemo_platform/types/evaluation/histogram.py +30 -0
- nemo_platform/types/evaluation/histogram_bin.py +33 -0
- nemo_platform/types/evaluation/inference_params.py +65 -0
- nemo_platform/types/evaluation/inference_params_param.py +53 -0
- nemo_platform/types/evaluation/json_score_parser.py +35 -0
- nemo_platform/types/evaluation/json_score_parser_param.py +34 -0
- nemo_platform/types/evaluation/llm_judge_metric.py +126 -0
- nemo_platform/types/evaluation/llm_judge_metric_param.py +103 -0
- nemo_platform/types/evaluation/llm_judge_metric_param_param.py +105 -0
- nemo_platform/types/evaluation/llm_judge_metric_response.py +120 -0
- nemo_platform/types/evaluation/metric_create_params.py +959 -0
- nemo_platform/types/evaluation/metric_create_response.py +75 -0
- nemo_platform/types/evaluation/metric_evaluate_params.py +111 -0
- nemo_platform/types/evaluation/metric_evaluation_job.py +65 -0
- nemo_platform/types/evaluation/metric_evaluation_jobs_list_filter_param.py +49 -0
- nemo_platform/types/evaluation/metric_evaluation_jobs_page.py +37 -0
- nemo_platform/types/evaluation/metric_evaluation_jobs_sort_field.py +22 -0
- nemo_platform/types/evaluation/metric_evaluation_response.py +98 -0
- nemo_platform/types/evaluation/metric_evaluation_row_score.py +45 -0
- nemo_platform/types/evaluation/metric_job_create_params.py +48 -0
- nemo_platform/types/evaluation/metric_job_get_logs_params.py +30 -0
- nemo_platform/types/evaluation/metric_job_list_params.py +44 -0
- nemo_platform/types/evaluation/metric_job_result.py +93 -0
- nemo_platform/types/evaluation/metric_job_result_list_params.py +114 -0
- nemo_platform/types/evaluation/metric_job_result_retrieve_params.py +50 -0
- nemo_platform/types/evaluation/metric_job_results_list_response.py +37 -0
- nemo_platform/types/evaluation/metric_jobs/__init__.py +18 -0
- nemo_platform/types/evaluation/metric_jobs/results/__init__.py +20 -0
- nemo_platform/types/evaluation/metric_jobs/results/row_score_download_params.py +28 -0
- nemo_platform/types/evaluation/metric_list_params.py +73 -0
- nemo_platform/types/evaluation/metric_offline_job.py +106 -0
- nemo_platform/types/evaluation/metric_offline_job_param.py +107 -0
- nemo_platform/types/evaluation/metric_online_agent_job.py +133 -0
- nemo_platform/types/evaluation/metric_online_agent_job_param.py +135 -0
- nemo_platform/types/evaluation/metric_online_job.py +127 -0
- nemo_platform/types/evaluation/metric_online_job_param.py +129 -0
- nemo_platform/types/evaluation/metric_output.py +30 -0
- nemo_platform/types/evaluation/metric_ref.py +22 -0
- nemo_platform/types/evaluation/metric_retrieve_response.py +98 -0
- nemo_platform/types/evaluation/metric_retriever_job.py +64 -0
- nemo_platform/types/evaluation/metric_retriever_job_param.py +65 -0
- nemo_platform/types/evaluation/metric_type.py +47 -0
- nemo_platform/types/evaluation/metrics_list_response.py +90 -0
- nemo_platform/types/evaluation/model.py +44 -0
- nemo_platform/types/evaluation/model_param.py +44 -0
- nemo_platform/types/evaluation/model_ref.py +22 -0
- nemo_platform/types/evaluation/nemo_agent_toolkit_remote_metric.py +87 -0
- nemo_platform/types/evaluation/nemo_agent_toolkit_remote_metric_param.py +64 -0
- nemo_platform/types/evaluation/nemo_agent_toolkit_remote_metric_param_param.py +65 -0
- nemo_platform/types/evaluation/nemo_agent_toolkit_remote_metric_response.py +80 -0
- nemo_platform/types/evaluation/noise_sensitivity_metric.py +89 -0
- nemo_platform/types/evaluation/noise_sensitivity_metric_param.py +66 -0
- nemo_platform/types/evaluation/noise_sensitivity_metric_param_param.py +67 -0
- nemo_platform/types/evaluation/noise_sensitivity_metric_response.py +85 -0
- nemo_platform/types/evaluation/number_check_metric.py +104 -0
- nemo_platform/types/evaluation/number_check_metric_param.py +81 -0
- nemo_platform/types/evaluation/number_check_metric_param_param.py +83 -0
- nemo_platform/types/evaluation/number_check_metric_response.py +97 -0
- nemo_platform/types/evaluation/parameter.py +42 -0
- nemo_platform/types/evaluation/parameter_param.py +40 -0
- nemo_platform/types/evaluation/percentiles.py +54 -0
- nemo_platform/types/evaluation/range_score.py +56 -0
- nemo_platform/types/evaluation/range_score_param.py +57 -0
- nemo_platform/types/evaluation/reasoning_params.py +41 -0
- nemo_platform/types/evaluation/reasoning_params_param.py +41 -0
- nemo_platform/types/evaluation/regex_score_parser.py +38 -0
- nemo_platform/types/evaluation/regex_score_parser_param.py +37 -0
- nemo_platform/types/evaluation/remote_metric.py +91 -0
- nemo_platform/types/evaluation/remote_metric_param.py +68 -0
- nemo_platform/types/evaluation/remote_metric_param_param.py +69 -0
- nemo_platform/types/evaluation/remote_metric_response.py +84 -0
- nemo_platform/types/evaluation/remote_score.py +49 -0
- nemo_platform/types/evaluation/remote_score_param.py +50 -0
- nemo_platform/types/evaluation/response_groundedness_metric.py +89 -0
- nemo_platform/types/evaluation/response_groundedness_metric_param.py +66 -0
- nemo_platform/types/evaluation/response_groundedness_metric_param_param.py +67 -0
- nemo_platform/types/evaluation/response_groundedness_metric_response.py +85 -0
- nemo_platform/types/evaluation/response_relevancy_metric.py +95 -0
- nemo_platform/types/evaluation/response_relevancy_metric_param.py +74 -0
- nemo_platform/types/evaluation/response_relevancy_metric_param_param.py +75 -0
- nemo_platform/types/evaluation/response_relevancy_metric_response.py +93 -0
- nemo_platform/types/evaluation/retriever_pipeline.py +34 -0
- nemo_platform/types/evaluation/retriever_pipeline_param.py +35 -0
- nemo_platform/types/evaluation/rouge_metric.py +76 -0
- nemo_platform/types/evaluation/rouge_metric_param.py +53 -0
- nemo_platform/types/evaluation/rouge_metric_param_param.py +53 -0
- nemo_platform/types/evaluation/rouge_metric_response.py +69 -0
- nemo_platform/types/evaluation/row_score.py +59 -0
- nemo_platform/types/evaluation/rubric.py +40 -0
- nemo_platform/types/evaluation/rubric_param.py +40 -0
- nemo_platform/types/evaluation/rubric_score.py +54 -0
- nemo_platform/types/evaluation/rubric_score_param.py +55 -0
- nemo_platform/types/evaluation/rubric_score_stat.py +38 -0
- nemo_platform/types/evaluation/run_config.py +39 -0
- nemo_platform/types/evaluation/run_config_online.py +51 -0
- nemo_platform/types/evaluation/run_config_online_model.py +73 -0
- nemo_platform/types/evaluation/run_config_online_model_param.py +75 -0
- nemo_platform/types/evaluation/run_config_online_param.py +51 -0
- nemo_platform/types/evaluation/run_config_param.py +39 -0
- nemo_platform/types/evaluation/string_check_metric.py +82 -0
- nemo_platform/types/evaluation/string_check_metric_param.py +59 -0
- nemo_platform/types/evaluation/string_check_metric_param_param.py +61 -0
- nemo_platform/types/evaluation/string_check_metric_response.py +75 -0
- nemo_platform/types/evaluation/system_benchmark.py +71 -0
- nemo_platform/types/evaluation/system_benchmark_offline_job.py +58 -0
- nemo_platform/types/evaluation/system_benchmark_offline_job_param.py +60 -0
- nemo_platform/types/evaluation/system_benchmark_online_job.py +54 -0
- nemo_platform/types/evaluation/system_benchmark_online_job_param.py +55 -0
- nemo_platform/types/evaluation/system_metric.py +70 -0
- nemo_platform/types/evaluation/system_metric_param.py +50 -0
- nemo_platform/types/evaluation/system_metric_param_param.py +51 -0
- nemo_platform/types/evaluation/system_metric_response.py +66 -0
- nemo_platform/types/evaluation/tool_call_accuracy_metric.py +70 -0
- nemo_platform/types/evaluation/tool_call_accuracy_metric_param.py +44 -0
- nemo_platform/types/evaluation/tool_call_accuracy_metric_param_param.py +44 -0
- nemo_platform/types/evaluation/tool_call_accuracy_metric_response.py +63 -0
- nemo_platform/types/evaluation/tool_calling_metric.py +70 -0
- nemo_platform/types/evaluation/tool_calling_metric_param.py +47 -0
- nemo_platform/types/evaluation/tool_calling_metric_param_param.py +47 -0
- nemo_platform/types/evaluation/tool_calling_metric_response.py +63 -0
- nemo_platform/types/evaluation/topic_adherence_metric.py +92 -0
- nemo_platform/types/evaluation/topic_adherence_metric_param.py +69 -0
- nemo_platform/types/evaluation/topic_adherence_metric_param_param.py +70 -0
- nemo_platform/types/evaluation/topic_adherence_metric_response.py +88 -0
- nemo_platform/types/files/__init__.py +45 -0
- nemo_platform/types/files/cache_status.py +22 -0
- nemo_platform/types/files/dataset_metadata_content.py +46 -0
- nemo_platform/types/files/dataset_metadata_content_param.py +45 -0
- nemo_platform/types/files/file_list_files_params.py +35 -0
- nemo_platform/types/files/file_upload_file_params.py +28 -0
- nemo_platform/types/files/fileset.py +62 -0
- nemo_platform/types/files/fileset_create_params.py +74 -0
- nemo_platform/types/files/fileset_file.py +36 -0
- nemo_platform/types/files/fileset_filter_param.py +54 -0
- nemo_platform/types/files/fileset_list_params.py +47 -0
- nemo_platform/types/files/fileset_metadata_param.py +46 -0
- nemo_platform/types/files/fileset_metadata_param_param.py +47 -0
- nemo_platform/types/files/fileset_outputs_page.py +37 -0
- nemo_platform/types/files/fileset_purpose.py +22 -0
- nemo_platform/types/files/fileset_update_params.py +49 -0
- nemo_platform/types/files/huggingface_storage_config.py +60 -0
- nemo_platform/types/files/huggingface_storage_config_param.py +60 -0
- nemo_platform/types/files/list_fileset_files_response.py +27 -0
- nemo_platform/types/files/local_storage_config.py +39 -0
- nemo_platform/types/files/local_storage_config_param.py +38 -0
- nemo_platform/types/files/ngc_storage_config.py +66 -0
- nemo_platform/types/files/ngc_storage_config_param.py +66 -0
- nemo_platform/types/files/otlp/__init__.py +22 -0
- nemo_platform/types/files/otlp/log_query_params.py +36 -0
- nemo_platform/types/files/otlp/otel_export_logs_partial_success.py +34 -0
- nemo_platform/types/files/otlp/otel_export_logs_service_response.py +35 -0
- nemo_platform/types/files/s3_storage_config.py +85 -0
- nemo_platform/types/files/s3_storage_config_param.py +85 -0
- nemo_platform/types/files/secret_ref.py +22 -0
- nemo_platform/types/files/storage_config_type.py +22 -0
- nemo_platform/types/guardrail/__init__.py +165 -0
- nemo_platform/types/guardrail/action_rails.py +36 -0
- nemo_platform/types/guardrail/action_rails_param.py +38 -0
- nemo_platform/types/guardrail/activated_rail.py +61 -0
- nemo_platform/types/guardrail/ai_defense_rail_config.py +36 -0
- nemo_platform/types/guardrail/ai_defense_rail_config_param.py +36 -0
- nemo_platform/types/guardrail/auto_align_options.py +29 -0
- nemo_platform/types/guardrail/auto_align_options_param.py +30 -0
- nemo_platform/types/guardrail/auto_align_rail_config.py +35 -0
- nemo_platform/types/guardrail/auto_align_rail_config_param.py +37 -0
- nemo_platform/types/guardrail/cache_stats_config.py +32 -0
- nemo_platform/types/guardrail/cache_stats_config_param.py +32 -0
- nemo_platform/types/guardrail/chat_completion_assistant_message_param.py +45 -0
- nemo_platform/types/guardrail/chat_completion_content_part_image_param.py +34 -0
- nemo_platform/types/guardrail/chat_completion_content_part_text_param.py +32 -0
- nemo_platform/types/guardrail/chat_completion_function_message_param.py +35 -0
- nemo_platform/types/guardrail/chat_completion_message_tool_call_param.py +37 -0
- nemo_platform/types/guardrail/chat_completion_system_message_param.py +35 -0
- nemo_platform/types/guardrail/chat_completion_tool_message_param.py +35 -0
- nemo_platform/types/guardrail/chat_completion_user_message_param.py +41 -0
- nemo_platform/types/guardrail/clavata_rail_config.py +47 -0
- nemo_platform/types/guardrail/clavata_rail_config_param.py +48 -0
- nemo_platform/types/guardrail/clavata_rail_options.py +36 -0
- nemo_platform/types/guardrail/clavata_rail_options_param.py +38 -0
- nemo_platform/types/guardrail/config_create_params.py +36 -0
- nemo_platform/types/guardrail/config_list_params.py +47 -0
- nemo_platform/types/guardrail/config_update_params.py +33 -0
- nemo_platform/types/guardrail/content_safety_config.py +34 -0
- nemo_platform/types/guardrail/content_safety_config_param.py +35 -0
- nemo_platform/types/guardrail/crowd_strike_aidr_rail_config.py +29 -0
- nemo_platform/types/guardrail/crowd_strike_aidr_rail_config_param.py +29 -0
- nemo_platform/types/guardrail/dialog_rails.py +34 -0
- nemo_platform/types/guardrail/dialog_rails_param.py +35 -0
- nemo_platform/types/guardrail/executed_action.py +48 -0
- nemo_platform/types/guardrail/fact_checking_rail_config.py +31 -0
- nemo_platform/types/guardrail/fact_checking_rail_config_param.py +32 -0
- nemo_platform/types/guardrail/fiddler_guardrails.py +35 -0
- nemo_platform/types/guardrail/fiddler_guardrails_param.py +35 -0
- nemo_platform/types/guardrail/function_call_param.py +35 -0
- nemo_platform/types/guardrail/function_param.py +35 -0
- nemo_platform/types/guardrail/g_li_ner_detection.py +51 -0
- nemo_platform/types/guardrail/g_li_ner_detection_options.py +29 -0
- nemo_platform/types/guardrail/g_li_ner_detection_options_param.py +31 -0
- nemo_platform/types/guardrail/g_li_ner_detection_param.py +52 -0
- nemo_platform/types/guardrail/generation_log.py +44 -0
- nemo_platform/types/guardrail/generation_log_options_param.py +50 -0
- nemo_platform/types/guardrail/generation_options_param.py +55 -0
- nemo_platform/types/guardrail/generation_rails_options_param.py +53 -0
- nemo_platform/types/guardrail/generation_stats.py +56 -0
- nemo_platform/types/guardrail/guardrail_check_params.py +164 -0
- nemo_platform/types/guardrail/guardrail_check_response.py +36 -0
- nemo_platform/types/guardrail/guardrail_config.py +59 -0
- nemo_platform/types/guardrail/guardrail_config_filter_param.py +49 -0
- nemo_platform/types/guardrail/guardrail_configs_page.py +37 -0
- nemo_platform/types/guardrail/guardrails_ai_rail_config.py +33 -0
- nemo_platform/types/guardrail/guardrails_ai_rail_config_param.py +35 -0
- nemo_platform/types/guardrail/guardrails_ai_validator_config.py +44 -0
- nemo_platform/types/guardrail/guardrails_ai_validator_config_param.py +45 -0
- nemo_platform/types/guardrail/guardrails_data.py +40 -0
- nemo_platform/types/guardrail/guardrails_data_param.py +67 -0
- nemo_platform/types/guardrail/image_u_rl_param.py +32 -0
- nemo_platform/types/guardrail/injection_detection.py +49 -0
- nemo_platform/types/guardrail/injection_detection_param.py +52 -0
- nemo_platform/types/guardrail/input_rails.py +32 -0
- nemo_platform/types/guardrail/input_rails_param.py +34 -0
- nemo_platform/types/guardrail/instruction.py +30 -0
- nemo_platform/types/guardrail/instruction_param.py +32 -0
- nemo_platform/types/guardrail/jailbreak_detection_config.py +52 -0
- nemo_platform/types/guardrail/jailbreak_detection_config_param.py +58 -0
- nemo_platform/types/guardrail/llm_call_info.py +63 -0
- nemo_platform/types/guardrail/log_adapter_config.py +41 -0
- nemo_platform/types/guardrail/log_adapter_config_param.py +27 -0
- nemo_platform/types/guardrail/message_template.py +30 -0
- nemo_platform/types/guardrail/message_template_param.py +32 -0
- nemo_platform/types/guardrail/model.py +55 -0
- nemo_platform/types/guardrail/model_cache_config.py +36 -0
- nemo_platform/types/guardrail/model_cache_config_param.py +37 -0
- nemo_platform/types/guardrail/model_param.py +55 -0
- nemo_platform/types/guardrail/model_parameters.py +51 -0
- nemo_platform/types/guardrail/model_parameters_param.py +38 -0
- nemo_platform/types/guardrail/multilingual_config.py +40 -0
- nemo_platform/types/guardrail/multilingual_config_param.py +41 -0
- nemo_platform/types/guardrail/output_rails.py +44 -0
- nemo_platform/types/guardrail/output_rails_param.py +46 -0
- nemo_platform/types/guardrail/output_rails_streaming_config.py +44 -0
- nemo_platform/types/guardrail/output_rails_streaming_config_param.py +44 -0
- nemo_platform/types/guardrail/pangea_rail_config.py +33 -0
- nemo_platform/types/guardrail/pangea_rail_config_param.py +34 -0
- nemo_platform/types/guardrail/pangea_rail_options.py +31 -0
- nemo_platform/types/guardrail/pangea_rail_options_param.py +33 -0
- nemo_platform/types/guardrail/patronus_evaluate_api_params.py +38 -0
- nemo_platform/types/guardrail/patronus_evaluate_api_params_param.py +40 -0
- nemo_platform/types/guardrail/patronus_evaluate_config.py +30 -0
- nemo_platform/types/guardrail/patronus_evaluate_config_param.py +31 -0
- nemo_platform/types/guardrail/patronus_evaluation_success_strategy.py +22 -0
- nemo_platform/types/guardrail/patronus_rail_config.py +33 -0
- nemo_platform/types/guardrail/patronus_rail_config_param.py +34 -0
- nemo_platform/types/guardrail/private_ai_detection.py +39 -0
- nemo_platform/types/guardrail/private_ai_detection_options.py +29 -0
- nemo_platform/types/guardrail/private_ai_detection_options_param.py +31 -0
- nemo_platform/types/guardrail/private_ai_detection_param.py +40 -0
- nemo_platform/types/guardrail/rail_status.py +26 -0
- nemo_platform/types/guardrail/rails.py +73 -0
- nemo_platform/types/guardrail/rails_config.py +79 -0
- nemo_platform/types/guardrail/rails_config_data.py +94 -0
- nemo_platform/types/guardrail/rails_config_data_param.py +95 -0
- nemo_platform/types/guardrail/rails_config_param.py +81 -0
- nemo_platform/types/guardrail/rails_param.py +74 -0
- nemo_platform/types/guardrail/reasoning_config.py +32 -0
- nemo_platform/types/guardrail/reasoning_config_param.py +32 -0
- nemo_platform/types/guardrail/regex_detection.py +36 -0
- nemo_platform/types/guardrail/regex_detection_options.py +32 -0
- nemo_platform/types/guardrail/regex_detection_options_param.py +34 -0
- nemo_platform/types/guardrail/regex_detection_param.py +37 -0
- nemo_platform/types/guardrail/retrieval_rails.py +29 -0
- nemo_platform/types/guardrail/retrieval_rails_param.py +31 -0
- nemo_platform/types/guardrail/sensitive_data_detection.py +43 -0
- nemo_platform/types/guardrail/sensitive_data_detection_options.py +37 -0
- nemo_platform/types/guardrail/sensitive_data_detection_options_param.py +39 -0
- nemo_platform/types/guardrail/sensitive_data_detection_param.py +45 -0
- nemo_platform/types/guardrail/single_call_config.py +31 -0
- nemo_platform/types/guardrail/single_call_config_param.py +31 -0
- nemo_platform/types/guardrail/status_enum.py +22 -0
- nemo_platform/types/guardrail/task_prompt.py +63 -0
- nemo_platform/types/guardrail/task_prompt_param.py +65 -0
- nemo_platform/types/guardrail/tool_input_rails.py +36 -0
- nemo_platform/types/guardrail/tool_input_rails_param.py +38 -0
- nemo_platform/types/guardrail/tool_output_rails.py +36 -0
- nemo_platform/types/guardrail/tool_output_rails_param.py +38 -0
- nemo_platform/types/guardrail/tracing_config.py +48 -0
- nemo_platform/types/guardrail/tracing_config_param.py +50 -0
- nemo_platform/types/guardrail/trend_micro_rail_config.py +51 -0
- nemo_platform/types/guardrail/trend_micro_rail_config_param.py +51 -0
- nemo_platform/types/guardrail/user_messages_config.py +42 -0
- nemo_platform/types/guardrail/user_messages_config_param.py +42 -0
- nemo_platform/types/iam/__init__.py +26 -0
- nemo_platform/types/iam/date_range_filter_param.py +36 -0
- nemo_platform/types/iam/role_binding.py +42 -0
- nemo_platform/types/iam/role_binding_create_params.py +39 -0
- nemo_platform/types/iam/role_binding_delete_params.py +30 -0
- nemo_platform/types/iam/role_binding_filter_param.py +49 -0
- nemo_platform/types/iam/role_binding_list_params.py +44 -0
- nemo_platform/types/iam/role_bindings_page.py +37 -0
- nemo_platform/types/inference/__init__.py +60 -0
- nemo_platform/types/inference/deployment_config_create_params.py +47 -0
- nemo_platform/types/inference/deployment_config_list_params.py +46 -0
- nemo_platform/types/inference/deployment_config_update_params.py +37 -0
- nemo_platform/types/inference/deployment_configs/__init__.py +20 -0
- nemo_platform/types/inference/deployment_configs/version_list_response.py +25 -0
- nemo_platform/types/inference/deployment_create_params.py +45 -0
- nemo_platform/types/inference/deployment_list_models_response.py +23 -0
- nemo_platform/types/inference/deployment_list_params.py +52 -0
- nemo_platform/types/inference/deployment_update_params.py +35 -0
- nemo_platform/types/inference/deployment_update_status_params.py +42 -0
- nemo_platform/types/inference/deployments/__init__.py +20 -0
- nemo_platform/types/inference/deployments/version_list_response.py +25 -0
- nemo_platform/types/inference/gateway/__init__.py +38 -0
- nemo_platform/types/inference/gateway/model_patch_params.py +31 -0
- nemo_platform/types/inference/gateway/model_patch_response.py +23 -0
- nemo_platform/types/inference/gateway/model_post_params.py +31 -0
- nemo_platform/types/inference/gateway/model_post_response.py +23 -0
- nemo_platform/types/inference/gateway/model_put_params.py +31 -0
- nemo_platform/types/inference/gateway/model_put_response.py +23 -0
- nemo_platform/types/inference/gateway/openai/__init__.py +18 -0
- nemo_platform/types/inference/gateway/openai/v1/__init__.py +21 -0
- nemo_platform/types/inference/gateway/openai/v1/openai_list_models_resp.py +31 -0
- nemo_platform/types/inference/gateway/openai/v1/openai_model_resp.py +34 -0
- nemo_platform/types/inference/gateway/openai_patch_params.py +29 -0
- nemo_platform/types/inference/gateway/openai_patch_response.py +23 -0
- nemo_platform/types/inference/gateway/openai_post_params.py +29 -0
- nemo_platform/types/inference/gateway/openai_post_response.py +23 -0
- nemo_platform/types/inference/gateway/openai_put_params.py +29 -0
- nemo_platform/types/inference/gateway/openai_put_response.py +23 -0
- nemo_platform/types/inference/gateway/provider_patch_params.py +31 -0
- nemo_platform/types/inference/gateway/provider_patch_response.py +23 -0
- nemo_platform/types/inference/gateway/provider_post_params.py +31 -0
- nemo_platform/types/inference/gateway/provider_post_response.py +23 -0
- nemo_platform/types/inference/gateway/provider_put_params.py +31 -0
- nemo_platform/types/inference/gateway/provider_put_response.py +23 -0
- nemo_platform/types/inference/gateway/provider_ready_response.py +23 -0
- nemo_platform/types/inference/k8s_nim_operator_config.py +57 -0
- nemo_platform/types/inference/k8s_nim_operator_config_param.py +58 -0
- nemo_platform/types/inference/middleware_call.py +55 -0
- nemo_platform/types/inference/middleware_call_param.py +56 -0
- nemo_platform/types/inference/model_deployment.py +97 -0
- nemo_platform/types/inference/model_deployment_config.py +76 -0
- nemo_platform/types/inference/model_deployment_config_filter_param.py +49 -0
- nemo_platform/types/inference/model_deployment_configs_page.py +37 -0
- nemo_platform/types/inference/model_deployment_filter_param.py +56 -0
- nemo_platform/types/inference/model_deployment_status.py +24 -0
- nemo_platform/types/inference/model_deployment_status_history_item.py +37 -0
- nemo_platform/types/inference/model_deployments_page.py +37 -0
- nemo_platform/types/inference/model_provider.py +134 -0
- nemo_platform/types/inference/model_provider_filter_param.py +56 -0
- nemo_platform/types/inference/model_provider_sort.py +24 -0
- nemo_platform/types/inference/model_provider_status.py +24 -0
- nemo_platform/types/inference/model_providers_page.py +37 -0
- nemo_platform/types/inference/model_type.py +22 -0
- nemo_platform/types/inference/nim_deployment.py +96 -0
- nemo_platform/types/inference/nim_deployment_param.py +93 -0
- nemo_platform/types/inference/provider_create_params.py +94 -0
- nemo_platform/types/inference/provider_list_params.py +47 -0
- nemo_platform/types/inference/provider_update_params.py +87 -0
- nemo_platform/types/inference/provider_update_status_params.py +48 -0
- nemo_platform/types/inference/served_model_mapping.py +35 -0
- nemo_platform/types/inference/served_model_mapping_param.py +32 -0
- nemo_platform/types/inference/virtual_model.py +91 -0
- nemo_platform/types/inference/virtual_model_create_params.py +83 -0
- nemo_platform/types/inference/virtual_model_inference_config.py +32 -0
- nemo_platform/types/inference/virtual_model_inference_config_param.py +34 -0
- nemo_platform/types/inference/virtual_model_list_params.py +35 -0
- nemo_platform/types/inference/virtual_model_patch_params.py +80 -0
- nemo_platform/types/inference/virtual_models_page.py +37 -0
- nemo_platform/types/intake/__init__.py +86 -0
- nemo_platform/types/intake/app.py +51 -0
- nemo_platform/types/intake/app_create_params.py +38 -0
- nemo_platform/types/intake/app_filter_param.py +46 -0
- nemo_platform/types/intake/app_list_params.py +44 -0
- nemo_platform/types/intake/app_patch_params.py +35 -0
- nemo_platform/types/intake/app_sort_field.py +22 -0
- nemo_platform/types/intake/apps/__init__.py +26 -0
- nemo_platform/types/intake/apps/task.py +54 -0
- nemo_platform/types/intake/apps/task_create_params.py +40 -0
- nemo_platform/types/intake/apps/task_filter_param.py +49 -0
- nemo_platform/types/intake/apps/task_list_params.py +44 -0
- nemo_platform/types/intake/apps/task_patch_params.py +37 -0
- nemo_platform/types/intake/apps/task_sort_field.py +22 -0
- nemo_platform/types/intake/apps/tasks_page.py +37 -0
- nemo_platform/types/intake/apps_page.py +37 -0
- nemo_platform/types/intake/entries/__init__.py +20 -0
- nemo_platform/types/intake/entries/event_create_params.py +40 -0
- nemo_platform/types/intake/entry.py +98 -0
- nemo_platform/types/intake/entry_context.py +79 -0
- nemo_platform/types/intake/entry_context_filter_param.py +41 -0
- nemo_platform/types/intake/entry_context_param.py +82 -0
- nemo_platform/types/intake/entry_create_params.py +85 -0
- nemo_platform/types/intake/entry_data.py +48 -0
- nemo_platform/types/intake/entry_data_param.py +51 -0
- nemo_platform/types/intake/entry_filter_param.py +85 -0
- nemo_platform/types/intake/entry_list_params.py +47 -0
- nemo_platform/types/intake/entry_patch_params.py +79 -0
- nemo_platform/types/intake/entry_sort_field.py +22 -0
- nemo_platform/types/intake/entry_user_rating_filter_param.py +31 -0
- nemo_platform/types/intake/entrys_page.py +37 -0
- nemo_platform/types/intake/evaluation_context_param.py +41 -0
- nemo_platform/types/intake/evaluator_result.py +52 -0
- nemo_platform/types/intake/evaluator_result_create_params.py +52 -0
- nemo_platform/types/intake/evaluator_result_data_type.py +22 -0
- nemo_platform/types/intake/evaluator_result_event.py +67 -0
- nemo_platform/types/intake/evaluator_result_event_param.py +69 -0
- nemo_platform/types/intake/evaluator_result_filter_param.py +49 -0
- nemo_platform/types/intake/evaluator_result_list_params.py +43 -0
- nemo_platform/types/intake/evaluator_result_sort_field.py +22 -0
- nemo_platform/types/intake/evaluator_results_page.py +37 -0
- nemo_platform/types/intake/export_config_param.py +44 -0
- nemo_platform/types/intake/export_config_param_param.py +45 -0
- nemo_platform/types/intake/export_preview_params.py +34 -0
- nemo_platform/types/intake/export_preview_response.py +39 -0
- nemo_platform/types/intake/exports/__init__.py +28 -0
- nemo_platform/types/intake/exports/export_config.py +44 -0
- nemo_platform/types/intake/exports/export_job.py +76 -0
- nemo_platform/types/intake/exports/export_job_filter_param.py +50 -0
- nemo_platform/types/intake/exports/export_job_sort_field.py +22 -0
- nemo_platform/types/intake/exports/export_jobs_page.py +37 -0
- nemo_platform/types/intake/exports/export_status_details.py +35 -0
- nemo_platform/types/intake/exports/job_create_params.py +40 -0
- nemo_platform/types/intake/exports/job_list_params.py +46 -0
- nemo_platform/types/intake/exports/job_status.py +22 -0
- nemo_platform/types/intake/flexible_entry_request.py +63 -0
- nemo_platform/types/intake/flexible_entry_request_param.py +51 -0
- nemo_platform/types/intake/flexible_entry_response.py +62 -0
- nemo_platform/types/intake/flexible_entry_response_param.py +49 -0
- nemo_platform/types/intake/flexible_message.py +56 -0
- nemo_platform/types/intake/flexible_message_param.py +43 -0
- nemo_platform/types/intake/float_filter_param.py +32 -0
- nemo_platform/types/intake/ingest/__init__.py +37 -0
- nemo_platform/types/intake/ingest/atif_agent_param.py +35 -0
- nemo_platform/types/intake/ingest/atif_content_part_image_param.py +30 -0
- nemo_platform/types/intake/ingest/atif_content_part_param.py +28 -0
- nemo_platform/types/intake/ingest/atif_content_part_text_param.py +28 -0
- nemo_platform/types/intake/ingest/atif_create_params.py +52 -0
- nemo_platform/types/intake/ingest/atif_final_metrics_param.py +37 -0
- nemo_platform/types/intake/ingest/atif_image_source_param.py +28 -0
- nemo_platform/types/intake/ingest/atif_metrics_param.py +41 -0
- nemo_platform/types/intake/ingest/atif_observation_param.py +29 -0
- nemo_platform/types/intake/ingest/atif_observation_result_param.py +36 -0
- nemo_platform/types/intake/ingest/atif_step_agent_param.py +58 -0
- nemo_platform/types/intake/ingest/atif_step_param.py +29 -0
- nemo_platform/types/intake/ingest/atif_step_system_param.py +43 -0
- nemo_platform/types/intake/ingest/atif_step_user_param.py +43 -0
- nemo_platform/types/intake/ingest/atif_subagent_trajectory_ref_param.py +33 -0
- nemo_platform/types/intake/ingest/atif_tool_call_param.py +31 -0
- nemo_platform/types/intake/ingest/chat_completion_create_params.py +66 -0
- nemo_platform/types/intake/ingest/chat_completions_ingest_response.py +26 -0
- nemo_platform/types/intake/ingest/otlp/__init__.py +18 -0
- nemo_platform/types/intake/ingest/otlp/v1/__init__.py +20 -0
- nemo_platform/types/intake/ingest/otlp/v1/ingest_response.py +26 -0
- nemo_platform/types/intake/message_role.py +22 -0
- nemo_platform/types/intake/reviewer_annotation_event.py +95 -0
- nemo_platform/types/intake/reviewer_annotation_event_param.py +97 -0
- nemo_platform/types/intake/span.py +100 -0
- nemo_platform/types/intake/span_evaluation_context.py +40 -0
- nemo_platform/types/intake/span_filter_param.py +95 -0
- nemo_platform/types/intake/span_kind.py +24 -0
- nemo_platform/types/intake/span_list_params.py +46 -0
- nemo_platform/types/intake/span_sort_field.py +22 -0
- nemo_platform/types/intake/span_status.py +22 -0
- nemo_platform/types/intake/spans/__init__.py +20 -0
- nemo_platform/types/intake/spans/evaluator_result_list_response.py +25 -0
- nemo_platform/types/intake/spans_page.py +37 -0
- nemo_platform/types/intake/thumb_direction.py +22 -0
- nemo_platform/types/intake/trace.py +65 -0
- nemo_platform/types/intake/trace_filter_param.py +60 -0
- nemo_platform/types/intake/trace_list_params.py +49 -0
- nemo_platform/types/intake/trace_retrieve_params.py +32 -0
- nemo_platform/types/intake/trace_sort_field.py +22 -0
- nemo_platform/types/intake/traces_page.py +37 -0
- nemo_platform/types/intake/usage.py +65 -0
- nemo_platform/types/intake/usage_param.py +68 -0
- nemo_platform/types/intake/user_action_event.py +65 -0
- nemo_platform/types/intake/user_action_event_param.py +68 -0
- nemo_platform/types/intake/user_feedback_event.py +84 -0
- nemo_platform/types/intake/user_feedback_event_param.py +86 -0
- nemo_platform/types/intake/user_rating.py +68 -0
- nemo_platform/types/intake/user_rating_param.py +70 -0
- nemo_platform/types/jobs/__init__.py +95 -0
- nemo_platform/types/jobs/compute_resource_spec.py +32 -0
- nemo_platform/types/jobs/compute_resource_spec_param.py +32 -0
- nemo_platform/types/jobs/compute_resources.py +46 -0
- nemo_platform/types/jobs/compute_resources_param.py +47 -0
- nemo_platform/types/jobs/container_spec.py +35 -0
- nemo_platform/types/jobs/container_spec_param.py +37 -0
- nemo_platform/types/jobs/cpu_execution_provider.py +46 -0
- nemo_platform/types/jobs/cpu_execution_provider_param.py +46 -0
- nemo_platform/types/jobs/distributed_gpu_execution_provider.py +46 -0
- nemo_platform/types/jobs/distributed_gpu_execution_provider_param.py +46 -0
- nemo_platform/types/jobs/docker_job_execution_profile.py +43 -0
- nemo_platform/types/jobs/docker_job_execution_profile_config.py +52 -0
- nemo_platform/types/jobs/docker_job_network_config.py +27 -0
- nemo_platform/types/jobs/docker_job_storage_config.py +36 -0
- nemo_platform/types/jobs/docker_volume_mount.py +47 -0
- nemo_platform/types/jobs/e2e_job_execution_profile.py +43 -0
- nemo_platform/types/jobs/gpu_execution_provider.py +46 -0
- nemo_platform/types/jobs/gpu_execution_provider_param.py +46 -0
- nemo_platform/types/jobs/image_pull_secret.py +27 -0
- nemo_platform/types/jobs/job_create_params.py +46 -0
- nemo_platform/types/jobs/job_execution_profile_config.py +42 -0
- nemo_platform/types/jobs/job_get_logs_params.py +41 -0
- nemo_platform/types/jobs/job_list_execution_profiles_response.py +37 -0
- nemo_platform/types/jobs/job_list_params.py +47 -0
- nemo_platform/types/jobs/job_update_status_details_params.py +29 -0
- nemo_platform/types/jobs/kubernetes_empty_dir_volume.py +32 -0
- nemo_platform/types/jobs/kubernetes_job_execution_profile.py +43 -0
- nemo_platform/types/jobs/kubernetes_job_execution_profile_config.py +101 -0
- nemo_platform/types/jobs/kubernetes_job_storage_config.py +40 -0
- nemo_platform/types/jobs/kubernetes_object_metadata.py +28 -0
- nemo_platform/types/jobs/kubernetes_persistent_volume_claim.py +32 -0
- nemo_platform/types/jobs/kubernetes_volume.py +37 -0
- nemo_platform/types/jobs/kubernetes_volume_mount.py +38 -0
- nemo_platform/types/jobs/platform_job_environment_variable.py +36 -0
- nemo_platform/types/jobs/platform_job_environment_variable_param.py +37 -0
- nemo_platform/types/jobs/platform_job_list_task_response.py +29 -0
- nemo_platform/types/jobs/platform_job_response.py +75 -0
- nemo_platform/types/jobs/platform_job_responses_page.py +37 -0
- nemo_platform/types/jobs/platform_job_secret_environment_variable_ref.py +27 -0
- nemo_platform/types/jobs/platform_job_secret_environment_variable_ref_param.py +29 -0
- nemo_platform/types/jobs/platform_job_sort_field.py +22 -0
- nemo_platform/types/jobs/platform_job_spec.py +30 -0
- nemo_platform/types/jobs/platform_job_spec_param.py +32 -0
- nemo_platform/types/jobs/platform_job_step.py +75 -0
- nemo_platform/types/jobs/platform_job_step_spec.py +63 -0
- nemo_platform/types/jobs/platform_job_step_spec_param.py +65 -0
- nemo_platform/types/jobs/platform_job_step_with_context.py +67 -0
- nemo_platform/types/jobs/platform_job_step_with_contexts_page.py +37 -0
- nemo_platform/types/jobs/platform_job_steps_list_filter_param.py +38 -0
- nemo_platform/types/jobs/platform_job_task.py +75 -0
- nemo_platform/types/jobs/platform_jobs_list_filter_param.py +51 -0
- nemo_platform/types/jobs/result_create_params.py +34 -0
- nemo_platform/types/jobs/result_list_params.py +34 -0
- nemo_platform/types/jobs/step_lifecycle.py +37 -0
- nemo_platform/types/jobs/step_lifecycle_param.py +37 -0
- nemo_platform/types/jobs/step_list_params.py +44 -0
- nemo_platform/types/jobs/step_update_status_params.py +44 -0
- nemo_platform/types/jobs/subprocess_execution_provider.py +33 -0
- nemo_platform/types/jobs/subprocess_execution_provider_param.py +34 -0
- nemo_platform/types/jobs/subprocess_job_execution_profile.py +36 -0
- nemo_platform/types/jobs/subprocess_job_execution_profile_config.py +49 -0
- nemo_platform/types/jobs/task_create_or_update_params.py +46 -0
- nemo_platform/types/jobs/volcano_job_execution_profile.py +39 -0
- nemo_platform/types/jobs/volcano_job_execution_profile_config.py +114 -0
- nemo_platform/types/members/__init__.py +24 -0
- nemo_platform/types/members/member_create_params.py +40 -0
- nemo_platform/types/members/member_delete_params.py +32 -0
- nemo_platform/types/members/member_update_params.py +37 -0
- nemo_platform/types/members/workspace_member.py +39 -0
- nemo_platform/types/members/workspace_member_list_response.py +29 -0
- nemo_platform/types/models/__init__.py +34 -0
- nemo_platform/types/models/adapter.py +73 -0
- nemo_platform/types/models/adapter_create_params.py +54 -0
- nemo_platform/types/models/adapter_update_params.py +37 -0
- nemo_platform/types/models/base_model_filter_param.py +29 -0
- nemo_platform/types/models/finetuning_type_filter_param.py +31 -0
- nemo_platform/types/models/lora.py +30 -0
- nemo_platform/types/models/lora_param.py +30 -0
- nemo_platform/types/models/model_create_params.py +93 -0
- nemo_platform/types/models/model_entity.py +112 -0
- nemo_platform/types/models/model_entity_filter_param.py +72 -0
- nemo_platform/types/models/model_entity_sort_field.py +22 -0
- nemo_platform/types/models/model_entitys_page.py +37 -0
- nemo_platform/types/models/model_list_params.py +50 -0
- nemo_platform/types/models/model_retrieve_params.py +29 -0
- nemo_platform/types/models/model_update_params.py +86 -0
- nemo_platform/types/projects/__init__.py +25 -0
- nemo_platform/types/projects/project.py +45 -0
- nemo_platform/types/projects/project_create_params.py +37 -0
- nemo_platform/types/projects/project_list_params.py +49 -0
- nemo_platform/types/projects/project_sort_field.py +22 -0
- nemo_platform/types/projects/project_update_params.py +29 -0
- nemo_platform/types/projects/projects_page.py +37 -0
- nemo_platform/types/safe_synthesizer/__init__.py +68 -0
- nemo_platform/types/safe_synthesizer/classify_config.py +41 -0
- nemo_platform/types/safe_synthesizer/classify_config_param.py +43 -0
- nemo_platform/types/safe_synthesizer/column.py +46 -0
- nemo_platform/types/safe_synthesizer/column_actions.py +36 -0
- nemo_platform/types/safe_synthesizer/column_actions_param.py +38 -0
- nemo_platform/types/safe_synthesizer/column_param.py +49 -0
- nemo_platform/types/safe_synthesizer/data_parameters.py +69 -0
- nemo_platform/types/safe_synthesizer/data_parameters_param.py +69 -0
- nemo_platform/types/safe_synthesizer/differential_privacy_hyperparams.py +48 -0
- nemo_platform/types/safe_synthesizer/differential_privacy_hyperparams_param.py +48 -0
- nemo_platform/types/safe_synthesizer/evaluation_parameters.py +64 -0
- nemo_platform/types/safe_synthesizer/evaluation_parameters_param.py +66 -0
- nemo_platform/types/safe_synthesizer/generate_parameters.py +104 -0
- nemo_platform/types/safe_synthesizer/generate_parameters_param.py +102 -0
- nemo_platform/types/safe_synthesizer/gliner_config.py +41 -0
- nemo_platform/types/safe_synthesizer/gliner_config_param.py +41 -0
- nemo_platform/types/safe_synthesizer/globals.py +45 -0
- nemo_platform/types/safe_synthesizer/globals_param.py +47 -0
- nemo_platform/types/safe_synthesizer/job_create_params.py +46 -0
- nemo_platform/types/safe_synthesizer/job_get_logs_params.py +30 -0
- nemo_platform/types/safe_synthesizer/job_list_params.py +44 -0
- nemo_platform/types/safe_synthesizer/jobs/__init__.py +21 -0
- nemo_platform/types/safe_synthesizer/jobs/safe_synthesizer_summary.py +95 -0
- nemo_platform/types/safe_synthesizer/jobs/safe_synthesizer_timing.py +41 -0
- nemo_platform/types/safe_synthesizer/ner_config.py +42 -0
- nemo_platform/types/safe_synthesizer/ner_config_param.py +44 -0
- nemo_platform/types/safe_synthesizer/pii_replacer_config.py +40 -0
- nemo_platform/types/safe_synthesizer/pii_replacer_config_param.py +42 -0
- nemo_platform/types/safe_synthesizer/row.py +50 -0
- nemo_platform/types/safe_synthesizer/row_actions.py +33 -0
- nemo_platform/types/safe_synthesizer/row_actions_param.py +35 -0
- nemo_platform/types/safe_synthesizer/row_param.py +53 -0
- nemo_platform/types/safe_synthesizer/safe_synthesizer_job.py +63 -0
- nemo_platform/types/safe_synthesizer/safe_synthesizer_job_config.py +55 -0
- nemo_platform/types/safe_synthesizer/safe_synthesizer_job_config_param.py +56 -0
- nemo_platform/types/safe_synthesizer/safe_synthesizer_jobs_list_filter_param.py +49 -0
- nemo_platform/types/safe_synthesizer/safe_synthesizer_jobs_page.py +37 -0
- nemo_platform/types/safe_synthesizer/safe_synthesizer_jobs_sort_field.py +22 -0
- nemo_platform/types/safe_synthesizer/safe_synthesizer_parameters.py +91 -0
- nemo_platform/types/safe_synthesizer/safe_synthesizer_parameters_param.py +92 -0
- nemo_platform/types/safe_synthesizer/step_definition.py +39 -0
- nemo_platform/types/safe_synthesizer/step_definition_param.py +41 -0
- nemo_platform/types/safe_synthesizer/time_series_parameters.py +73 -0
- nemo_platform/types/safe_synthesizer/time_series_parameters_param.py +74 -0
- nemo_platform/types/safe_synthesizer/training_hyperparams.py +165 -0
- nemo_platform/types/safe_synthesizer/training_hyperparams_param.py +167 -0
- nemo_platform/types/safe_synthesizer/validation_parameters.py +54 -0
- nemo_platform/types/safe_synthesizer/validation_parameters_param.py +54 -0
- nemo_platform/types/secrets/__init__.py +28 -0
- nemo_platform/types/secrets/platform_secret_access_response.py +33 -0
- nemo_platform/types/secrets/platform_secret_admin_rotation_response.py +28 -0
- nemo_platform/types/secrets/platform_secret_response.py +40 -0
- nemo_platform/types/secrets/platform_secret_responses_page.py +37 -0
- nemo_platform/types/secrets/secret_create_params.py +39 -0
- nemo_platform/types/secrets/secret_list_params.py +32 -0
- nemo_platform/types/secrets/secret_update_params.py +32 -0
- nemo_platform/types/shared/__init__.py +50 -0
- nemo_platform/types/shared/api_endpoint_data.py +43 -0
- nemo_platform/types/shared/auth_context.py +48 -0
- nemo_platform/types/shared/auth_discovery_response.py +32 -0
- nemo_platform/types/shared/backend_format.py +22 -0
- nemo_platform/types/shared/datetime_filter.py +33 -0
- nemo_platform/types/shared/delete_response.py +33 -0
- nemo_platform/types/shared/error_response.py +46 -0
- nemo_platform/types/shared/field_error.py +28 -0
- nemo_platform/types/shared/file_storage_type.py +22 -0
- nemo_platform/types/shared/fileset_metadata.py +46 -0
- nemo_platform/types/shared/finetuning_type.py +48 -0
- nemo_platform/types/shared/generic_sort_field.py +22 -0
- nemo_platform/types/shared/http_validation_error.py +27 -0
- nemo_platform/types/shared/linear_layer_spec.py +33 -0
- nemo_platform/types/shared/mamba_config.py +44 -0
- nemo_platform/types/shared/mo_e_config.py +41 -0
- nemo_platform/types/shared/model_metadata_content.py +38 -0
- nemo_platform/types/shared/model_spec.py +111 -0
- nemo_platform/types/shared/oidc_discovery_response.py +42 -0
- nemo_platform/types/shared/pagination_data.py +37 -0
- nemo_platform/types/shared/platform_job_list_result_response.py +27 -0
- nemo_platform/types/shared/platform_job_log.py +34 -0
- nemo_platform/types/shared/platform_job_log_page.py +33 -0
- nemo_platform/types/shared/platform_job_result_response.py +44 -0
- nemo_platform/types/shared/platform_job_status.py +24 -0
- nemo_platform/types/shared/platform_job_status_response.py +48 -0
- nemo_platform/types/shared/platform_job_step_status_response.py +48 -0
- nemo_platform/types/shared/platform_job_task_status_response.py +47 -0
- nemo_platform/types/shared/prompt_data.py +47 -0
- nemo_platform/types/shared/sliding_window_config.py +27 -0
- nemo_platform/types/shared/tool_call_config.py +46 -0
- nemo_platform/types/shared/tool_calling_metadata_content.py +48 -0
- nemo_platform/types/shared/validation_error.py +34 -0
- nemo_platform/types/shared_params/__init__.py +33 -0
- nemo_platform/types/shared_params/api_endpoint_data.py +38 -0
- nemo_platform/types/shared_params/backend_format.py +24 -0
- nemo_platform/types/shared_params/datetime_filter.py +34 -0
- nemo_platform/types/shared_params/file_storage_type.py +24 -0
- nemo_platform/types/shared_params/finetuning_type.py +50 -0
- nemo_platform/types/shared_params/generic_sort_field.py +24 -0
- nemo_platform/types/shared_params/linear_layer_spec.py +35 -0
- nemo_platform/types/shared_params/mamba_config.py +44 -0
- nemo_platform/types/shared_params/mo_e_config.py +41 -0
- nemo_platform/types/shared_params/model_metadata_content.py +39 -0
- nemo_platform/types/shared_params/model_spec.py +113 -0
- nemo_platform/types/shared_params/platform_job_status.py +26 -0
- nemo_platform/types/shared_params/prompt_data.py +48 -0
- nemo_platform/types/shared_params/sliding_window_config.py +29 -0
- nemo_platform/types/shared_params/tool_call_config.py +46 -0
- nemo_platform/types/shared_params/tool_calling_metadata_content.py +48 -0
- nemo_platform/types/workspaces/__init__.py +24 -0
- nemo_platform/types/workspaces/workspace.py +48 -0
- nemo_platform/types/workspaces/workspace_create_params.py +41 -0
- nemo_platform/types/workspaces/workspace_list_params.py +47 -0
- nemo_platform/types/workspaces/workspace_update_params.py +27 -0
- nemo_platform/types/workspaces/workspaces_page.py +37 -0
- nemo_platform/ui/__init__.py +15 -0
- nemo_platform/ui/output.py +109 -0
- nemo_platform/ui/prompts.py +678 -0
- nemo_platform_plugin/.agents/skills/creating-a-plugin/SKILL.md +275 -0
- nemo_platform_plugin/.agents/skills/plugin-config/SKILL.md +168 -0
- nemo_platform_plugin/.agents/skills/plugin-controller/SKILL.md +320 -0
- nemo_platform_plugin/.agents/skills/plugin-entities/SKILL.md +290 -0
- nemo_platform_plugin/.agents/skills/plugin-function/SKILL.md +292 -0
- nemo_platform_plugin/.agents/skills/plugin-inference-middleware/SKILL.md +346 -0
- nemo_platform_plugin/.agents/skills/plugin-job/SKILL.md +247 -0
- nemo_platform_plugin/.agents/skills/plugin-platform-services/SKILL.md +239 -0
- nemo_platform_plugin/.agents/skills/plugin-platform-services/services-reference.md +192 -0
- nemo_platform_plugin/.agents/skills/plugin-service/SKILL.md +181 -0
- nemo_platform_plugin/.agents/skills/plugin-service/crud-example.md +353 -0
- nemo_platform_plugin/.agents/skills/plugin-testing/SKILL.md +206 -0
- nemo_platform_plugin/README.md +43 -0
- nemo_platform_plugin/_base.py +53 -0
- nemo_platform_plugin/_spec_flags.py +573 -0
- nemo_platform_plugin/api/filter.py +189 -0
- nemo_platform_plugin/api/parsed_filter.py +288 -0
- nemo_platform_plugin/api/text_filter.py +291 -0
- nemo_platform_plugin/cli.py +152 -0
- nemo_platform_plugin/cli_errors.py +232 -0
- nemo_platform_plugin/cli_renderer.py +92 -0
- nemo_platform_plugin/commands.py +1433 -0
- nemo_platform_plugin/config.py +844 -0
- nemo_platform_plugin/controller.py +148 -0
- nemo_platform_plugin/dependencies.py +64 -0
- nemo_platform_plugin/discovery.py +484 -0
- nemo_platform_plugin/docs/ARCHITECTURE.md +160 -0
- nemo_platform_plugin/docs/CONFIG.md +151 -0
- nemo_platform_plugin/docs/CONTROLLER.md +209 -0
- nemo_platform_plugin/docs/ENTITY.md +186 -0
- nemo_platform_plugin/docs/INFERENCE_MIDDLEWARE.md +408 -0
- nemo_platform_plugin/docs/JOB.md +237 -0
- nemo_platform_plugin/docs/QUICKSTART.md +306 -0
- nemo_platform_plugin/docs/SERVICE.md +302 -0
- nemo_platform_plugin/entities.py +793 -0
- nemo_platform_plugin/entity.py +79 -0
- nemo_platform_plugin/entity_client.py +54 -0
- nemo_platform_plugin/filter_ops.py +169 -0
- nemo_platform_plugin/function.py +299 -0
- nemo_platform_plugin/function_context.py +61 -0
- nemo_platform_plugin/functions/frames.py +69 -0
- nemo_platform_plugin/functions/routes.py +454 -0
- nemo_platform_plugin/inference_middleware.py +1191 -0
- nemo_platform_plugin/interface.py +28 -0
- nemo_platform_plugin/job.py +370 -0
- nemo_platform_plugin/job_context.py +86 -0
- nemo_platform_plugin/job_results.py +209 -0
- nemo_platform_plugin/jobs/_cli_options.py +204 -0
- nemo_platform_plugin/jobs/api_factory.py +1178 -0
- nemo_platform_plugin/jobs/constants.py +27 -0
- nemo_platform_plugin/jobs/docker.py +76 -0
- nemo_platform_plugin/jobs/exceptions.py +8 -0
- nemo_platform_plugin/jobs/file_manager.py +238 -0
- nemo_platform_plugin/jobs/openapi_utils.py +174 -0
- nemo_platform_plugin/jobs/profile.py +110 -0
- nemo_platform_plugin/jobs/result_manager.py +291 -0
- nemo_platform_plugin/jobs/routes.py +297 -0
- nemo_platform_plugin/jobs/schemas.py +174 -0
- nemo_platform_plugin/refs.py +154 -0
- nemo_platform_plugin/run_dependencies.py +137 -0
- nemo_platform_plugin/scheduler.py +537 -0
- nemo_platform_plugin/schema.py +216 -0
- nemo_platform_plugin/sdk.py +34 -0
- nemo_platform_plugin/seed.py +71 -0
- nemo_platform_plugin/service.py +130 -0
- nemo_platform_plugin/tasks/dispatcher.py +226 -0
- nemo_platform_plugin-0.1.0.dist-info/METADATA +88 -0
- nemo_platform_plugin-0.1.0.dist-info/RECORD +1224 -0
- nemo_platform_plugin-0.1.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,1433 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""CLI integration for NemoJob and NemoFunction primitives.
|
|
5
|
+
|
|
6
|
+
Two parallel helpers, one per primitive:
|
|
7
|
+
|
|
8
|
+
- :func:`add_job_commands` — three-verb subgroups
|
|
9
|
+
(``run``/``submit``/``explain``) per :class:`~nemo_platform_plugin.job.NemoJob`,
|
|
10
|
+
routed through :class:`~nemo_platform_plugin.scheduler.NemoJobScheduler`.
|
|
11
|
+
- :func:`add_function_commands` — two-verb subgroups
|
|
12
|
+
(``run``/``submit``) per :class:`~nemo_platform_plugin.function.NemoFunction`.
|
|
13
|
+
Functions don't have an ``explain`` verb because their only schema
|
|
14
|
+
is :attr:`~nemo_platform_plugin.function.NemoFunction.spec_schema` and that's
|
|
15
|
+
introspected through ``--help`` directly.
|
|
16
|
+
|
|
17
|
+
:func:`add_job_commands` is the bridge between the ``nemo.jobs`` and
|
|
18
|
+
``nemo.cli`` surfaces. The platform calls it at startup for each plugin that
|
|
19
|
+
has registered both a CLI group and jobs, injecting a generated **sub-group**
|
|
20
|
+
for every job into the plugin's :class:`typer.Typer` group. Each sub-group
|
|
21
|
+
exposes three verbs — ``run``, ``submit``, ``explain`` — matching
|
|
22
|
+
:class:`~nemo_platform_plugin.scheduler.NemoJobScheduler`.
|
|
23
|
+
|
|
24
|
+
Plugin authors do **not** call this themselves — it is called automatically
|
|
25
|
+
by the platform's CLI loader. The result is that each job becomes available
|
|
26
|
+
as::
|
|
27
|
+
|
|
28
|
+
nemo <plugin> <job-name> run [--config ...] [--config-file ...]
|
|
29
|
+
nemo <plugin> <job-name> submit [--profile ...] [--cluster ...] [-o ...]
|
|
30
|
+
nemo <plugin> <job-name> explain [--profile ...] [--cluster ...]
|
|
31
|
+
|
|
32
|
+
The **bare form** ``nemo <plugin> <job-name>`` prints usage and exits with
|
|
33
|
+
status 1. No implicit default verb — the submitter's choice of execution
|
|
34
|
+
target is always explicit. This breaks the previous one-line form; the
|
|
35
|
+
fix is typing ``run`` (or ``submit``) explicitly.
|
|
36
|
+
|
|
37
|
+
Phase 1 MR 1.2c delivers the CLI shape. ``submit`` and ``explain`` delegate
|
|
38
|
+
to :class:`NemoJobScheduler` stubs that raise
|
|
39
|
+
:class:`NotImplementedError`; MR 1.3 and MR 1.4 wire them. ``run`` works
|
|
40
|
+
end-to-end today.
|
|
41
|
+
|
|
42
|
+
Generated command interface
|
|
43
|
+
---------------------------
|
|
44
|
+
|
|
45
|
+
``run``
|
|
46
|
+
Execute the job in-process. Accepts ``--config <json>`` (default ``{}``)
|
|
47
|
+
and ``--config-file <path>`` (takes precedence over ``--config``). The
|
|
48
|
+
scheduler validates against :attr:`~nemo_platform_plugin.job.NemoJob.spec_schema`
|
|
49
|
+
/ :attr:`~nemo_platform_plugin.job.NemoJob.input_spec_schema` when declared.
|
|
50
|
+
|
|
51
|
+
``submit``
|
|
52
|
+
Submit the job to a cluster. Phase 1 MR 1.3 wires this.
|
|
53
|
+
|
|
54
|
+
``explain``
|
|
55
|
+
Print the job's spec / options schemas. Phase 1 MR 1.4 wires this.
|
|
56
|
+
|
|
57
|
+
Example
|
|
58
|
+
-------
|
|
59
|
+
|
|
60
|
+
Given::
|
|
61
|
+
|
|
62
|
+
class SayHelloJob(NemoJob):
|
|
63
|
+
name = "say-hello"
|
|
64
|
+
description = "Greet a name."
|
|
65
|
+
|
|
66
|
+
def run(self, config: dict) -> dict:
|
|
67
|
+
return {"result": f"Hello, {config.get('name', 'world')}!"}
|
|
68
|
+
|
|
69
|
+
The platform generates::
|
|
70
|
+
|
|
71
|
+
$ nemo example say-hello run --config '{"name": "Claude"}'
|
|
72
|
+
{
|
|
73
|
+
"result": "Hello, Claude!"
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
$ nemo example say-hello
|
|
77
|
+
Usage: nemo example say-hello [OPTIONS] COMMAND [ARGS]...
|
|
78
|
+
...
|
|
79
|
+
$ echo $?
|
|
80
|
+
1
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
from __future__ import annotations
|
|
84
|
+
|
|
85
|
+
import asyncio
|
|
86
|
+
import inspect
|
|
87
|
+
import json
|
|
88
|
+
import logging
|
|
89
|
+
import os
|
|
90
|
+
from collections.abc import AsyncIterator, Callable, Iterable, Mapping
|
|
91
|
+
from pathlib import Path
|
|
92
|
+
from typing import Any, Literal, Optional, cast
|
|
93
|
+
|
|
94
|
+
import httpx
|
|
95
|
+
import typer
|
|
96
|
+
from nemo_platform_plugin._spec_flags import (
|
|
97
|
+
UNSET,
|
|
98
|
+
SpecLeafField,
|
|
99
|
+
build_callback_signature,
|
|
100
|
+
build_epilog,
|
|
101
|
+
build_overlay,
|
|
102
|
+
deep_merge,
|
|
103
|
+
kw,
|
|
104
|
+
walk_spec_leaves,
|
|
105
|
+
)
|
|
106
|
+
from nemo_platform_plugin.cli import NemoCLI
|
|
107
|
+
from nemo_platform_plugin.cli_errors import print_http_request_error, print_http_status_error
|
|
108
|
+
from nemo_platform_plugin.cli_renderer import CLIRenderer, RendererContext
|
|
109
|
+
from nemo_platform_plugin.function import NemoFunction, returns_async_iterator
|
|
110
|
+
from nemo_platform_plugin.function_context import FunctionContext
|
|
111
|
+
from nemo_platform_plugin.functions.routes import DEFAULT_FUNCTION_PATH, NDJSON_MEDIA_TYPE
|
|
112
|
+
from nemo_platform_plugin.job import NemoJob
|
|
113
|
+
from nemo_platform_plugin.jobs._cli_options import (
|
|
114
|
+
load_options_file,
|
|
115
|
+
load_spec_file,
|
|
116
|
+
merge_options,
|
|
117
|
+
parse_dotted_kv_list,
|
|
118
|
+
)
|
|
119
|
+
from nemo_platform_plugin.run_dependencies import LocalRunError
|
|
120
|
+
from nemo_platform_plugin.scheduler import NemoJobScheduler
|
|
121
|
+
from pydantic import BaseModel, ValidationError
|
|
122
|
+
|
|
123
|
+
logger = logging.getLogger(__name__)
|
|
124
|
+
|
|
125
|
+
# Submit host resolution for both functions and jobs:
|
|
126
|
+
# ``--base-url`` > ``--cluster`` (resolved from CLI config) > active context
|
|
127
|
+
# base URL > ``$NMP_BASE_URL`` > localhost.
|
|
128
|
+
_DEFAULT_BASE_URL_ENV_VAR = "NMP_BASE_URL"
|
|
129
|
+
_DEFAULT_BASE_URL = "http://localhost:8080"
|
|
130
|
+
|
|
131
|
+
# Rich-help panels for the function and job CLIs. Mirror PR #160's
|
|
132
|
+
# panel names but scoped per primitive so jobs and functions sharing a
|
|
133
|
+
# plugin still group cleanly when both are documented in --help.
|
|
134
|
+
_PANEL_SPEC_SOURCE: str = "Spec Source"
|
|
135
|
+
_PANEL_FUNCTION_SPEC: str = "Function Spec"
|
|
136
|
+
_PANEL_JOB_SPEC: str = "Job Spec"
|
|
137
|
+
_PANEL_SUBMISSION: str = "Submission"
|
|
138
|
+
|
|
139
|
+
# Static flag names declared by each function verb. Spec fields whose
|
|
140
|
+
# names collide with these are dropped from the auto-generated flag
|
|
141
|
+
# set; they remain reachable via ``--spec`` / ``--spec-file``. ``run``
|
|
142
|
+
# is deliberately permissive — only the JSON-passing flags and the
|
|
143
|
+
# context-input ``workspace`` flag are reserved. ``submit`` layers on
|
|
144
|
+
# the URL-routing flags + the request-id surface.
|
|
145
|
+
_FN_RUN_RESERVED_FLAGS: frozenset[str] = frozenset({"spec", "spec_file", "workspace"})
|
|
146
|
+
_FN_SUBMIT_RESERVED_FLAGS: frozenset[str] = _FN_RUN_RESERVED_FLAGS | frozenset({"cluster", "base_url", "request_id"})
|
|
147
|
+
|
|
148
|
+
# Static flag names declared by each job verb. ``run`` reserves the
|
|
149
|
+
# spec-source flags plus the deprecated ``--config`` / ``--config-file``
|
|
150
|
+
# aliases (kept during the rename transition). ``submit`` layers on the
|
|
151
|
+
# submission-routing flags (``--profile`` / ``--cluster`` / ``--base-url``
|
|
152
|
+
# / ``--workspace``) and the options-passthrough flags (``-o`` /
|
|
153
|
+
# ``--options-file``). Reserved spec fields remain reachable via
|
|
154
|
+
# ``--spec`` / ``--spec-file`` JSON.
|
|
155
|
+
_JOB_RUN_RESERVED_FLAGS: frozenset[str] = frozenset({"spec", "spec_file", "config", "config_file"})
|
|
156
|
+
_JOB_SUBMIT_RESERVED_FLAGS: frozenset[str] = _JOB_RUN_RESERVED_FLAGS | frozenset(
|
|
157
|
+
{"options", "options_file", "profile", "cluster", "base_url", "workspace"}
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
# ---------------------------------------------------------------------------
|
|
162
|
+
# Renderer plumbing
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _output_format_is_json(typer_ctx: typer.Context | None) -> bool:
|
|
167
|
+
"""Return True when the global ``--output-format json`` flag is set.
|
|
168
|
+
|
|
169
|
+
Read from ``typer_ctx.obj.overrides`` — the ``CLIContext`` populated by
|
|
170
|
+
the top-level :func:`nemo_platform_ext.cli.app.main` callback. Defensive
|
|
171
|
+
against non-``CLIContext`` objects so plugins exercising the CLI directly
|
|
172
|
+
in tests (with ``ctx.obj = None``) still work.
|
|
173
|
+
"""
|
|
174
|
+
if typer_ctx is None:
|
|
175
|
+
return False
|
|
176
|
+
state = typer_ctx.obj
|
|
177
|
+
if state is None:
|
|
178
|
+
return False
|
|
179
|
+
overrides = getattr(state, "overrides", None)
|
|
180
|
+
if not isinstance(overrides, dict):
|
|
181
|
+
return False
|
|
182
|
+
return overrides.get("output_format") == "json"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _make_renderer_context(
|
|
186
|
+
*,
|
|
187
|
+
cli_kwargs: Mapping[str, Any],
|
|
188
|
+
verb: Literal["run", "submit"],
|
|
189
|
+
is_local: bool,
|
|
190
|
+
) -> RendererContext:
|
|
191
|
+
"""Build the per-invocation :class:`RendererContext` passed to renderer methods."""
|
|
192
|
+
from rich.console import Console
|
|
193
|
+
|
|
194
|
+
return RendererContext(console=Console(), cli_kwargs=cli_kwargs, verb=verb, is_local=is_local)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
async def _drive_async_renderer(
|
|
198
|
+
stream: AsyncIterator[Any],
|
|
199
|
+
renderer_cls: type[CLIRenderer],
|
|
200
|
+
*,
|
|
201
|
+
rctx: RendererContext,
|
|
202
|
+
) -> CLIRenderer:
|
|
203
|
+
"""Drive *renderer_cls*'s ``on_start`` + ``on_frame`` over an async stream.
|
|
204
|
+
|
|
205
|
+
Returns the renderer instance so the sync caller (the one calling
|
|
206
|
+
:func:`asyncio.run`) can fire :meth:`CLIRenderer.on_complete` *after*
|
|
207
|
+
the loop tears down — important because renderers commonly use
|
|
208
|
+
event-loop-bound libraries (e.g. ``prompt_toolkit.Application.run``)
|
|
209
|
+
in ``on_complete``, and those use their own :func:`asyncio.run` which
|
|
210
|
+
raises ``RuntimeError: asyncio.run() cannot be called from a running
|
|
211
|
+
event loop`` when nested.
|
|
212
|
+
|
|
213
|
+
:meth:`CLIRenderer.on_error` is fired in-loop because the exception
|
|
214
|
+
is most usefully handled where it surfaced; the exception still
|
|
215
|
+
propagates so the caller sees it.
|
|
216
|
+
"""
|
|
217
|
+
renderer = renderer_cls()
|
|
218
|
+
renderer.on_start(ctx=rctx)
|
|
219
|
+
try:
|
|
220
|
+
async for frame in stream:
|
|
221
|
+
renderer.on_frame(frame, ctx=rctx)
|
|
222
|
+
except BaseException as exc:
|
|
223
|
+
renderer.on_error(exc, ctx=rctx)
|
|
224
|
+
raise
|
|
225
|
+
return renderer
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _drive_sync_renderer(
|
|
229
|
+
line_iter: Iterable[str],
|
|
230
|
+
renderer_cls: type[CLIRenderer],
|
|
231
|
+
*,
|
|
232
|
+
rctx: RendererContext,
|
|
233
|
+
) -> CLIRenderer:
|
|
234
|
+
"""Drive *renderer_cls*'s ``on_start`` + ``on_frame`` over a sync line iterator.
|
|
235
|
+
|
|
236
|
+
Returns the renderer; the caller fires :meth:`CLIRenderer.on_complete`.
|
|
237
|
+
Symmetric with :func:`_drive_async_renderer` so all three drivers obey
|
|
238
|
+
the same contract: in-loop work happens here (``on_start`` / per-frame
|
|
239
|
+
/ ``on_error``), the post-stream summary happens at the call site after
|
|
240
|
+
this returns. That contract matters even for sync drivers: it keeps the
|
|
241
|
+
plugin-author mental model consistent and avoids re-introducing
|
|
242
|
+
nested-event-loop hazards if a sync driver is ever wrapped in an async
|
|
243
|
+
context.
|
|
244
|
+
|
|
245
|
+
Each non-empty NDJSON line is parsed as JSON and passed to
|
|
246
|
+
:meth:`CLIRenderer.on_frame`. Lines that fail to parse pass through as
|
|
247
|
+
raw strings — renderers can decide how to handle non-JSON output.
|
|
248
|
+
"""
|
|
249
|
+
renderer = renderer_cls()
|
|
250
|
+
renderer.on_start(ctx=rctx)
|
|
251
|
+
try:
|
|
252
|
+
for line in line_iter:
|
|
253
|
+
if not line:
|
|
254
|
+
continue
|
|
255
|
+
try:
|
|
256
|
+
frame: object = json.loads(line)
|
|
257
|
+
except json.JSONDecodeError:
|
|
258
|
+
frame = line
|
|
259
|
+
renderer.on_frame(frame, ctx=rctx)
|
|
260
|
+
except BaseException as exc:
|
|
261
|
+
renderer.on_error(exc, ctx=rctx)
|
|
262
|
+
raise
|
|
263
|
+
return renderer
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _drive_single_value_renderer(
|
|
267
|
+
callable_: Callable[[], Any],
|
|
268
|
+
renderer_cls: type[CLIRenderer],
|
|
269
|
+
*,
|
|
270
|
+
rctx: RendererContext,
|
|
271
|
+
) -> CLIRenderer:
|
|
272
|
+
"""Drive *renderer_cls* around a synchronous, non-streaming result.
|
|
273
|
+
|
|
274
|
+
Used for jobs (which return a single dict). The result is passed to
|
|
275
|
+
:meth:`CLIRenderer.on_frame` once. Returns the renderer so the caller
|
|
276
|
+
can fire :meth:`CLIRenderer.on_complete` — same contract as the
|
|
277
|
+
streaming drivers.
|
|
278
|
+
"""
|
|
279
|
+
renderer = renderer_cls()
|
|
280
|
+
renderer.on_start(ctx=rctx)
|
|
281
|
+
try:
|
|
282
|
+
result = callable_()
|
|
283
|
+
renderer.on_frame(result, ctx=rctx)
|
|
284
|
+
except BaseException as exc:
|
|
285
|
+
renderer.on_error(exc, ctx=rctx)
|
|
286
|
+
raise
|
|
287
|
+
return renderer
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def add_job_commands(
|
|
291
|
+
cli_app: typer.Typer,
|
|
292
|
+
jobs: dict[str, type[NemoJob]],
|
|
293
|
+
*,
|
|
294
|
+
cli: NemoCLI | None = None,
|
|
295
|
+
) -> None:
|
|
296
|
+
"""Inject three-verb subcommand groups for each job into *cli_app*.
|
|
297
|
+
|
|
298
|
+
Each entry in *jobs* produces one :class:`typer.Typer` sub-group
|
|
299
|
+
registered under :attr:`~nemo_platform_plugin.job.NemoJob.name` in a ``"Jobs"``
|
|
300
|
+
rich help panel. The sub-group owns ``run`` / ``submit`` / ``explain``
|
|
301
|
+
commands.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
cli_app: The plugin's :class:`typer.Typer` group to inject
|
|
305
|
+
sub-groups into. Typically the value returned by
|
|
306
|
+
:meth:`~nemo_platform_plugin.cli.NemoCLI.get_cli`.
|
|
307
|
+
jobs: Mapping of entry-point key → :class:`~nemo_platform_plugin.job.NemoJob`
|
|
308
|
+
subclass, already filtered to the relevant plugin. Typically a
|
|
309
|
+
subset of the dict returned by
|
|
310
|
+
:func:`~nemo_platform_plugin.discovery.discover_jobs`.
|
|
311
|
+
cli: Optional plugin :class:`~nemo_platform_plugin.cli.NemoCLI` instance. When
|
|
312
|
+
supplied, :meth:`~nemo_platform_plugin.cli.NemoCLI.update_job_cli` is
|
|
313
|
+
invoked once per job after default verb registration and before
|
|
314
|
+
the sub-group is mounted, giving the plugin a chance to amend
|
|
315
|
+
the auto-generated CLI surface.
|
|
316
|
+
"""
|
|
317
|
+
scheduler = NemoJobScheduler()
|
|
318
|
+
for job_cls in jobs.values():
|
|
319
|
+
_register_job_subgroup(cli_app, job_cls, scheduler, cli=cli)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _register_job_subgroup(
|
|
323
|
+
cli_app: typer.Typer,
|
|
324
|
+
job_cls: type[NemoJob],
|
|
325
|
+
scheduler: NemoJobScheduler,
|
|
326
|
+
*,
|
|
327
|
+
cli: NemoCLI | None = None,
|
|
328
|
+
) -> None:
|
|
329
|
+
"""Register a ``<job-name>`` sub-group with run / submit / explain verbs."""
|
|
330
|
+
job_group = typer.Typer(
|
|
331
|
+
name=job_cls.name,
|
|
332
|
+
help=job_cls.description or f"Manage the {job_cls.name} job.",
|
|
333
|
+
no_args_is_help=False, # we override bare behavior to exit non-zero
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
@job_group.callback(invoke_without_command=True)
|
|
337
|
+
def _root(ctx: typer.Context) -> None: # pragma: no cover - trivial delegation
|
|
338
|
+
"""Stub callback — prints usage and exits 1 when no verb is given."""
|
|
339
|
+
if ctx.invoked_subcommand is None:
|
|
340
|
+
typer.echo(ctx.get_help())
|
|
341
|
+
raise typer.Exit(code=1)
|
|
342
|
+
|
|
343
|
+
_add_run_command(job_group, job_cls, scheduler, cli=cli)
|
|
344
|
+
_add_submit_command(job_group, job_cls, scheduler, cli=cli)
|
|
345
|
+
_add_explain_command(job_group, job_cls, scheduler)
|
|
346
|
+
|
|
347
|
+
if cli is not None:
|
|
348
|
+
cli.update_job_cli(job_cls, job_group)
|
|
349
|
+
|
|
350
|
+
cli_app.add_typer(job_group, name=job_cls.name, rich_help_panel="Jobs")
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
# ---------------------------------------------------------------------------
|
|
354
|
+
# run — local execution, wired via NemoJobScheduler.run_local
|
|
355
|
+
# ---------------------------------------------------------------------------
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def _job_input_schema(job_cls: type[NemoJob]) -> type[BaseModel] | None:
|
|
359
|
+
"""Pick the schema that the submitter's input is validated against.
|
|
360
|
+
|
|
361
|
+
Mirrors the precedence used by
|
|
362
|
+
:meth:`~nemo_platform_plugin.scheduler.NemoJobScheduler._validate_and_compile`:
|
|
363
|
+
:attr:`~nemo_platform_plugin.job.NemoJob.input_spec_schema` when declared,
|
|
364
|
+
else :attr:`~nemo_platform_plugin.job.NemoJob.spec_schema`. Both ``run`` and
|
|
365
|
+
``submit`` accept the same shape from the user, so we walk a single
|
|
366
|
+
schema for both verbs. Returns ``None`` when neither attribute is
|
|
367
|
+
declared — the auto-flag generator treats ``None`` as "no leaves",
|
|
368
|
+
keeping legacy schema-less jobs unchanged.
|
|
369
|
+
"""
|
|
370
|
+
return job_cls.input_spec_schema or job_cls.spec_schema
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _add_run_command(
|
|
374
|
+
group: typer.Typer,
|
|
375
|
+
job_cls: type[NemoJob],
|
|
376
|
+
scheduler: NemoJobScheduler,
|
|
377
|
+
*,
|
|
378
|
+
cli: NemoCLI | None = None,
|
|
379
|
+
) -> None:
|
|
380
|
+
"""Register the ``run`` verb. Generates per-field flags from the input schema.
|
|
381
|
+
|
|
382
|
+
Each scalar leaf in :func:`_job_input_schema` becomes a Typer option
|
|
383
|
+
named after its dotted path, kebab-cased per segment (``--name``,
|
|
384
|
+
``--target.url``). Precedence at runtime is ``--spec-file`` (base)
|
|
385
|
+
→ ``--spec`` JSON (overlay) → per-field flags (top overlay), with
|
|
386
|
+
the deprecated ``--config`` / ``--config-file`` aliases routed
|
|
387
|
+
through the same precedence as ``--spec`` / ``--spec-file``.
|
|
388
|
+
|
|
389
|
+
When *cli* supplies a renderer via ``get_job_renderer(verb="run")`` (and
|
|
390
|
+
``--output-format json`` is not set), the renderer's lifecycle wraps the
|
|
391
|
+
synchronous ``run_local`` call: ``on_start`` → ``on_frame(result)`` →
|
|
392
|
+
``on_complete``. The default-printer fallback echoes the dict result
|
|
393
|
+
when no renderer is supplied.
|
|
394
|
+
"""
|
|
395
|
+
schema = _job_input_schema(job_cls)
|
|
396
|
+
leaves = walk_spec_leaves(schema, reserved=_JOB_RUN_RESERVED_FLAGS)
|
|
397
|
+
|
|
398
|
+
def _run(typer_ctx: typer.Context, **kwargs: object) -> None:
|
|
399
|
+
original_kwargs = dict(kwargs)
|
|
400
|
+
spec_str: str = cast(str, kwargs.pop("spec", "{}"))
|
|
401
|
+
spec_file: Path | None = cast("Path | None", kwargs.pop("spec_file", None))
|
|
402
|
+
config: str | None = cast("str | None", kwargs.pop("config", None))
|
|
403
|
+
config_file: Path | None = cast("Path | None", kwargs.pop("config_file", None))
|
|
404
|
+
|
|
405
|
+
effective_spec_str = config if config is not None else spec_str
|
|
406
|
+
effective_spec_file = config_file if config_file is not None else spec_file
|
|
407
|
+
base = _load_spec(effective_spec_str, effective_spec_file)
|
|
408
|
+
overlay = build_overlay(leaves, kwargs, unset_sentinel=UNSET)
|
|
409
|
+
data = deep_merge(base, overlay)
|
|
410
|
+
logger.debug("Running job %r locally with spec %r", job_cls.name, data)
|
|
411
|
+
sdk, async_sdk = _resolve_local_cli_sdks(typer_ctx)
|
|
412
|
+
|
|
413
|
+
renderer_cls: type[CLIRenderer] | None = None
|
|
414
|
+
if cli is not None and not _output_format_is_json(typer_ctx):
|
|
415
|
+
renderer_cls = cli.get_job_renderer(job_cls, verb="run")
|
|
416
|
+
|
|
417
|
+
def _do_run() -> Any:
|
|
418
|
+
return scheduler.run_local(job_cls, data, sdk=sdk, async_sdk=async_sdk)
|
|
419
|
+
|
|
420
|
+
renderer: CLIRenderer | None = None
|
|
421
|
+
rctx: RendererContext | None = None
|
|
422
|
+
try:
|
|
423
|
+
if renderer_cls is not None:
|
|
424
|
+
rctx = _make_renderer_context(
|
|
425
|
+
cli_kwargs=original_kwargs,
|
|
426
|
+
verb="run",
|
|
427
|
+
is_local=True,
|
|
428
|
+
)
|
|
429
|
+
renderer = _drive_single_value_renderer(_do_run, renderer_cls, rctx=rctx)
|
|
430
|
+
else:
|
|
431
|
+
result = _do_run()
|
|
432
|
+
typer.echo(json.dumps(result, indent=2))
|
|
433
|
+
except LocalRunError as exc:
|
|
434
|
+
typer.echo(f"Error: {exc}", err=True)
|
|
435
|
+
raise typer.Exit(code=1) from exc
|
|
436
|
+
|
|
437
|
+
# on_complete fires after the driver returns, in the same contract
|
|
438
|
+
# the async renderer driver follows — see _drive_sync_renderer.
|
|
439
|
+
if renderer is not None and rctx is not None:
|
|
440
|
+
renderer.on_complete(ctx=rctx)
|
|
441
|
+
|
|
442
|
+
help_text = f"Run {job_cls.name} locally, in-process."
|
|
443
|
+
_run.__signature__ = _build_job_run_signature(leaves) # type: ignore[attr-defined]
|
|
444
|
+
group.command(name="run", help=help_text)(_run)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def _build_job_run_signature(leaves: list[SpecLeafField]) -> inspect.Signature:
|
|
448
|
+
"""Compose the synthetic signature for the job ``run`` verb.
|
|
449
|
+
|
|
450
|
+
Static flags (``--spec`` / ``--spec-file``) come first under the
|
|
451
|
+
``Spec Source`` panel, followed by the auto-generated per-field
|
|
452
|
+
flags under ``Job Spec``, then the hidden ``--config`` /
|
|
453
|
+
``--config-file`` deprecated aliases.
|
|
454
|
+
"""
|
|
455
|
+
static_params = [
|
|
456
|
+
# ``typer.Context`` is auto-injected by Click via ``pass_context``,
|
|
457
|
+
# which passes it as the first positional argument. Hand-built
|
|
458
|
+
# rather than via ``kw()`` because ``kw()`` only emits
|
|
459
|
+
# ``KEYWORD_ONLY`` params (which Click's positional injection
|
|
460
|
+
# would reject).
|
|
461
|
+
inspect.Parameter(
|
|
462
|
+
"typer_ctx",
|
|
463
|
+
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
464
|
+
annotation=typer.Context,
|
|
465
|
+
),
|
|
466
|
+
kw(
|
|
467
|
+
"spec",
|
|
468
|
+
str,
|
|
469
|
+
typer.Option(
|
|
470
|
+
"{}",
|
|
471
|
+
"--spec",
|
|
472
|
+
help="Spec as a JSON string.",
|
|
473
|
+
rich_help_panel=_PANEL_SPEC_SOURCE,
|
|
474
|
+
),
|
|
475
|
+
),
|
|
476
|
+
kw(
|
|
477
|
+
"spec_file",
|
|
478
|
+
Optional[Path],
|
|
479
|
+
typer.Option(
|
|
480
|
+
None,
|
|
481
|
+
"--spec-file",
|
|
482
|
+
help="Path to a YAML or JSON spec file (used as base; per-flag values override).",
|
|
483
|
+
rich_help_panel=_PANEL_SPEC_SOURCE,
|
|
484
|
+
),
|
|
485
|
+
),
|
|
486
|
+
]
|
|
487
|
+
# Deprecated aliases — kept for the transition period (MR 1.3b).
|
|
488
|
+
# Trailing so they sort below the auto-generated panel in --help,
|
|
489
|
+
# though they're hidden anyway and only matter for backwards-compatible
|
|
490
|
+
# invocations.
|
|
491
|
+
trailing_params = [
|
|
492
|
+
kw(
|
|
493
|
+
"config",
|
|
494
|
+
Optional[str],
|
|
495
|
+
typer.Option(
|
|
496
|
+
None,
|
|
497
|
+
"--config",
|
|
498
|
+
help="(Deprecated — use --spec.) Spec as a JSON string.",
|
|
499
|
+
hidden=True,
|
|
500
|
+
),
|
|
501
|
+
),
|
|
502
|
+
kw(
|
|
503
|
+
"config_file",
|
|
504
|
+
Optional[Path],
|
|
505
|
+
typer.Option(
|
|
506
|
+
None,
|
|
507
|
+
"--config-file",
|
|
508
|
+
help="(Deprecated — use --spec-file.) Path to a JSON spec file.",
|
|
509
|
+
hidden=True,
|
|
510
|
+
),
|
|
511
|
+
),
|
|
512
|
+
]
|
|
513
|
+
return build_callback_signature(
|
|
514
|
+
static_params,
|
|
515
|
+
leaves,
|
|
516
|
+
rich_help_panel=_PANEL_JOB_SPEC,
|
|
517
|
+
trailing_params=trailing_params,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
# ---------------------------------------------------------------------------
|
|
522
|
+
# submit — stub delegate to scheduler.submit_remote (wired in MR 1.3)
|
|
523
|
+
# ---------------------------------------------------------------------------
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _add_submit_command(
|
|
527
|
+
group: typer.Typer,
|
|
528
|
+
job_cls: type[NemoJob],
|
|
529
|
+
scheduler: NemoJobScheduler,
|
|
530
|
+
*,
|
|
531
|
+
cli: NemoCLI | None = None,
|
|
532
|
+
) -> None:
|
|
533
|
+
"""Register the ``submit`` verb. Generates per-field flags + static submit flags.
|
|
534
|
+
|
|
535
|
+
Static flags (``--spec`` / ``--spec-file``, ``-o`` / ``--options-file``,
|
|
536
|
+
``--profile``, ``--cluster``, ``--base-url``, ``--workspace``, plus the
|
|
537
|
+
hidden ``--config`` / ``--config-file`` deprecated aliases) come first;
|
|
538
|
+
spec fields whose names collide with any of those are dropped from
|
|
539
|
+
the auto-generated set and remain reachable via ``--spec`` /
|
|
540
|
+
``--spec-file``.
|
|
541
|
+
|
|
542
|
+
When *cli* supplies a renderer via ``get_job_renderer(verb="submit")``
|
|
543
|
+
(and ``--output-format json`` is not set), the renderer's lifecycle
|
|
544
|
+
wraps the ``submit_remote`` call: ``on_start`` → ``on_frame(result)`` →
|
|
545
|
+
``on_complete``.
|
|
546
|
+
"""
|
|
547
|
+
schema = _job_input_schema(job_cls)
|
|
548
|
+
leaves = walk_spec_leaves(schema, reserved=_JOB_SUBMIT_RESERVED_FLAGS)
|
|
549
|
+
|
|
550
|
+
def _submit(typer_ctx: typer.Context, **kwargs: object) -> None:
|
|
551
|
+
original_kwargs = dict(kwargs)
|
|
552
|
+
spec_str: str = cast(str, kwargs.pop("spec", "{}"))
|
|
553
|
+
spec_file: Path | None = cast("Path | None", kwargs.pop("spec_file", None))
|
|
554
|
+
options: list[str] = cast("list[str]", kwargs.pop("options", []))
|
|
555
|
+
options_file: Path | None = cast("Path | None", kwargs.pop("options_file", None))
|
|
556
|
+
profile: str | None = cast("str | None", kwargs.pop("profile", None))
|
|
557
|
+
cluster: str | None = cast("str | None", kwargs.pop("cluster", None))
|
|
558
|
+
base_url: str | None = cast("str | None", kwargs.pop("base_url", None))
|
|
559
|
+
workspace: str = cast(str, kwargs.pop("workspace", "default"))
|
|
560
|
+
config: str | None = cast("str | None", kwargs.pop("config", None))
|
|
561
|
+
config_file: Path | None = cast("Path | None", kwargs.pop("config_file", None))
|
|
562
|
+
|
|
563
|
+
effective_spec_str = config if config is not None else spec_str
|
|
564
|
+
effective_spec_file = config_file if config_file is not None else spec_file
|
|
565
|
+
base = _load_spec(effective_spec_str, effective_spec_file)
|
|
566
|
+
overlay = build_overlay(leaves, kwargs, unset_sentinel=UNSET)
|
|
567
|
+
spec_data = deep_merge(base, overlay)
|
|
568
|
+
|
|
569
|
+
try:
|
|
570
|
+
merged_options = _merge_options_inputs(options, options_file)
|
|
571
|
+
except ValueError as exc:
|
|
572
|
+
typer.echo(f"Error: {exc}", err=True)
|
|
573
|
+
raise typer.Exit(code=1) from exc
|
|
574
|
+
|
|
575
|
+
renderer_cls: type[CLIRenderer] | None = None
|
|
576
|
+
if cli is not None and not _output_format_is_json(typer_ctx):
|
|
577
|
+
renderer_cls = cli.get_job_renderer(job_cls, verb="submit")
|
|
578
|
+
|
|
579
|
+
def _do_submit() -> Any:
|
|
580
|
+
return scheduler.submit_remote(
|
|
581
|
+
job_cls,
|
|
582
|
+
spec_data,
|
|
583
|
+
base_url=_resolve_submit_base_url(typer_ctx, base_url=base_url, cluster=cluster),
|
|
584
|
+
workspace=workspace,
|
|
585
|
+
profile=profile,
|
|
586
|
+
options=merged_options or None,
|
|
587
|
+
)
|
|
588
|
+
|
|
589
|
+
renderer: CLIRenderer | None = None
|
|
590
|
+
rctx: RendererContext | None = None
|
|
591
|
+
try:
|
|
592
|
+
if renderer_cls is not None:
|
|
593
|
+
rctx = _make_renderer_context(
|
|
594
|
+
cli_kwargs=original_kwargs,
|
|
595
|
+
verb="submit",
|
|
596
|
+
is_local=False,
|
|
597
|
+
)
|
|
598
|
+
renderer = _drive_single_value_renderer(_do_submit, renderer_cls, rctx=rctx)
|
|
599
|
+
else:
|
|
600
|
+
result = _do_submit()
|
|
601
|
+
typer.echo(json.dumps(result, indent=2))
|
|
602
|
+
except (NotImplementedError, ValueError) as exc:
|
|
603
|
+
typer.echo(f"Error: {exc}", err=True)
|
|
604
|
+
raise typer.Exit(code=2) from exc
|
|
605
|
+
except httpx.HTTPStatusError as exc:
|
|
606
|
+
print_http_status_error(exc, action=f"submit {job_cls.name}")
|
|
607
|
+
raise typer.Exit(code=2) from exc
|
|
608
|
+
except httpx.RequestError as exc:
|
|
609
|
+
print_http_request_error(exc, action=f"submit {job_cls.name}")
|
|
610
|
+
raise typer.Exit(code=2) from exc
|
|
611
|
+
except httpx.HTTPError as exc:
|
|
612
|
+
typer.echo(f"Error: submit {job_cls.name} failed: {exc}", err=True)
|
|
613
|
+
raise typer.Exit(code=2) from exc
|
|
614
|
+
|
|
615
|
+
if renderer is not None and rctx is not None:
|
|
616
|
+
renderer.on_complete(ctx=rctx)
|
|
617
|
+
|
|
618
|
+
help_text = f"Submit {job_cls.name} to a cluster."
|
|
619
|
+
_submit.__signature__ = _build_job_submit_signature(leaves) # type: ignore[attr-defined]
|
|
620
|
+
group.command(name="submit", help=help_text)(_submit)
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
def _build_job_submit_signature(leaves: list[SpecLeafField]) -> inspect.Signature:
|
|
624
|
+
"""Compose the synthetic signature for the job ``submit`` verb."""
|
|
625
|
+
static_params = [
|
|
626
|
+
inspect.Parameter(
|
|
627
|
+
"typer_ctx",
|
|
628
|
+
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
629
|
+
annotation=typer.Context,
|
|
630
|
+
),
|
|
631
|
+
kw(
|
|
632
|
+
"spec",
|
|
633
|
+
str,
|
|
634
|
+
typer.Option(
|
|
635
|
+
"{}",
|
|
636
|
+
"--spec",
|
|
637
|
+
help="Spec as a JSON string.",
|
|
638
|
+
rich_help_panel=_PANEL_SPEC_SOURCE,
|
|
639
|
+
),
|
|
640
|
+
),
|
|
641
|
+
kw(
|
|
642
|
+
"spec_file",
|
|
643
|
+
Optional[Path],
|
|
644
|
+
typer.Option(
|
|
645
|
+
None,
|
|
646
|
+
"--spec-file",
|
|
647
|
+
help="Path to a YAML or JSON spec file (used as base; per-flag values override).",
|
|
648
|
+
rich_help_panel=_PANEL_SPEC_SOURCE,
|
|
649
|
+
),
|
|
650
|
+
),
|
|
651
|
+
kw(
|
|
652
|
+
"options",
|
|
653
|
+
list[str],
|
|
654
|
+
typer.Option(
|
|
655
|
+
[], # noqa: B006 — Typer evaluates the default lazily per invocation
|
|
656
|
+
"-o",
|
|
657
|
+
help="Backend option override, 'backend.key=value' (repeatable).",
|
|
658
|
+
rich_help_panel=_PANEL_SUBMISSION,
|
|
659
|
+
),
|
|
660
|
+
),
|
|
661
|
+
kw(
|
|
662
|
+
"options_file",
|
|
663
|
+
Optional[Path],
|
|
664
|
+
typer.Option(
|
|
665
|
+
None,
|
|
666
|
+
"--options-file",
|
|
667
|
+
help="Path to a YAML or JSON options file (nested by backend name).",
|
|
668
|
+
rich_help_panel=_PANEL_SUBMISSION,
|
|
669
|
+
),
|
|
670
|
+
),
|
|
671
|
+
kw(
|
|
672
|
+
"profile",
|
|
673
|
+
Optional[str],
|
|
674
|
+
typer.Option(
|
|
675
|
+
None,
|
|
676
|
+
"--profile",
|
|
677
|
+
help=(
|
|
678
|
+
"Execution profile (operator-configured). Required unless the active "
|
|
679
|
+
"cluster has a 'default' profile."
|
|
680
|
+
),
|
|
681
|
+
rich_help_panel=_PANEL_SUBMISSION,
|
|
682
|
+
),
|
|
683
|
+
),
|
|
684
|
+
kw(
|
|
685
|
+
"cluster",
|
|
686
|
+
Optional[str],
|
|
687
|
+
typer.Option(
|
|
688
|
+
None,
|
|
689
|
+
"--cluster",
|
|
690
|
+
help="Configured cluster name to resolve via the NeMo CLI config.",
|
|
691
|
+
rich_help_panel=_PANEL_SUBMISSION,
|
|
692
|
+
),
|
|
693
|
+
),
|
|
694
|
+
kw(
|
|
695
|
+
"base_url",
|
|
696
|
+
Optional[str],
|
|
697
|
+
typer.Option(
|
|
698
|
+
None,
|
|
699
|
+
"--base-url",
|
|
700
|
+
help=("Explicit plugin-service base URL (overrides --cluster and all other submit host resolution)."),
|
|
701
|
+
rich_help_panel=_PANEL_SUBMISSION,
|
|
702
|
+
),
|
|
703
|
+
),
|
|
704
|
+
kw(
|
|
705
|
+
"workspace",
|
|
706
|
+
str,
|
|
707
|
+
typer.Option(
|
|
708
|
+
"default",
|
|
709
|
+
"--workspace",
|
|
710
|
+
help="Workspace scope for the submission.",
|
|
711
|
+
rich_help_panel=_PANEL_SUBMISSION,
|
|
712
|
+
),
|
|
713
|
+
),
|
|
714
|
+
]
|
|
715
|
+
trailing_params = [
|
|
716
|
+
kw(
|
|
717
|
+
"config",
|
|
718
|
+
Optional[str],
|
|
719
|
+
typer.Option(
|
|
720
|
+
None,
|
|
721
|
+
"--config",
|
|
722
|
+
help="(Deprecated — use --spec.) Spec as a JSON string.",
|
|
723
|
+
hidden=True,
|
|
724
|
+
),
|
|
725
|
+
),
|
|
726
|
+
kw(
|
|
727
|
+
"config_file",
|
|
728
|
+
Optional[Path],
|
|
729
|
+
typer.Option(
|
|
730
|
+
None,
|
|
731
|
+
"--config-file",
|
|
732
|
+
help="(Deprecated — use --spec-file.) Path to a JSON spec file.",
|
|
733
|
+
hidden=True,
|
|
734
|
+
),
|
|
735
|
+
),
|
|
736
|
+
]
|
|
737
|
+
return build_callback_signature(
|
|
738
|
+
static_params,
|
|
739
|
+
leaves,
|
|
740
|
+
rich_help_panel=_PANEL_JOB_SPEC,
|
|
741
|
+
trailing_params=trailing_params,
|
|
742
|
+
)
|
|
743
|
+
|
|
744
|
+
|
|
745
|
+
# ---------------------------------------------------------------------------
|
|
746
|
+
# explain — stub delegate to scheduler.explain (wired in MR 1.4)
|
|
747
|
+
# ---------------------------------------------------------------------------
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
def _add_explain_command(
|
|
751
|
+
group: typer.Typer,
|
|
752
|
+
job_cls: type[NemoJob],
|
|
753
|
+
scheduler: NemoJobScheduler,
|
|
754
|
+
) -> None:
|
|
755
|
+
def _explain(
|
|
756
|
+
profile: Optional[str] = typer.Option(
|
|
757
|
+
None,
|
|
758
|
+
"--profile",
|
|
759
|
+
help="Annotate the bundle with this profile. Profile metadata lands in MR 1.4b.",
|
|
760
|
+
),
|
|
761
|
+
cluster: Optional[str] = typer.Option(
|
|
762
|
+
None,
|
|
763
|
+
"--cluster",
|
|
764
|
+
help="Accepted for forward compatibility; unused in MR 1.4a.",
|
|
765
|
+
),
|
|
766
|
+
) -> None:
|
|
767
|
+
"""Print schemas for the job (spec / input_spec / options)."""
|
|
768
|
+
del cluster # reserved for MR 1.4b when /execution-profiles fetch lands
|
|
769
|
+
bundle = scheduler.explain(job_cls, profile=profile)
|
|
770
|
+
typer.echo(json.dumps(bundle, indent=2))
|
|
771
|
+
|
|
772
|
+
_explain.__doc__ = f"Show the schemas for {job_cls.name}."
|
|
773
|
+
group.command(name="explain", help=_explain.__doc__)(_explain)
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
# ---------------------------------------------------------------------------
|
|
777
|
+
# Shared helpers
|
|
778
|
+
# ---------------------------------------------------------------------------
|
|
779
|
+
|
|
780
|
+
|
|
781
|
+
def _load_spec(spec_str: str, spec_file: Path | None) -> dict:
|
|
782
|
+
"""Load the spec dict from ``--spec`` JSON or ``--spec-file`` path.
|
|
783
|
+
|
|
784
|
+
``--spec-file`` wins when both are provided. Supports YAML or JSON
|
|
785
|
+
(extension-driven). Raises :class:`typer.Exit` with a helpful error
|
|
786
|
+
message on malformed content **or** on top-level non-mapping
|
|
787
|
+
payloads (e.g. ``--spec '[]'``); the downstream
|
|
788
|
+
:func:`~nemo_platform_plugin._spec_flags.deep_merge` assumes a dict and
|
|
789
|
+
would otherwise fail with a raw ``TypeError`` after a per-field
|
|
790
|
+
overlay was applied.
|
|
791
|
+
"""
|
|
792
|
+
try:
|
|
793
|
+
if spec_file is not None:
|
|
794
|
+
loaded = load_spec_file(spec_file)
|
|
795
|
+
else:
|
|
796
|
+
loaded = json.loads(spec_str)
|
|
797
|
+
except (json.JSONDecodeError, ValueError) as exc:
|
|
798
|
+
typer.echo(f"Error: invalid spec — {exc}", err=True)
|
|
799
|
+
raise typer.Exit(code=1) from exc
|
|
800
|
+
if not isinstance(loaded, dict):
|
|
801
|
+
typer.echo(
|
|
802
|
+
f"Error: invalid spec — expected a JSON/YAML object, got {type(loaded).__name__}.",
|
|
803
|
+
err=True,
|
|
804
|
+
)
|
|
805
|
+
raise typer.Exit(code=1)
|
|
806
|
+
return loaded
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
def _merge_options_inputs(options: list[str], options_file: Path | None) -> dict:
|
|
810
|
+
"""Combine ``--options-file`` contents (base) with ``-o`` overrides (overlay).
|
|
811
|
+
|
|
812
|
+
Precedence: individual ``-o`` flags win over values in ``--options-file``.
|
|
813
|
+
Both inputs are optional; an empty result is fine (the submit body
|
|
814
|
+
omits the ``options`` field when the merged dict is empty).
|
|
815
|
+
"""
|
|
816
|
+
base: dict = load_options_file(options_file) if options_file is not None else {}
|
|
817
|
+
overlay: dict = parse_dotted_kv_list(options)
|
|
818
|
+
return merge_options(base, overlay)
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
# ---------------------------------------------------------------------------
|
|
822
|
+
# NemoFunction CLI — two verbs (run / submit), no `explain`
|
|
823
|
+
# ---------------------------------------------------------------------------
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
def add_function_commands(
|
|
827
|
+
cli_app: typer.Typer,
|
|
828
|
+
functions: dict[str, type[NemoFunction]],
|
|
829
|
+
*,
|
|
830
|
+
cli: NemoCLI | None = None,
|
|
831
|
+
) -> None:
|
|
832
|
+
"""Inject two-verb subcommand groups for each function into *cli_app*.
|
|
833
|
+
|
|
834
|
+
Each entry produces one :class:`typer.Typer` sub-group registered
|
|
835
|
+
under :attr:`~nemo_platform_plugin.function.NemoFunction.name` in a
|
|
836
|
+
``"Functions"`` rich help panel. The sub-group owns ``run`` and
|
|
837
|
+
``submit`` commands. There is no ``explain`` verb — functions
|
|
838
|
+
have a single ``spec_schema`` and ``--help`` is the introspection
|
|
839
|
+
surface.
|
|
840
|
+
|
|
841
|
+
Args:
|
|
842
|
+
cli_app: The plugin's :class:`typer.Typer` group to inject
|
|
843
|
+
sub-groups into. Typically the value returned by
|
|
844
|
+
:meth:`~nemo_platform_plugin.cli.NemoCLI.get_cli`.
|
|
845
|
+
functions: Mapping of entry-point key →
|
|
846
|
+
:class:`~nemo_platform_plugin.function.NemoFunction` subclass,
|
|
847
|
+
already filtered to the relevant plugin. Typically a
|
|
848
|
+
subset of the dict returned by
|
|
849
|
+
:func:`~nemo_platform_plugin.discovery.discover_functions`.
|
|
850
|
+
cli: Optional plugin :class:`~nemo_platform_plugin.cli.NemoCLI` instance. When
|
|
851
|
+
supplied, :meth:`~nemo_platform_plugin.cli.NemoCLI.update_function_cli` is
|
|
852
|
+
invoked once per function after default verb registration and
|
|
853
|
+
before the sub-group is mounted, giving the plugin a chance to
|
|
854
|
+
amend the auto-generated CLI surface.
|
|
855
|
+
"""
|
|
856
|
+
for fn_cls in functions.values():
|
|
857
|
+
_register_function_subgroup(cli_app, fn_cls, cli=cli)
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
def _register_function_subgroup(
|
|
861
|
+
cli_app: typer.Typer,
|
|
862
|
+
fn_cls: type[NemoFunction],
|
|
863
|
+
*,
|
|
864
|
+
cli: NemoCLI | None = None,
|
|
865
|
+
) -> None:
|
|
866
|
+
"""Register a ``<fn-name>`` sub-group with run / submit verbs."""
|
|
867
|
+
fn_group = typer.Typer(
|
|
868
|
+
name=fn_cls.name,
|
|
869
|
+
help=fn_cls.description or f"Manage the {fn_cls.name} function.",
|
|
870
|
+
no_args_is_help=False,
|
|
871
|
+
)
|
|
872
|
+
|
|
873
|
+
@fn_group.callback(invoke_without_command=True)
|
|
874
|
+
def _root(ctx: typer.Context) -> None: # pragma: no cover - trivial delegation
|
|
875
|
+
"""Stub callback — prints usage and exits 1 when no verb is given."""
|
|
876
|
+
if ctx.invoked_subcommand is None:
|
|
877
|
+
typer.echo(ctx.get_help())
|
|
878
|
+
raise typer.Exit(code=1)
|
|
879
|
+
|
|
880
|
+
_add_function_run_command(fn_group, fn_cls, cli=cli)
|
|
881
|
+
_add_function_submit_command(fn_group, fn_cls, cli=cli)
|
|
882
|
+
|
|
883
|
+
if cli is not None:
|
|
884
|
+
cli.update_function_cli(fn_cls, fn_group)
|
|
885
|
+
|
|
886
|
+
cli_app.add_typer(fn_group, name=fn_cls.name, rich_help_panel="Functions")
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
# ---- run --------------------------------------------------------- #
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def _add_function_run_command(
|
|
893
|
+
group: typer.Typer,
|
|
894
|
+
fn_cls: type[NemoFunction],
|
|
895
|
+
*,
|
|
896
|
+
cli: NemoCLI | None = None,
|
|
897
|
+
) -> None:
|
|
898
|
+
"""Register the ``run`` verb. Generates per-field flags from ``spec_schema``.
|
|
899
|
+
|
|
900
|
+
Each scalar leaf in ``spec_schema`` becomes a Typer option named
|
|
901
|
+
after its dotted path, kebab-cased per segment (``--name``,
|
|
902
|
+
``--target.url``). Precedence at runtime is ``--spec-file`` (base)
|
|
903
|
+
→ ``--spec`` JSON (overlay) → per-field flags (top overlay).
|
|
904
|
+
Validation happens after the merge — surfaces a single
|
|
905
|
+
``ValidationError`` for the merged spec rather than per-overlay.
|
|
906
|
+
|
|
907
|
+
When *cli* supplies a renderer via ``get_function_renderer(verb="run")``
|
|
908
|
+
(and ``--output-format json`` is not set), the renderer's lifecycle
|
|
909
|
+
drives the streamed output instead of the default per-frame echo.
|
|
910
|
+
"""
|
|
911
|
+
leaves = walk_spec_leaves(fn_cls.spec_schema, reserved=_FN_RUN_RESERVED_FLAGS)
|
|
912
|
+
|
|
913
|
+
def _run(typer_ctx: typer.Context, **kwargs: object) -> None:
|
|
914
|
+
original_kwargs = dict(kwargs)
|
|
915
|
+
spec_str: str = cast(str, kwargs.pop("spec", "{}"))
|
|
916
|
+
spec_file: Path | None = cast("Path | None", kwargs.pop("spec_file", None))
|
|
917
|
+
workspace: str = cast(str, kwargs.pop("workspace", "default"))
|
|
918
|
+
|
|
919
|
+
base = _load_spec(spec_str, spec_file)
|
|
920
|
+
overlay = build_overlay(leaves, kwargs, unset_sentinel=UNSET)
|
|
921
|
+
merged = deep_merge(base, overlay)
|
|
922
|
+
try:
|
|
923
|
+
spec_obj = fn_cls.spec_schema.model_validate(merged, context={"is_local": True})
|
|
924
|
+
except ValidationError as exc:
|
|
925
|
+
typer.echo(f"Error: invalid spec for {fn_cls.name}: {exc}", err=True)
|
|
926
|
+
raise typer.Exit(code=1) from exc
|
|
927
|
+
|
|
928
|
+
sdk, async_sdk = _resolve_local_cli_sdks(typer_ctx)
|
|
929
|
+
ctx = FunctionContext(workspace=workspace)
|
|
930
|
+
renderer_cls: type[CLIRenderer] | None = None
|
|
931
|
+
if cli is not None and not _output_format_is_json(typer_ctx):
|
|
932
|
+
renderer_cls = cli.get_function_renderer(fn_cls, verb="run")
|
|
933
|
+
try:
|
|
934
|
+
outcome = asyncio.run(
|
|
935
|
+
_invoke_function_locally(
|
|
936
|
+
fn_cls,
|
|
937
|
+
spec_obj,
|
|
938
|
+
ctx,
|
|
939
|
+
sdk=sdk,
|
|
940
|
+
async_sdk=async_sdk,
|
|
941
|
+
renderer_cls=renderer_cls,
|
|
942
|
+
cli_kwargs=original_kwargs,
|
|
943
|
+
)
|
|
944
|
+
)
|
|
945
|
+
except LocalRunError as exc:
|
|
946
|
+
typer.echo(f"Error: {exc}", err=True)
|
|
947
|
+
raise typer.Exit(code=1) from exc
|
|
948
|
+
except KeyboardInterrupt as exc: # pragma: no cover - terminal-only
|
|
949
|
+
typer.echo("Interrupted.", err=True)
|
|
950
|
+
raise typer.Exit(code=130) from exc
|
|
951
|
+
|
|
952
|
+
# Fire on_complete *outside* asyncio.run so renderers using event-loop-bound
|
|
953
|
+
# libraries (e.g. prompt_toolkit's Application.run for an interactive
|
|
954
|
+
# record browser) don't collide with the loop we just tore down.
|
|
955
|
+
if outcome is not None:
|
|
956
|
+
renderer, rctx = outcome
|
|
957
|
+
renderer.on_complete(ctx=rctx)
|
|
958
|
+
|
|
959
|
+
help_text = f"Run {fn_cls.name} locally, in-process."
|
|
960
|
+
epilog = build_epilog(schema=fn_cls.spec_schema, leaves=leaves, kind="Function")
|
|
961
|
+
_run.__signature__ = _build_function_run_signature(leaves) # type: ignore[attr-defined]
|
|
962
|
+
group.command(name="run", help=help_text, epilog=epilog)(_run)
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
def _build_function_run_signature(leaves: list[SpecLeafField]) -> inspect.Signature:
|
|
966
|
+
"""Compose the synthetic signature for the function ``run`` verb."""
|
|
967
|
+
static_params = [
|
|
968
|
+
# ``typer.Context`` is auto-injected by Click via ``pass_context``,
|
|
969
|
+
# which passes it as the first positional argument. Hand-built
|
|
970
|
+
# rather than via ``kw()`` because ``kw()`` only emits
|
|
971
|
+
# ``KEYWORD_ONLY`` params (which Click's positional injection
|
|
972
|
+
# would reject).
|
|
973
|
+
inspect.Parameter(
|
|
974
|
+
"typer_ctx",
|
|
975
|
+
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
976
|
+
annotation=typer.Context,
|
|
977
|
+
),
|
|
978
|
+
kw(
|
|
979
|
+
"spec",
|
|
980
|
+
str,
|
|
981
|
+
typer.Option(
|
|
982
|
+
"{}",
|
|
983
|
+
"--spec",
|
|
984
|
+
help="Spec as a JSON string.",
|
|
985
|
+
rich_help_panel=_PANEL_SPEC_SOURCE,
|
|
986
|
+
),
|
|
987
|
+
),
|
|
988
|
+
kw(
|
|
989
|
+
"spec_file",
|
|
990
|
+
Optional[Path],
|
|
991
|
+
typer.Option(
|
|
992
|
+
None,
|
|
993
|
+
"--spec-file",
|
|
994
|
+
help="Path to a YAML or JSON spec file (used as base; per-flag values override).",
|
|
995
|
+
rich_help_panel=_PANEL_SPEC_SOURCE,
|
|
996
|
+
),
|
|
997
|
+
),
|
|
998
|
+
kw(
|
|
999
|
+
"workspace",
|
|
1000
|
+
str,
|
|
1001
|
+
typer.Option(
|
|
1002
|
+
"default",
|
|
1003
|
+
"--workspace",
|
|
1004
|
+
help="Workspace identity passed to the function as ctx.workspace.",
|
|
1005
|
+
rich_help_panel=_PANEL_SPEC_SOURCE,
|
|
1006
|
+
),
|
|
1007
|
+
),
|
|
1008
|
+
]
|
|
1009
|
+
return build_callback_signature(
|
|
1010
|
+
static_params,
|
|
1011
|
+
leaves,
|
|
1012
|
+
rich_help_panel=_PANEL_FUNCTION_SPEC,
|
|
1013
|
+
)
|
|
1014
|
+
|
|
1015
|
+
|
|
1016
|
+
async def _invoke_function_locally(
|
|
1017
|
+
fn_cls: type[NemoFunction],
|
|
1018
|
+
spec_obj: BaseModel,
|
|
1019
|
+
ctx: FunctionContext,
|
|
1020
|
+
*,
|
|
1021
|
+
sdk: object | None,
|
|
1022
|
+
async_sdk: object | None,
|
|
1023
|
+
renderer_cls: type[CLIRenderer] | None = None,
|
|
1024
|
+
cli_kwargs: Mapping[str, Any] | None = None,
|
|
1025
|
+
) -> tuple[CLIRenderer, RendererContext] | None:
|
|
1026
|
+
"""Call ``fn_cls().run(spec, ...)`` and print result(s) to stdout.
|
|
1027
|
+
|
|
1028
|
+
Mirrors :func:`~nemo_platform_plugin.run_dependencies.resolve_run_kwargs` for the SDK
|
|
1029
|
+
parameters: inject when the caller supplied an SDK, leave the parameter
|
|
1030
|
+
unbound (Python default applies) when the function's signature has a
|
|
1031
|
+
default to fall back on, and raise :class:`LocalRunError` only when
|
|
1032
|
+
the function declared a required SDK parameter that we can't satisfy.
|
|
1033
|
+
|
|
1034
|
+
When *renderer_cls* is supplied, drive the renderer's ``on_start`` +
|
|
1035
|
+
``on_frame`` around the streamed iterator and return ``(renderer, rctx)``
|
|
1036
|
+
so the caller can fire ``on_complete`` *after* :func:`asyncio.run`
|
|
1037
|
+
returns — see :func:`_drive_async_renderer`. Renderers don't apply to
|
|
1038
|
+
non-streaming returns — those still echo via
|
|
1039
|
+
:func:`_format_value_for_stdout` and this function returns ``None``.
|
|
1040
|
+
"""
|
|
1041
|
+
instance = fn_cls()
|
|
1042
|
+
run_params = fn_cls.run_signature().parameters
|
|
1043
|
+
kwargs: dict[str, Any] = {}
|
|
1044
|
+
if "ctx" in run_params:
|
|
1045
|
+
kwargs["ctx"] = ctx
|
|
1046
|
+
if "is_local" in run_params:
|
|
1047
|
+
kwargs["is_local"] = True
|
|
1048
|
+
for param_name, value in (("sdk", sdk), ("async_sdk", async_sdk)):
|
|
1049
|
+
param = run_params.get(param_name)
|
|
1050
|
+
if param is None:
|
|
1051
|
+
continue
|
|
1052
|
+
if value is not None:
|
|
1053
|
+
kwargs[param_name] = value
|
|
1054
|
+
continue
|
|
1055
|
+
if param.default is inspect.Parameter.empty:
|
|
1056
|
+
raise LocalRunError(
|
|
1057
|
+
f"{fn_cls.__name__}.run requires a `{param_name}` argument; "
|
|
1058
|
+
f"configure your `nemo` CLI context (e.g. `nemo config use-context ...`) "
|
|
1059
|
+
f"or pass `{param_name}` to the local invoker."
|
|
1060
|
+
)
|
|
1061
|
+
|
|
1062
|
+
result = instance.run(spec_obj, **kwargs)
|
|
1063
|
+
if returns_async_iterator(result):
|
|
1064
|
+
if renderer_cls is not None:
|
|
1065
|
+
rctx = _make_renderer_context(
|
|
1066
|
+
cli_kwargs=cli_kwargs or {},
|
|
1067
|
+
verb="run",
|
|
1068
|
+
is_local=True,
|
|
1069
|
+
)
|
|
1070
|
+
renderer = await _drive_async_renderer(result, renderer_cls, rctx=rctx)
|
|
1071
|
+
return renderer, rctx
|
|
1072
|
+
async for frame in result:
|
|
1073
|
+
typer.echo(_format_frame_for_stdout(frame))
|
|
1074
|
+
return
|
|
1075
|
+
awaited = await result
|
|
1076
|
+
typer.echo(_format_value_for_stdout(awaited))
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
def _resolve_local_cli_sdks(
|
|
1080
|
+
typer_ctx: typer.Context,
|
|
1081
|
+
) -> tuple[object | None, object | None]:
|
|
1082
|
+
"""Build sync + async SDKs from a CLI-like context, if present."""
|
|
1083
|
+
state = typer_ctx.obj
|
|
1084
|
+
if not state:
|
|
1085
|
+
return None, None
|
|
1086
|
+
sdk = state.get_client() if hasattr(state, "get_client") else None
|
|
1087
|
+
async_sdk = state.get_async_client() if hasattr(state, "get_async_client") else None
|
|
1088
|
+
return sdk, async_sdk
|
|
1089
|
+
|
|
1090
|
+
|
|
1091
|
+
# ---- submit ------------------------------------------------------ #
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
def _add_function_submit_command(
|
|
1095
|
+
group: typer.Typer,
|
|
1096
|
+
fn_cls: type[NemoFunction],
|
|
1097
|
+
*,
|
|
1098
|
+
cli: NemoCLI | None = None,
|
|
1099
|
+
) -> None:
|
|
1100
|
+
"""Register the ``submit`` verb. Generates per-field flags + static submit flags.
|
|
1101
|
+
|
|
1102
|
+
When *cli* supplies a renderer via ``get_function_renderer(verb="submit")``
|
|
1103
|
+
(and ``--output-format json`` is not set), the renderer's lifecycle drives
|
|
1104
|
+
the streamed NDJSON response instead of the default per-line echo.
|
|
1105
|
+
"""
|
|
1106
|
+
leaves = walk_spec_leaves(fn_cls.spec_schema, reserved=_FN_SUBMIT_RESERVED_FLAGS)
|
|
1107
|
+
|
|
1108
|
+
def _submit(typer_ctx: typer.Context, **kwargs: object) -> None:
|
|
1109
|
+
original_kwargs = dict(kwargs)
|
|
1110
|
+
spec_str: str = kwargs.pop("spec", "{}") # type: ignore[assignment]
|
|
1111
|
+
spec_file: Path | None = kwargs.pop("spec_file", None) # type: ignore[assignment]
|
|
1112
|
+
cluster: str | None = kwargs.pop("cluster", None) # type: ignore[assignment]
|
|
1113
|
+
base_url: str | None = kwargs.pop("base_url", None) # type: ignore[assignment]
|
|
1114
|
+
workspace: str = kwargs.pop("workspace", "default") # type: ignore[assignment]
|
|
1115
|
+
request_id: str | None = kwargs.pop("request_id", None) # type: ignore[assignment]
|
|
1116
|
+
|
|
1117
|
+
base = _load_spec(spec_str, spec_file)
|
|
1118
|
+
overlay = build_overlay(leaves, kwargs, unset_sentinel=UNSET)
|
|
1119
|
+
spec_data = deep_merge(base, overlay)
|
|
1120
|
+
try:
|
|
1121
|
+
fn_cls.spec_schema.model_validate(spec_data)
|
|
1122
|
+
except ValidationError as exc:
|
|
1123
|
+
typer.echo(f"Error: invalid spec for {fn_cls.name}: {exc}", err=True)
|
|
1124
|
+
raise typer.Exit(code=1) from exc
|
|
1125
|
+
|
|
1126
|
+
url = _build_function_submit_url(
|
|
1127
|
+
typer_ctx,
|
|
1128
|
+
fn_cls,
|
|
1129
|
+
base_url=base_url,
|
|
1130
|
+
cluster=cluster,
|
|
1131
|
+
workspace=workspace,
|
|
1132
|
+
)
|
|
1133
|
+
headers: dict[str, str] = {}
|
|
1134
|
+
if request_id is not None:
|
|
1135
|
+
headers["X-Request-ID"] = request_id
|
|
1136
|
+
|
|
1137
|
+
renderer_cls: type[CLIRenderer] | None = None
|
|
1138
|
+
if cli is not None and not _output_format_is_json(typer_ctx):
|
|
1139
|
+
renderer_cls = cli.get_function_renderer(fn_cls, verb="submit")
|
|
1140
|
+
|
|
1141
|
+
try:
|
|
1142
|
+
_post_function_submit(
|
|
1143
|
+
url,
|
|
1144
|
+
spec_data,
|
|
1145
|
+
headers=headers,
|
|
1146
|
+
renderer_cls=renderer_cls,
|
|
1147
|
+
cli_kwargs=original_kwargs,
|
|
1148
|
+
)
|
|
1149
|
+
except httpx.HTTPStatusError as exc:
|
|
1150
|
+
print_http_status_error(exc, action=f"submit {fn_cls.name}")
|
|
1151
|
+
raise typer.Exit(code=2) from exc
|
|
1152
|
+
except httpx.RequestError as exc:
|
|
1153
|
+
print_http_request_error(exc, action=f"submit {fn_cls.name}")
|
|
1154
|
+
raise typer.Exit(code=2) from exc
|
|
1155
|
+
except httpx.HTTPError as exc:
|
|
1156
|
+
typer.echo(f"Error: submit {fn_cls.name} failed: {exc}", err=True)
|
|
1157
|
+
raise typer.Exit(code=2) from exc
|
|
1158
|
+
|
|
1159
|
+
help_text = f"Submit {fn_cls.name} over HTTP."
|
|
1160
|
+
epilog = build_epilog(schema=fn_cls.spec_schema, leaves=leaves, kind="Function")
|
|
1161
|
+
_submit.__signature__ = _build_function_submit_signature(leaves) # type: ignore[attr-defined]
|
|
1162
|
+
group.command(name="submit", help=help_text, epilog=epilog)(_submit)
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
def _build_function_submit_signature(leaves: list[SpecLeafField]) -> inspect.Signature:
|
|
1166
|
+
"""Compose the synthetic signature for the function ``submit`` verb."""
|
|
1167
|
+
static_params = [
|
|
1168
|
+
inspect.Parameter(
|
|
1169
|
+
"typer_ctx",
|
|
1170
|
+
kind=inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
1171
|
+
annotation=typer.Context,
|
|
1172
|
+
),
|
|
1173
|
+
kw(
|
|
1174
|
+
"spec",
|
|
1175
|
+
str,
|
|
1176
|
+
typer.Option(
|
|
1177
|
+
"{}",
|
|
1178
|
+
"--spec",
|
|
1179
|
+
help="Spec as a JSON string.",
|
|
1180
|
+
rich_help_panel=_PANEL_SPEC_SOURCE,
|
|
1181
|
+
),
|
|
1182
|
+
),
|
|
1183
|
+
kw(
|
|
1184
|
+
"spec_file",
|
|
1185
|
+
Optional[Path],
|
|
1186
|
+
typer.Option(
|
|
1187
|
+
None,
|
|
1188
|
+
"--spec-file",
|
|
1189
|
+
help="Path to a YAML or JSON spec file (used as base; per-flag values override).",
|
|
1190
|
+
rich_help_panel=_PANEL_SPEC_SOURCE,
|
|
1191
|
+
),
|
|
1192
|
+
),
|
|
1193
|
+
kw(
|
|
1194
|
+
"cluster",
|
|
1195
|
+
Optional[str],
|
|
1196
|
+
typer.Option(
|
|
1197
|
+
None,
|
|
1198
|
+
"--cluster",
|
|
1199
|
+
help="Configured cluster name to resolve via the NeMo CLI config.",
|
|
1200
|
+
rich_help_panel=_PANEL_SUBMISSION,
|
|
1201
|
+
),
|
|
1202
|
+
),
|
|
1203
|
+
kw(
|
|
1204
|
+
"base_url",
|
|
1205
|
+
Optional[str],
|
|
1206
|
+
typer.Option(
|
|
1207
|
+
None,
|
|
1208
|
+
"--base-url",
|
|
1209
|
+
help=("Explicit plugin-service base URL (overrides --cluster and all other submit host resolution)."),
|
|
1210
|
+
rich_help_panel=_PANEL_SUBMISSION,
|
|
1211
|
+
),
|
|
1212
|
+
),
|
|
1213
|
+
kw(
|
|
1214
|
+
"workspace",
|
|
1215
|
+
str,
|
|
1216
|
+
typer.Option(
|
|
1217
|
+
"default",
|
|
1218
|
+
"--workspace",
|
|
1219
|
+
help="Workspace path segment used in the submit URL.",
|
|
1220
|
+
rich_help_panel=_PANEL_SUBMISSION,
|
|
1221
|
+
),
|
|
1222
|
+
),
|
|
1223
|
+
kw(
|
|
1224
|
+
"request_id",
|
|
1225
|
+
Optional[str],
|
|
1226
|
+
typer.Option(
|
|
1227
|
+
None,
|
|
1228
|
+
"--request-id",
|
|
1229
|
+
help="Set the X-Request-ID header (echoed back in ctx.request_id).",
|
|
1230
|
+
rich_help_panel=_PANEL_SUBMISSION,
|
|
1231
|
+
),
|
|
1232
|
+
),
|
|
1233
|
+
]
|
|
1234
|
+
return build_callback_signature(
|
|
1235
|
+
static_params,
|
|
1236
|
+
leaves,
|
|
1237
|
+
rich_help_panel=_PANEL_FUNCTION_SPEC,
|
|
1238
|
+
)
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
def _post_function_submit(
|
|
1242
|
+
url: str,
|
|
1243
|
+
body: dict,
|
|
1244
|
+
*,
|
|
1245
|
+
headers: dict[str, str],
|
|
1246
|
+
timeout: float = 30.0,
|
|
1247
|
+
renderer_cls: type[CLIRenderer] | None = None,
|
|
1248
|
+
cli_kwargs: Mapping[str, Any] | None = None,
|
|
1249
|
+
) -> None:
|
|
1250
|
+
"""POST *body* to *url* and stream/print the response.
|
|
1251
|
+
|
|
1252
|
+
Streams NDJSON line by line when the server returns
|
|
1253
|
+
:data:`NDJSON_MEDIA_TYPE`; otherwise pretty-prints the JSON body.
|
|
1254
|
+
Surfaces 4xx / 5xx as ``httpx.HTTPStatusError`` for the caller to
|
|
1255
|
+
translate into a Typer exit.
|
|
1256
|
+
|
|
1257
|
+
When *renderer_cls* is supplied, the renderer drives the per-line NDJSON
|
|
1258
|
+
iteration through its lifecycle. Each parsed JSON line is passed to
|
|
1259
|
+
:meth:`CLIRenderer.on_frame`. The non-NDJSON fallback path is unchanged.
|
|
1260
|
+
"""
|
|
1261
|
+
logger.debug("submit %s", url)
|
|
1262
|
+
with httpx.Client(timeout=timeout) as client:
|
|
1263
|
+
with client.stream("POST", url, json=body, headers=headers) as response:
|
|
1264
|
+
if response.status_code >= 400:
|
|
1265
|
+
# Buffer the body so the caller's error formatter
|
|
1266
|
+
# can read ``exc.response.text`` without raising
|
|
1267
|
+
# ``ResponseNotRead`` — ``client.stream`` opens the
|
|
1268
|
+
# response unbuffered, and the ``with`` block closes
|
|
1269
|
+
# the stream before the caller sees the exception.
|
|
1270
|
+
response.read()
|
|
1271
|
+
response.raise_for_status()
|
|
1272
|
+
content_type = response.headers.get("content-type", "")
|
|
1273
|
+
if NDJSON_MEDIA_TYPE in content_type:
|
|
1274
|
+
if renderer_cls is not None:
|
|
1275
|
+
rctx = _make_renderer_context(
|
|
1276
|
+
cli_kwargs=cli_kwargs or {},
|
|
1277
|
+
verb="submit",
|
|
1278
|
+
is_local=False,
|
|
1279
|
+
)
|
|
1280
|
+
renderer = _drive_sync_renderer(response.iter_lines(), renderer_cls, rctx=rctx)
|
|
1281
|
+
renderer.on_complete(ctx=rctx)
|
|
1282
|
+
return
|
|
1283
|
+
for line in response.iter_lines():
|
|
1284
|
+
if not line:
|
|
1285
|
+
continue
|
|
1286
|
+
typer.echo(_pretty_print_jsonl_line(line))
|
|
1287
|
+
return
|
|
1288
|
+
response.read()
|
|
1289
|
+
try:
|
|
1290
|
+
typer.echo(json.dumps(response.json(), indent=2))
|
|
1291
|
+
except ValueError:
|
|
1292
|
+
typer.echo(response.text)
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
# ---- helpers ----------------------------------------------------- #
|
|
1296
|
+
|
|
1297
|
+
|
|
1298
|
+
def _resolve_cluster_name_to_base_url(cluster_name: str) -> str:
|
|
1299
|
+
"""Resolve a configured cluster name to its base URL."""
|
|
1300
|
+
from nemo_platform.config.config import Config
|
|
1301
|
+
|
|
1302
|
+
config = Config.load()
|
|
1303
|
+
for cluster in config.get_config_file().clusters:
|
|
1304
|
+
if cluster.name == cluster_name:
|
|
1305
|
+
return str(cluster.base_url)
|
|
1306
|
+
raise ValueError(
|
|
1307
|
+
f"Unknown cluster '{cluster_name}'. Use `nemo config view --all-contexts` to inspect configured clusters or pass `--base-url`."
|
|
1308
|
+
)
|
|
1309
|
+
|
|
1310
|
+
|
|
1311
|
+
def _resolve_submit_base_url(
|
|
1312
|
+
typer_ctx: typer.Context,
|
|
1313
|
+
*,
|
|
1314
|
+
base_url: str | None,
|
|
1315
|
+
cluster: str | None,
|
|
1316
|
+
) -> str:
|
|
1317
|
+
"""Resolve submit host precedence shared by function and job submit."""
|
|
1318
|
+
if base_url is not None:
|
|
1319
|
+
return base_url
|
|
1320
|
+
if cluster is not None:
|
|
1321
|
+
return _resolve_cluster_name_to_base_url(cluster)
|
|
1322
|
+
|
|
1323
|
+
state = typer_ctx.obj
|
|
1324
|
+
if state is not None and hasattr(state, "get_base_url"):
|
|
1325
|
+
resolved = state.get_base_url(default=None)
|
|
1326
|
+
if resolved:
|
|
1327
|
+
return cast(str, resolved)
|
|
1328
|
+
|
|
1329
|
+
return os.environ.get(_DEFAULT_BASE_URL_ENV_VAR) or _DEFAULT_BASE_URL
|
|
1330
|
+
|
|
1331
|
+
|
|
1332
|
+
def _build_function_submit_url(
|
|
1333
|
+
typer_ctx: typer.Context,
|
|
1334
|
+
fn_cls: type[NemoFunction],
|
|
1335
|
+
*,
|
|
1336
|
+
base_url: str | None,
|
|
1337
|
+
cluster: str | None,
|
|
1338
|
+
workspace: str,
|
|
1339
|
+
) -> str:
|
|
1340
|
+
"""Resolve the full POST URL for *fn_cls*.
|
|
1341
|
+
|
|
1342
|
+
Precedence for the host: explicit ``--base-url`` > configured
|
|
1343
|
+
``--cluster`` > active context base URL > ``$NMP_BASE_URL`` >
|
|
1344
|
+
localhost. The path is the canonical
|
|
1345
|
+
``/apis/<plugin>/v2/workspaces/<ws>/<name>``, with
|
|
1346
|
+
:attr:`NemoFunction.endpoint` substituting the trailing segment
|
|
1347
|
+
when set.
|
|
1348
|
+
"""
|
|
1349
|
+
host = _resolve_submit_base_url(typer_ctx, base_url=base_url, cluster=cluster)
|
|
1350
|
+
api_segment = _api_segment_for_function(fn_cls)
|
|
1351
|
+
trailing = (fn_cls.endpoint or DEFAULT_FUNCTION_PATH).replace("{name}", fn_cls.name)
|
|
1352
|
+
if not trailing.startswith("/"):
|
|
1353
|
+
trailing = "/" + trailing
|
|
1354
|
+
return f"{host.rstrip('/')}/apis/{api_segment}/v2/workspaces/{workspace}{trailing}"
|
|
1355
|
+
|
|
1356
|
+
|
|
1357
|
+
def _api_segment_for_function(fn_cls: type[NemoFunction]) -> str:
|
|
1358
|
+
"""Plugin-name segment of the URL for *fn_cls*.
|
|
1359
|
+
|
|
1360
|
+
Functions register under the ``nemo.functions`` entry-point group
|
|
1361
|
+
keyed as ``<plugin>.<function>``, and the platform mounts their
|
|
1362
|
+
routes under that ``<plugin>`` segment. The authoritative source of
|
|
1363
|
+
truth is therefore the entry-point key — not the Python module
|
|
1364
|
+
path. We resolve it via :func:`~nemo_platform_plugin.discovery.discover_functions`
|
|
1365
|
+
so a plugin registered as ``my-plugin.greet`` correctly maps to
|
|
1366
|
+
``/apis/my-plugin/...``, even though its package directory is
|
|
1367
|
+
``nemo_my_plugin/`` (which the old module-name heuristic would
|
|
1368
|
+
have collapsed to ``my`` and 404'd against).
|
|
1369
|
+
|
|
1370
|
+
When the function class isn't installed as an entry point — unit
|
|
1371
|
+
tests with inline classes, ad-hoc invocations from a checkout —
|
|
1372
|
+
fall back to deriving the segment from the top-level package name
|
|
1373
|
+
with the ``nemo_`` prefix stripped and underscores converted to
|
|
1374
|
+
dashes. This mirrors :func:`nemo_platform_plugin.scheduler._api_segment_for`
|
|
1375
|
+
so the two CLIs stay consistent. Crucially, the fallback no longer
|
|
1376
|
+
strips a trailing ``_plugin`` from the module name: a plugin whose
|
|
1377
|
+
actual key happens to be ``my-plugin`` would be silently rewritten
|
|
1378
|
+
to ``my``.
|
|
1379
|
+
"""
|
|
1380
|
+
from nemo_platform_plugin.discovery import discover_functions
|
|
1381
|
+
|
|
1382
|
+
try:
|
|
1383
|
+
registered = discover_functions()
|
|
1384
|
+
except Exception:
|
|
1385
|
+
registered = {}
|
|
1386
|
+
for key, registered_cls in registered.items():
|
|
1387
|
+
if registered_cls is fn_cls and "." in key:
|
|
1388
|
+
return key.split(".", 1)[0]
|
|
1389
|
+
|
|
1390
|
+
module = fn_cls.__module__.split(".")[0]
|
|
1391
|
+
if module.startswith("nemo_"):
|
|
1392
|
+
module = module[len("nemo_") :]
|
|
1393
|
+
return module.replace("_", "-")
|
|
1394
|
+
|
|
1395
|
+
|
|
1396
|
+
def _format_frame_for_stdout(frame: Any) -> str:
|
|
1397
|
+
"""Render a streaming frame as a single line of pretty JSON.
|
|
1398
|
+
|
|
1399
|
+
Pydantic models go through ``model_dump_json``; dicts and lists
|
|
1400
|
+
through ``json.dumps``; anything else through ``str()``. The
|
|
1401
|
+
output is one line per frame so consumers can pipe to ``jq -c``
|
|
1402
|
+
or grep for ``kind`` discriminators without a parser.
|
|
1403
|
+
"""
|
|
1404
|
+
if isinstance(frame, BaseModel):
|
|
1405
|
+
return frame.model_dump_json()
|
|
1406
|
+
if isinstance(frame, (dict, list)):
|
|
1407
|
+
return json.dumps(frame, default=str)
|
|
1408
|
+
return str(frame)
|
|
1409
|
+
|
|
1410
|
+
|
|
1411
|
+
def _format_value_for_stdout(value: Any) -> str:
|
|
1412
|
+
"""Render a non-streaming return value as multi-line pretty JSON."""
|
|
1413
|
+
if isinstance(value, BaseModel):
|
|
1414
|
+
return value.model_dump_json(indent=2)
|
|
1415
|
+
if isinstance(value, (dict, list)):
|
|
1416
|
+
return json.dumps(value, indent=2, default=str)
|
|
1417
|
+
return str(value)
|
|
1418
|
+
|
|
1419
|
+
|
|
1420
|
+
def _pretty_print_jsonl_line(line: str) -> str:
|
|
1421
|
+
"""Best-effort pretty-print for an NDJSON line.
|
|
1422
|
+
|
|
1423
|
+
Falls back to the raw line when the line isn't valid JSON so
|
|
1424
|
+
operators see exactly what the server emitted (useful for
|
|
1425
|
+
debugging non-conforming streams). Streams are kept one frame per
|
|
1426
|
+
line — pretty-printing within a frame would defeat the JSONL
|
|
1427
|
+
contract for downstream piping.
|
|
1428
|
+
"""
|
|
1429
|
+
try:
|
|
1430
|
+
parsed = json.loads(line)
|
|
1431
|
+
except json.JSONDecodeError:
|
|
1432
|
+
return line
|
|
1433
|
+
return json.dumps(parsed)
|