Concurrency and Async
Tinker client methods return an APIFuture which supports both asyncio and synchronous contexts:
await client.method(...)in asyncio contextsclient.method(...).result()in sync contexts
Methods that create other clients are also async, so make sure to use await or .result() before using sampling and training clients:
sampling_client = await service_client.create_sampling_client(...)
training_client = await service_client.create_lora_training_client(...)
APIFutures also easily interoperate with standard Python asyncio and concurrency utilities:
- Use
api_future.coroutine()to create a coroutine that can be used with Python built-ins likeasyncio.create_taskandasyncio.gather. - Use
api_future.future()to create a standardconcurrent.futures.Future.
Pipeline training operations
See Clock Cycles & Pipelining for more details on how Tinker clock cycles work behind the scenes.
The general pattern is to submit both training operations in order before waiting:
# no await, just submit. Tinker guarantees execution order.
forward_backward_future = training_client.forward_backward(batch, "cross_entropy")
optim_future = training_client.optim_step(adam_params)
# await the futures after submitting so that they are on the same clock cycle
forward_backward_result = await forward_backward_future
optim_result = await optim_future
Migration guide for 0.37.0 and below
Prior to 0.38.0, Tinker SDK methods did not consistently return APIFutures, so there were explicitly _async variants of many methods. To migrate, follow the below instructions (or send them to a coding agent).
Client creation
Async
# old
sampling_client = await service_client.create_sampling_client_async(...)
training_client = await service_client.create_lora_training_client_async(...)
# new
sampling_client = await service_client.create_sampling_client(...)
training_client = await service_client.create_lora_training_client(...)
Sync
# old
sampling_client = service_client.create_sampling_client(...)
training_client = service_client.create_lora_training_client(...)
# new
sampling_client = service_client.create_sampling_client(...).result()
training_client = service_client.create_lora_training_client(...).result()
Rest Client
# old
rest_client = service_client.create_rest_client(...)
# new
# for managing runs
rest_client = tinker.RestClient()
# for admin operations like billing
admin_client = tinker.AdminClient()
Training Operations
Async
# old
fwd_bwd_fut = await training_client.forward_backward_async(batch, "cross_entropy")
optim_fut = await training_client.optim_step_async(adam_params)
await fwd_bwd_fut
await optim_fut
# new
fwd_bwd_fut = training_client.forward_backward(batch, "cross_entropy")
optim_fut = training_client.optim_step(adam_params)
await fwd_bwd_fut
await optim_fut
Sync
# old
fwd_bwd_fut = training_client.forward_backward(batch, "cross_entropy")
optim_fut = training_client.optim_step(adam_params)
fwd_bwd_fut.result()
optim_fut.result()
# new: no change
fwd_bwd_fut = training_client.forward_backward(batch, "cross_entropy")
optim_fut = training_client.optim_step(adam_params)
fwd_bwd_fut.result()
optim_fut.result()
Sampling
Async
# old
response = await sampling_client.sample_async(prompt, num_samples=1, sampling_params=params)
logprobs = await sampling_client.compute_logprobs_async(prompt)
# new
response = await sampling_client.sample(prompt, num_samples=1, sampling_params=params)
logprobs = await sampling_client.compute_logprobs(prompt)
Sync
# old
response = sampling_client.sample(prompt, num_samples=1, sampling_params=params).result()
logprobs = sampling_client.compute_logprobs(prompt).result()
# new: no change
response = sampling_client.sample(prompt, num_samples=1, sampling_params=params).result()
logprobs = sampling_client.compute_logprobs(prompt).result()
sample() and compute_logprobs() used to return a concurrent.futures.Future. Calling its methods, such as done() or add_done_callback(), on the returned future still works but warns; call .future() first instead.
Concurrent requests with asyncio.gather
# old
responses = await asyncio.gather(
*[sampling_client.sample_async(prompt, num_samples=1, sampling_params=params) for prompt in prompts]
)
# new
responses = await asyncio.gather(
*[sampling_client.sample(prompt, num_samples=1, sampling_params=params).coroutine() for prompt in prompts]
)
Each sample() call submits its request immediately; .coroutine() only changes how you wait for it.
Sampling clients from a training client
save_weights_and_get_sampling_client() and TrainingClient.create_sampling_client() now return a future of the client, like the ServiceClient factories.
# old (async)
sampling_client = await training_client.save_weights_and_get_sampling_client_async(name="step-100")
# new (async)
sampling_client = await training_client.save_weights_and_get_sampling_client(name="step-100")
# old (sync)
sampling_client = training_client.save_weights_and_get_sampling_client(name="step-100")
# new (sync)
sampling_client = training_client.save_weights_and_get_sampling_client(name="step-100").result()
Full Replacement table
The replacements use await; in synchronous code, call .result() on the returned future instead. Deprecated methods still run but raise a FutureWarning; methods marked removed no longer exist.
| Previous API | Replacement |
|---|---|
await training_client.get_info_async() |
training_client.get_info() (no network request) |
await sampling_client.get_base_model_async() |
sampling_client.get_base_model() (no network request) |
await service_client.create_lora_training_client_async(...) |
await service_client.create_lora_training_client(...) |
await service_client.create_training_client_from_state_async(path) |
await service_client.create_training_client_from_state(path) |
await service_client.create_training_client_from_state_with_optimizer_async(path) |
await service_client.create_training_client_from_state_with_optimizer(path) |
await service_client.create_sampling_client_async(...) |
await service_client.create_sampling_client(...) |
await training_client.create_sampling_client_async(path) |
await training_client.create_sampling_client(path) |
await training_client.save_weights_and_get_sampling_client_async(...) |
await training_client.save_weights_and_get_sampling_client(...) |
training_client = service_client.create_lora_training_client(...) |
training_client = service_client.create_lora_training_client(...).result() |
training_client = service_client.create_training_client_from_state(path) |
training_client = service_client.create_training_client_from_state(path).result() |
training_client = service_client.create_training_client_from_state_with_optimizer(path) |
training_client = service_client.create_training_client_from_state_with_optimizer(path).result() |
sampling_client = service_client.create_sampling_client(...) |
sampling_client = service_client.create_sampling_client(...).result() |
sampling_client = training_client.create_sampling_client(path) |
sampling_client = training_client.create_sampling_client(path).result() |
sampling_client = training_client.save_weights_and_get_sampling_client(...) |
sampling_client = training_client.save_weights_and_get_sampling_client(...).result() |
await sampling_client.sample_async(...) |
await sampling_client.sample(...) |
await sampling_client.compute_logprobs_async(...) |
await sampling_client.compute_logprobs(...) |
fut = await training_client.forward_async(...) |
fut = training_client.forward(...) |
fut = await training_client.forward_backward_async(...) |
fut = training_client.forward_backward(...) |
fut = await training_client.forward_backward_custom_async(...) |
fut = training_client.forward_backward_custom(...) |
fut = await training_client.optim_step_async(...) |
fut = training_client.optim_step(...) |
fut = await training_client.save_state_async(...) |
fut = training_client.save_state(...) |
fut = await training_client.save_weights_for_sampler_async(...) |
fut = training_client.save_weights_for_sampler(...) |
fut = await training_client.save_weights_external_async(...) |
fut = training_client.save_weights_external(...) |
training_client.load_state(path) / load_state_async(path) |
training_client = await service_client.create_training_client_from_state(path) |
training_client.load_state_with_optimizer(path) / load_state_with_optimizer_async(path) |
training_client = await service_client.create_training_client_from_state_with_optimizer(path) |
service_client.get_server_capabilities() / await service_client.get_server_capabilities_async() |
await rest_client.get_server_capabilities() |
service_client.create_rest_client() |
rest_client = tinker.RestClient() |
await rest_client.get_training_run_async(training_run_id) |
await rest_client.get_training_run(training_run_id) |
await rest_client.get_training_run_by_tinker_path_async(tinker_path) |
await rest_client.get_training_run_by_tinker_path(tinker_path) |
await rest_client.list_training_runs_async(...) |
await rest_client.list_training_runs(...) |
await rest_client.list_checkpoints_async(training_run_id) |
await rest_client.list_checkpoints(training_run_id) |
await rest_client.list_user_checkpoints_async(...) |
await rest_client.list_user_checkpoints(...) |
await rest_client.get_external_weights_urls_async(tinker_path) |
await rest_client.get_external_weights_urls(tinker_path) |
await rest_client.delete_checkpoint_async(training_run_id, checkpoint_id) |
await rest_client.delete_checkpoint(training_run_id, checkpoint_id) |
await rest_client.delete_checkpoint_from_tinker_path_async(tinker_path) |
await rest_client.delete_checkpoint_from_tinker_path(tinker_path) |
await rest_client.copy_weights_async(path, ...) |
await rest_client.copy_weights(path, ...) |
await rest_client.publish_checkpoint_from_tinker_path_async(tinker_path) |
await rest_client.publish_checkpoint_from_tinker_path(tinker_path) |
await rest_client.unpublish_checkpoint_from_tinker_path_async(tinker_path) |
await rest_client.unpublish_checkpoint_from_tinker_path(tinker_path) |
await rest_client.set_checkpoint_ttl_from_tinker_path_async(tinker_path, ttl_seconds) |
await rest_client.set_checkpoint_ttl_from_tinker_path(tinker_path, ttl_seconds) |
await rest_client.get_session_async(session_id) |
await rest_client.get_session(session_id) |
await rest_client.list_sessions_async(...) |
await rest_client.list_sessions(...) |
await rest_client.assign_session_project_async(session_id, project_id) |
await rest_client.assign_session_project(session_id, project_id) |
await rest_client.export_session_trace_async(session_id) |
await rest_client.export_session_trace(session_id) |
await rest_client.get_sampler_async(sampler_id) |
await rest_client.get_sampler(sampler_id) |
await rest_client.search_sessions_async(request) (removed) |
await rest_client.search_sessions(request) |
rest_client.get_billing_usage(...) / get_billing_usage_async(...) |
await admin_client.get_billing_usage(...) |
rest_client.get_current_checkpoint_storage_usage() / get_current_checkpoint_storage_usage_async() |
await admin_client.get_current_checkpoint_storage_usage() |
rest_client.get_audit_log(...) / get_audit_log_async(...) |
await admin_client.get_audit_log(...) |
await admin_client.get_billing_usage_async(...) |
await admin_client.get_billing_usage(...) |
await admin_client.get_current_checkpoint_storage_usage_async() |
await admin_client.get_current_checkpoint_storage_usage() |
await admin_client.get_audit_log_async(...) |
await admin_client.get_audit_log(...) |
done() / add_done_callback() on a RestClient or AdminClient result, or passing it to concurrent.futures.wait() / as_completed() (removed) |
Call .future() on the result first |
done() / add_done_callback() on a sample() or compute_logprobs() result |
Call .future() on the result first |
rest_client.get_checkpoint_archive_url(...) / get_checkpoint_archive_url_from_tinker_path(...) (removed in 0.36.0) |
Export with save_weights_external, then use get_external_weights_urls; see Download weights. |
For the training rows, keep awaiting fut as before, and submit the next operation before awaiting, as in Pipeline training operations.