Current section
Files
Jump to
Current section
Files
examples/repomix-output.xml
This file is a merged representation of the entire codebase, combined into a single document by Repomix.
The content has been processed where content has been compressed (code blocks are separated by ⋮---- delimiter).
<file_summary>
This section contains a summary of this file.
<purpose>
This file contains a packed representation of the entire repository's contents.
It is designed to be easily consumable by AI systems for analysis, code review,
or other automated processes.
</purpose>
<file_format>
The content is organized as follows:
1. This summary section
2. Repository information
3. Directory structure
4. Repository files (if enabled)
5. Multiple file entries, each consisting of:
- File path as an attribute
- Full contents of the file
</file_format>
<usage_guidelines>
- This file should be treated as read-only. Any changes should be made to the
original repository files, not this packed version.
- When processing this file, use the file path to distinguish
between different files in the repository.
- Be aware that this file may contain sensitive information. Handle it with
the same level of security as you would the original repository.
</usage_guidelines>
<notes>
- Some files may have been excluded based on .gitignore rules and Repomix's configuration
- Binary files are not included in this packed representation. Please refer to the Repository Structure section for a complete list of file paths, including binary files
- Files matching patterns in .gitignore are excluded
- Files matching default ignore patterns are excluded
- Content has been compressed - code blocks are separated by ⋮---- delimiter
- Files are sorted by Git change count (files with more changes are at the bottom)
</notes>
</file_summary>
<directory_structure>
async_client_creation.exs
checkpoint_download.exs
checkpoints_management.exs
cli_run_prompt_file.exs
cli_run_text.exs
sampling_basic.exs
sessions_management.exs
training_loop.exs
</directory_structure>
<files>
This section contains the contents of the repository's files.
<file path="async_client_creation.exs">
defmodule Tinkex.Examples.AsyncClientCreation do
@moduledoc """
Example demonstrating async client creation patterns.
Shows how to:
- Create multiple sampling clients concurrently
- Use Task.await_many for parallel operations
"""
alias Tinkex.{ServiceClient, SamplingClient, Config}
def run do
IO.puts("=== Tinkex Async Client Creation Example ===\n")
{:ok, _} = Application.ensure_all_started(:tinkex)
base_url =
System.get_env("TINKER_BASE_URL") ||
Application.get_env(
:tinkex,
:base_url,
"https://tinker.thinkingmachines.dev/services/tinker-prod"
)
config =
Config.new(
api_key: System.get_env("TINKER_API_KEY") || raise("TINKER_API_KEY required"),
base_url: base_url
)
# Get checkpoint paths from environment or use single base model
checkpoint_paths =
case System.get_env("TINKER_CHECKPOINT_PATHS") do
nil -> []
paths -> String.split(paths, ",")
end
{:ok, service_pid} = ServiceClient.start_link(config: config)
if length(checkpoint_paths) > 0 do
create_multiple_clients(service_pid, checkpoint_paths)
else
create_single_client_async(service_pid)
end
GenServer.stop(service_pid)
IO.puts("\n=== Example Complete ===")
end
defp create_single_client_async(service_pid) do
IO.puts("Creating sampling client asynchronously...")
base_model = System.get_env("TINKER_BASE_MODEL") || "meta-llama/Llama-3.2-1B"
task = ServiceClient.create_sampling_client_async(service_pid, base_model: base_model)
IO.puts("Task created, awaiting result...")
case Task.await(task, 30_000) do
{:ok, pid} ->
IO.puts("✓ Sampling client created: #{inspect(pid)}")
GenServer.stop(pid)
{:error, reason} ->
IO.puts("✗ Failed: #{inspect(reason)}")
end
end
defp create_multiple_clients(service_pid, checkpoint_paths) do
IO.puts("Creating #{length(checkpoint_paths)} sampling clients concurrently...\n")
# Start timing
start_time = System.monotonic_time(:millisecond)
# Create tasks for all clients
tasks =
Enum.map(checkpoint_paths, fn path ->
IO.puts(" Starting task for: #{path}")
SamplingClient.create_async(service_pid, model_path: path)
end)
# Wait for all to complete
IO.puts("\nAwaiting all tasks...")
results = Task.await_many(tasks, 60_000)
# Calculate time
elapsed = System.monotonic_time(:millisecond) - start_time
# Report results
IO.puts("\nResults (#{elapsed}ms total):")
Enum.zip(checkpoint_paths, results)
|> Enum.each(fn {path, result} ->
case result do
{:ok, pid} ->
IO.puts(" ✓ #{path} -> #{inspect(pid)}")
GenServer.stop(pid)
{:error, reason} ->
IO.puts(" ✗ #{path} -> #{inspect(reason)}")
end
end)
successes =
Enum.count(results, fn
{:ok, _} -> true
_ -> false
end)
IO.puts("\nSuccess: #{successes}/#{length(results)}")
end
end
Tinkex.Examples.AsyncClientCreation.run()
</file>
<file path="checkpoint_download.exs">
defmodule Tinkex.Examples.CheckpointDownloadExample do
@moduledoc """
Example demonstrating checkpoint download functionality.
Shows how to:
- Download and extract checkpoint archive
- Track progress during download
"""
alias Tinkex.{ServiceClient, RestClient, CheckpointDownload, Config, Error}
def run do
IO.puts("=== Tinkex Checkpoint Download Example ===\n")
{:ok, _} = Application.ensure_all_started(:tinkex)
base_url =
System.get_env("TINKER_BASE_URL") ||
Application.get_env(
:tinkex,
:base_url,
"https://tinker.thinkingmachines.dev/services/tinker-prod"
)
config =
Config.new(
api_key: System.get_env("TINKER_API_KEY") || raise("TINKER_API_KEY required"),
base_url: base_url
)
{:ok, service_pid} = ServiceClient.start_link(config: config)
{:ok, rest_client} = ServiceClient.create_rest_client(service_pid)
{checkpoint_path, path_source} =
case resolve_checkpoint_path(rest_client, service_pid) do
{:ok, path, source} ->
{path, source}
{:error, :no_checkpoints} ->
raise """
No checkpoints found for this account. Set TINKER_CHECKPOINT_PATH (e.g., tinker://run-123/weights/0001)
or create a checkpoint via training before running the example.
"""
{:error, :no_available_checkpoints} ->
raise """
Could not find a downloadable checkpoint automatically. The most recent entries returned 404.
Set TINKER_CHECKPOINT_PATH to a known-good checkpoint path and rerun the example.
"""
{:error, {:api_error, %Error{} = error}} ->
raise """
Failed to discover checkpoints automatically: #{Error.format(error)}.
Set TINKER_CHECKPOINT_PATH manually or rerun later.
"""
{:error, {:api_error, other}} ->
raise """
Failed to discover checkpoints automatically: #{inspect(other)}.
Set TINKER_CHECKPOINT_PATH manually or rerun later.
"""
end
if path_source == :auto do
IO.puts(
"TINKER_CHECKPOINT_PATH not provided; downloading first available checkpoint:\n #{checkpoint_path}\n"
)
end
output_dir = System.get_env("TINKER_OUTPUT_DIR") || File.cwd!()
IO.puts("Downloading checkpoint: #{checkpoint_path}")
IO.puts("Output directory: #{output_dir}\n")
# Progress callback
progress_fn = fn downloaded, total ->
percent = if total > 0, do: Float.round(downloaded / total * 100, 1), else: 0
IO.write("\rProgress: #{percent}% (#{format_size(downloaded)} / #{format_size(total)})")
end
force? = System.get_env("FORCE") == "true"
case CheckpointDownload.download(rest_client, checkpoint_path,
output_dir: output_dir,
force: force?,
progress: progress_fn
) do
{:ok, result} ->
IO.puts("\n\nDownload complete!")
IO.puts("Extracted to: #{result.destination}")
if File.exists?(result.destination) do
files = File.ls!(result.destination)
IO.puts("\nExtracted files (#{length(files)}):")
Enum.each(files, fn file ->
path = Path.join(result.destination, file)
stat = File.stat!(path)
IO.puts(" • #{file} (#{format_size(stat.size)})")
end)
end
{:error, {:exists, path}} ->
IO.puts("\nError: Directory already exists: #{path}")
IO.puts("Use FORCE=true to overwrite")
{:error, %Tinkex.Error{status: 404} = error} ->
IO.write("""
Error: #{Tinkex.Error.format(error)}
The checkpoint might no longer exist, or the service returned 404 for the latest entry.
Try setting TINKER_CHECKPOINT_PATH explicitly to a known-good value or set FORCE=true if the
directory already exists.
""")
{:error, error} ->
IO.puts("\nError: #{inspect(error)}")
end
GenServer.stop(service_pid)
IO.puts("\n=== Example Complete ===")
end
defp format_size(bytes) when bytes < 1024, do: "#{bytes} B"
defp format_size(bytes) when bytes < 1024 * 1024, do: "#{Float.round(bytes / 1024, 1)} KB"
defp format_size(bytes) when bytes < 1024 * 1024 * 1024,
do: "#{Float.round(bytes / 1024 / 1024, 1)} MB"
defp format_size(bytes), do: "#{Float.round(bytes / 1024 / 1024 / 1024, 2)} GB"
defp resolve_checkpoint_path(rest_client, service_pid) do
case System.get_env("TINKER_CHECKPOINT_PATH") do
nil ->
case find_available_checkpoint(rest_client) do
{:ok, path} ->
{:ok, path, :auto}
{:error, :no_available_checkpoints} ->
with {:ok, path} <- create_temporary_checkpoint(service_pid) do
{:ok, path, :generated}
end
{:error, other} ->
{:error, other}
end
path ->
{:ok, path, :env}
end
end
defp find_available_checkpoint(rest_client, offset \\ 0) do
case RestClient.list_user_checkpoints(rest_client, limit: 10, offset: offset) do
{:ok, %Tinkex.Types.CheckpointsListResponse{checkpoints: []}} ->
if offset == 0, do: {:error, :no_checkpoints}, else: {:error, :no_available_checkpoints}
{:ok, %Tinkex.Types.CheckpointsListResponse{checkpoints: checkpoints}} ->
case Enum.reduce_while(checkpoints, {:error, :no_available_checkpoints}, fn ckpt, _acc ->
case RestClient.get_checkpoint_archive_url(rest_client, ckpt.tinker_path) do
{:ok, _} ->
{:halt, {:ok, ckpt.tinker_path}}
{:error, %Error{status: 404}} ->
{:cont, {:error, :no_available_checkpoints}}
{:error, other} ->
{:halt, {:error, {:api_error, other}}}
end
end) do
{:ok, path} ->
{:ok, path}
{:error, :no_available_checkpoints} ->
find_available_checkpoint(rest_client, offset + length(checkpoints))
{:error, other} ->
{:error, other}
end
{:error, error} ->
{:error, {:api_error, error}}
end
end
defp create_temporary_checkpoint(service_pid) do
base_model = System.get_env("TINKER_BASE_MODEL") || "meta-llama/Llama-3.1-8B"
with {:ok, training_pid} <-
ServiceClient.create_lora_training_client(service_pid, base_model: base_model),
{:ok, save_task} <- TrainingClient.save_weights_for_sampler(training_pid) do
result = Task.await(save_task, 60_000)
GenServer.stop(training_pid)
case result do
{:ok, %{"path" => path}} when is_binary(path) ->
{:ok, path}
{:ok, %{path: path}} when is_binary(path) ->
{:ok, path}
{:error, _} = error ->
error
other ->
{:error,
Error.new(:request_failed, "Unexpected save_weights_for_sampler response",
data: other
)}
end
else
{:error, _} = error ->
error
end
end
end
Tinkex.Examples.CheckpointDownloadExample.run()
</file>
<file path="checkpoints_management.exs">
defmodule Tinkex.Examples.CheckpointsManagement do
@moduledoc """
Example demonstrating checkpoint management APIs.
Shows how to:
- List all user checkpoints
- List checkpoints for a specific run
- View checkpoint details
"""
alias Tinkex.{ServiceClient, RestClient, Config}
def run do
IO.puts("=== Tinkex Checkpoint Management Example ===\n")
{:ok, _} = Application.ensure_all_started(:tinkex)
base_url =
System.get_env("TINKER_BASE_URL") ||
Application.get_env(
:tinkex,
:base_url,
"https://tinker.thinkingmachines.dev/services/tinker-prod"
)
config =
Config.new(
api_key: System.get_env("TINKER_API_KEY") || raise("TINKER_API_KEY required"),
base_url: base_url
)
{:ok, service_pid} = ServiceClient.start_link(config: config)
{:ok, rest_client} = ServiceClient.create_rest_client(service_pid)
# List all user checkpoints
list_all_checkpoints(rest_client)
# If run_id is provided, list checkpoints for that run
if run_id = System.get_env("TINKER_RUN_ID") do
list_run_checkpoints(rest_client, run_id)
end
GenServer.stop(service_pid)
IO.puts("\n=== Example Complete ===")
end
defp list_all_checkpoints(rest_client) do
IO.puts("--- All User Checkpoints ---")
case RestClient.list_user_checkpoints(rest_client, limit: 20) do
{:ok, response} ->
total =
if response.cursor,
do: response.cursor["total_count"],
else: length(response.checkpoints)
IO.puts("Found #{length(response.checkpoints)} of #{total || "?"} checkpoints:\n")
Enum.each(response.checkpoints, fn ckpt ->
size = if ckpt.size_bytes, do: format_size(ckpt.size_bytes), else: "N/A"
IO.puts(" #{ckpt.checkpoint_id}")
IO.puts(" Path: #{ckpt.tinker_path}")
IO.puts(" Type: #{ckpt.checkpoint_type}")
IO.puts(" Size: #{size}")
IO.puts(" Public: #{ckpt.public}")
IO.puts(" Created: #{ckpt.time}")
IO.puts("")
end)
{:error, error} ->
IO.puts("Error: #{inspect(error)}")
end
end
defp list_run_checkpoints(rest_client, run_id) do
IO.puts("\n--- Checkpoints for Run: #{run_id} ---")
case RestClient.list_checkpoints(rest_client, run_id) do
{:ok, response} ->
IO.puts("Found #{length(response.checkpoints)} checkpoints:")
Enum.each(response.checkpoints, fn ckpt ->
IO.puts(" • #{ckpt.tinker_path}")
end)
{:error, error} ->
IO.puts("Error: #{inspect(error)}")
end
end
defp format_size(bytes) when bytes < 1024, do: "#{bytes} B"
defp format_size(bytes) when bytes < 1024 * 1024, do: "#{Float.round(bytes / 1024, 1)} KB"
defp format_size(bytes) when bytes < 1024 * 1024 * 1024,
do: "#{Float.round(bytes / 1024 / 1024, 1)} MB"
defp format_size(bytes), do: "#{Float.round(bytes / 1024 / 1024 / 1024, 2)} GB"
end
Tinkex.Examples.CheckpointsManagement.run()
</file>
<file path="cli_run_prompt_file.exs">
defmodule Tinkex.Examples.CLIRunPromptFile do
@moduledoc false
alias Tinkex.CLI
def run do
{:ok, _} = Application.ensure_all_started(:tinkex)
api_key =
System.get_env("TINKER_API_KEY") ||
raise "Set TINKER_API_KEY to run this example"
base_model = System.get_env("TINKER_BASE_MODEL", "meta-llama/Llama-3.1-8B")
base_url = System.get_env("TINKER_BASE_URL")
prompt_content =
System.get_env("TINKER_PROMPT_TOKENS") ||
System.get_env("TINKER_PROMPT", "Hello from a prompt file")
tmp_dir = System.tmp_dir!()
prompt_path = Path.join(tmp_dir, "tinkex_prompt_#{System.unique_integer([:positive])}.txt")
output_path = Path.join(tmp_dir, "tinkex_output_#{System.unique_integer([:positive])}.json")
File.write!(prompt_path, prompt_content)
args =
[
"run",
"--base-model",
base_model,
"--prompt-file",
prompt_path,
"--json",
"--output",
output_path,
"--api-key",
api_key
]
|> maybe_add_base_url(base_url)
IO.puts("Running CLI with prompt file #{prompt_path}")
case CLI.run(args) do
{:ok, %{response: _resp}} ->
IO.puts("JSON output written to #{output_path}")
IO.puts("Preview:")
IO.puts(File.read!(output_path))
{:error, reason} ->
IO.puts(:stderr, "CLI failed: #{inspect(reason)}")
end
end
defp maybe_add_base_url(args, nil), do: args
defp maybe_add_base_url(args, ""), do: args
defp maybe_add_base_url(args, url), do: args ++ ["--base-url", url]
end
Tinkex.Examples.CLIRunPromptFile.run()
</file>
<file path="cli_run_text.exs">
defmodule Tinkex.Examples.CLIRunText do
@moduledoc false
alias Tinkex.CLI
def run do
{:ok, _} = Application.ensure_all_started(:tinkex)
api_key =
System.get_env("TINKER_API_KEY") ||
raise "Set TINKER_API_KEY to run this example"
base_model = System.get_env("TINKER_BASE_MODEL", "meta-llama/Llama-3.1-8B")
base_url = System.get_env("TINKER_BASE_URL")
prompt = System.get_env("TINKER_PROMPT", "Hello from the CLI runner")
max_tokens = env_integer("TINKER_MAX_TOKENS", 64)
temperature = env_float("TINKER_TEMPERATURE", 0.7)
num_samples = env_integer("TINKER_NUM_SAMPLES", 1)
args =
[
"run",
"--base-model",
base_model,
"--prompt",
prompt,
"--max-tokens",
Integer.to_string(max_tokens),
"--temperature",
Float.to_string(temperature),
"--num-samples",
Integer.to_string(num_samples),
"--api-key",
api_key
]
|> maybe_add_base_url(base_url)
IO.puts("Running CLI with args: #{Enum.join(args, " ")}")
case CLI.run(args) do
{:ok, %{response: response}} ->
IO.inspect(response, label: "sampling response")
{:error, reason} ->
IO.puts(:stderr, "CLI failed: #{inspect(reason)}")
end
end
defp env_integer(var, default) do
case System.get_env(var) do
nil ->
default
value ->
try do
String.to_integer(value)
rescue
_ -> default
end
end
end
defp env_float(var, default) do
case System.get_env(var) do
nil ->
default
value ->
try do
String.to_float(value)
rescue
_ -> default
end
end
end
defp maybe_add_base_url(args, nil), do: args
defp maybe_add_base_url(args, ""), do: args
defp maybe_add_base_url(args, url), do: args ++ ["--base-url", url]
end
Tinkex.Examples.CLIRunText.run()
</file>
<file path="sampling_basic.exs">
defmodule Tinkex.Examples.SamplingBasic do
@moduledoc false
alias Tinkex.Error
def run do
{:ok, _} = Application.ensure_all_started(:tinkex)
api_key =
System.get_env("TINKER_API_KEY") ||
raise "Set TINKER_API_KEY to run this example"
base_url =
System.get_env(
"TINKER_BASE_URL",
"https://tinker.thinkingmachines.dev/services/tinker-prod"
)
base_model = System.get_env("TINKER_BASE_MODEL", "meta-llama/Llama-3.1-8B")
prompt_text = System.get_env("TINKER_PROMPT", "Hello from Tinkex!")
max_tokens = env_integer("TINKER_MAX_TOKENS", 64)
temperature = env_float("TINKER_TEMPERATURE", 0.7)
num_samples = env_integer("TINKER_NUM_SAMPLES", 1)
await_timeout = env_integer("TINKER_SAMPLE_TIMEOUT", 30_000)
config = Tinkex.Config.new(api_key: api_key, base_url: base_url)
{:ok, service} = Tinkex.ServiceClient.start_link(config: config)
{:ok, sampler} = Tinkex.ServiceClient.create_sampling_client(service, base_model: base_model)
{:ok, prompt} = Tinkex.Types.ModelInput.from_text(prompt_text, model_name: base_model)
params = %Tinkex.Types.SamplingParams{max_tokens: max_tokens, temperature: temperature}
IO.puts("Sampling #{num_samples} sequence(s) from #{base_model} ...")
{:ok, task} =
Tinkex.SamplingClient.sample(sampler, prompt, params,
num_samples: num_samples,
prompt_logprobs: false
)
case Task.await(task, await_timeout) do
{:ok, response} ->
IO.puts("Received #{length(response.sequences)} sequence(s):")
Enum.with_index(response.sequences, 1)
|> Enum.each(fn {seq, idx} ->
text = decode(seq.tokens, base_model)
IO.puts("Sample #{idx}: #{text}")
end)
{:error, error} ->
IO.puts(:stderr, "Sampling failed: #{format_error(error)}")
end
end
defp decode(tokens, model_name) do
case Tinkex.Tokenizer.decode(tokens, model_name) do
{:ok, text} -> text
{:error, reason} -> "[decode failed: #{inspect(reason)}]"
end
end
defp env_integer(var, default) do
case System.get_env(var) do
nil ->
default
value ->
try do
String.to_integer(value)
rescue
_ -> default
end
end
end
defp env_float(var, default) do
case System.get_env(var) do
nil ->
default
value ->
try do
String.to_float(value)
rescue
_ -> default
end
end
end
defp format_error(%Error{} = error), do: Error.format(error)
defp format_error(other), do: inspect(other)
end
Tinkex.Examples.SamplingBasic.run()
</file>
<file path="sessions_management.exs">
defmodule Tinkex.Examples.SessionsManagement do
@moduledoc """
Example demonstrating session management APIs.
Shows how to:
- Create a RestClient
- List all sessions
- Get session details
"""
alias Tinkex.{ServiceClient, RestClient, Config}
def run do
IO.puts("=== Tinkex Session Management Example ===\n")
# Ensure application is started
{:ok, _} = Application.ensure_all_started(:tinkex)
# Create config from environment
base_url =
System.get_env("TINKER_BASE_URL") ||
Application.get_env(
:tinkex,
:base_url,
"https://tinker.thinkingmachines.dev/services/tinker-prod"
)
config =
Config.new(
api_key: System.get_env("TINKER_API_KEY") || raise("TINKER_API_KEY required"),
base_url: base_url
)
# Start ServiceClient
IO.puts("Starting ServiceClient...")
{:ok, service_pid} = ServiceClient.start_link(config: config)
# Create RestClient
IO.puts("Creating RestClient...")
{:ok, rest_client} = ServiceClient.create_rest_client(service_pid)
# List sessions
IO.puts("\n--- Listing Sessions ---")
case RestClient.list_sessions(rest_client, limit: 10) do
{:ok, response} ->
IO.puts("Found #{length(response.sessions)} sessions:")
Enum.each(response.sessions, fn session_id ->
IO.puts(" • #{session_id}")
end)
# Get details for first session if available
if length(response.sessions) > 0 do
[first_session | _] = response.sessions
get_session_details(rest_client, first_session)
end
{:error, error} ->
IO.puts("Error listing sessions: #{inspect(error)}")
end
# Cleanup
GenServer.stop(service_pid)
IO.puts("\n=== Example Complete ===")
end
defp get_session_details(rest_client, session_id) do
IO.puts("\n--- Session Details: #{session_id} ---")
case RestClient.get_session(rest_client, session_id) do
{:ok, response} ->
IO.puts("Training Runs: #{length(response.training_run_ids)}")
Enum.each(response.training_run_ids, fn run_id ->
IO.puts(" • #{run_id}")
end)
IO.puts("Samplers: #{length(response.sampler_ids)}")
Enum.each(response.sampler_ids, fn sampler_id ->
IO.puts(" • #{sampler_id}")
end)
{:error, error} ->
IO.puts("Error getting session: #{inspect(error)}")
end
end
end
Tinkex.Examples.SessionsManagement.run()
</file>
<file path="training_loop.exs">
defmodule Tinkex.Examples.TrainingLoop do
@moduledoc false
@default_base_url "https://tinker.thinkingmachines.dev/services/tinker-prod"
@default_model "meta-llama/Llama-3.1-8B"
@await_timeout 60_000
alias Tinkex.Error
alias Tinkex.Types.TensorData
def run do
{:ok, _} = Application.ensure_all_started(:tinkex)
api_key = fetch_env!("TINKER_API_KEY")
base_url = System.get_env("TINKER_BASE_URL", @default_base_url)
base_model = System.get_env("TINKER_BASE_MODEL", @default_model)
prompt = System.get_env("TINKER_PROMPT", "Fine-tuning sample prompt")
sample_after? = System.get_env("TINKER_SAMPLE_AFTER_TRAIN", "0") not in ["0", "false", nil]
sample_prompt = System.get_env("TINKER_SAMPLE_PROMPT", "Hello from fine-tuned weights!")
IO.puts("Base URL: #{base_url}")
IO.puts("Base model: #{base_model}")
config = Tinkex.Config.new(api_key: api_key, base_url: base_url)
with {:ok, service} <- Tinkex.ServiceClient.start_link(config: config),
{:ok, training} <- create_training_client(service, base_model),
{:ok, model_input} <- build_model_input(prompt, base_model, training) do
run_training_steps(service, training, model_input, base_model, sample_after?, sample_prompt)
else
{:error, %Error{} = error} ->
halt_with_error("Initialization failed", error)
{:error, other} ->
halt("Initialization failed: #{inspect(other)}")
end
end
defp create_training_client(service, base_model) do
Tinkex.ServiceClient.create_lora_training_client(service,
base_model: base_model,
lora_config: %Tinkex.Types.LoraConfig{rank: 16}
)
end
defp build_model_input(prompt, base_model, training) do
Tinkex.Types.ModelInput.from_text(prompt, model_name: base_model, training_client: training)
end
defp run_training_steps(
service,
training,
model_input,
base_model,
sample_after?,
sample_prompt
) do
target_tokens = first_chunk_tokens(model_input)
datum =
Tinkex.Types.Datum.new(%{
model_input: model_input,
# Server expects loss_fn_inputs values as tensors with dtype/shape.
loss_fn_inputs: %{
target_tokens: to_tensor(target_tokens, :int64),
weights: to_tensor(List.duplicate(1.0, length(target_tokens)), :float32)
}
})
loop_start = System.monotonic_time(:millisecond)
fb_task =
start_task(
Tinkex.TrainingClient.forward_backward(training, [datum], :cross_entropy),
"forward_backward"
)
fb_output = await_task(fb_task, "forward_backward")
IO.inspect(fb_output.metrics, label: "forward_backward metrics")
optim_task =
start_task(
Tinkex.TrainingClient.optim_step(training, %Tinkex.Types.AdamParams{}),
"optim_step"
)
optim_output = await_task(optim_task, "optim_step")
IO.inspect(optim_output.metrics, label: "optim_step metrics")
save_task =
start_task(
Tinkex.TrainingClient.save_weights_for_sampler(training),
"save_weights_for_sampler"
)
save_result = await_task(save_task, "save_weights_for_sampler")
IO.inspect(save_result, label: "save_weights_for_sampler response")
if sample_after? do
sample_with_saved_weights(service, save_result, base_model, sample_prompt)
end
duration_ms = System.monotonic_time(:millisecond) - loop_start
IO.puts("Training loop finished in #{duration_ms} ms")
end
defp start_task(result, label) do
case result do
{:ok, task} ->
task
{:error, %Error{} = error} ->
halt_with_error("#{label} failed", error)
other ->
halt("#{label} failed: #{inspect(other)}")
end
end
defp await_task(task, label) do
try do
case Task.await(task, @await_timeout) do
{:ok, result} ->
result
{:error, %Error{} = error} ->
halt_with_error("#{label} error", error)
other ->
halt("#{label} returned unexpected response: #{inspect(other)}")
end
catch
:exit, reason ->
halt("#{label} task exited: #{inspect(reason)}")
end
end
defp fetch_env!(var) do
case System.get_env(var) do
nil -> halt("Set #{var} to run this example")
value -> value
end
end
defp halt_with_error(prefix, %Error{} = error) do
IO.puts(:stderr, "#{prefix}: #{Error.format(error)}")
if error.data, do: IO.puts(:stderr, "Error data: #{inspect(error.data)}")
System.halt(1)
end
defp halt(message) do
IO.puts(:stderr, message)
System.halt(1)
end
defp first_chunk_tokens(%Tinkex.Types.ModelInput{chunks: [chunk | _]}) do
Map.get(chunk, :tokens) || Map.get(chunk, "tokens") || []
end
defp first_chunk_tokens(_), do: []
defp to_tensor(tokens, dtype) when is_list(tokens) do
seq_len = length(tokens)
%TensorData{data: tokens, dtype: dtype, shape: [seq_len]}
end
defp to_tensor(_, dtype), do: %TensorData{data: [], dtype: dtype, shape: [0]}
defp sample_with_saved_weights(service, save_result, base_model, sample_prompt)
when is_map(save_result) do
model_path = save_result["path"] || save_result[:path]
sampling_session_id = save_result["sampling_session_id"] || save_result[:sampling_session_id]
case create_sampler(service, model_path, sampling_session_id, base_model) do
{:ok, sampler} ->
do_sample(sampler, base_model, sample_prompt)
{:error, reason} ->
IO.puts(:stderr, "Skipping sampling; failed to create sampler: #{inspect(reason)}")
end
end
defp sample_with_saved_weights(_service, _save_result, _base_model, _prompt), do: :ok
defp create_sampler(service, model_path, _sampling_session_id, base_model) do
opts =
if model_path do
[model_path: model_path, base_model: base_model]
else
# Fall back to base_model-only sampler if no path returned.
[base_model: base_model]
end
|> maybe_put_sampling_session_seq_id()
case Tinkex.ServiceClient.create_sampling_client(service, opts) do
{:ok, sampler} -> {:ok, sampler}
{:error, _} = error -> error
end
end
defp maybe_put_sampling_session_seq_id(opts) do
# Use first sampler seq_id for simplicity.
Keyword.put_new(opts, :sampling_session_seq_id, 0)
end
defp do_sample(sampler, base_model, prompt) do
{:ok, model_input} = Tinkex.Types.ModelInput.from_text(prompt, model_name: base_model)
params = %Tinkex.Types.SamplingParams{max_tokens: 32, temperature: 0.7}
with {:ok, task} <-
Tinkex.SamplingClient.sample(sampler, model_input, params,
num_samples: 1,
prompt_logprobs: false
),
{:ok, response} <- Task.await(task, 30_000) do
[seq | _] = response.sequences
text = decode(seq.tokens, base_model)
IO.puts("Sample from saved weights: #{text}")
else
{:error, error} ->
IO.puts(:stderr, "Sampling after train failed: #{inspect(error)}")
end
end
defp decode(tokens, model_name) do
case Tinkex.Tokenizer.decode(tokens, model_name) do
{:ok, text} -> text
{:error, reason} -> "[decode failed: #{inspect(reason)}]"
end
end
end
Tinkex.Examples.TrainingLoop.run()
</file>
</files>