internal/training/deploy.go, internal/training/db/, internal/api/training/models.go, internal/sys/sidecar/, gligabao.go
Deploying a trained model means handing its weights to code of yours. gligabao does the part that is the same for everybody, getting the weights from where they were trained onto the local disk and checking them, and calls a hook with the result: a deployer, a Go callback registered by the program that embeds gligabao. What the deployer does with the files (upload them to a service, copy them to a bucket, convert them to another format, …) is its own business; gligabao has no built-in destination.
This page describes how deploying works inside and the hook’s contract. The hook’s entry in the list of all hooks is Hooks; the trained models themselves (the gb_trained_models table, where the weights live) are in Trained models.
Deploying is part of the training API: it is there only when the server runs in training mode (--training) and, to fetch the weights, with an active sidecar.
The flow
Deploy runs, which can take minutes for a large model. Callers must not use a short timeout. If the client disconnects, the request’s ctx is cancelled, which Deploy should notice; the deployment is then recorded as failed.
Registering a deployer
Deployers are passed inServeOptions.TrainedModelDeployers (there is no command-line or configuration-file way to register one, since a deployer is code):
What Deploy receives
What Deploy returns
Ref: identifies what the deployment made, in the deployer’s own terms (the name of a model created elsewhere, an object-store path, …). A string, stored and returned with the deployment.Details: anything else worth keeping; amap[string]anythat must be JSON-encodable (a deployer that returns details that are not is treated as failed). May benil.- A non-nil error, or a panic (it is caught), makes the deployment
failed; the error text is stored and returned.
Rules of the hook
- Read only.
Diris shared with later deployments of the same model.Deploymust not modify or delete the files in it; copy, archive or convert into a place of its own. - Concurrent. Deploy requests are handled concurrently, also several for the same model, so
Deploymust be safe for concurrent use. The files of one model are fetched once even then (see below). - Honour
ctx. It is the request’s context: it is cancelled if the client goes away. The fetch has its own limit of 30 minutes;Deployhas none besidesctx. - No retries. gligabao runs
Deployonce per request. A failed deployment stays failed; ask for a new one to try again (the files are already on the disk by then). - No sidecar. The deployer never talks to the sidecar; it finds the weights on the local disk.
Validation at startup
Serve checks the registered deployers and returns an error (invalid trained model deployer: ...) for any of these, before the server starts:
- more than 20 deployers;
- a
Namethat is empty, longer than 64 characters, or not letters, digits,_,-and.starting with a letter or digit; - two deployers with the same
Name; - a deployer with no
Deployfunction.
400.
Fetching the weights
The weights of a trained model live on the sidecar’s storage (Modal: the models volume), not on the server. Before it calls the deployer, gligabao copies them to the server’s disk. This is the part of deploying that is the same for every deployer.- Where.
<trained model deploy dir>/<job id>, an absolute path (Dir). The deployment directory isserve --trained-model-deploy-dir(ServeOptions.TrainedModelDeployDir); the default is atrained_modelsdirectory next to the database file (build/trained_models). There is one subdirectory per training job, so models never mix. A job ID that is not usable as a directory name is refused (400). - What. Every file in the model’s
files(the list recorded when the job completed), at its recorded relative path; nested paths are allowed. A path that is absolute, contains..or uses a backslash is refused. - How. One
GET /models/file?model_path=trained/<job id>&file=<path>request per file to the sidecar, which streams the raw bytes out of the volume (see Sidecar training API); the file is never held in memory. - Verified. Each file must have exactly the recorded size and the recorded sha256 (a model with no sha256 recorded for a file is checked by size only). A mismatch fails the deployment; nothing is deployed from unverified bytes.
- Atomic. A file is downloaded under a temporary name (
.fetch-*, in its own directory) and renamed to its real name only when it is complete and verified, so the real names never hold half a file, even if the server dies halfway. - Reused. A file that is already in
Dirwith the right size and sha256 is kept and not downloaded again; only missing or damaged files are fetched. Deploying the same model a second time therefore costs only the verification. - Serialized. Fetches into one directory are serialized: two simultaneous deployments of the same model do not download it twice; the second waits and then finds the files in place.
- Never deleted. gligabao does not clean
Dir. The files stay until you remove them (rm -r build/trained_models/<job id>); the next deploy fetches them again. Model weights are large, so plan for the disk space. - No recorded files. A model with no recorded files (for instance one saved by a version before files were listed) cannot be deployed: the deployment fails with “the trained model has no recorded files”.
The record of a deployment
Every deployment that gets past the checks of steps 1 and 2 is stored ingb_trained_model_deployments, when it starts and again when it ends, so each can be reviewed later, failures included:
The table exists on SQLite and PostgreSQL and is created automatically. A row that stays
running long after it started was interrupted, for example by a restart of the server: nothing resumes it, and nothing marks it failed.
HTTP API
Admin UI
In the admin UI’s Training view, the action box of a trained model (click its row) has a Deploy button, after Audit: it opens a dialog listing the registered deployers with their descriptions, each with its own Deploy button; the one pressed callsPOST .../deployments with that deployer, and its outcome (status, ref, duration, or the error) is shown in its row. The Deployers block at the bottom of the view lists the registered deployers; with none registered, Deploy is disabled.
System events
Each deployment that reaches the fetch emits a system event to theSystemEvent hook:
In the code
internal/training/deploy.go (the whole flow), internal/training/db/ (RecordTrainedModelDeployment, FinishTrainedModelDeployment, GetTrainedModelDeployment, ListTrainedModelDeployments; the table for SQLite and PostgreSQL), internal/api/training/models.go (DeployOf, ListDeploymentsOf), internal/sys/sidecar/ (Client.DownloadModelFile), gligabao.go (the public types and ServeOptions).
training.DeployModel(ctx, db, fetcher, deployers, dir, model, DeployRequest{Deployer, Options})runs steps 2 to 7 and returns the stored deployment. Its errors are*training.Errorwith a kind that the handler maps to a status (invalid: 400, upstream: 502, internal: 500).training.FetchModelFiles(ctx, fetcher, model, dir)is the fetch alone;fetcheris atraining.ModelFileFetcher, one method,DownloadModelFile(ctx, modelPath, file, w), which*sidecar.Clientimplements.training.ModelDir(root, model)gives the directory of a model.training.ResolveDeployers(list)is the startup validation.
Limits and not included
- Deployers are code in the embedding program; none is built in. There is no endpoint to register one at run time.
- A deployment is one synchronous request. There is no background deploy, no progress, no cancellation besides the client leaving, and no resuming.
- gligabao does not merge LoRA adapters into the base model, and does not convert formats: the deployer gets the files as they were saved (for the LoRA fine-tuning:
adapter_config.json,adapter_model.safetensorsand the tokenizer files, not the base model;adapter_config.jsonnames the base model). - Deployments are never deleted, and neither are the fetched files.