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,1947 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
"""Interactive setup wizard for NeMo Platform.
|
|
5
|
+
|
|
6
|
+
Full onboarding flow: start local services, register an inference provider,
|
|
7
|
+
install AI agent skills, and optionally deploy a demo agent.
|
|
8
|
+
Supports both interactive and non-interactive (``--auto``) modes.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import importlib.util
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
import subprocess
|
|
17
|
+
import time
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from importlib.resources import files
|
|
20
|
+
from importlib.resources.abc import Traversable
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Annotated
|
|
23
|
+
from urllib.parse import urlparse
|
|
24
|
+
|
|
25
|
+
import httpx
|
|
26
|
+
import typer
|
|
27
|
+
import yaml as _yaml
|
|
28
|
+
from nemo_platform import NeMoPlatform
|
|
29
|
+
from nmp.common.config import nmp_user_data_dir
|
|
30
|
+
from rich import box
|
|
31
|
+
from rich.console import Console
|
|
32
|
+
from rich.panel import Panel
|
|
33
|
+
|
|
34
|
+
from nemo_platform.cli.commands.skills.base import Scope, Skill
|
|
35
|
+
from nemo_platform.cli.commands.skills.registry import get_installer, load_skills
|
|
36
|
+
from nemo_platform.cli.core.context import CLIContext
|
|
37
|
+
from nemo_platform.cli.core.errors import handle_errors
|
|
38
|
+
from nemo_platform.config.config import Config
|
|
39
|
+
from nemo_platform.config.models import ConfigFile, ConfigParams, LocalServicesConfig
|
|
40
|
+
from nemo_platform.ui.prompts import (
|
|
41
|
+
UserCancelled,
|
|
42
|
+
is_interactive,
|
|
43
|
+
non_empty_validator,
|
|
44
|
+
prompt_choice,
|
|
45
|
+
prompt_confirm,
|
|
46
|
+
prompt_multiselect,
|
|
47
|
+
prompt_password,
|
|
48
|
+
prompt_select,
|
|
49
|
+
prompt_text,
|
|
50
|
+
provider_name_validator,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
logger = logging.getLogger(__name__)
|
|
54
|
+
console = Console(stderr=True)
|
|
55
|
+
|
|
56
|
+
CHECK = "[green]✓[/green]"
|
|
57
|
+
CROSS = "[red]✗[/red]"
|
|
58
|
+
WARN = "[yellow]![/yellow]"
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
# Known provider catalog
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass(frozen=True)
|
|
66
|
+
class KnownProvider:
|
|
67
|
+
"""A well-known inference provider with pre-configured connection details."""
|
|
68
|
+
|
|
69
|
+
name: str
|
|
70
|
+
label: str
|
|
71
|
+
description: str
|
|
72
|
+
host_url: str
|
|
73
|
+
auth_header_format: str | None = None
|
|
74
|
+
default_extra_headers: dict[str, str] | None = None
|
|
75
|
+
env_var: str | None = None
|
|
76
|
+
requires_api_key: bool = True
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
KNOWN_PROVIDERS: tuple[KnownProvider, ...] = (
|
|
80
|
+
KnownProvider(
|
|
81
|
+
name="nvidia-build",
|
|
82
|
+
label="NVIDIA Build",
|
|
83
|
+
description="NVIDIA-hosted models via build.nvidia.com",
|
|
84
|
+
host_url="https://integrate.api.nvidia.com",
|
|
85
|
+
env_var="NVIDIA_API_KEY",
|
|
86
|
+
),
|
|
87
|
+
KnownProvider(
|
|
88
|
+
name="openai",
|
|
89
|
+
label="OpenAI",
|
|
90
|
+
description="GPT-4.1, o3, o4-mini",
|
|
91
|
+
host_url="https://api.openai.com/v1",
|
|
92
|
+
env_var="OPENAI_API_KEY",
|
|
93
|
+
),
|
|
94
|
+
KnownProvider(
|
|
95
|
+
name="anthropic",
|
|
96
|
+
label="Anthropic",
|
|
97
|
+
description="Claude Opus 4, Sonnet 4, Haiku",
|
|
98
|
+
host_url="https://api.anthropic.com",
|
|
99
|
+
auth_header_format="X-Api-Key: {{ auth_secret }}",
|
|
100
|
+
default_extra_headers={"anthropic-version": "2023-06-01"},
|
|
101
|
+
env_var="ANTHROPIC_API_KEY",
|
|
102
|
+
),
|
|
103
|
+
KnownProvider(
|
|
104
|
+
name="google-gemini",
|
|
105
|
+
label="Google Gemini",
|
|
106
|
+
description="Gemini 2.5 Flash, Pro",
|
|
107
|
+
host_url="https://generativelanguage.googleapis.com/v1beta/openai",
|
|
108
|
+
env_var="GEMINI_API_KEY",
|
|
109
|
+
),
|
|
110
|
+
KnownProvider(
|
|
111
|
+
name="ollama",
|
|
112
|
+
label="Ollama (local)",
|
|
113
|
+
description="Local models, no API key needed",
|
|
114
|
+
host_url="http://localhost:11434/v1",
|
|
115
|
+
requires_api_key=False,
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
_KNOWN_PROVIDERS_BY_NAME: dict[str, KnownProvider] = {p.name: p for p in KNOWN_PROVIDERS}
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# Onboarding paths — shown after setup completes
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
_NEMO_DOCS_URL = "https://docs.nvidia.com/nemo/platform"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@dataclass(frozen=True)
|
|
130
|
+
class OnboardingPath:
|
|
131
|
+
"""A goal-oriented onboarding option shown at the end of interactive setup."""
|
|
132
|
+
|
|
133
|
+
value: str
|
|
134
|
+
label: str
|
|
135
|
+
skill_prompt: str
|
|
136
|
+
docs_url: str
|
|
137
|
+
note: str | None = None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
ONBOARDING_PATHS: tuple[OnboardingPath, ...] = (
|
|
141
|
+
OnboardingPath(
|
|
142
|
+
value="optimize",
|
|
143
|
+
label="Build and optimize agents",
|
|
144
|
+
skill_prompt="Build and optimize an agent using NeMo Platform",
|
|
145
|
+
docs_url=f"{_NEMO_DOCS_URL}/agents",
|
|
146
|
+
note="Open a coding agent session in your agent's project directory",
|
|
147
|
+
),
|
|
148
|
+
OnboardingPath(
|
|
149
|
+
value="explore",
|
|
150
|
+
label="Explore the platform",
|
|
151
|
+
skill_prompt="What can I do with NeMo Platform?",
|
|
152
|
+
docs_url=_NEMO_DOCS_URL,
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
_ONBOARDING_PATHS_BY_VALUE: dict[str, OnboardingPath] = {p.value: p for p in ONBOARDING_PATHS}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass(frozen=True)
|
|
160
|
+
class ProbeConfig:
|
|
161
|
+
"""How to probe a provider's auth-required endpoint for key validation."""
|
|
162
|
+
|
|
163
|
+
method: str
|
|
164
|
+
path: str
|
|
165
|
+
body: dict | None = None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
# NVIDIA's gateway routes by model name before checking auth, so a fake model
|
|
169
|
+
# returns 404 without ever validating the key. We use a real, stable model so
|
|
170
|
+
# the gateway reaches the auth layer and returns 401/403 for bad credentials.
|
|
171
|
+
_NVIDIA_BUILD_PROBE_MODEL = "meta/llama-3.1-8b-instruct"
|
|
172
|
+
|
|
173
|
+
_PROBE_CONFIGS: dict[str, ProbeConfig] = {
|
|
174
|
+
"nvidia-build": ProbeConfig(
|
|
175
|
+
"POST",
|
|
176
|
+
"v1/chat/completions",
|
|
177
|
+
{"model": _NVIDIA_BUILD_PROBE_MODEL, "messages": [], "max_tokens": 1},
|
|
178
|
+
),
|
|
179
|
+
"openai": ProbeConfig("GET", "models"),
|
|
180
|
+
"anthropic": ProbeConfig("GET", "v1/models"),
|
|
181
|
+
"google-gemini": ProbeConfig("GET", "models"),
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@dataclass(frozen=True)
|
|
186
|
+
class KeyValidationResult:
|
|
187
|
+
"""Outcome of an API key validation probe."""
|
|
188
|
+
|
|
189
|
+
passed: bool
|
|
190
|
+
message: str
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# Env vars probed during --auto mode, in priority order.
|
|
194
|
+
_AUTO_ENV_VARS: tuple[tuple[str, str], ...] = (
|
|
195
|
+
("NEMO_DEFAULT_INFERENCE_KEY", "NEMO_DEFAULT_INFERENCE_BASE_URL"),
|
|
196
|
+
("NVIDIA_API_KEY", ""),
|
|
197
|
+
("OPENAI_API_KEY", ""),
|
|
198
|
+
("ANTHROPIC_API_KEY", ""),
|
|
199
|
+
("GEMINI_API_KEY", ""),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
_KEY_VALIDATION_TIMEOUT = 10.0
|
|
203
|
+
_KEY_REJECTED_STATUS_CODES = (401, 403)
|
|
204
|
+
_KEY_REJECTED_MESSAGE = "API key validation failed. The provider rejected the credentials."
|
|
205
|
+
|
|
206
|
+
_MODEL_DISCOVERY_ROUND_SECONDS = 30
|
|
207
|
+
_MODEL_DISCOVERY_MAX_ROUNDS = 2
|
|
208
|
+
_MODEL_DISCOVERY_POLL_INTERVAL = 1
|
|
209
|
+
_SERVICE_STARTUP_TIMEOUT_SECONDS = 240
|
|
210
|
+
_SERVICE_STARTUP_POLL_INTERVAL = 0.5
|
|
211
|
+
_AGENT_DEPLOY_TIMEOUT_SECONDS = 120
|
|
212
|
+
_AGENT_DEPLOY_POLL_INTERVAL = 1
|
|
213
|
+
_AGENT_API_READINESS_TIMEOUT = 30
|
|
214
|
+
_AGENT_API_READINESS_POLL_INTERVAL = 1
|
|
215
|
+
_KILL_WAIT_TIMEOUT = 10
|
|
216
|
+
_CONTROLLER_HEALTH_RETRY_DELAY = 3.0
|
|
217
|
+
_POST_START_REACHABLE_RETRIES = 6
|
|
218
|
+
_POST_START_REACHABLE_DELAY = 2.0
|
|
219
|
+
|
|
220
|
+
_DEMO_AGENT_NAME = "calculator-agent"
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _pause(seconds: float) -> None:
|
|
224
|
+
time.sleep(seconds)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
# Filesystem markers that indicate which coding agents are in use.
|
|
228
|
+
_AGENT_MARKERS: tuple[tuple[str, str], ...] = (
|
|
229
|
+
("AGENTS.md", "codex"),
|
|
230
|
+
(".cursor", "cursor"),
|
|
231
|
+
(".opencode", "opencode"),
|
|
232
|
+
(".claude", "claude"),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# ---------------------------------------------------------------------------
|
|
237
|
+
# Helpers — platform reachability
|
|
238
|
+
# ---------------------------------------------------------------------------
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _bootstrap_config_if_missing(base_url: str, workspace: str) -> None:
|
|
242
|
+
"""Write a minimal cluster + context into the config file when one isn't seeded.
|
|
243
|
+
|
|
244
|
+
``nemo setup`` can run before any config is on disk (first-time install)
|
|
245
|
+
*or* with a partial config containing only ``local_services.data_dir``
|
|
246
|
+
written earlier in the same setup invocation by ``_save_data_dir``.
|
|
247
|
+
Later steps — in particular ``_save_default_model`` — call
|
|
248
|
+
``Config.write`` with *only* a ``default_model`` param. If there's no
|
|
249
|
+
cluster on disk at that point, ``ensure_context`` will fail with
|
|
250
|
+
``Cluster '<name>' does not exist and no base_url provided to create
|
|
251
|
+
it`` because it has no ``base_url`` to attach to a new cluster.
|
|
252
|
+
|
|
253
|
+
Calling this once after the platform is confirmed reachable seeds the
|
|
254
|
+
cluster + context so that all subsequent writes succeed. Idempotent:
|
|
255
|
+
if a cluster is already present, the call is a no-op. Any existing
|
|
256
|
+
``local_services`` block (e.g. the persisted data dir) is preserved.
|
|
257
|
+
"""
|
|
258
|
+
config_path = Config.get_default_config_path()
|
|
259
|
+
if config_path.exists():
|
|
260
|
+
try:
|
|
261
|
+
existing = Config.load(config_path=config_path).get_config_file()
|
|
262
|
+
except Exception:
|
|
263
|
+
# Unreadable existing config — fall through and rewrite.
|
|
264
|
+
logger.debug("Failed to load existing config; will reseed", exc_info=True)
|
|
265
|
+
else:
|
|
266
|
+
if existing.clusters:
|
|
267
|
+
# Cluster already present — assume bootstrap is done.
|
|
268
|
+
return
|
|
269
|
+
params: ConfigParams = {"base_url": base_url, "workspace": workspace}
|
|
270
|
+
Config.write(params)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _check_platform_reachable(base_url: str, timeout: float = 5.0) -> bool:
|
|
274
|
+
"""Return True if the platform health endpoint responds."""
|
|
275
|
+
try:
|
|
276
|
+
resp = httpx.get(f"{base_url.rstrip('/')}/health/ready", timeout=timeout)
|
|
277
|
+
return resp.status_code == 200
|
|
278
|
+
except Exception:
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _check_platform_reachable_with_retries(
|
|
283
|
+
base_url: str,
|
|
284
|
+
retries: int = _POST_START_REACHABLE_RETRIES,
|
|
285
|
+
delay: float = _POST_START_REACHABLE_DELAY,
|
|
286
|
+
) -> bool:
|
|
287
|
+
"""Check platform reachability with retries.
|
|
288
|
+
|
|
289
|
+
Right after startup the platform may briefly report ready then flip back
|
|
290
|
+
to not-ready while controllers begin heavy work (e.g. model reconciliation).
|
|
291
|
+
A single-shot check can hit this window and falsely report failure.
|
|
292
|
+
"""
|
|
293
|
+
for attempt in range(retries):
|
|
294
|
+
if _check_platform_reachable(base_url):
|
|
295
|
+
return True
|
|
296
|
+
if attempt < retries - 1:
|
|
297
|
+
_pause(delay)
|
|
298
|
+
return False
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _check_controller_health(base_url: str, timeout: float = 5.0) -> tuple[bool, str]:
|
|
302
|
+
"""Query ``/status`` and assess controller health.
|
|
303
|
+
|
|
304
|
+
Returns ``(True, "")`` when controllers are populated and all healthy.
|
|
305
|
+
Returns ``(False, detail)`` when unhealthy, unreachable, or empty after retry.
|
|
306
|
+
|
|
307
|
+
If ``controllers.status`` is empty on the first call (startup timing race),
|
|
308
|
+
waits ``_CONTROLLER_HEALTH_RETRY_DELAY`` seconds and retries once.
|
|
309
|
+
"""
|
|
310
|
+
for attempt in range(2):
|
|
311
|
+
try:
|
|
312
|
+
resp = httpx.get(f"{base_url.rstrip('/')}/status", timeout=timeout)
|
|
313
|
+
if resp.status_code != 200:
|
|
314
|
+
return False, f"Unexpected status {resp.status_code} from /status endpoint."
|
|
315
|
+
data = resp.json()
|
|
316
|
+
except httpx.RequestError:
|
|
317
|
+
return False, "Could not reach platform status endpoint."
|
|
318
|
+
except ValueError:
|
|
319
|
+
return False, "Invalid JSON from /status endpoint."
|
|
320
|
+
|
|
321
|
+
controllers = data.get("controllers") if isinstance(data, dict) else None
|
|
322
|
+
if not isinstance(controllers, dict):
|
|
323
|
+
return False, "Invalid /status payload (missing controllers)."
|
|
324
|
+
status_map = controllers.get("status")
|
|
325
|
+
if not isinstance(status_map, dict):
|
|
326
|
+
status_map = {}
|
|
327
|
+
|
|
328
|
+
if status_map:
|
|
329
|
+
unhealthy = [name for name, ok in status_map.items() if not ok]
|
|
330
|
+
if unhealthy:
|
|
331
|
+
return False, f"Unhealthy controllers: {', '.join(unhealthy)}"
|
|
332
|
+
return True, ""
|
|
333
|
+
|
|
334
|
+
if attempt == 0:
|
|
335
|
+
_pause(_CONTROLLER_HEALTH_RETRY_DELAY)
|
|
336
|
+
|
|
337
|
+
return False, "No controllers reported status. Controller threads may have crashed before registering."
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def _verify_platform_health(base_url: str) -> bool:
|
|
341
|
+
"""Final health gate before declaring setup complete.
|
|
342
|
+
|
|
343
|
+
Returns True if the platform is healthy (caller prints the success banner).
|
|
344
|
+
Returns False after printing red or yellow diagnostics to the console.
|
|
345
|
+
"""
|
|
346
|
+
ok, detail = _check_controller_health(base_url)
|
|
347
|
+
if ok:
|
|
348
|
+
return True
|
|
349
|
+
|
|
350
|
+
if "no controllers" in detail.lower():
|
|
351
|
+
console.print(f"\n{WARN} [yellow]Could not confirm controller health ({detail}).[/yellow]")
|
|
352
|
+
console.print(" Setup may have succeeded, but verify with:")
|
|
353
|
+
console.print(" [cyan]nemo services status[/cyan]")
|
|
354
|
+
else:
|
|
355
|
+
console.print(f"\n{CROSS} [red]Platform controllers are unhealthy.[/red]")
|
|
356
|
+
console.print(f" {detail}")
|
|
357
|
+
console.print()
|
|
358
|
+
console.print(" The models controller may have crashed during startup.")
|
|
359
|
+
console.print(" Check service logs with: [cyan]nemo services logs[/cyan]")
|
|
360
|
+
console.print()
|
|
361
|
+
console.print(" Try: [cyan]nemo services run[/cyan] (restart services)")
|
|
362
|
+
|
|
363
|
+
return False
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
def _provider_exists(client: NeMoPlatform, name: str, workspace: str) -> bool:
|
|
367
|
+
"""Return True if a provider with *name* already exists."""
|
|
368
|
+
try:
|
|
369
|
+
client.inference.providers.retrieve(name, workspace=workspace)
|
|
370
|
+
return True
|
|
371
|
+
except Exception:
|
|
372
|
+
return False
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def _secret_exists(client: NeMoPlatform, name: str, workspace: str) -> bool:
|
|
376
|
+
"""Return True if a secret with *name* already exists."""
|
|
377
|
+
try:
|
|
378
|
+
client.secrets.retrieve(name, workspace=workspace)
|
|
379
|
+
return True
|
|
380
|
+
except Exception:
|
|
381
|
+
return False
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _create_secret(client: NeMoPlatform, name: str, value: str, workspace: str) -> None:
|
|
385
|
+
client.secrets.create(name=name, value=value, workspace=workspace)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def _update_secret(client: NeMoPlatform, name: str, value: str, workspace: str) -> None:
|
|
389
|
+
client.secrets.update(name, value=value, workspace=workspace)
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _create_provider(
|
|
393
|
+
client: NeMoPlatform,
|
|
394
|
+
*,
|
|
395
|
+
name: str,
|
|
396
|
+
host_url: str,
|
|
397
|
+
secret_name: str | None,
|
|
398
|
+
workspace: str,
|
|
399
|
+
auth_header_format: str | None = None,
|
|
400
|
+
default_extra_headers: dict[str, str] | None = None,
|
|
401
|
+
) -> None:
|
|
402
|
+
kwargs: dict = {
|
|
403
|
+
"name": name,
|
|
404
|
+
"host_url": host_url,
|
|
405
|
+
"workspace": workspace,
|
|
406
|
+
}
|
|
407
|
+
if secret_name:
|
|
408
|
+
kwargs["api_key_secret_name"] = secret_name
|
|
409
|
+
if auth_header_format:
|
|
410
|
+
header_name, _, header_value = auth_header_format.partition(":")
|
|
411
|
+
if header_name and header_value:
|
|
412
|
+
kwargs["required_extra_headers"] = {header_name.strip(): header_value.strip()}
|
|
413
|
+
if default_extra_headers:
|
|
414
|
+
kwargs["default_extra_headers"] = default_extra_headers
|
|
415
|
+
client.inference.providers.create(**kwargs)
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def _update_provider(
|
|
419
|
+
client: NeMoPlatform,
|
|
420
|
+
*,
|
|
421
|
+
name: str,
|
|
422
|
+
host_url: str,
|
|
423
|
+
secret_name: str | None,
|
|
424
|
+
workspace: str,
|
|
425
|
+
default_extra_headers: dict[str, str] | None = None,
|
|
426
|
+
) -> None:
|
|
427
|
+
kwargs: dict = {
|
|
428
|
+
"host_url": host_url,
|
|
429
|
+
"workspace": workspace,
|
|
430
|
+
}
|
|
431
|
+
if secret_name:
|
|
432
|
+
kwargs["api_key_secret_name"] = secret_name
|
|
433
|
+
if default_extra_headers:
|
|
434
|
+
kwargs["default_extra_headers"] = default_extra_headers
|
|
435
|
+
client.inference.providers.update(name, **kwargs)
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
_PROVIDER_UNHEALTHY_STATUSES = frozenset({"ERROR", "LOST"})
|
|
439
|
+
_NON_COMPLIANT_MARKER = "Non-OpenAI compliant"
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def _wait_for_models(
|
|
443
|
+
client: NeMoPlatform,
|
|
444
|
+
provider_name: str,
|
|
445
|
+
workspace: str,
|
|
446
|
+
host_url: str = "",
|
|
447
|
+
round_seconds: int = _MODEL_DISCOVERY_ROUND_SECONDS,
|
|
448
|
+
max_rounds: int = _MODEL_DISCOVERY_MAX_ROUNDS,
|
|
449
|
+
) -> list[str]:
|
|
450
|
+
"""Poll until provider has at least one served model. Returns entity IDs.
|
|
451
|
+
|
|
452
|
+
Retries in rounds so the user sees progress rather than a long silence.
|
|
453
|
+
Checks provider status each poll and exits early if the provider is
|
|
454
|
+
flagged as non-compliant or unhealthy, so the user isn't left waiting
|
|
455
|
+
for models that will never arrive.
|
|
456
|
+
"""
|
|
457
|
+
start = time.monotonic()
|
|
458
|
+
for attempt in range(max_rounds):
|
|
459
|
+
deadline = time.monotonic() + round_seconds
|
|
460
|
+
with console.status("[bold cyan]Waiting for model discovery...") as status:
|
|
461
|
+
while time.monotonic() < deadline:
|
|
462
|
+
elapsed = int(time.monotonic() - start)
|
|
463
|
+
status.update(f"[bold cyan]Waiting for model discovery... ({elapsed}s)")
|
|
464
|
+
try:
|
|
465
|
+
provider = client.inference.providers.retrieve(provider_name, workspace=workspace)
|
|
466
|
+
served = getattr(provider, "served_models", None) or []
|
|
467
|
+
if served:
|
|
468
|
+
model_ids = [m.model_entity_id for m in served if getattr(m, "model_entity_id", None)]
|
|
469
|
+
if model_ids:
|
|
470
|
+
return model_ids
|
|
471
|
+
|
|
472
|
+
provider_status = getattr(provider, "status", None) or ""
|
|
473
|
+
provider_msg = getattr(provider, "status_message", None) or ""
|
|
474
|
+
|
|
475
|
+
if _NON_COMPLIANT_MARKER in provider_msg:
|
|
476
|
+
url_hint = f" ({host_url})" if host_url else ""
|
|
477
|
+
console.print(
|
|
478
|
+
f"\n {WARN} Provider '{provider_name}'{url_hint} returned a non-OpenAI "
|
|
479
|
+
f"compliant response from GET /v1/models."
|
|
480
|
+
)
|
|
481
|
+
console.print(" Check that the host URL points to an OpenAI-compatible API endpoint.")
|
|
482
|
+
console.print(
|
|
483
|
+
" The provider is still registered and usable for direct inference, "
|
|
484
|
+
"but automatic model discovery is disabled."
|
|
485
|
+
)
|
|
486
|
+
return []
|
|
487
|
+
|
|
488
|
+
if provider_status in _PROVIDER_UNHEALTHY_STATUSES:
|
|
489
|
+
url_hint = f" ({host_url})" if host_url else ""
|
|
490
|
+
console.print(f"\n {WARN} Provider '{provider_name}'{url_hint} is in {provider_status} state.")
|
|
491
|
+
if provider_msg:
|
|
492
|
+
console.print(f" {provider_msg}")
|
|
493
|
+
console.print(" Check the host URL and API key, then re-run [cyan]nemo setup[/cyan].")
|
|
494
|
+
return []
|
|
495
|
+
|
|
496
|
+
except Exception:
|
|
497
|
+
logger.debug("Model discovery poll for '%s' failed", provider_name, exc_info=True)
|
|
498
|
+
_pause(_MODEL_DISCOVERY_POLL_INTERVAL)
|
|
499
|
+
if attempt < max_rounds - 1:
|
|
500
|
+
console.print(f" {WARN} Models not available yet, retrying...")
|
|
501
|
+
return []
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def _get_all_model_entity_ids(client: NeMoPlatform, workspace: str) -> list[str]:
|
|
505
|
+
"""Return all model entity IDs across all providers."""
|
|
506
|
+
entity_ids: list[str] = []
|
|
507
|
+
try:
|
|
508
|
+
page = client.inference.providers.list(workspace=workspace)
|
|
509
|
+
for provider in page.data:
|
|
510
|
+
for model in getattr(provider, "served_models", None) or []:
|
|
511
|
+
if hasattr(model, "model_entity_id") and model.model_entity_id:
|
|
512
|
+
entity_ids.append(model.model_entity_id)
|
|
513
|
+
except Exception:
|
|
514
|
+
logger.debug("Failed to list model entity IDs", exc_info=True)
|
|
515
|
+
return sorted(set(entity_ids))
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _get_all_model_choices(client: NeMoPlatform, workspace: str) -> list[tuple[str, str]]:
|
|
519
|
+
"""Return picker choices as (entity_id, label) across all providers."""
|
|
520
|
+
choices: list[tuple[str, str]] = []
|
|
521
|
+
try:
|
|
522
|
+
page = client.inference.providers.list(workspace=workspace)
|
|
523
|
+
for provider in page.data:
|
|
524
|
+
provider_name = getattr(provider, "name", "unknown-provider")
|
|
525
|
+
for model in getattr(provider, "served_models", None) or []:
|
|
526
|
+
model_entity_id = getattr(model, "model_entity_id", None)
|
|
527
|
+
if model_entity_id:
|
|
528
|
+
label = f"{_display_model_name(model_entity_id)} ({provider_name})"
|
|
529
|
+
choices.append((model_entity_id, label))
|
|
530
|
+
except Exception:
|
|
531
|
+
logger.debug("Failed to list model choices", exc_info=True)
|
|
532
|
+
return sorted(set(choices), key=lambda item: item[1])
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def _display_model_name(model_entity_id: str) -> str:
|
|
536
|
+
"""Strip workspace prefix from a model entity ID for display."""
|
|
537
|
+
return model_entity_id.split("/", 1)[-1] if "/" in model_entity_id else model_entity_id
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def _resolve_provider_for_url(base_url: str) -> KnownProvider | None:
|
|
541
|
+
"""Find a known provider whose host_url matches *base_url*."""
|
|
542
|
+
normalized = base_url.rstrip("/")
|
|
543
|
+
for p in KNOWN_PROVIDERS:
|
|
544
|
+
if p.host_url.rstrip("/") == normalized:
|
|
545
|
+
return p
|
|
546
|
+
return None
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
# ---------------------------------------------------------------------------
|
|
550
|
+
# Service startup
|
|
551
|
+
# ---------------------------------------------------------------------------
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
def _load_persisted_data_dir() -> str | None:
|
|
555
|
+
"""Return the local data directory previously chosen via ``nemo setup``."""
|
|
556
|
+
config_path = Config.get_default_config_path()
|
|
557
|
+
if not config_path.exists():
|
|
558
|
+
return None
|
|
559
|
+
try:
|
|
560
|
+
config = Config.load(config_path=config_path)
|
|
561
|
+
except Exception:
|
|
562
|
+
logger.debug("Failed to load config for local_services lookup", exc_info=True)
|
|
563
|
+
return None
|
|
564
|
+
local = config.get_config_file().local_services
|
|
565
|
+
return local.data_dir if local else None
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def _save_data_dir(data_dir: str) -> None:
|
|
569
|
+
"""Persist the chosen local data directory to the user's config file.
|
|
570
|
+
|
|
571
|
+
Reads any existing config so other fields (contexts, clusters, etc.) are
|
|
572
|
+
preserved, then overwrites just ``local_services.data_dir``.
|
|
573
|
+
"""
|
|
574
|
+
config_path = Config.get_default_config_path()
|
|
575
|
+
if config_path.exists():
|
|
576
|
+
config = Config.load(config_path=config_path)
|
|
577
|
+
else:
|
|
578
|
+
config = Config.create(config_path, ConfigFile())
|
|
579
|
+
config_file = config.get_config_file()
|
|
580
|
+
config_file.local_services = LocalServicesConfig(data_dir=data_dir)
|
|
581
|
+
config.save()
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def _prompt_data_dir() -> str:
|
|
585
|
+
"""Prompt the user for a local data directory and persist the choice.
|
|
586
|
+
|
|
587
|
+
Pre-fills the prompt with the previously-persisted directory if any,
|
|
588
|
+
otherwise the XDG default (``~/.local/share/nemo``). The chosen
|
|
589
|
+
directory is where local services persist SQLite DB, encryption key,
|
|
590
|
+
and uploaded files.
|
|
591
|
+
"""
|
|
592
|
+
persisted = _load_persisted_data_dir()
|
|
593
|
+
default_dir = persisted or str(nmp_user_data_dir())
|
|
594
|
+
|
|
595
|
+
chosen = prompt_text(
|
|
596
|
+
message="Local data directory:",
|
|
597
|
+
default=default_dir,
|
|
598
|
+
validator=non_empty_validator("Data directory"),
|
|
599
|
+
).strip()
|
|
600
|
+
|
|
601
|
+
_save_data_dir(chosen)
|
|
602
|
+
return chosen
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def _resolve_services_port(base_url: str) -> int:
|
|
606
|
+
"""Extract the port from *base_url*, defaulting to 8080."""
|
|
607
|
+
parsed = urlparse(base_url)
|
|
608
|
+
return parsed.port or 8080
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def _start_services_background(base_url: str, data_dir: str | None = None) -> subprocess.Popen:
|
|
612
|
+
"""Launch ``nemo services run`` as a background process.
|
|
613
|
+
|
|
614
|
+
Delegates to the shared process lifecycle module which uses flock-based
|
|
615
|
+
instance tracking. If *data_dir* is provided, it's forwarded so the
|
|
616
|
+
subprocess inherits ``NMP_DATA_DIR`` (unless the parent shell already
|
|
617
|
+
exported it).
|
|
618
|
+
"""
|
|
619
|
+
from nemo_platform.cli.commands.services._process import compute_scope, start_background
|
|
620
|
+
|
|
621
|
+
port = _resolve_services_port(base_url)
|
|
622
|
+
scope = compute_scope(port=port)
|
|
623
|
+
return start_background(scope=scope, port=port, data_dir=data_dir)
|
|
624
|
+
|
|
625
|
+
|
|
626
|
+
def _last_startup_service(log_path: Path | None) -> str:
|
|
627
|
+
"""Read the most recently logged ``[STARTUP] service:<name>`` from the service log."""
|
|
628
|
+
if log_path is None or not log_path.exists():
|
|
629
|
+
return ""
|
|
630
|
+
try:
|
|
631
|
+
text = log_path.read_text(errors="replace")
|
|
632
|
+
except OSError:
|
|
633
|
+
return ""
|
|
634
|
+
last = ""
|
|
635
|
+
tag = "[STARTUP] service:"
|
|
636
|
+
for line in text.splitlines():
|
|
637
|
+
if tag in line:
|
|
638
|
+
last = line.split(tag, 1)[1].split(":")[0]
|
|
639
|
+
return last
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
def _wait_for_platform(
|
|
643
|
+
base_url: str,
|
|
644
|
+
timeout: int = _SERVICE_STARTUP_TIMEOUT_SECONDS,
|
|
645
|
+
poll_interval: float = _SERVICE_STARTUP_POLL_INTERVAL,
|
|
646
|
+
log_path: Path | None = None,
|
|
647
|
+
) -> bool:
|
|
648
|
+
"""Poll until the platform health endpoint responds. Returns True on success.
|
|
649
|
+
|
|
650
|
+
When *log_path* is provided, the spinner shows the last service that
|
|
651
|
+
finished loading so users see that progress is being made during a
|
|
652
|
+
slow cold start.
|
|
653
|
+
"""
|
|
654
|
+
start = time.monotonic()
|
|
655
|
+
deadline = start + timeout
|
|
656
|
+
with console.status("[bold cyan]Waiting for platform...") as status:
|
|
657
|
+
while time.monotonic() < deadline:
|
|
658
|
+
elapsed = int(time.monotonic() - start)
|
|
659
|
+
svc = _last_startup_service(log_path)
|
|
660
|
+
hint = f" — loaded {svc}" if svc else ""
|
|
661
|
+
status.update(f"[bold cyan]Waiting for platform... ({elapsed}s){hint}")
|
|
662
|
+
if _check_platform_reachable(base_url, timeout=1.0):
|
|
663
|
+
return True
|
|
664
|
+
_pause(poll_interval)
|
|
665
|
+
return False
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
def _kill_existing_services(base_url: str) -> None:
|
|
669
|
+
"""Find and kill any running ``nemo services run`` processes.
|
|
670
|
+
|
|
671
|
+
Delegates to the shared process lifecycle module.
|
|
672
|
+
"""
|
|
673
|
+
from nemo_platform.cli.commands.services._process import compute_scope, stop_instance
|
|
674
|
+
|
|
675
|
+
port = _resolve_services_port(base_url)
|
|
676
|
+
scope = compute_scope(port=port)
|
|
677
|
+
stop_instance(scope, timeout=2.0, force=True)
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
def _maybe_start_services(
|
|
681
|
+
base_url: str,
|
|
682
|
+
auto: bool,
|
|
683
|
+
start_services: bool | None,
|
|
684
|
+
timeout: int = _SERVICE_STARTUP_TIMEOUT_SECONDS,
|
|
685
|
+
) -> None:
|
|
686
|
+
"""Start services if requested, restarting if already running.
|
|
687
|
+
|
|
688
|
+
In interactive mode (auto=False), prompts the user if start_services is None.
|
|
689
|
+
In auto mode, only starts if start_services is explicitly True.
|
|
690
|
+
|
|
691
|
+
When start_services is True and the platform is already running, the
|
|
692
|
+
existing processes are stopped and restarted so the full service set
|
|
693
|
+
(including any newly installed plugins) is picked up. Data lives in
|
|
694
|
+
SQLite so nothing is lost across restarts.
|
|
695
|
+
"""
|
|
696
|
+
already_running = _check_platform_reachable(base_url)
|
|
697
|
+
|
|
698
|
+
if already_running and start_services is not True:
|
|
699
|
+
console.print(f"{CHECK} Platform already running at {base_url}\n")
|
|
700
|
+
return
|
|
701
|
+
|
|
702
|
+
should_start = start_services
|
|
703
|
+
if should_start is None:
|
|
704
|
+
if auto:
|
|
705
|
+
console.print(f"{CROSS} Cannot reach platform at {base_url}")
|
|
706
|
+
console.print(" Start the platform first, or pass --start-services:")
|
|
707
|
+
console.print(" [cyan]nemo setup --auto --start-services[/cyan]")
|
|
708
|
+
console.print(" [cyan]nemo services run[/cyan]")
|
|
709
|
+
raise typer.Exit(1)
|
|
710
|
+
should_start = (
|
|
711
|
+
prompt_choice(
|
|
712
|
+
message=f"Platform not reachable at {base_url}. Start local services?",
|
|
713
|
+
options=[("yes", "Yes, start services now"), ("no", "No, I'll start them myself")],
|
|
714
|
+
default="yes",
|
|
715
|
+
)
|
|
716
|
+
== "yes"
|
|
717
|
+
)
|
|
718
|
+
|
|
719
|
+
if not should_start:
|
|
720
|
+
console.print(f"{CROSS} Cannot reach platform at {base_url}")
|
|
721
|
+
console.print(" Start the platform first:")
|
|
722
|
+
console.print(" [cyan]nemo services run[/cyan] (local development)")
|
|
723
|
+
raise typer.Exit(1)
|
|
724
|
+
|
|
725
|
+
# Pick (and persist) the local data directory before launching services
|
|
726
|
+
# so the chosen path takes effect on this run. Interactive mode prompts;
|
|
727
|
+
# ``--auto`` reuses whatever was previously persisted (or service default).
|
|
728
|
+
if auto:
|
|
729
|
+
data_dir = _load_persisted_data_dir()
|
|
730
|
+
else:
|
|
731
|
+
data_dir = _prompt_data_dir()
|
|
732
|
+
|
|
733
|
+
if already_running:
|
|
734
|
+
console.print(" Restarting platform services...")
|
|
735
|
+
_kill_existing_services(base_url)
|
|
736
|
+
deadline = time.time() + _KILL_WAIT_TIMEOUT
|
|
737
|
+
while time.time() < deadline and _check_platform_reachable(base_url, timeout=1.0):
|
|
738
|
+
_pause(1)
|
|
739
|
+
else:
|
|
740
|
+
console.print(" Starting platform services...")
|
|
741
|
+
proc = _start_services_background(base_url, data_dir=data_dir)
|
|
742
|
+
|
|
743
|
+
from nemo_platform.cli.commands.services._process import compute_scope, log_path_for
|
|
744
|
+
|
|
745
|
+
port = _resolve_services_port(base_url)
|
|
746
|
+
log = log_path_for(compute_scope(port=port))
|
|
747
|
+
|
|
748
|
+
if not _wait_for_platform(base_url, timeout=timeout, log_path=log):
|
|
749
|
+
exit_code = proc.poll()
|
|
750
|
+
if exit_code is not None:
|
|
751
|
+
console.print(f"{CROSS} Service process exited early (exit code {exit_code})")
|
|
752
|
+
else:
|
|
753
|
+
proc.terminate()
|
|
754
|
+
console.print(f"{CROSS} Platform did not become ready within {timeout}s")
|
|
755
|
+
console.print(f" Check {log} for details.")
|
|
756
|
+
raise typer.Exit(1)
|
|
757
|
+
|
|
758
|
+
console.print(f"{CHECK} Platform running at {base_url} (pid {proc.pid})\n")
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
# ---------------------------------------------------------------------------
|
|
762
|
+
# Skills installation
|
|
763
|
+
# ---------------------------------------------------------------------------
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
def _detect_coding_agents() -> list[tuple[str, str]]:
|
|
767
|
+
"""Detect coding agents from filesystem markers in the project root.
|
|
768
|
+
|
|
769
|
+
Returns list of (marker, agent_name) for each detected agent.
|
|
770
|
+
"""
|
|
771
|
+
project_root = _find_project_root()
|
|
772
|
+
detected = []
|
|
773
|
+
for marker, agent_name in _AGENT_MARKERS:
|
|
774
|
+
if (project_root / marker).exists():
|
|
775
|
+
detected.append((marker, agent_name))
|
|
776
|
+
return detected
|
|
777
|
+
|
|
778
|
+
|
|
779
|
+
def _find_project_root() -> Path:
|
|
780
|
+
"""Find the project root by looking for a .git directory, falling back to cwd."""
|
|
781
|
+
cwd = Path.cwd()
|
|
782
|
+
current = cwd
|
|
783
|
+
while current != current.parent:
|
|
784
|
+
if (current / ".git").exists():
|
|
785
|
+
return current
|
|
786
|
+
current = current.parent
|
|
787
|
+
return cwd
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
def _load_skills_with_warnings() -> tuple[dict[str, Skill], list[str]]:
|
|
791
|
+
"""Load skills while capturing any plugin-discovery warnings.
|
|
792
|
+
|
|
793
|
+
The registry emits ``logger.warning`` records when a plugin's skills directory
|
|
794
|
+
is missing, malformed, or invalid. We capture those records so they can be
|
|
795
|
+
surfaced in the preview before any install happens, rather than scrolling
|
|
796
|
+
past mid-install.
|
|
797
|
+
|
|
798
|
+
Defensive against (a) callers that raised the registry logger's level above
|
|
799
|
+
WARNING (e.g. a future ``--quiet`` flag) and (b) the ``@lru_cache`` on the
|
|
800
|
+
underlying loader, which would otherwise replay a cached dict with no
|
|
801
|
+
warnings on repeat calls.
|
|
802
|
+
"""
|
|
803
|
+
from nemo_platform.cli.commands.skills import registry as _registry
|
|
804
|
+
|
|
805
|
+
captured: list[str] = []
|
|
806
|
+
|
|
807
|
+
class _Capture(logging.Handler):
|
|
808
|
+
def emit(self, record: logging.LogRecord) -> None:
|
|
809
|
+
captured.append(record.getMessage())
|
|
810
|
+
|
|
811
|
+
handler = _Capture(level=logging.WARNING)
|
|
812
|
+
original_level = _registry.logger.level
|
|
813
|
+
_registry.logger.addHandler(handler)
|
|
814
|
+
if original_level > logging.WARNING or original_level == logging.NOTSET:
|
|
815
|
+
_registry.logger.setLevel(logging.WARNING)
|
|
816
|
+
_registry.clear_cache()
|
|
817
|
+
try:
|
|
818
|
+
skills = load_skills()
|
|
819
|
+
finally:
|
|
820
|
+
_registry.logger.removeHandler(handler)
|
|
821
|
+
_registry.logger.setLevel(original_level)
|
|
822
|
+
return skills, captured
|
|
823
|
+
|
|
824
|
+
|
|
825
|
+
def _print_plugin_warnings(plugin_warnings: list[str]) -> None:
|
|
826
|
+
"""Surface plugin-discovery warnings before the interactive skills prompt."""
|
|
827
|
+
if not plugin_warnings:
|
|
828
|
+
return
|
|
829
|
+
console.print(" [yellow]Plugin warnings:[/yellow]")
|
|
830
|
+
for msg in plugin_warnings:
|
|
831
|
+
console.print(f" {WARN} {msg}")
|
|
832
|
+
console.print()
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
_BUILTIN_SOURCE_NAME = "nemo-platform"
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
def _skill_sources_of(skills: dict[str, Skill]) -> dict[str, list[Skill]]:
|
|
839
|
+
"""Group skills by their source (built-in vs each plugin).
|
|
840
|
+
|
|
841
|
+
The built-in source is keyed by ``nemo-platform``; plugin skills are keyed
|
|
842
|
+
by ``Skill.source_plugin``. Returned dict preserves insertion order so the
|
|
843
|
+
built-in group appears first, then plugins in discovery order.
|
|
844
|
+
"""
|
|
845
|
+
sources: dict[str, list[Skill]] = {}
|
|
846
|
+
for skill in skills.values():
|
|
847
|
+
key = skill.source_plugin or _BUILTIN_SOURCE_NAME
|
|
848
|
+
sources.setdefault(key, []).append(skill)
|
|
849
|
+
return sources
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
def _filter_agents_by_scope(agents: list[str], scope: Scope) -> tuple[list[str], list[tuple[str, str]]]:
|
|
853
|
+
"""Split agents into (installable, skipped) based on whether each supports the chosen scope.
|
|
854
|
+
|
|
855
|
+
Returns:
|
|
856
|
+
(kept, skipped) where ``skipped`` is a list of (agent_name, reason) tuples.
|
|
857
|
+
"""
|
|
858
|
+
kept: list[str] = []
|
|
859
|
+
skipped: list[tuple[str, str]] = []
|
|
860
|
+
for agent in agents:
|
|
861
|
+
installer = get_installer(agent)
|
|
862
|
+
if scope in installer.supported_scopes:
|
|
863
|
+
kept.append(agent)
|
|
864
|
+
else:
|
|
865
|
+
supported = ", ".join(s.value for s in installer.supported_scopes) or "none"
|
|
866
|
+
skipped.append((agent, f"does not support '{scope.value}' scope (supports: {supported})"))
|
|
867
|
+
return kept, skipped
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def _print_final_skills_summary(agents: list[str], scope: Scope, skill_names: list[str]) -> None:
|
|
871
|
+
"""Print the planned action before final confirmation."""
|
|
872
|
+
if not skill_names or not agents:
|
|
873
|
+
return
|
|
874
|
+
project_root = _find_project_root()
|
|
875
|
+
agent_list = ", ".join(agents)
|
|
876
|
+
console.print(
|
|
877
|
+
f" Installing [bold]{len(skill_names)}[/bold] skill(s) for "
|
|
878
|
+
f"[bold]{agent_list}[/bold] at [bold]{scope.value}[/bold] scope:"
|
|
879
|
+
)
|
|
880
|
+
for agent in agents:
|
|
881
|
+
installer = get_installer(agent)
|
|
882
|
+
# Show the parent directory of one representative skill so the user
|
|
883
|
+
# sees the destination root, not a single SKILL.md path.
|
|
884
|
+
example = installer.get_install_path(scope, project_root, skill_names[0])
|
|
885
|
+
console.print(f" {agent} → {example.parent.parent}/")
|
|
886
|
+
console.print()
|
|
887
|
+
|
|
888
|
+
|
|
889
|
+
def _run_skill_install(
|
|
890
|
+
*,
|
|
891
|
+
agents: list[str],
|
|
892
|
+
scope: Scope,
|
|
893
|
+
skill_names: list[str],
|
|
894
|
+
all_skills: dict[str, Skill],
|
|
895
|
+
project_root: Path,
|
|
896
|
+
) -> None:
|
|
897
|
+
"""Run the actual installer for each agent with the chosen skill subset.
|
|
898
|
+
|
|
899
|
+
Skill names are expected to come from a validated source-selection path
|
|
900
|
+
(interactive multiselect or ``--skills-from`` flag), so any name not in
|
|
901
|
+
``all_skills`` would be an internal bug. If every agent's install fails,
|
|
902
|
+
raises ``typer.Exit(1)`` so callers in ``--auto`` see a non-zero exit.
|
|
903
|
+
"""
|
|
904
|
+
chosen = {name: all_skills[name] for name in skill_names if name in all_skills}
|
|
905
|
+
if not chosen:
|
|
906
|
+
console.print(f" {WARN} No skills selected to install.")
|
|
907
|
+
return
|
|
908
|
+
|
|
909
|
+
successes = 0
|
|
910
|
+
failures = 0
|
|
911
|
+
for agent in agents:
|
|
912
|
+
try:
|
|
913
|
+
installer = get_installer(agent)
|
|
914
|
+
installer.install(scope, project_root, chosen)
|
|
915
|
+
console.print(f" {CHECK} Installed {len(chosen)} skill(s) for {agent}")
|
|
916
|
+
successes += 1
|
|
917
|
+
except Exception as exc:
|
|
918
|
+
console.print(f" {WARN} Failed to install skills for {agent}: {exc}")
|
|
919
|
+
failures += 1
|
|
920
|
+
|
|
921
|
+
if failures and not successes:
|
|
922
|
+
raise typer.Exit(1)
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
def _parse_csv_flag(value: str | None) -> list[str] | None:
|
|
926
|
+
"""Parse a comma-separated CLI flag into a list, dropping empty entries."""
|
|
927
|
+
if value is None:
|
|
928
|
+
return None
|
|
929
|
+
parts = [p.strip() for p in value.split(",")]
|
|
930
|
+
cleaned = [p for p in parts if p]
|
|
931
|
+
return cleaned or None
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
def _maybe_install_skills(
|
|
935
|
+
auto: bool,
|
|
936
|
+
install_skills: bool | None,
|
|
937
|
+
*,
|
|
938
|
+
skills_agents: list[str] | None = None,
|
|
939
|
+
skills_scope: Scope | None = None,
|
|
940
|
+
skills_from: list[str] | None = None,
|
|
941
|
+
) -> None:
|
|
942
|
+
"""Install coding agent skills if requested.
|
|
943
|
+
|
|
944
|
+
``--install-skills`` is the master opt-in. ``--skills-agents``,
|
|
945
|
+
``--skills-scope``, and ``--skills-from`` are filters that narrow what
|
|
946
|
+
gets installed when the master is set; on their own they do nothing in
|
|
947
|
+
non-interactive mode. This mirrors ``_maybe_deploy_agent`` and
|
|
948
|
+
``_maybe_start_services``, which also require their boolean master flag
|
|
949
|
+
to be explicitly True under ``--auto``.
|
|
950
|
+
|
|
951
|
+
``--skills-from`` selects by *source* (the built-in ``nemo-platform`` set
|
|
952
|
+
or a plugin name) rather than by individual skill name. Picking one source
|
|
953
|
+
installs every skill that source provides.
|
|
954
|
+
|
|
955
|
+
Interactive mode walks the user through a source multi-select (each source
|
|
956
|
+
expanded to show its skills as read-only sub-labels, with ``s`` to skip the
|
|
957
|
+
whole step), an agent multi-select, a scope choice, and a final
|
|
958
|
+
confirmation; filter flags pre-populate the defaults.
|
|
959
|
+
"""
|
|
960
|
+
if install_skills is False:
|
|
961
|
+
return
|
|
962
|
+
|
|
963
|
+
# Validate --skills-agents up-front: a typo like `--skills-agents copex` should
|
|
964
|
+
# fail loudly before any platform work, regardless of detection state.
|
|
965
|
+
if skills_agents:
|
|
966
|
+
for agent in skills_agents:
|
|
967
|
+
get_installer(agent)
|
|
968
|
+
|
|
969
|
+
detected = _detect_coding_agents()
|
|
970
|
+
# --skills-agents overrides detection: an explicit instruction to install for
|
|
971
|
+
# an agent wins over "we didn't find a marker file for it." Detection is still
|
|
972
|
+
# load-bearing as the default when the flag is absent.
|
|
973
|
+
if not detected and not skills_agents:
|
|
974
|
+
if not auto:
|
|
975
|
+
console.print(f" {WARN} No coding agents detected in project (no .cursor/, AGENTS.md, etc.)")
|
|
976
|
+
return
|
|
977
|
+
|
|
978
|
+
all_skills, plugin_warnings = _load_skills_with_warnings()
|
|
979
|
+
if not all_skills:
|
|
980
|
+
console.print(f" {WARN} No NeMo skills available to install.")
|
|
981
|
+
return
|
|
982
|
+
|
|
983
|
+
sources = _skill_sources_of(all_skills)
|
|
984
|
+
source_names = list(sources.keys())
|
|
985
|
+
|
|
986
|
+
# Validate --skills-from up-front, same shape as --skills-agents.
|
|
987
|
+
if skills_from:
|
|
988
|
+
unknown = [s for s in skills_from if s not in sources]
|
|
989
|
+
if unknown:
|
|
990
|
+
known = ", ".join(source_names)
|
|
991
|
+
raise typer.BadParameter(
|
|
992
|
+
f"Unknown skill source(s): {', '.join(unknown)}. Known sources: {known}",
|
|
993
|
+
param_hint="--skills-from",
|
|
994
|
+
)
|
|
995
|
+
|
|
996
|
+
detected_names = [name for _, name in detected]
|
|
997
|
+
# Interactive menu options: detected agents plus any extras the user explicitly
|
|
998
|
+
# asked for. dict.fromkeys preserves order and deduplicates.
|
|
999
|
+
menu_agent_names = list(dict.fromkeys(detected_names + (skills_agents or [])))
|
|
1000
|
+
project_root = _find_project_root()
|
|
1001
|
+
non_interactive = auto or not is_interactive()
|
|
1002
|
+
|
|
1003
|
+
def _skills_for_sources(chosen_sources: list[str]) -> list[str]:
|
|
1004
|
+
return [skill.name for source in chosen_sources for skill in sources[source]]
|
|
1005
|
+
|
|
1006
|
+
if non_interactive:
|
|
1007
|
+
# Non-interactive path requires the master switch to be explicitly True.
|
|
1008
|
+
# Filter flags alone don't opt in (matches --start-services / --deploy-agent).
|
|
1009
|
+
if install_skills is not True:
|
|
1010
|
+
return
|
|
1011
|
+
chosen_agents = skills_agents or detected_names
|
|
1012
|
+
chosen_scope = skills_scope or Scope.PROJECT
|
|
1013
|
+
chosen_sources = skills_from or source_names
|
|
1014
|
+
chosen_skills = _skills_for_sources(chosen_sources)
|
|
1015
|
+
chosen_agents, skipped = _filter_agents_by_scope(chosen_agents, chosen_scope)
|
|
1016
|
+
for agent, reason in skipped:
|
|
1017
|
+
console.print(f" {WARN} Skipping {agent}: {reason}")
|
|
1018
|
+
if not chosen_agents:
|
|
1019
|
+
console.print(f" {WARN} No installable agents for scope '{chosen_scope.value}'.")
|
|
1020
|
+
return
|
|
1021
|
+
_run_skill_install(
|
|
1022
|
+
agents=chosen_agents,
|
|
1023
|
+
scope=chosen_scope,
|
|
1024
|
+
skill_names=chosen_skills,
|
|
1025
|
+
all_skills=all_skills,
|
|
1026
|
+
project_root=project_root,
|
|
1027
|
+
)
|
|
1028
|
+
return
|
|
1029
|
+
|
|
1030
|
+
# Interactive path: sources → agents → scope → confirm.
|
|
1031
|
+
_print_plugin_warnings(plugin_warnings)
|
|
1032
|
+
|
|
1033
|
+
source_defaults = skills_from if skills_from else source_names
|
|
1034
|
+
source_options = [(name, f"{name} (built-in)" if name == _BUILTIN_SOURCE_NAME else name) for name in source_names]
|
|
1035
|
+
source_sub_labels = {name: [skill.name for skill in sources[name]] for name in source_names}
|
|
1036
|
+
chosen_sources = prompt_multiselect(
|
|
1037
|
+
message="Install skills:",
|
|
1038
|
+
options=source_options,
|
|
1039
|
+
defaults=source_defaults,
|
|
1040
|
+
sub_labels=source_sub_labels,
|
|
1041
|
+
min_choices=1,
|
|
1042
|
+
allow_skip=True,
|
|
1043
|
+
indent=2,
|
|
1044
|
+
)
|
|
1045
|
+
if chosen_sources is None:
|
|
1046
|
+
console.print(f" {WARN} Skipping skill installation.")
|
|
1047
|
+
return
|
|
1048
|
+
chosen_skills = _skills_for_sources(chosen_sources)
|
|
1049
|
+
|
|
1050
|
+
agent_defaults = skills_agents if skills_agents else detected_names
|
|
1051
|
+
chosen_agents = prompt_multiselect(
|
|
1052
|
+
message="Install skills for which agents?",
|
|
1053
|
+
options=[(name, get_installer(name).display_name) for name in menu_agent_names],
|
|
1054
|
+
defaults=agent_defaults,
|
|
1055
|
+
min_choices=1,
|
|
1056
|
+
indent=2,
|
|
1057
|
+
)
|
|
1058
|
+
|
|
1059
|
+
scope_default = (skills_scope or Scope.PROJECT).value
|
|
1060
|
+
chosen_scope = Scope(
|
|
1061
|
+
prompt_choice(
|
|
1062
|
+
message="Install scope:",
|
|
1063
|
+
options=[
|
|
1064
|
+
(Scope.PROJECT.value, "Local (this repo: .agents/, .cursor/, .claude/, ...)"),
|
|
1065
|
+
(Scope.USER.value, "Global (user home: ~/.agents/, ~/.claude/, ...)"),
|
|
1066
|
+
],
|
|
1067
|
+
default=scope_default,
|
|
1068
|
+
indent=2,
|
|
1069
|
+
)
|
|
1070
|
+
)
|
|
1071
|
+
|
|
1072
|
+
chosen_agents, skipped = _filter_agents_by_scope(chosen_agents, chosen_scope)
|
|
1073
|
+
for agent, reason in skipped:
|
|
1074
|
+
console.print(f" {WARN} Skipping {agent}: {reason}")
|
|
1075
|
+
if not chosen_agents:
|
|
1076
|
+
console.print(f" {WARN} No installable agents for scope '{chosen_scope.value}'.")
|
|
1077
|
+
return
|
|
1078
|
+
|
|
1079
|
+
_print_final_skills_summary(chosen_agents, chosen_scope, chosen_skills)
|
|
1080
|
+
if not prompt_confirm("Proceed?", default=True, indent=2):
|
|
1081
|
+
return
|
|
1082
|
+
|
|
1083
|
+
_run_skill_install(
|
|
1084
|
+
agents=chosen_agents,
|
|
1085
|
+
scope=chosen_scope,
|
|
1086
|
+
skill_names=chosen_skills,
|
|
1087
|
+
all_skills=all_skills,
|
|
1088
|
+
project_root=project_root,
|
|
1089
|
+
)
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
# ---------------------------------------------------------------------------
|
|
1093
|
+
# Agent deployment
|
|
1094
|
+
# ---------------------------------------------------------------------------
|
|
1095
|
+
|
|
1096
|
+
|
|
1097
|
+
def _agents_plugin_available() -> bool:
|
|
1098
|
+
"""Return True if the nemo-agents plugin is importable."""
|
|
1099
|
+
return importlib.util.find_spec("nemo_agents_plugin") is not None
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
def _agent_config_path() -> Traversable | None:
|
|
1103
|
+
"""Return the path to the calculator-agent demo config YAML, or None."""
|
|
1104
|
+
try:
|
|
1105
|
+
candidate = files("calculator_agent").joinpath("calculator-agent.yml")
|
|
1106
|
+
if candidate.is_file():
|
|
1107
|
+
return candidate
|
|
1108
|
+
except (ImportError, ModuleNotFoundError):
|
|
1109
|
+
logger.debug("calculator_agent package not importable; demo agent config unavailable", exc_info=True)
|
|
1110
|
+
|
|
1111
|
+
return None
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
def _agent_exists(base_url: str, workspace: str) -> bool:
|
|
1115
|
+
"""Return True if the demo agent already exists on the platform."""
|
|
1116
|
+
try:
|
|
1117
|
+
resp = httpx.get(
|
|
1118
|
+
f"{base_url.rstrip('/')}/apis/agents/v2/workspaces/{workspace}/agents/{_DEMO_AGENT_NAME}",
|
|
1119
|
+
timeout=10.0,
|
|
1120
|
+
)
|
|
1121
|
+
return resp.status_code == 200
|
|
1122
|
+
except Exception:
|
|
1123
|
+
return False
|
|
1124
|
+
|
|
1125
|
+
|
|
1126
|
+
def _agents_api_ready(base_url: str, workspace: str) -> bool:
|
|
1127
|
+
"""Return True if the agents API is responding."""
|
|
1128
|
+
try:
|
|
1129
|
+
resp = httpx.get(
|
|
1130
|
+
f"{base_url.rstrip('/')}/apis/agents/v2/workspaces/{workspace}/agents",
|
|
1131
|
+
timeout=3.0,
|
|
1132
|
+
)
|
|
1133
|
+
return resp.status_code == 200
|
|
1134
|
+
except Exception:
|
|
1135
|
+
return False
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
def _deploy_demo_agent(base_url: str, workspace: str, config_path: Traversable, default_model: str) -> bool:
|
|
1139
|
+
"""Create and deploy the demo calculator agent. Returns True on success."""
|
|
1140
|
+
from nemo_agents_plugin.utils import expand_env_vars
|
|
1141
|
+
|
|
1142
|
+
api_base = base_url.rstrip("/")
|
|
1143
|
+
|
|
1144
|
+
if not _agent_exists(base_url, workspace):
|
|
1145
|
+
config_dict = _yaml.safe_load(config_path.read_text(encoding="utf-8"))
|
|
1146
|
+
config_dict = expand_env_vars(config_dict, vars_dict={"NEMO_DEFAULT_MODEL": default_model})
|
|
1147
|
+
payload = {"name": _DEMO_AGENT_NAME, "description": "Demo calculator agent", "config": config_dict}
|
|
1148
|
+
resp = httpx.post(
|
|
1149
|
+
f"{api_base}/apis/agents/v2/workspaces/{workspace}/agents",
|
|
1150
|
+
json=payload,
|
|
1151
|
+
timeout=30.0,
|
|
1152
|
+
)
|
|
1153
|
+
resp.raise_for_status()
|
|
1154
|
+
console.print(f" {CHECK} Created agent '{_DEMO_AGENT_NAME}'")
|
|
1155
|
+
else:
|
|
1156
|
+
console.print(f" {CHECK} Agent '{_DEMO_AGENT_NAME}' already exists")
|
|
1157
|
+
|
|
1158
|
+
resp = httpx.post(
|
|
1159
|
+
f"{api_base}/apis/agents/v2/workspaces/{workspace}/deployments",
|
|
1160
|
+
json={"agent": _DEMO_AGENT_NAME},
|
|
1161
|
+
timeout=30.0,
|
|
1162
|
+
)
|
|
1163
|
+
if resp.status_code == 409:
|
|
1164
|
+
console.print(f" {CHECK} Agent '{_DEMO_AGENT_NAME}' already deployed")
|
|
1165
|
+
return True
|
|
1166
|
+
|
|
1167
|
+
resp.raise_for_status()
|
|
1168
|
+
deployment_name = resp.json().get("name", "")
|
|
1169
|
+
console.print(f" {CHECK} Deployed agent '{_DEMO_AGENT_NAME}'")
|
|
1170
|
+
|
|
1171
|
+
# Poll the specific deployment we just created by name, not the full
|
|
1172
|
+
# list. Previous runs may leave stale "failed" deployments that would
|
|
1173
|
+
# confuse a list-and-scan approach.
|
|
1174
|
+
start = time.monotonic()
|
|
1175
|
+
deadline = start + _AGENT_DEPLOY_TIMEOUT_SECONDS
|
|
1176
|
+
with console.status("[bold cyan]Waiting for agent deployment...") as spinner:
|
|
1177
|
+
while time.monotonic() < deadline:
|
|
1178
|
+
elapsed = int(time.monotonic() - start)
|
|
1179
|
+
spinner.update(f"[bold cyan]Waiting for agent deployment... ({elapsed}s)")
|
|
1180
|
+
try:
|
|
1181
|
+
dep_resp = httpx.get(
|
|
1182
|
+
f"{api_base}/apis/agents/v2/workspaces/{workspace}/deployments/{deployment_name}",
|
|
1183
|
+
timeout=3.0,
|
|
1184
|
+
)
|
|
1185
|
+
if dep_resp.status_code == 200:
|
|
1186
|
+
dep_status = dep_resp.json().get("status", "")
|
|
1187
|
+
if dep_status == "running":
|
|
1188
|
+
return True
|
|
1189
|
+
if dep_status == "failed":
|
|
1190
|
+
console.print(f" {CROSS} Agent deployment failed")
|
|
1191
|
+
return False
|
|
1192
|
+
except Exception:
|
|
1193
|
+
logger.debug("Agent deployment status poll failed", exc_info=True)
|
|
1194
|
+
_pause(_AGENT_DEPLOY_POLL_INTERVAL)
|
|
1195
|
+
|
|
1196
|
+
console.print(f" {WARN} Agent deployment did not reach running state within {_AGENT_DEPLOY_TIMEOUT_SECONDS}s")
|
|
1197
|
+
return False
|
|
1198
|
+
|
|
1199
|
+
|
|
1200
|
+
def _maybe_deploy_agent(
|
|
1201
|
+
base_url: str,
|
|
1202
|
+
workspace: str,
|
|
1203
|
+
auto: bool,
|
|
1204
|
+
deploy_agent: bool | None,
|
|
1205
|
+
default_model: str | None = None,
|
|
1206
|
+
) -> bool:
|
|
1207
|
+
"""Optionally deploy the demo calculator agent.
|
|
1208
|
+
|
|
1209
|
+
In interactive mode (auto=False), prompts the user if deploy_agent is None.
|
|
1210
|
+
Default is **no** -- the demo is opt-in for users who don't have their own
|
|
1211
|
+
agent yet. In auto mode, only deploys if deploy_agent is explicitly True.
|
|
1212
|
+
|
|
1213
|
+
Returns True if the agent was deployed (used by CTA messaging).
|
|
1214
|
+
"""
|
|
1215
|
+
if not _agents_plugin_available():
|
|
1216
|
+
console.print(f" {WARN} nemo-agents plugin not installed, skipping agent deployment")
|
|
1217
|
+
console.print(" Run [cyan]make bootstrap[/cyan] from the repo root to install all plugins,")
|
|
1218
|
+
console.print(" then re-run: [cyan]nemo setup --deploy-agent[/cyan]")
|
|
1219
|
+
return False
|
|
1220
|
+
|
|
1221
|
+
should_deploy = deploy_agent
|
|
1222
|
+
if should_deploy is None:
|
|
1223
|
+
if auto:
|
|
1224
|
+
return False
|
|
1225
|
+
console.print(
|
|
1226
|
+
" NeMo Platform optimizes AI agents. If you don't have your own\n"
|
|
1227
|
+
" agent yet, you can deploy a demo calculator agent to try things out.\n"
|
|
1228
|
+
)
|
|
1229
|
+
should_deploy = (
|
|
1230
|
+
prompt_choice(
|
|
1231
|
+
message="Deploy the demo agent?",
|
|
1232
|
+
options=[("no", "No, skip"), ("yes", "Yes, deploy it")],
|
|
1233
|
+
default="no",
|
|
1234
|
+
)
|
|
1235
|
+
== "yes"
|
|
1236
|
+
)
|
|
1237
|
+
|
|
1238
|
+
if not should_deploy:
|
|
1239
|
+
return False
|
|
1240
|
+
|
|
1241
|
+
if not default_model:
|
|
1242
|
+
console.print(
|
|
1243
|
+
f" {WARN} No default model selected, skipping agent deployment "
|
|
1244
|
+
"(the demo agent template needs a resolved model)"
|
|
1245
|
+
)
|
|
1246
|
+
return False
|
|
1247
|
+
|
|
1248
|
+
config_path = _agent_config_path()
|
|
1249
|
+
if config_path is None:
|
|
1250
|
+
console.print(f" {WARN} Could not find calculator-agent config YAML, skipping agent deployment")
|
|
1251
|
+
return False
|
|
1252
|
+
|
|
1253
|
+
start = time.monotonic()
|
|
1254
|
+
deadline = start + _AGENT_API_READINESS_TIMEOUT
|
|
1255
|
+
api_ready = False
|
|
1256
|
+
with console.status("[bold cyan]Waiting for agents API...") as spinner:
|
|
1257
|
+
while time.monotonic() < deadline:
|
|
1258
|
+
elapsed = int(time.monotonic() - start)
|
|
1259
|
+
spinner.update(f"[bold cyan]Waiting for agents API... ({elapsed}s)")
|
|
1260
|
+
if _agents_api_ready(base_url, workspace):
|
|
1261
|
+
api_ready = True
|
|
1262
|
+
break
|
|
1263
|
+
_pause(_AGENT_API_READINESS_POLL_INTERVAL)
|
|
1264
|
+
if not api_ready:
|
|
1265
|
+
console.print(f" {WARN} Agents API not ready at {base_url}, skipping agent deployment")
|
|
1266
|
+
console.print(" Ensure the agents service is running (e.g. [cyan]nemo services run --services agents[/cyan])")
|
|
1267
|
+
return False
|
|
1268
|
+
|
|
1269
|
+
try:
|
|
1270
|
+
return _deploy_demo_agent(base_url, workspace, config_path, default_model=default_model)
|
|
1271
|
+
except Exception as exc:
|
|
1272
|
+
console.print(f" {WARN} Agent deployment failed: {exc}")
|
|
1273
|
+
return False
|
|
1274
|
+
|
|
1275
|
+
|
|
1276
|
+
# ---------------------------------------------------------------------------
|
|
1277
|
+
# Interactive flow helpers
|
|
1278
|
+
# ---------------------------------------------------------------------------
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
def _select_provider() -> KnownProvider | None:
|
|
1282
|
+
"""Prompt user to pick one provider. Returns None for 'custom'."""
|
|
1283
|
+
options = [(p.name, f"{p.label:<20s} {p.description}") for p in KNOWN_PROVIDERS]
|
|
1284
|
+
options.append(("custom", "Custom provider Enter URL and key manually"))
|
|
1285
|
+
|
|
1286
|
+
result = prompt_choice(
|
|
1287
|
+
message="",
|
|
1288
|
+
options=options,
|
|
1289
|
+
default=KNOWN_PROVIDERS[0].name,
|
|
1290
|
+
)
|
|
1291
|
+
|
|
1292
|
+
if result == "custom":
|
|
1293
|
+
return None
|
|
1294
|
+
return _KNOWN_PROVIDERS_BY_NAME[result]
|
|
1295
|
+
|
|
1296
|
+
|
|
1297
|
+
def _prompt_custom_provider() -> tuple[str, str, str | None]:
|
|
1298
|
+
"""Prompt for custom provider details. Returns (name, host_url, api_key_or_none)."""
|
|
1299
|
+
name = prompt_text(
|
|
1300
|
+
"Provider name: ",
|
|
1301
|
+
validator=provider_name_validator(),
|
|
1302
|
+
hint="Start with a lowercase letter, then lowercase letters, digits, or hyphens; 2-63 chars (e.g. my-vllm-provider)",
|
|
1303
|
+
).strip()
|
|
1304
|
+
|
|
1305
|
+
host_url = prompt_text("Provider base URL: ").strip()
|
|
1306
|
+
if not host_url:
|
|
1307
|
+
raise typer.Exit(1)
|
|
1308
|
+
|
|
1309
|
+
api_key = prompt_password("API key (leave empty if none): ").strip() or None
|
|
1310
|
+
|
|
1311
|
+
return name, host_url, api_key
|
|
1312
|
+
|
|
1313
|
+
|
|
1314
|
+
def _collect_credential(provider: KnownProvider) -> str:
|
|
1315
|
+
"""Prompt for the API key, checking the env var first."""
|
|
1316
|
+
if not provider.requires_api_key:
|
|
1317
|
+
return ""
|
|
1318
|
+
|
|
1319
|
+
env_val = os.environ.get(provider.env_var or "") if provider.env_var else None
|
|
1320
|
+
if env_val:
|
|
1321
|
+
masked = f"***{env_val[-4:]}" if len(env_val) > 4 else "****"
|
|
1322
|
+
console.print(f" Found {provider.env_var} in environment ({masked})")
|
|
1323
|
+
key = prompt_password(f"{provider.label} API key [{masked}]: ")
|
|
1324
|
+
return key.strip() if key.strip() else env_val
|
|
1325
|
+
|
|
1326
|
+
key = prompt_password(
|
|
1327
|
+
f"{provider.label} API key: ",
|
|
1328
|
+
validator=non_empty_validator("API key"),
|
|
1329
|
+
)
|
|
1330
|
+
return key.strip()
|
|
1331
|
+
|
|
1332
|
+
|
|
1333
|
+
def _register_provider_interactive(
|
|
1334
|
+
client: NeMoPlatform,
|
|
1335
|
+
*,
|
|
1336
|
+
provider_name: str,
|
|
1337
|
+
host_url: str,
|
|
1338
|
+
api_key: str | None,
|
|
1339
|
+
workspace: str,
|
|
1340
|
+
auth_header_format: str | None = None,
|
|
1341
|
+
default_extra_headers: dict[str, str] | None = None,
|
|
1342
|
+
) -> None:
|
|
1343
|
+
"""Create or update secret + provider for idempotent re-runs."""
|
|
1344
|
+
secret_name = f"{provider_name}-api-key" if api_key else None
|
|
1345
|
+
|
|
1346
|
+
if secret_name:
|
|
1347
|
+
if _secret_exists(client, secret_name, workspace):
|
|
1348
|
+
_update_secret(client, secret_name, api_key, workspace)
|
|
1349
|
+
console.print(f" {CHECK} Updated secret '{secret_name}'")
|
|
1350
|
+
else:
|
|
1351
|
+
_create_secret(client, secret_name, api_key, workspace)
|
|
1352
|
+
console.print(f" {CHECK} Created secret '{secret_name}'")
|
|
1353
|
+
|
|
1354
|
+
if _provider_exists(client, provider_name, workspace):
|
|
1355
|
+
_update_provider(
|
|
1356
|
+
client,
|
|
1357
|
+
name=provider_name,
|
|
1358
|
+
host_url=host_url,
|
|
1359
|
+
secret_name=secret_name,
|
|
1360
|
+
workspace=workspace,
|
|
1361
|
+
default_extra_headers=default_extra_headers,
|
|
1362
|
+
)
|
|
1363
|
+
console.print(f" {CHECK} Updated provider '{provider_name}' ({host_url})")
|
|
1364
|
+
else:
|
|
1365
|
+
_create_provider(
|
|
1366
|
+
client,
|
|
1367
|
+
name=provider_name,
|
|
1368
|
+
host_url=host_url,
|
|
1369
|
+
secret_name=secret_name,
|
|
1370
|
+
workspace=workspace,
|
|
1371
|
+
auth_header_format=auth_header_format,
|
|
1372
|
+
default_extra_headers=default_extra_headers,
|
|
1373
|
+
)
|
|
1374
|
+
console.print(f" {CHECK} Registered provider '{provider_name}' ({host_url})")
|
|
1375
|
+
|
|
1376
|
+
|
|
1377
|
+
def _validate_api_key(
|
|
1378
|
+
provider_name: str,
|
|
1379
|
+
host_url: str,
|
|
1380
|
+
api_key: str | None,
|
|
1381
|
+
*,
|
|
1382
|
+
auth_header_format: str | None = None,
|
|
1383
|
+
default_extra_headers: dict[str, str] | None = None,
|
|
1384
|
+
timeout: float = _KEY_VALIDATION_TIMEOUT,
|
|
1385
|
+
) -> KeyValidationResult:
|
|
1386
|
+
"""Probe the provider with the API key to detect auth failures early.
|
|
1387
|
+
|
|
1388
|
+
Makes a single lightweight request to an auth-required endpoint.
|
|
1389
|
+
Returns ``passed=False`` only on a definitive 401/403 rejection.
|
|
1390
|
+
Network errors and unknown providers are treated as *passed* to avoid
|
|
1391
|
+
blocking setup when the provider is unreachable.
|
|
1392
|
+
"""
|
|
1393
|
+
if not api_key:
|
|
1394
|
+
return KeyValidationResult(passed=True, message="")
|
|
1395
|
+
|
|
1396
|
+
probe = _PROBE_CONFIGS.get(provider_name)
|
|
1397
|
+
if probe is None:
|
|
1398
|
+
return KeyValidationResult(
|
|
1399
|
+
passed=True,
|
|
1400
|
+
message=f"No validation probe configured for provider '{provider_name}'; skipping key check.",
|
|
1401
|
+
)
|
|
1402
|
+
|
|
1403
|
+
headers: dict[str, str] = {}
|
|
1404
|
+
if auth_header_format:
|
|
1405
|
+
header_name, _, template = auth_header_format.partition(":")
|
|
1406
|
+
headers[header_name.strip()] = template.strip().replace("{{ auth_secret }}", api_key)
|
|
1407
|
+
else:
|
|
1408
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
1409
|
+
|
|
1410
|
+
if default_extra_headers:
|
|
1411
|
+
headers.update(default_extra_headers)
|
|
1412
|
+
|
|
1413
|
+
url = f"{host_url.rstrip('/')}/{probe.path}"
|
|
1414
|
+
|
|
1415
|
+
try:
|
|
1416
|
+
resp = httpx.request(
|
|
1417
|
+
probe.method,
|
|
1418
|
+
url,
|
|
1419
|
+
headers=headers,
|
|
1420
|
+
json=probe.body,
|
|
1421
|
+
timeout=timeout,
|
|
1422
|
+
)
|
|
1423
|
+
if resp.status_code in _KEY_REJECTED_STATUS_CODES:
|
|
1424
|
+
return KeyValidationResult(passed=False, message=_KEY_REJECTED_MESSAGE)
|
|
1425
|
+
if 200 <= resp.status_code < 300:
|
|
1426
|
+
return KeyValidationResult(passed=True, message="")
|
|
1427
|
+
return KeyValidationResult(
|
|
1428
|
+
passed=True,
|
|
1429
|
+
message=f"Could not validate API key (received HTTP {resp.status_code}).",
|
|
1430
|
+
)
|
|
1431
|
+
except httpx.TimeoutException:
|
|
1432
|
+
logger.debug("API key validation timed out for '%s'", provider_name)
|
|
1433
|
+
return KeyValidationResult(passed=True, message="Could not validate API key (request timed out).")
|
|
1434
|
+
except Exception as exc:
|
|
1435
|
+
logger.debug("API key validation failed for '%s': %s", provider_name, exc)
|
|
1436
|
+
return KeyValidationResult(passed=True, message=f"Could not validate API key ({exc}).")
|
|
1437
|
+
|
|
1438
|
+
|
|
1439
|
+
def _select_default_model(client: NeMoPlatform, workspace: str) -> str | None:
|
|
1440
|
+
"""Let the user pick a default model from discovered models."""
|
|
1441
|
+
display_models = _get_all_model_choices(client, workspace)
|
|
1442
|
+
if not display_models:
|
|
1443
|
+
console.print(f" {WARN} No models discovered yet. You can set a default later.")
|
|
1444
|
+
return None
|
|
1445
|
+
|
|
1446
|
+
result = prompt_select(
|
|
1447
|
+
"Choose your default model:",
|
|
1448
|
+
choices=display_models,
|
|
1449
|
+
)
|
|
1450
|
+
return result
|
|
1451
|
+
|
|
1452
|
+
|
|
1453
|
+
def _save_default_model(cli_context: CLIContext, model_entity_id: str) -> None:
|
|
1454
|
+
"""Persist the default model to the CLI config file."""
|
|
1455
|
+
context = cli_context.get_sdk_context()
|
|
1456
|
+
Config.write({"default_model": model_entity_id}, context_name=context.context_name)
|
|
1457
|
+
|
|
1458
|
+
|
|
1459
|
+
def _check_ollama_running(host_url: str) -> bool:
|
|
1460
|
+
"""Probe Ollama endpoint to check if it's running."""
|
|
1461
|
+
try:
|
|
1462
|
+
resp = httpx.get(f"{host_url.rstrip('/')}/models", timeout=3.0)
|
|
1463
|
+
return resp.status_code == 200
|
|
1464
|
+
except Exception:
|
|
1465
|
+
return False
|
|
1466
|
+
|
|
1467
|
+
|
|
1468
|
+
# ---------------------------------------------------------------------------
|
|
1469
|
+
# Auto (non-interactive) mode
|
|
1470
|
+
# ---------------------------------------------------------------------------
|
|
1471
|
+
|
|
1472
|
+
|
|
1473
|
+
def _auto_setup(client: NeMoPlatform, workspace: str) -> bool:
|
|
1474
|
+
"""Register a provider from environment variables. Returns True on success."""
|
|
1475
|
+
for key_var, url_var in _AUTO_ENV_VARS:
|
|
1476
|
+
api_key = os.environ.get(key_var)
|
|
1477
|
+
if not api_key:
|
|
1478
|
+
continue
|
|
1479
|
+
|
|
1480
|
+
base_url = os.environ.get(url_var, "").strip() if url_var else ""
|
|
1481
|
+
if base_url:
|
|
1482
|
+
known = _resolve_provider_for_url(base_url)
|
|
1483
|
+
if known:
|
|
1484
|
+
provider_name = known.name
|
|
1485
|
+
else:
|
|
1486
|
+
hostname = urlparse(base_url).hostname or "custom"
|
|
1487
|
+
provider_name = hostname.replace(".", "-")
|
|
1488
|
+
host_url = base_url
|
|
1489
|
+
auth_header_format = known.auth_header_format if known else None
|
|
1490
|
+
default_extra_headers = known.default_extra_headers if known else None
|
|
1491
|
+
else:
|
|
1492
|
+
for p in KNOWN_PROVIDERS:
|
|
1493
|
+
if p.env_var == key_var and p.requires_api_key:
|
|
1494
|
+
provider_name = p.name
|
|
1495
|
+
host_url = p.host_url
|
|
1496
|
+
auth_header_format = p.auth_header_format
|
|
1497
|
+
default_extra_headers = p.default_extra_headers
|
|
1498
|
+
break
|
|
1499
|
+
else:
|
|
1500
|
+
continue
|
|
1501
|
+
|
|
1502
|
+
key_result = _validate_api_key(
|
|
1503
|
+
provider_name,
|
|
1504
|
+
host_url,
|
|
1505
|
+
api_key,
|
|
1506
|
+
auth_header_format=auth_header_format,
|
|
1507
|
+
default_extra_headers=default_extra_headers,
|
|
1508
|
+
)
|
|
1509
|
+
if not key_result.passed:
|
|
1510
|
+
console.print(f" {CROSS} {key_result.message}")
|
|
1511
|
+
console.print(f" Check the value of ${key_var} and try again.")
|
|
1512
|
+
raise typer.Exit(1)
|
|
1513
|
+
if key_result.message:
|
|
1514
|
+
console.print(f" {WARN} {key_result.message}")
|
|
1515
|
+
|
|
1516
|
+
secret_name = f"{provider_name}-api-key"
|
|
1517
|
+
if _secret_exists(client, secret_name, workspace):
|
|
1518
|
+
_update_secret(client, secret_name, api_key, workspace)
|
|
1519
|
+
console.print(f" {CHECK} Updated secret '{secret_name}' (from ${key_var})")
|
|
1520
|
+
else:
|
|
1521
|
+
_create_secret(client, secret_name, api_key, workspace)
|
|
1522
|
+
console.print(f" {CHECK} Created secret '{secret_name}' (from ${key_var})")
|
|
1523
|
+
|
|
1524
|
+
if _provider_exists(client, provider_name, workspace):
|
|
1525
|
+
_update_provider(
|
|
1526
|
+
client,
|
|
1527
|
+
name=provider_name,
|
|
1528
|
+
host_url=host_url,
|
|
1529
|
+
secret_name=secret_name,
|
|
1530
|
+
workspace=workspace,
|
|
1531
|
+
default_extra_headers=default_extra_headers,
|
|
1532
|
+
)
|
|
1533
|
+
console.print(f" {CHECK} Updated provider '{provider_name}' ({host_url})")
|
|
1534
|
+
else:
|
|
1535
|
+
_create_provider(
|
|
1536
|
+
client,
|
|
1537
|
+
name=provider_name,
|
|
1538
|
+
host_url=host_url,
|
|
1539
|
+
secret_name=secret_name,
|
|
1540
|
+
workspace=workspace,
|
|
1541
|
+
auth_header_format=auth_header_format,
|
|
1542
|
+
default_extra_headers=default_extra_headers,
|
|
1543
|
+
)
|
|
1544
|
+
console.print(f" {CHECK} Registered provider '{provider_name}' ({host_url})")
|
|
1545
|
+
|
|
1546
|
+
return True
|
|
1547
|
+
|
|
1548
|
+
return False
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
# ---------------------------------------------------------------------------
|
|
1552
|
+
# Main command
|
|
1553
|
+
# ---------------------------------------------------------------------------
|
|
1554
|
+
|
|
1555
|
+
|
|
1556
|
+
@handle_errors
|
|
1557
|
+
def setup_command(
|
|
1558
|
+
ctx: typer.Context,
|
|
1559
|
+
auto: Annotated[
|
|
1560
|
+
bool,
|
|
1561
|
+
typer.Option("--auto", help="Non-interactive mode: register provider from environment variables"),
|
|
1562
|
+
] = False,
|
|
1563
|
+
workspace: Annotated[
|
|
1564
|
+
str,
|
|
1565
|
+
typer.Option("--workspace", "-w", help="Target workspace"),
|
|
1566
|
+
] = "default",
|
|
1567
|
+
start_services: Annotated[
|
|
1568
|
+
bool | None,
|
|
1569
|
+
typer.Option("--start-services/--no-start-services", help="Start local platform services"),
|
|
1570
|
+
] = None,
|
|
1571
|
+
install_skills: Annotated[
|
|
1572
|
+
bool | None,
|
|
1573
|
+
typer.Option("--install-skills/--no-install-skills", help="Install NeMo skills for coding agents"),
|
|
1574
|
+
] = None,
|
|
1575
|
+
skills_agents: Annotated[
|
|
1576
|
+
str | None,
|
|
1577
|
+
typer.Option(
|
|
1578
|
+
"--skills-agents",
|
|
1579
|
+
help=(
|
|
1580
|
+
"Comma-separated list of agents to install skills for (e.g. 'codex,cursor'). "
|
|
1581
|
+
"Default: all detected. Only applied when --install-skills is set."
|
|
1582
|
+
),
|
|
1583
|
+
),
|
|
1584
|
+
] = None,
|
|
1585
|
+
skills_scope: Annotated[
|
|
1586
|
+
Scope | None,
|
|
1587
|
+
typer.Option(
|
|
1588
|
+
"--skills-scope",
|
|
1589
|
+
help=(
|
|
1590
|
+
"Install scope for skills: 'project' (this repo) or 'user' (home). "
|
|
1591
|
+
"Default: project. Only applied when --install-skills is set."
|
|
1592
|
+
),
|
|
1593
|
+
case_sensitive=False,
|
|
1594
|
+
),
|
|
1595
|
+
] = None,
|
|
1596
|
+
skills_from: Annotated[
|
|
1597
|
+
str | None,
|
|
1598
|
+
typer.Option(
|
|
1599
|
+
"--skills-from",
|
|
1600
|
+
help=(
|
|
1601
|
+
"Comma-separated list of skill sources to install from "
|
|
1602
|
+
"(e.g. 'nemo-platform,nemo-evaluator-plugin'). Use 'nemo-platform' "
|
|
1603
|
+
"for the built-in set. Default: all sources. "
|
|
1604
|
+
"Only applied when --install-skills is set."
|
|
1605
|
+
),
|
|
1606
|
+
),
|
|
1607
|
+
] = None,
|
|
1608
|
+
deploy_agent: Annotated[
|
|
1609
|
+
bool | None,
|
|
1610
|
+
typer.Option("--deploy-agent/--no-deploy-agent", help="Deploy the demo calculator agent"),
|
|
1611
|
+
] = None,
|
|
1612
|
+
ready_timeout: Annotated[
|
|
1613
|
+
int | None,
|
|
1614
|
+
typer.Option(
|
|
1615
|
+
"--ready-timeout",
|
|
1616
|
+
help=f"Seconds to wait for platform readiness (default: {_SERVICE_STARTUP_TIMEOUT_SECONDS})",
|
|
1617
|
+
),
|
|
1618
|
+
] = None,
|
|
1619
|
+
) -> None:
|
|
1620
|
+
"""Set up NeMo Platform: start services, configure a provider, install skills.
|
|
1621
|
+
|
|
1622
|
+
Walks through starting local services, selecting a provider, entering
|
|
1623
|
+
credentials, registering the provider with the platform, picking a
|
|
1624
|
+
default model, installing coding agent skills, and optionally deploying
|
|
1625
|
+
a demo agent.
|
|
1626
|
+
|
|
1627
|
+
Requires an interactive terminal (TTY). In non-interactive contexts
|
|
1628
|
+
(CI, piped input), pass --auto to use environment variables instead.
|
|
1629
|
+
|
|
1630
|
+
Use --auto for non-interactive setup from environment variables
|
|
1631
|
+
(NEMO_DEFAULT_INFERENCE_KEY, NVIDIA_API_KEY, OPENAI_API_KEY,
|
|
1632
|
+
ANTHROPIC_API_KEY, GEMINI_API_KEY).
|
|
1633
|
+
Override the default model with NEMO_DEFAULT_MODEL.
|
|
1634
|
+
|
|
1635
|
+
Examples:
|
|
1636
|
+
nemo setup
|
|
1637
|
+
nemo setup --auto
|
|
1638
|
+
nemo setup --auto --start-services --install-skills --deploy-agent
|
|
1639
|
+
nemo setup --auto --start-services --ready-timeout 360
|
|
1640
|
+
nemo setup --workspace my-workspace
|
|
1641
|
+
nemo setup --no-install-skills --no-deploy-agent
|
|
1642
|
+
"""
|
|
1643
|
+
cli_context: CLIContext = ctx.obj
|
|
1644
|
+
base_url = cli_context.get_base_url()
|
|
1645
|
+
|
|
1646
|
+
console.print("\n[bold cyan]NeMo Platform Setup[/bold cyan]\n")
|
|
1647
|
+
|
|
1648
|
+
if not auto and not is_interactive():
|
|
1649
|
+
console.print(f"\n{CROSS} Detected non-interactive shell. Pass [bold]--auto[/bold] or run in a TTY.\n")
|
|
1650
|
+
raise typer.Exit(1)
|
|
1651
|
+
|
|
1652
|
+
effective_timeout = _SERVICE_STARTUP_TIMEOUT_SECONDS if ready_timeout is None else ready_timeout
|
|
1653
|
+
if effective_timeout <= 0:
|
|
1654
|
+
raise typer.BadParameter("--ready-timeout must be greater than 0", param_hint="--ready-timeout")
|
|
1655
|
+
_maybe_start_services(base_url, auto, start_services, timeout=effective_timeout)
|
|
1656
|
+
|
|
1657
|
+
if not _check_platform_reachable_with_retries(base_url):
|
|
1658
|
+
console.print(f"\n{CROSS} Cannot reach platform at {base_url}")
|
|
1659
|
+
raise typer.Exit(1)
|
|
1660
|
+
|
|
1661
|
+
console.print(f"{CHECK} Platform reachable at {base_url}\n")
|
|
1662
|
+
|
|
1663
|
+
# Ensure the config file exists on disk so later Config.write() calls
|
|
1664
|
+
# (e.g. saving the default model) can find the cluster and context.
|
|
1665
|
+
# Without this, a fresh install (no config.yaml) hits "Cluster
|
|
1666
|
+
# 'default-cluster' does not exist" when _save_default_model runs.
|
|
1667
|
+
_bootstrap_config_if_missing(base_url, workspace)
|
|
1668
|
+
cli_context.reset_sdk_context()
|
|
1669
|
+
|
|
1670
|
+
client = cli_context.get_client()
|
|
1671
|
+
|
|
1672
|
+
try:
|
|
1673
|
+
client.workspaces.retrieve(workspace)
|
|
1674
|
+
except Exception:
|
|
1675
|
+
try:
|
|
1676
|
+
client.workspaces.create(name=workspace)
|
|
1677
|
+
console.print(f" {CHECK} Created workspace '{workspace}'")
|
|
1678
|
+
except Exception as create_err:
|
|
1679
|
+
# Distinguish a race (workspace appeared between retrieve and create)
|
|
1680
|
+
# from a real failure (permissions, server error).
|
|
1681
|
+
try:
|
|
1682
|
+
client.workspaces.retrieve(workspace)
|
|
1683
|
+
except Exception:
|
|
1684
|
+
raise create_err from None
|
|
1685
|
+
|
|
1686
|
+
skills_agents_list = _parse_csv_flag(skills_agents)
|
|
1687
|
+
skills_from_list = _parse_csv_flag(skills_from)
|
|
1688
|
+
|
|
1689
|
+
if auto:
|
|
1690
|
+
_run_auto_mode(
|
|
1691
|
+
cli_context,
|
|
1692
|
+
client,
|
|
1693
|
+
workspace,
|
|
1694
|
+
base_url,
|
|
1695
|
+
install_skills,
|
|
1696
|
+
deploy_agent,
|
|
1697
|
+
skills_agents=skills_agents_list,
|
|
1698
|
+
skills_scope=skills_scope,
|
|
1699
|
+
skills_from=skills_from_list,
|
|
1700
|
+
)
|
|
1701
|
+
else:
|
|
1702
|
+
_run_interactive_mode(
|
|
1703
|
+
cli_context,
|
|
1704
|
+
client,
|
|
1705
|
+
workspace,
|
|
1706
|
+
base_url,
|
|
1707
|
+
install_skills,
|
|
1708
|
+
deploy_agent,
|
|
1709
|
+
skills_agents=skills_agents_list,
|
|
1710
|
+
skills_scope=skills_scope,
|
|
1711
|
+
skills_from=skills_from_list,
|
|
1712
|
+
)
|
|
1713
|
+
|
|
1714
|
+
|
|
1715
|
+
def _run_auto_mode(
|
|
1716
|
+
cli_context: CLIContext,
|
|
1717
|
+
client: NeMoPlatform,
|
|
1718
|
+
workspace: str,
|
|
1719
|
+
base_url: str,
|
|
1720
|
+
install_skills: bool | None,
|
|
1721
|
+
deploy_agent: bool | None,
|
|
1722
|
+
*,
|
|
1723
|
+
skills_agents: list[str] | None = None,
|
|
1724
|
+
skills_scope: Scope | None = None,
|
|
1725
|
+
skills_from: list[str] | None = None,
|
|
1726
|
+
) -> None:
|
|
1727
|
+
"""Non-interactive provider registration from environment variables."""
|
|
1728
|
+
console.print("[bold]Auto-detecting provider from environment...[/bold]\n")
|
|
1729
|
+
if not _auto_setup(client, workspace):
|
|
1730
|
+
console.print(f"{CROSS} No provider credentials found in environment.")
|
|
1731
|
+
env_var_names = ", ".join(key for key, _ in _AUTO_ENV_VARS)
|
|
1732
|
+
console.print(f" Set one of: {env_var_names}")
|
|
1733
|
+
raise typer.Exit(1)
|
|
1734
|
+
|
|
1735
|
+
console.print("\n Waiting for model discovery...")
|
|
1736
|
+
entity_ids: list[str] = []
|
|
1737
|
+
for attempt in range(_MODEL_DISCOVERY_MAX_ROUNDS):
|
|
1738
|
+
deadline = time.time() + _MODEL_DISCOVERY_ROUND_SECONDS
|
|
1739
|
+
while time.time() < deadline:
|
|
1740
|
+
entity_ids = _get_all_model_entity_ids(client, workspace)
|
|
1741
|
+
if entity_ids:
|
|
1742
|
+
break
|
|
1743
|
+
_pause(_MODEL_DISCOVERY_POLL_INTERVAL)
|
|
1744
|
+
if entity_ids:
|
|
1745
|
+
break
|
|
1746
|
+
if attempt < _MODEL_DISCOVERY_MAX_ROUNDS - 1:
|
|
1747
|
+
console.print(f" {WARN} Models not available yet, retrying...")
|
|
1748
|
+
|
|
1749
|
+
default_model = os.environ.get("NEMO_DEFAULT_MODEL", "").strip()
|
|
1750
|
+
if not default_model and entity_ids:
|
|
1751
|
+
default_model = entity_ids[0]
|
|
1752
|
+
|
|
1753
|
+
if default_model:
|
|
1754
|
+
_save_default_model(cli_context, default_model)
|
|
1755
|
+
console.print(f" {CHECK} Default model: {default_model}")
|
|
1756
|
+
else:
|
|
1757
|
+
console.print(f" {WARN} No default model set (no models discovered yet)")
|
|
1758
|
+
console.print(" Run [cyan]nemo setup[/cyan] again after models sync, or set via env var:")
|
|
1759
|
+
console.print(" [cyan]export NEMO_DEFAULT_MODEL=<model>[/cyan]")
|
|
1760
|
+
|
|
1761
|
+
_maybe_install_skills(
|
|
1762
|
+
auto=True,
|
|
1763
|
+
install_skills=install_skills,
|
|
1764
|
+
skills_agents=skills_agents,
|
|
1765
|
+
skills_scope=skills_scope,
|
|
1766
|
+
skills_from=skills_from,
|
|
1767
|
+
)
|
|
1768
|
+
_maybe_deploy_agent(base_url, workspace, auto=True, deploy_agent=deploy_agent, default_model=default_model)
|
|
1769
|
+
|
|
1770
|
+
if _verify_platform_health(base_url):
|
|
1771
|
+
console.print(f"\n{CHECK} [green]Setup complete![/green]")
|
|
1772
|
+
else:
|
|
1773
|
+
raise typer.Exit(1)
|
|
1774
|
+
|
|
1775
|
+
|
|
1776
|
+
def _run_interactive_mode(
|
|
1777
|
+
cli_context: CLIContext,
|
|
1778
|
+
client: NeMoPlatform,
|
|
1779
|
+
workspace: str,
|
|
1780
|
+
base_url: str,
|
|
1781
|
+
install_skills: bool | None,
|
|
1782
|
+
deploy_agent: bool | None,
|
|
1783
|
+
*,
|
|
1784
|
+
skills_agents: list[str] | None = None,
|
|
1785
|
+
skills_scope: Scope | None = None,
|
|
1786
|
+
skills_from: list[str] | None = None,
|
|
1787
|
+
) -> None:
|
|
1788
|
+
"""Walk the user through provider selection, credential entry, and model choice."""
|
|
1789
|
+
try:
|
|
1790
|
+
provider_name, host_url, api_key, auth_header_format, default_extra_headers = _interactive_collect_provider()
|
|
1791
|
+
|
|
1792
|
+
if api_key:
|
|
1793
|
+
console.print("\n Validating API key...")
|
|
1794
|
+
key_result = _validate_api_key(
|
|
1795
|
+
provider_name,
|
|
1796
|
+
host_url,
|
|
1797
|
+
api_key,
|
|
1798
|
+
auth_header_format=auth_header_format,
|
|
1799
|
+
default_extra_headers=default_extra_headers,
|
|
1800
|
+
)
|
|
1801
|
+
if not key_result.passed:
|
|
1802
|
+
console.print(f" {CROSS} {key_result.message}")
|
|
1803
|
+
console.print(" Please check your API key and run [cyan]nemo setup[/cyan] again.")
|
|
1804
|
+
raise typer.Exit(1)
|
|
1805
|
+
if key_result.message:
|
|
1806
|
+
console.print(f" {WARN} {key_result.message}")
|
|
1807
|
+
else:
|
|
1808
|
+
console.print(f" {CHECK} API key validated")
|
|
1809
|
+
|
|
1810
|
+
console.print("\n[bold]Step 3: Register model provider[/bold]\n")
|
|
1811
|
+
_register_provider_interactive(
|
|
1812
|
+
client,
|
|
1813
|
+
provider_name=provider_name,
|
|
1814
|
+
host_url=host_url,
|
|
1815
|
+
api_key=api_key,
|
|
1816
|
+
workspace=workspace,
|
|
1817
|
+
auth_header_format=auth_header_format,
|
|
1818
|
+
default_extra_headers=default_extra_headers,
|
|
1819
|
+
)
|
|
1820
|
+
|
|
1821
|
+
console.print("\n[bold]Step 4: Discover models[/bold]\n")
|
|
1822
|
+
console.print(" Waiting for model discovery...")
|
|
1823
|
+
models = _wait_for_models(client, provider_name, workspace, host_url=host_url)
|
|
1824
|
+
if models:
|
|
1825
|
+
console.print(f" {CHECK} Found {len(models)} model(s)")
|
|
1826
|
+
else:
|
|
1827
|
+
console.print(f" {WARN} No models discovered yet (provider may still be syncing)")
|
|
1828
|
+
|
|
1829
|
+
console.print("\n[bold]Step 5: Choose default model[/bold]\n")
|
|
1830
|
+
fallback_model_choices = _get_all_model_choices(client, workspace) if not models else []
|
|
1831
|
+
if not models and fallback_model_choices:
|
|
1832
|
+
console.print(f" {WARN} Models from existing providers are available, but not from '{provider_name}' yet.")
|
|
1833
|
+
|
|
1834
|
+
default_model = _select_default_model(client, workspace) if models else None
|
|
1835
|
+
if default_model:
|
|
1836
|
+
_save_default_model(cli_context, default_model)
|
|
1837
|
+
console.print(f" {CHECK} Default model set to {_display_model_name(default_model)}")
|
|
1838
|
+
else:
|
|
1839
|
+
console.print(f" {WARN} No default model set for this provider yet.")
|
|
1840
|
+
console.print(" Run [cyan]nemo setup[/cyan] again after models sync")
|
|
1841
|
+
|
|
1842
|
+
console.print("\n[bold]Step 6: Install skills[/bold]\n")
|
|
1843
|
+
_maybe_install_skills(
|
|
1844
|
+
auto=False,
|
|
1845
|
+
install_skills=install_skills,
|
|
1846
|
+
skills_agents=skills_agents,
|
|
1847
|
+
skills_scope=skills_scope,
|
|
1848
|
+
skills_from=skills_from,
|
|
1849
|
+
)
|
|
1850
|
+
|
|
1851
|
+
console.print("\n[bold]Step 7: Demo agent (optional)[/bold]\n")
|
|
1852
|
+
demo_deployed = _maybe_deploy_agent(
|
|
1853
|
+
base_url, workspace, auto=False, deploy_agent=deploy_agent, default_model=default_model
|
|
1854
|
+
)
|
|
1855
|
+
|
|
1856
|
+
_print_onboarding(base_url, provider_name, default_model, demo_deployed=demo_deployed)
|
|
1857
|
+
|
|
1858
|
+
except UserCancelled:
|
|
1859
|
+
console.print(f"\n{WARN} Setup cancelled.")
|
|
1860
|
+
raise typer.Exit(0) from None
|
|
1861
|
+
|
|
1862
|
+
|
|
1863
|
+
def _interactive_collect_provider() -> tuple[str, str, str | None, str | None, dict[str, str] | None]:
|
|
1864
|
+
"""Steps 1-2: select provider and collect credentials.
|
|
1865
|
+
|
|
1866
|
+
Returns (provider_name, host_url, api_key, auth_header_format, default_extra_headers).
|
|
1867
|
+
"""
|
|
1868
|
+
console.print("[bold]Step 1: Choose a model provider[/bold]\n")
|
|
1869
|
+
selected = _select_provider()
|
|
1870
|
+
|
|
1871
|
+
if selected is None:
|
|
1872
|
+
name, host_url, api_key = _prompt_custom_provider()
|
|
1873
|
+
return name, host_url, api_key, None, None
|
|
1874
|
+
|
|
1875
|
+
if selected.name == "ollama":
|
|
1876
|
+
if not _check_ollama_running(selected.host_url):
|
|
1877
|
+
console.print(f"\n {WARN} Ollama does not appear to be running at {selected.host_url}")
|
|
1878
|
+
console.print(" Make sure Ollama is started before using it for inference.")
|
|
1879
|
+
else:
|
|
1880
|
+
console.print(f"\n {CHECK} Ollama detected at {selected.host_url}")
|
|
1881
|
+
|
|
1882
|
+
if selected.requires_api_key:
|
|
1883
|
+
console.print("\n[bold]Step 2: Enter API key[/bold]\n")
|
|
1884
|
+
api_key: str | None = _collect_credential(selected)
|
|
1885
|
+
else:
|
|
1886
|
+
api_key = None
|
|
1887
|
+
|
|
1888
|
+
return selected.name, selected.host_url, api_key, selected.auth_header_format, selected.default_extra_headers
|
|
1889
|
+
|
|
1890
|
+
|
|
1891
|
+
def _render_onboarding_card(value: str) -> None:
|
|
1892
|
+
"""Print a Rich Panel card for the selected onboarding path."""
|
|
1893
|
+
path = _ONBOARDING_PATHS_BY_VALUE.get(value)
|
|
1894
|
+
if path is None:
|
|
1895
|
+
return
|
|
1896
|
+
|
|
1897
|
+
lines: list[str] = []
|
|
1898
|
+
if path.note:
|
|
1899
|
+
lines.append(f" [bold]{path.note}[/bold]")
|
|
1900
|
+
lines.append("")
|
|
1901
|
+
lines.append(" [bold]Ask your coding agent:[/bold]")
|
|
1902
|
+
lines.append(f' [cyan]"{path.skill_prompt}"[/cyan]')
|
|
1903
|
+
lines.append("")
|
|
1904
|
+
lines.append(f" [bold]Docs:[/bold] [link={path.docs_url}]{path.docs_url}[/link]")
|
|
1905
|
+
|
|
1906
|
+
console.print(
|
|
1907
|
+
Panel(
|
|
1908
|
+
"\n".join(lines),
|
|
1909
|
+
title=f"[bold]{path.label}[/bold]",
|
|
1910
|
+
title_align="left",
|
|
1911
|
+
border_style="green",
|
|
1912
|
+
box=box.ROUNDED,
|
|
1913
|
+
padding=(1, 1),
|
|
1914
|
+
)
|
|
1915
|
+
)
|
|
1916
|
+
|
|
1917
|
+
|
|
1918
|
+
def _print_onboarding(
|
|
1919
|
+
base_url: str,
|
|
1920
|
+
provider_name: str,
|
|
1921
|
+
default_model: str | None,
|
|
1922
|
+
*,
|
|
1923
|
+
demo_deployed: bool = False,
|
|
1924
|
+
) -> None:
|
|
1925
|
+
"""Print setup summary, then present goal-oriented onboarding paths."""
|
|
1926
|
+
if not _verify_platform_health(base_url):
|
|
1927
|
+
raise typer.Exit(1)
|
|
1928
|
+
|
|
1929
|
+
console.print(f"\n{CHECK} [green bold]Setup complete![/green bold]")
|
|
1930
|
+
console.print(f" Provider: {provider_name}")
|
|
1931
|
+
if default_model:
|
|
1932
|
+
console.print(f" Default model: {_display_model_name(default_model)}")
|
|
1933
|
+
if demo_deployed:
|
|
1934
|
+
console.print(f" Demo agent: {_DEMO_AGENT_NAME}")
|
|
1935
|
+
|
|
1936
|
+
console.print("\n[bold cyan]Getting started[/bold cyan]")
|
|
1937
|
+
|
|
1938
|
+
options = [(p.value, p.label) for p in ONBOARDING_PATHS]
|
|
1939
|
+
selected = prompt_choice(
|
|
1940
|
+
"Select how you would like to get started",
|
|
1941
|
+
options,
|
|
1942
|
+
default="optimize",
|
|
1943
|
+
indent=2,
|
|
1944
|
+
)
|
|
1945
|
+
|
|
1946
|
+
console.print()
|
|
1947
|
+
_render_onboarding_card(selected)
|