git.iko.soy/opencode-android
on c0621ff help sign in
77 lines 2.5 kB history raw
1package soy.iko.opencode.data.model
3import androidx.compose.runtime.Immutable
4import kotlinx.serialization.Serializable
6/** Response of `GET /config/providers`. */
7@Immutable
8@Serializable
9data class ProvidersResponse(
10 val providers: List<Provider> = emptyList(),
11 val default: Map<String, String> = emptyMap(),
14@Immutable
15@Serializable
16data class Provider(
17 val id: String,
18 val name: String? = null,
19 val models: Map<String, ModelInfo> = emptyMap(),
20) {
21 val displayName: String get() = name?.takeIf { it.isNotBlank() } ?: id
24@Immutable
25@Serializable
26data class ModelInfo(
27 val id: String? = null,
28 val name: String? = null,
29) {
30 fun displayName(modelKey: String): String =
31 name?.takeIf { it.isNotBlank() } ?: id ?: modelKey
34/** A flattened provider/model selection used by the prompt body and the model picker. */
35@Immutable
36@Serializable
37data class ModelRef(
38 val providerID: String,
39 val modelID: String,
42/** A single selectable entry in the model picker, flattened from [ProvidersResponse]. */
43@Immutable
44data class ModelOption(
45 val providerID: String,
46 val modelID: String,
47 val providerLabel: String,
48 val modelLabel: String,
49) {
50 val ref: ModelRef get() = ModelRef(providerID, modelID)
53/** Flatten providers into a sorted list of pickable options. */
54fun ProvidersResponse.toOptions(): List<ModelOption> =
55 providers.flatMap { provider ->
56 provider.models.map { (modelKey, info) ->
57 ModelOption(
58 providerID = provider.id,
59 modelID = info.id ?: modelKey,
60 providerLabel = provider.displayName,
61 modelLabel = info.displayName(modelKey),
62 )
63 }
64 }.sortedWith(
65 // CASE_INSENSITIVE_ORDER compares in place; compareBy { it.x.lowercase() } would
66 // allocate a fresh lowercased string on every comparison (O(N log N) allocations).
67 compareBy<ModelOption, String>(String.CASE_INSENSITIVE_ORDER) { it.providerLabel }
68 .thenBy(String.CASE_INSENSITIVE_ORDER) { it.modelLabel },
69 )
71/** The default option to preselect: the server's default for some provider, else the first model. */
72fun ProvidersResponse.defaultOption(options: List<ModelOption> = toOptions()): ModelOption? {
73 default.entries.firstOrNull()?.let { (providerID, modelID) ->
74 options.firstOrNull { it.providerID == providerID && it.modelID == modelID }?.let { return it }
75 }
76 return options.firstOrNull()