Skip to documentation
Documentation navigation

Documentation navigation

Documentation / guides

Ownership and concurrency

Understand which side owns requests, streams, bodies, and concurrent access.

developer

Inference keeps ownership explicit at each boundary. A caller owns request values and the context; a client owns transport resources; a returned stream owns its response body until Close.

Ownership table

Value or operationOwnerConcurrency contract
Request and MessagesCallerDo not mutate while the client encodes it
ModelCaller or catalogueClone deep-copies sampling metadata
context.ContextCallerCancellation and deadline are caller-controlled
ResponseCaller after Invoke returnsOrdinary Go values; no internal synchronization
StreamReader.NextReaderCalls are serialized internally
StreamReader.CloseCaller and readerIdempotent; wrapped closer runs once
HTTP response bodyClient/stream readerReleased by EOF cleanup or explicit Close
reader, err := client.Stream(ctx, req)
if err != nil {
	return err
}
defer func() {
	if closeErr := reader.Close(); closeErr != nil {
		log.Printf("stream close: %v", closeErr)
	}
}()

Close is deliberately not serialized behind a blocking Next, so it can interrupt I/O. The underlying next and close functions must tolerate that concurrency. Do not call Next concurrently to obtain parallel chunks; the reader will serialize it, not make the provider response parallel-safe.

Deep copies at model boundaries

Model.Clone, Sampling.Clone, and WithSampling copy pointer and slice fields. Raw content byte slices created by ordinary struct literals are not automatically copied; copy caller-owned bytes before handing a request to asynchronous code.

Proof

Related: Streaming inference, Close streams.

← back to documentation