1package soy.iko.opencode.data.model
3import androidx.compose.runtime.Immutable
4import kotlinx.serialization.Serializable
6/** Response of `GET /config/providers`. */
9data class ProvidersResponse(
10 val providers: List<Provider> = emptyList(),
11 val default: Map<String, String> = emptyMap(),
17 // Defaulted so one id-less element in GET /config/providers doesn't fail the whole list decode
18 // (emptying the model picker) — matches MessageInfo's defaulted id and the resilient-decoding
19 // philosophy. A blank id is degraded but recoverable; a thrown decode is not.
21 val name: String? = null,
22 val models: Map<String, ModelInfo> = emptyMap(),
24 val displayName: String get() = name?.takeIf { it.isNotBlank() } ?: id
30 val id: String? = null,
31 val name: String? = null,
33 fun displayName(modelKey: String): String =
34 name?.takeIf { it.isNotBlank() } ?: id ?: modelKey
37/** A flattened provider/model selection used by the prompt body and the model picker. */
41 val providerID: String,
45/** A single selectable entry in the model picker, flattened from [ProvidersResponse]. */
47data class ModelOption(
48 val providerID: String,
50 val providerLabel: String,
51 val modelLabel: String,
53 val ref: ModelRef get() = ModelRef(providerID, modelID)
56/** Flatten providers into a sorted list of pickable options. */
57fun ProvidersResponse.toOptions(): List<ModelOption> =
58 providers.flatMap { provider ->
59 provider.models.map { (modelKey, info) ->
61 providerID = provider.id,
62 modelID = info.id ?: modelKey,
63 providerLabel = provider.displayName,
64 modelLabel = info.displayName(modelKey),
68 // CASE_INSENSITIVE_ORDER compares in place; compareBy { it.x.lowercase() } would
69 // allocate a fresh lowercased string on every comparison (O(N log N) allocations).
70 compareBy<ModelOption, String>(String.CASE_INSENSITIVE_ORDER) { it.providerLabel }
71 .thenBy(String.CASE_INSENSITIVE_ORDER) { it.modelLabel },
74/** The default option to preselect: the server's default for some provider, else the first model. */
75fun ProvidersResponse.defaultOption(options: List<ModelOption> = toOptions()): ModelOption? {
76 // Try each configured provider default in turn and take the first whose model actually
77 // survives in `options` (the list may be filtered). Checking only the first `default` entry
78 // would mispreselect an arbitrary first option whenever that one provider's default model
79 // isn't present, ignoring the other configured defaults.
80 for ((providerID, modelID) in default) {
81 options.firstOrNull { it.providerID == providerID && it.modelID == modelID }?.let { return it }
83 return options.firstOrNull()
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list