Skip to content

Commit 6ff06ea

Browse files
committed
feat(ios): add model picker favorites and recents with working iOS model switching
1 parent 2b9bd7c commit 6ff06ea

9 files changed

Lines changed: 482 additions & 43 deletions

File tree

apps/.i18n/native-source.json

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5707,55 +5707,55 @@
57075707
},
57085708
{
57095709
"kind": "conditional-branch",
5710-
"line": 476,
5710+
"line": 503,
57115711
"path": "apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeManager.kt",
57125712
"source": "Listening",
57135713
"surface": "android",
57145714
"id": "native.android.b4f67e96603515bd"
57155715
},
57165716
{
57175717
"kind": "conditional-branch",
5718-
"line": 476,
5718+
"line": 503,
57195719
"path": "apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeManager.kt",
57205720
"source": "Ready",
57215721
"surface": "android",
57225722
"id": "native.android.b88e4278ead724ba"
57235723
},
57245724
{
57255725
"kind": "conditional-branch",
5726-
"line": 863,
5726+
"line": 906,
57275727
"path": "apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeManager.kt",
57285728
"source": "Off",
57295729
"surface": "android",
57305730
"id": "native.android.8d8230088f3baa2a"
57315731
},
57325732
{
57335733
"kind": "conditional-branch",
5734-
"line": 864,
5734+
"line": 907,
57355735
"path": "apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeManager.kt",
57365736
"source": "Talk failed: Realtime provider closed unexpectedly.",
57375737
"surface": "android",
57385738
"id": "native.android.d6b7f86564ac3147"
57395739
},
57405740
{
57415741
"kind": "conditional-branch",
5742-
"line": 865,
5742+
"line": 908,
57435743
"path": "apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeManager.kt",
57445744
"source": "Talk failed: Realtime provider closed: $reason",
57455745
"surface": "android",
57465746
"id": "native.android.eb6ac739ad55e63a"
57475747
},
57485748
{
57495749
"kind": "conditional-branch",
5750-
"line": 1812,
5750+
"line": 1911,
57515751
"path": "apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeManager.kt",
57525752
"source": "Aborted",
57535753
"surface": "android",
57545754
"id": "native.android.13531fe081a45711"
57555755
},
57565756
{
57575757
"kind": "conditional-branch",
5758-
"line": 1812,
5758+
"line": 1911,
57595759
"path": "apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeManager.kt",
57605760
"source": "Chat error",
57615761
"surface": "android",
@@ -20299,151 +20299,191 @@
2029920299
},
2030020300
{
2030120301
"kind": "ui-call",
20302-
"line": 192,
20302+
"line": 195,
2030320303
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2030420304
"source": "Thinking",
2030520305
"surface": "apple",
2030620306
"id": "native.apple.510c6dfa05924ae2"
2030720307
},
2030820308
{
2030920309
"kind": "ui-call",
20310-
"line": 211,
20310+
"line": 216,
2031120311
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2031220312
"source": "Model",
2031320313
"surface": "apple",
2031420314
"id": "native.apple.0fce9d743a640218"
2031520315
},
2031620316
{
2031720317
"kind": "ui-call",
20318-
"line": 234,
20318+
"line": 233,
20319+
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
20320+
"source": "Pinned",
20321+
"surface": "apple",
20322+
"id": "native.apple.745b004e0b5b6b80"
20323+
},
20324+
{
20325+
"kind": "ui-call",
20326+
"line": 241,
20327+
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
20328+
"source": "Recent",
20329+
"surface": "apple",
20330+
"id": "native.apple.69381a0a462ccd84"
20331+
},
20332+
{
20333+
"kind": "ui-call",
20334+
"line": 249,
20335+
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
20336+
"source": "Models",
20337+
"surface": "apple",
20338+
"id": "native.apple.aa321cf4820426ef"
20339+
},
20340+
{
20341+
"kind": "conditional-branch",
20342+
"line": 278,
20343+
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
20344+
"source": "Pin model",
20345+
"surface": "apple",
20346+
"id": "native.apple.75550b03f06d6abc"
20347+
},
20348+
{
20349+
"kind": "conditional-branch",
20350+
"line": 278,
20351+
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
20352+
"source": "Unpin model",
20353+
"surface": "apple",
20354+
"id": "native.apple.af4df27bdbacb5de"
20355+
},
20356+
{
20357+
"kind": "ui-call",
20358+
"line": 282,
2031920359
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2032020360
"source": "Session",
2032120361
"surface": "apple",
2032220362
"id": "native.apple.5dfc0700d8dff8de"
2032320363
},
2032420364
{
2032520365
"kind": "ui-modifier",
20326-
"line": 298,
20366+
"line": 346,
2032720367
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2032820368
"source": "Add Image",
2032920369
"surface": "apple",
2033020370
"id": "native.apple.284b2b94c272591d"
2033120371
},
2033220372
{
2033320373
"kind": "ui-modifier",
20334-
"line": 299,
20374+
"line": 347,
2033520375
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2033620376
"source": "Attachments",
2033720377
"surface": "apple",
2033820378
"id": "native.apple.1963dfa3285cb715"
2033920379
},
2034020380
{
2034120381
"kind": "conditional-branch",
20342-
"line": 469,
20382+
"line": 517,
2034320383
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2034420384
"source": "Talk",
2034520385
"surface": "apple",
2034620386
"id": "native.apple.ee2c0d22f5861159"
2034720387
},
2034820388
{
2034920389
"kind": "conditional-branch",
20350-
"line": 515,
20390+
"line": 563,
2035120391
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2035220392
"source": "Start realtime chat",
2035320393
"surface": "apple",
2035420394
"id": "native.apple.5d5041aefe6dfddd"
2035520395
},
2035620396
{
2035720397
"kind": "conditional-branch",
20358-
"line": 515,
20398+
"line": 563,
2035920399
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2036020400
"source": "Stop realtime chat",
2036120401
"surface": "apple",
2036220402
"id": "native.apple.21a7d3a3386a2218"
2036320403
},
2036420404
{
2036520405
"kind": "conditional-branch",
20366-
"line": 548,
20406+
"line": 596,
2036720407
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2036820408
"source": "Start",
2036920409
"surface": "apple",
2037020410
"id": "native.apple.88c2de261ddae511"
2037120411
},
2037220412
{
2037320413
"kind": "conditional-branch",
20374-
"line": 548,
20414+
"line": 596,
2037520415
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2037620416
"source": "Stop",
2037720417
"surface": "apple",
2037820418
"id": "native.apple.72e5aa4446eaca9e"
2037920419
},
2038020420
{
2038120421
"kind": "ui-call",
20382-
"line": 658,
20422+
"line": 706,
2038320423
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2038420424
"source": "Loading commands",
2038520425
"surface": "apple",
2038620426
"id": "native.apple.9d4bdedcbf74d86c"
2038720427
},
2038820428
{
2038920429
"kind": "ui-call",
20390-
"line": 667,
20430+
"line": 715,
2039120431
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2039220432
"source": "Commands unavailable",
2039320433
"surface": "apple",
2039420434
"id": "native.apple.fdd34aa94629c962"
2039520435
},
2039620436
{
2039720437
"kind": "ui-call",
20398-
"line": 676,
20438+
"line": 724,
2039920439
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2040020440
"source": "Retry",
2040120441
"surface": "apple",
2040220442
"id": "native.apple.a1953e3e9be8b8d5"
2040320443
},
2040420444
{
2040520445
"kind": "ui-call",
20406-
"line": 685,
20446+
"line": 733,
2040720447
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2040820448
"source": "No matching commands",
2040920449
"surface": "apple",
2041020450
"id": "native.apple.0f942ca442f88936"
2041120451
},
2041220452
{
2041320453
"kind": "ui-modifier",
20414-
"line": 817,
20454+
"line": 865,
2041520455
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2041620456
"source": "Stop response",
2041720457
"surface": "apple",
2041820458
"id": "native.apple.afd8a2b9e3bf6d60"
2041920459
},
2042020460
{
2042120461
"kind": "ui-modifier",
20422-
"line": 842,
20462+
"line": 890,
2042320463
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2042420464
"source": "Send message",
2042520465
"surface": "apple",
2042620466
"id": "native.apple.d30969d467c66537"
2042720467
},
2042820468
{
2042920469
"kind": "ui-modifier",
20430-
"line": 857,
20470+
"line": 905,
2043120471
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2043220472
"source": "Refresh",
2043320473
"surface": "apple",
2043420474
"id": "native.apple.f4473f79bb87b2fd"
2043520475
},
2043620476
{
2043720477
"kind": "conditional-branch",
20438-
"line": 965,
20478+
"line": 1013,
2043920479
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2044020480
"source": "Connecting...",
2044120481
"surface": "apple",
2044220482
"id": "native.apple.6c984c0e3ed3a69e"
2044320483
},
2044420484
{
2044520485
"kind": "conditional-branch",
20446-
"line": 965,
20486+
"line": 1013,
2044720487
"path": "apps/shared/OpenClawKit/Sources/OpenClawChatUI/ChatComposer.swift",
2044820488
"source": "Gateway connected",
2044920489
"surface": "apple",

apps/ios/Sources/Chat/IOSGatewayChatTransport.swift

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,40 @@ struct IOSGatewayChatTransport: OpenClawChatTransport {
3535
var agentId: String?
3636
}
3737

38+
private struct SessionPatchModelParams: Encodable {
39+
var key: String
40+
var agentId: String?
41+
var model: String?
42+
43+
enum CodingKeys: String, CodingKey {
44+
case key
45+
case agentId
46+
case model
47+
}
48+
49+
func encode(to encoder: Encoder) throws {
50+
var container = encoder.container(keyedBy: CodingKeys.self)
51+
try container.encode(self.key, forKey: .key)
52+
try container.encodeIfPresent(self.agentId, forKey: .agentId)
53+
if let model {
54+
try container.encode(model, forKey: .model)
55+
} else {
56+
try container.encodeNil(forKey: .model)
57+
}
58+
}
59+
}
60+
61+
private struct ModelsListResponse: Decodable {
62+
var models: [Model]
63+
64+
struct Model: Decodable {
65+
var id: String
66+
var name: String
67+
var provider: String
68+
var contextWindow: Int?
69+
}
70+
}
71+
3872
private struct ChatSendParams: Codable {
3973
var sessionKey: String
4074
var agentId: String?
@@ -138,6 +172,18 @@ struct IOSGatewayChatTransport: OpenClawChatTransport {
138172
completed: self.isAgentWaitCompletionStatus(status))
139173
}
140174

175+
static func decodeModelChoices(_ data: Data) throws -> [OpenClawChatModelChoice] {
176+
let decoded = try JSONDecoder().decode(ModelsListResponse.self, from: data)
177+
return decoded.models.map { model in
178+
let name = model.name.trimmingCharacters(in: .whitespacesAndNewlines)
179+
return OpenClawChatModelChoice(
180+
modelID: model.id,
181+
name: name.isEmpty ? model.id : model.name,
182+
provider: model.provider,
183+
contextWindow: model.contextWindow)
184+
}
185+
}
186+
141187
static func makeCreateSessionParamsJSON(
142188
key: String,
143189
agentId: String? = nil,
@@ -164,6 +210,14 @@ struct IOSGatewayChatTransport: OpenClawChatTransport {
164210
try self.encodeParams(SessionKeyParams(key: sessionKey, agentId: agentId))
165211
}
166212

213+
static func makeSessionPatchModelParamsJSON(
214+
sessionKey: String,
215+
agentId: String? = nil,
216+
model: String?) throws -> String
217+
{
218+
try self.encodeParams(SessionPatchModelParams(key: sessionKey, agentId: agentId, model: model))
219+
}
220+
167221
private static func makeHistoryParamsJSON(sessionKey: String, agentId: String?) throws -> String {
168222
struct Params: Codable {
169223
var sessionKey: String
@@ -221,6 +275,22 @@ struct IOSGatewayChatTransport: OpenClawChatTransport {
221275
return try JSONDecoder().decode(OpenClawChatSessionsListResponse.self, from: res)
222276
}
223277

278+
func listModels() async throws -> [OpenClawChatModelChoice] {
279+
let response = try await self.gateway.request(
280+
method: "models.list",
281+
paramsJSON: nil,
282+
timeoutSeconds: 15)
283+
return try Self.decodeModelChoices(response)
284+
}
285+
286+
func setSessionModel(sessionKey: String, model: String?) async throws {
287+
let json = try Self.makeSessionPatchModelParamsJSON(
288+
sessionKey: sessionKey,
289+
agentId: self.selectedGlobalAgentId(for: sessionKey),
290+
model: model)
291+
_ = try await self.gateway.request(method: "sessions.patch", paramsJSON: json, timeoutSeconds: 15)
292+
}
293+
224294
func setActiveSessionKey(_ sessionKey: String) async throws {
225295
struct Params: Codable {
226296
var key: String

0 commit comments

Comments
 (0)