Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2c1b382
feat(client): Add new Jobs client
matthewgrossman Jul 6, 2026
db71c48
Merge branch 'main' into mgrossman/aircore-874-migrate-jobs-service-t…
matthewgrossman Jul 8, 2026
068cd8c
Merge branch 'main' into mgrossman/aircore-874-migrate-jobs-service-t…
matthewgrossman Jul 8, 2026
e45c54a
fixes
matthewgrossman Jul 9, 2026
22dd9b1
Merge branch 'main' into mgrossman/aircore-874-migrate-jobs-service-t…
matthewgrossman Jul 9, 2026
19f241e
add test client
matthewgrossman Jul 9, 2026
241f430
Merge branch 'main' into mgrossman/aircore-874-migrate-jobs-service-t…
matthewgrossman Jul 13, 2026
c2acdf1
fix(jobs): restore OpenAPI spec parity and vendor quickstart CLI
maxdubrinsky Jul 13, 2026
0406d93
fix(jobs): correct supports_persistent_storage and address review nits
maxdubrinsky Jul 13, 2026
2968b97
fix diff
matthewgrossman Jul 14, 2026
538e654
Merge branch 'main' into mgrossman/aircore-874-migrate-jobs-service-t…
matthewgrossman Jul 14, 2026
3302606
code review
matthewgrossman Jul 14, 2026
8fbc56e
add tests
matthewgrossman Jul 14, 2026
50de89b
fix(jobs): add controllers test dir to pytest pythonpath
maxdubrinsky Jul 14, 2026
831bd1e
fix pagination
matthewgrossman Jul 14, 2026
eeb7273
Merge branch 'mgrossman/aircore-874-migrate-jobs-service-to-nemoclien…
matthewgrossman Jul 14, 2026
afd6c8b
fix metadata parsing
matthewgrossman Jul 14, 2026
2ba4d43
update comments
matthewgrossman Jul 14, 2026
8284e2a
remove covariant types
matthewgrossman Jul 14, 2026
eb380c8
simplify
matthewgrossman Jul 14, 2026
9261020
Merge branch 'main' into mgrossman/aircore-874-migrate-jobs-service-t…
matthewgrossman Jul 14, 2026
e18e173
coderabbit
matthewgrossman Jul 14, 2026
19dcb6d
fix(client): support pagination metadata on Python 3.11
matthewgrossman Jul 14, 2026
eff7a48
Merge branch 'main' into mgrossman/aircore-874-migrate-jobs-service-t…
matthewgrossman Jul 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,9 @@ def _run_job_diagnostic(port: int, registry: str, tag: str, *, admin_email: str
import uuid

from nemo_platform import NeMoPlatform
from nemo_platform_plugin.client.adapter import client_from_platform
from nemo_platform_plugin.jobs.client import JobsClient
from nemo_platform_plugin.jobs.types import CreatePlatformJobRequest

# When auth is enabled, use an unsigned JWT for the admin principal.
default_headers = None
Expand Down Expand Up @@ -997,29 +1000,32 @@ def _run_job_diagnostic(port: int, registry: str, tag: str, *, admin_email: str
job_name = f"diagnostic-{uuid.uuid4().hex[:8]}"
console.print(f" • Creating diagnostic job: {job_name}")

job = client.jobs.create(
platform_spec={
"steps": [
{
"name": "diagnostic",
"executor": {
"provider": "cpu",
"container": {
"image": cpu_image,
"entrypoint": [
"python",
"-c",
"import sys; print(f'Python {sys.version}'); print('Job system is working correctly!')",
],
jobs_client = client_from_platform(client, JobsClient)
job = jobs_client.create_job(
body=CreatePlatformJobRequest(
platform_spec={
"steps": [
{
"name": "diagnostic",
"executor": {
"provider": "cpu",
"container": {
"image": cpu_image,
"entrypoint": [
"python",
"-c",
"import sys; print(f'Python {sys.version}'); print('Job system is working correctly!')",
],
},
},
},
}
]
},
source="quickstart-doctor",
spec={},
name=job_name,
)
}
]
},
source="quickstart-doctor",
spec={},
name=job_name,
)
).data()

console.print(" • Waiting for job to complete...")

Expand All @@ -1028,10 +1034,10 @@ def _run_job_diagnostic(port: int, registry: str, tag: str, *, admin_email: str
poll_interval = 2
elapsed = 0
status = "pending"
job_status = client.jobs.retrieve(job.name)
job_status = jobs_client.get_job(name=job.name).data()

while elapsed < max_wait:
job_status = client.jobs.retrieve(job.name)
job_status = jobs_client.get_job(name=job.name).data()
status = job_status.status

if status in ("completed", "error", "cancelled"):
Expand All @@ -1058,9 +1064,9 @@ def _run_job_diagnostic(port: int, registry: str, tag: str, *, admin_email: str
# Fetch and display logs
console.print("\n [bold]Job output:[/bold]")
try:
logs = client.jobs.get_logs(job.name)
logs = jobs_client.list_job_logs(name=job.name)
log_lines = []
for log_entry in logs:
for log_entry in logs.items():
if hasattr(log_entry, "message"):
log_lines.append(log_entry.message)

Expand All @@ -1075,7 +1081,7 @@ def _run_job_diagnostic(port: int, registry: str, tag: str, *, admin_email: str
# Clean up the job (only if successful)
if status == "completed":
try:
client.jobs.delete(job.name)
jobs_client.delete_job(name=job.name)
except Exception:
pass # Ignore cleanup errors
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ def make_sync_resource(platform: NeMoPlatform) -> NemoClient:

from nemo_platform import AsyncNeMoPlatform, NeMoPlatform
from nemo_platform_plugin.client.client import AsyncNemoClient, NemoClient
from nemo_platform_plugin.client.types import RetryPolicy

SyncT = TypeVar("SyncT", bound=NemoClient)
AsyncT = TypeVar("AsyncT", bound=AsyncNemoClient)
Expand Down Expand Up @@ -50,9 +51,23 @@ def client_from_platform(
_skip = {"accept", "accept-encoding", "connection", "user-agent", "host"}
headers = {k: v for k, v in platform._client.headers.items() if k.lower() not in _skip} # type: ignore[union-attr]

retry = RetryPolicy(max_retries=platform.max_retries)
if isinstance(platform, AsyncNeMoPlatform):
if not issubclass(client_cls, AsyncNemoClient):
raise TypeError("AsyncNeMoPlatform requires an AsyncNemoClient class")
return client_cls(
base_url=str(platform.base_url).rstrip("/"),
workspace=platform.workspace,
default_headers=headers or None,
retry=retry,
http_client=platform._client,
)
if not issubclass(client_cls, NemoClient):
raise TypeError("NeMoPlatform requires a NemoClient class")
return client_cls(
base_url=str(platform.base_url).rstrip("/"),
workspace=platform.workspace,
default_headers=headers or None,
http_client=platform._client, # type: ignore[arg-type]
retry=retry,
http_client=platform._client,
)
Loading
Loading