Getting Started: Fetching Remote Resources
What you will build
You will connect flyte-sdk to a Flyte deployment, discover task versions with remote.Task.listall, fetch a deployed task definition with remote.Task.get, and inspect its TaskDetails. You will then use the same lazy task reference with flyte.with_runcontext(...).run.aio, inspect the returned remote.Run, and retrieve details for an existing run.
Prerequisites
You need:
- flyte-sdk installed and access to a Flyte deployment endpoint or API key;
- a project and domain in which to list tasks and runs; and
- credentials and transport settings accepted by
flyte.init.
Remote entities call ensure_client() and read the organization, project, domain, and listing batch size from Flyte's initialized configuration. Initialize before calling any remote API method. flyte.init accepts either endpoint or api_key; initialization raises an InitializationError if neither is supplied.
import flyte
flyte.init(
endpoint="https://flyte.example.com",
org="my-org",
project="my-project",
domain="development",
)
After this call, the default project and domain are used by task and run requests unless a method supplies an override. The endpoint and authentication settings are used to create the remote client and its gRPC service stubs.
Discover deployed tasks
List task metadata
Import Task from the public flyte.remote package and iterate over Task.listall. The production get task command uses the same call:
from flyte.remote import Task
for task in Task.listall(limit=100):
print(task.name, task.version)
Each item is a lightweight Task wrapping the task-list protobuf. Its name and version properties identify the deployed version, but the list result is not the complete task definition.
You can narrow the request by exact task name or by an environment prefix, and can override the initialized project or domain:
from flyte.remote import Task
for task in Task.listall(
by_task_name="my_task",
project="my-project",
domain="development",
sort_by=("created_at", "desc"),
limit=10,
):
print(task.name, task.version)
by_task_name creates an equality filter on name. by_task_env creates a contains filter for "<environment>." in the task name. Task listing follows server pagination tokens and stops at the requested limit. If the limit exceeds the configured batch_size (whose default is 1000), requests are split using that batch size.
The CLI uses an exact-name filter when a name is supplied without a version, and otherwise lists all tasks:
console.print(common.format("Tasks", Task.listall(by_task_name=name, limit=limit), cfg.output_format))
Here name, limit, console, common, and cfg are the command's existing CLI values; the line is from cli/_get.py.
Fetch and inspect one task
Keep lookup lazy until you need the definition
A single task lookup requires either an explicit version or an auto_version value. Task.get delegates to TaskDetails.get and returns a LazyEntity; it does not immediately call GetTaskDetails.
from flyte.remote import Task
reference = Task.get(name="my_task", version="20240201")
print(reference)
The visible result identifies the object as a future, using the task name (for example, Future for task with name my_task). To download the definition, call fetch:
task_details = reference.fetch()
print(task_details.name)
print(task_details.version)
print(task_details.task_type)
print(task_details.required_args)
fetch() invokes the asynchronous getter once under a lock and caches the resulting TaskDetails. The syncified method also has an asynchronous form, reference.fetch.aio(). TaskDetails then exposes the identity, task type, default input names, and required input names from the remote specification.
The fetched definition contains additional inspection data:
print(task_details.interface)
print(task_details.cache)
print(task_details.resources)
print(task_details.secrets)
interface is converted with flyte.types.guess_interface and cached as a property. cache is derived from the task metadata, resources is a (requests, limits) tuple for a container task (or () without a container), and secrets is a list of secret keys.
The CLI's versioned lookup follows this same lazy-to-materialized sequence:
v = Task.get(name=name, version=version)
t = v.fetch()
console.print(common.format(f"Task {name}", [t], "json"))
This is the path in cli/_get.py; the command currently requires both a name and version for a specific-task lookup.
Resolve a version automatically
Use auto_version="latest" when you want TaskDetails.get to first list the named task, sort by created_at descending, and select one result:
latest = Task.get(name="my_task", auto_version="latest")
latest_details = latest.fetch()
print(latest_details.version)
If no matching task is found, fetching the lazy reference raises flyte.errors.ReferenceTaskError. The only accepted automatic values are "latest" and "current". auto_version="current" obtains the version from flyte.ctx().version and is valid only inside a Flyte task context; outside one, fetching raises ValueError.
Submit a remote task and inspect its run
A LazyEntity can be passed directly to the run machinery. The CLI's remote-run path resolves the task and submits it through a remote run context:
async def _run():
import flyte
import flyte.remote
task = flyte.remote.Task.get(self.task_name, version=self.version, auto_version="latest")
r = await flyte.with_runcontext(
copy_style=self.run_args.copy_style,
mode="local" if self.run_args.local else "remote",
name=self.run_args.name,
).run.aio(task, **ctx.params)
This is the production code from cli/_run.py: self.task_name, self.version, self.run_args, and ctx.params are values supplied by that Click command. In a remote invocation, the keyword arguments must match the fetched task interface. The runner fetches a lazy remote task when it needs its details.
The returned Run represents the execution and exposes its identity and state:
if isinstance(r, Run) and r.action is not None:
print(r.name)
print(r.phase)
print(r.url)
Run constructs its Action from pb2.action; constructing one without an action raises RuntimeError("Run does not have an action"). url is generated from the initialized client's endpoint and the run's project, domain, and name.
Wait for completion and inspect the execution:
await r.wait.aio(quiet=True)
print(r.done())
run_details = await r.details.aio()
outputs = await run_details.outputs()
wait delegates to Action.wait and accepts wait_for="terminal" or wait_for="running". Run.details downloads RunDetails the first time and caches it on the Run; subsequent calls reuse that object. RunDetails.outputs() delegates to its ActionDetails and returns ActionOutputs.
To display logs, use the syncified show_logs method. The CLI follows a newly created run this way:
await r.show_logs.aio(max_lines=30, show_ts=True, raw=False)
Run.watch delegates to the underlying action's watch behavior, while phase, raw_phase, done, and url provide direct run inspection properties.
Retrieve an existing run
Use Run.get when you already know the run name. The synchronous and asynchronous forms are both available because the method is syncified:
from flyte.remote import Run
run = Run.get(name="my-run")
print(run.name, run.phase)
run_details = run.details()
print(run_details.name, run_details.task_name)
The lookup resolves RunDetails.get, reconstructs a Run from the returned action data, and retains those details for the first run.details() call. In asynchronous code, the corresponding production hybrid-run path is:
from flyte._internal.runtime.task_serde import extract_code_bundle
from flyte.remote import Run
run = await Run.get.aio(name=name)
if run:
run_details = await run.details.aio()
spec = run_details.action_details.pb2.resolved_task_spec
code_bundle = extract_code_bundle(spec)
That code is used by flyte-sdk's hybrid runner to recover the resolved task specification and code bundle for an existing run.
For a list of existing executions, use Run.listall:
from flyte.remote import Run
for run in Run.listall(limit=100):
print(run.name, run.phase, run.url)
Run listing uses server pagination and enforces the requested limit; each request uses a maximum request limit of 100. The CLI's get run command uses Run.listall(limit=limit) when no run name is supplied. For a named run, that command uses RunDetails.get(name=name) directly to format detailed output.
Operational details to keep in mind
- Reference-task calls are keyword-only.
TaskDetails.__call__rejects positional arguments withReferenceTaskErrorand checks the number of supplied arguments againstrequired_args. - Calling a fetched reference task is intended for an active Flyte task context with a controller. In that context, the controller receives the task protobuf and the
max_inline_io_bytesvalue, which defaults to10 * 1024 * 1024. The local/non-controller path is not a completed execution path in the source. LazyEntity.overridefetches the definition and mutates its protobuf in place. Environment overrides clear and replace the existing container environment; short name, secrets, resources, retries, and timeout are supported byTaskDetails.override.Run.sync()currently returnsself; its docstring identifies it as a placeholder and it does not refresh remote state.Run.abort()callsAbortRun. ANOT_FOUNDgRPC response is treated as already terminated and returns silently; other RPC errors are re-raised.
You now have the complete first-contact flow: initialize the client, list lightweight Task records, materialize a TaskDetails only when needed, resolve latest versions when appropriate, submit a lazy task through a remote run context, and use Run or RunDetails to inspect executions. Next, use the task interface and required-input metadata to construct valid keyword arguments for a deployed task, then use wait, show_logs, details, and outputs as the execution progresses.