query() offers two model-selection modes:
- Fixed model (default): the entire session uses one model.
- Dynamic selection: a callback is called before every LLM request to return the model. You can route by purpose (main conversation, sub-agent, context compaction, etc.).
SessionOptions.Builder.modelSelection(...). ModelSelection.staticModel(...) is the fixed model. Dynamic selection has two callback styles: ModelSelection.dynamic(...) returns only a model ID, and ModelSelection.policy(...) returns a structured result that can carry parameters or keep the CLI's current model. The callback runs only when the CLI needs to choose a model, never during initialization.
Fixed model
Specify it via the model option; omit to use the account default:
Dynamic selection
Provide a callback function that is invoked before every LLM request:
ModelRequestContext methods such as purpose(), sessionId(), and availableModels(); optional fields return Optional.
Routing by purpose
Use a different model per purpose:
Returning model parameters
Return parameters with the selected model to override the context window and thinking depth for that LLM request. These keys are camelCase at the SDK control boundary:
ModelSelection.policy(...); the parameters(JsonObject) content is passed to the CLI as is. To keep the model the CLI already chose, return ModelPolicyResult.keepCurrent().
These values should match configurations the current model supports. Context-window and thinking metadata are exposed through the callback's availableModels input in ModelInfo.context_config and ModelInfo.thinking_config. Java's ModelInfo does not model this metadata; read it from ModelInfo.raw().
Timeout
The callback has a default timeout (in milliseconds). Increase it when the callback performs remote I/O:
0 means no limit.
Runtime operations
While a session is running you can fetch the account's currently available model list:
getAvailableModels() on a QoderSession; you can query right after the session opens, without sending input. Pass ModelCatalogOptions to choose explicitly between a live read and the cache. When the CLI omits the isEnabled field, Java's ModelInfo.isEnabled() returns false, unlike the TypeScript example above, which treats it as true.
You can also switch the current model in fixed-model mode. For specific method signatures, see SDK References. Java has no method for switching the current model.
Error handling
Dynamic-selection mode has no automatic fallback — a callback that times out, throws, or returns an empty model causes the query to fail. Handle errors inside the callback:
exceptionally(...) only covers the callback's own failures; waiting past resolveModelTimeoutMs still fails. These failures are not delivered to the application as exceptions. The SDK only returns an error code to the CLI (MODEL_RESOLUTION_TIMEOUT for a timeout, CALLBACK_EXECUTION_FAILED for a callback failure) and emits an inbound_control_request_failed diagnostic event whose detail contains error=<code>. To observe these failures, register a diagnostics listener, and handle the turn's result through ResultMessage.outcome().
For full field and type definitions, see SDK References.