@xybrid/react-native 0.10.0-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +298 -0
  3. package/android/build.gradle +61 -0
  4. package/android/src/main/AndroidManifest.xml +1 -0
  5. package/android/src/main/java/ai/xybrid/reactnative/XybridCodec.kt +481 -0
  6. package/android/src/main/java/ai/xybrid/reactnative/XybridHandles.kt +139 -0
  7. package/android/src/main/java/ai/xybrid/reactnative/XybridModule.kt +665 -0
  8. package/android/src/main/java/ai/xybrid/reactnative/XybridPackage.kt +27 -0
  9. package/ios/XybridCodec.swift +508 -0
  10. package/ios/XybridHandles.swift +191 -0
  11. package/ios/XybridModule.mm +494 -0
  12. package/ios/XybridModuleImpl.swift +887 -0
  13. package/ios/XybridSwift/Xybrid.swift +1401 -0
  14. package/ios/XybridSwift/xybrid_bolt.swift +3198 -0
  15. package/ios/xybrid_natives.rb +178 -0
  16. package/lib/commonjs/NativeXybrid.js +24 -0
  17. package/lib/commonjs/base64.js +45 -0
  18. package/lib/commonjs/cancel.js +35 -0
  19. package/lib/commonjs/context.js +67 -0
  20. package/lib/commonjs/envelope.js +79 -0
  21. package/lib/commonjs/errors.js +61 -0
  22. package/lib/commonjs/index.js +59 -0
  23. package/lib/commonjs/model.js +307 -0
  24. package/lib/commonjs/pipeline.js +55 -0
  25. package/lib/commonjs/presets.js +32 -0
  26. package/lib/commonjs/session.js +76 -0
  27. package/lib/commonjs/types.js +10 -0
  28. package/lib/commonjs/wire.js +285 -0
  29. package/lib/commonjs/xybrid.js +133 -0
  30. package/lib/module/NativeXybrid.js +22 -0
  31. package/lib/module/base64.js +41 -0
  32. package/lib/module/cancel.js +29 -0
  33. package/lib/module/context.js +60 -0
  34. package/lib/module/envelope.js +73 -0
  35. package/lib/module/errors.js +56 -0
  36. package/lib/module/index.js +36 -0
  37. package/lib/module/model.js +298 -0
  38. package/lib/module/pipeline.js +48 -0
  39. package/lib/module/presets.js +27 -0
  40. package/lib/module/session.js +69 -0
  41. package/lib/module/types.js +9 -0
  42. package/lib/module/wire.js +271 -0
  43. package/lib/module/xybrid.js +127 -0
  44. package/lib/typescript/NativeXybrid.d.ts +68 -0
  45. package/lib/typescript/NativeXybrid.d.ts.map +1 -0
  46. package/lib/typescript/base64.d.ts +5 -0
  47. package/lib/typescript/base64.d.ts.map +1 -0
  48. package/lib/typescript/cancel.d.ts +24 -0
  49. package/lib/typescript/cancel.d.ts.map +1 -0
  50. package/lib/typescript/context.d.ts +40 -0
  51. package/lib/typescript/context.d.ts.map +1 -0
  52. package/lib/typescript/envelope.d.ts +51 -0
  53. package/lib/typescript/envelope.d.ts.map +1 -0
  54. package/lib/typescript/errors.d.ts +12 -0
  55. package/lib/typescript/errors.d.ts.map +1 -0
  56. package/lib/typescript/index.d.ts +28 -0
  57. package/lib/typescript/index.d.ts.map +1 -0
  58. package/lib/typescript/model.d.ts +211 -0
  59. package/lib/typescript/model.d.ts.map +1 -0
  60. package/lib/typescript/pipeline.d.ts +35 -0
  61. package/lib/typescript/pipeline.d.ts.map +1 -0
  62. package/lib/typescript/presets.d.ts +21 -0
  63. package/lib/typescript/presets.d.ts.map +1 -0
  64. package/lib/typescript/session.d.ts +50 -0
  65. package/lib/typescript/session.d.ts.map +1 -0
  66. package/lib/typescript/types.d.ts +313 -0
  67. package/lib/typescript/types.d.ts.map +1 -0
  68. package/lib/typescript/wire.d.ts +130 -0
  69. package/lib/typescript/wire.d.ts.map +1 -0
  70. package/lib/typescript/xybrid.d.ts +89 -0
  71. package/lib/typescript/xybrid.d.ts.map +1 -0
  72. package/package.json +88 -0
  73. package/react-native-xybrid.podspec +50 -0
  74. package/src/NativeXybrid.ts +131 -0
  75. package/src/base64.ts +43 -0
  76. package/src/cancel.ts +47 -0
  77. package/src/context.ts +70 -0
  78. package/src/envelope.ts +115 -0
  79. package/src/errors.ts +67 -0
  80. package/src/index.ts +91 -0
  81. package/src/model.ts +408 -0
  82. package/src/pipeline.ts +66 -0
  83. package/src/presets.ts +36 -0
  84. package/src/session.ts +76 -0
  85. package/src/types.ts +362 -0
  86. package/src/wire.ts +408 -0
  87. package/src/xybrid.ts +176 -0
@@ -0,0 +1,3198 @@
1
+ // Auto-generated by BoltFFI. Do not edit.
2
+ // swiftlint:disable all
3
+ // swiftformat:disable all
4
+
5
+ import Foundation
6
+ import XybridFFI
7
+
8
+ public struct FfiError: Error {
9
+ public let message: String
10
+
11
+ public init(message: String) {
12
+ self.message = message
13
+ }
14
+ }
15
+
16
+ /// Single metadata key/value entry. BoltFFI doesn't auto-derive
17
+ /// `WireEncode` for `HashMap<String, String>`, so we expose metadata as
18
+ /// `Vec<XybridMetadataEntry>`. The conversion back to `HashMap` happens
19
+ /// at the facade boundary inside [`XybridEnvelope::into`].
20
+ public struct XybridMetadataEntry: Hashable, Equatable, Sendable {
21
+ public var key: String
22
+ public var value: String
23
+
24
+ public init(key: String, value: String) {
25
+ self.key = key
26
+ self.value = value
27
+ }
28
+
29
+ @inlinable static func decode(from reader: inout WireReader) -> XybridMetadataEntry {
30
+ XybridMetadataEntry(key: reader.readString(), value: reader.readString())
31
+ }
32
+
33
+ @inlinable func encode(to writer: inout WireWriter) {
34
+ writer.writeString(self.key)
35
+ writer.writeString(self.value)
36
+ }
37
+ }
38
+
39
+ public struct XybridEnvelope: Hashable, Equatable, Sendable {
40
+ public var kind: XybridEnvelopeKind
41
+ public var metadata: [XybridMetadataEntry]
42
+
43
+ public init(kind: XybridEnvelopeKind, metadata: [XybridMetadataEntry]) {
44
+ self.kind = kind
45
+ self.metadata = metadata
46
+ }
47
+
48
+ @inlinable static func decode(from reader: inout WireReader) -> XybridEnvelope {
49
+ XybridEnvelope(kind: XybridEnvelopeKind.decode(from: &reader), metadata: reader.readArray { reader in XybridMetadataEntry.decode(from: &reader) })
50
+ }
51
+
52
+ @inlinable func encode(to writer: inout WireWriter) {
53
+ self.kind.encode(to: &writer)
54
+ writer.writeArray(self.metadata) { writer, boltffiValue0 in boltffiValue0.encode(to: &writer) }
55
+ }
56
+ }
57
+
58
+ /// A tool (function) the model may ask to call.
59
+ ///
60
+ /// `parameters_json` is the JSON Schema for the arguments, carried as a JSON
61
+ /// string because no binding generator can describe an arbitrary JSON tree.
62
+ public struct XybridToolDefinition: Hashable, Equatable, Sendable {
63
+ public var name: String
64
+ public var description: String
65
+ public var parametersJson: String
66
+
67
+ public init(name: String, description: String, parametersJson: String) {
68
+ self.name = name
69
+ self.description = description
70
+ self.parametersJson = parametersJson
71
+ }
72
+
73
+ @inlinable static func decode(from reader: inout WireReader) -> XybridToolDefinition {
74
+ XybridToolDefinition(name: reader.readString(), description: reader.readString(), parametersJson: reader.readString())
75
+ }
76
+
77
+ @inlinable func encode(to writer: inout WireWriter) {
78
+ writer.writeString(self.name)
79
+ writer.writeString(self.description)
80
+ writer.writeString(self.parametersJson)
81
+ }
82
+ }
83
+
84
+ /// One tool call the model emitted, from [`XybridResult::tool_calls`].
85
+ public struct XybridToolCall: Hashable, Equatable, Sendable {
86
+ public var id: String
87
+ public var name: String
88
+ public var argumentsJson: String
89
+
90
+ public init(id: String, name: String, argumentsJson: String) {
91
+ self.id = id
92
+ self.name = name
93
+ self.argumentsJson = argumentsJson
94
+ }
95
+
96
+ @inlinable static func decode(from reader: inout WireReader) -> XybridToolCall {
97
+ XybridToolCall(id: reader.readString(), name: reader.readString(), argumentsJson: reader.readString())
98
+ }
99
+
100
+ @inlinable func encode(to writer: inout WireWriter) {
101
+ writer.writeString(self.id)
102
+ writer.writeString(self.name)
103
+ writer.writeString(self.argumentsJson)
104
+ }
105
+ }
106
+
107
+ /// The outcome of running one tool, fed back with [`tool_results_envelope`].
108
+ public struct XybridToolResult: Hashable, Equatable, Sendable {
109
+ /// The [`XybridToolCall::id`] this answers.
110
+ public var callId: String
111
+ public var name: String
112
+ /// The tool's output as a JSON string.
113
+ public var contentJson: String
114
+
115
+ public init(callId: String, name: String, contentJson: String) {
116
+ self.callId = callId
117
+ self.name = name
118
+ self.contentJson = contentJson
119
+ }
120
+
121
+ @inlinable static func decode(from reader: inout WireReader) -> XybridToolResult {
122
+ XybridToolResult(callId: reader.readString(), name: reader.readString(), contentJson: reader.readString())
123
+ }
124
+
125
+ @inlinable func encode(to writer: inout WireWriter) {
126
+ writer.writeString(self.callId)
127
+ writer.writeString(self.name)
128
+ writer.writeString(self.contentJson)
129
+ }
130
+ }
131
+
132
+ public struct XybridGenerationConfig: Hashable, Equatable, Sendable {
133
+ public var maxTokens: UInt32?
134
+ public var temperature: Float?
135
+ public var topP: Float?
136
+ public var minP: Float?
137
+ public var topK: UInt32?
138
+ public var repetitionPenalty: Float?
139
+ public var stopSequences: [String]
140
+ /// Optional GBNF grammar constraining generation to structured output
141
+ /// (local llama backend only). Produce one from a JSON Schema with
142
+ /// [`json_schema_to_gbnf`], or pass raw GBNF. Appended last: `#[data]`
143
+ /// PODs serialize by field order across the FFI boundary.
144
+ public var grammar: String?
145
+ /// Tools the model may call this turn. Empty means no tool calling —
146
+ /// existing behavior, unchanged. Appended after `grammar` for the same
147
+ /// field-order reason.
148
+ ///
149
+ /// Tool calling is llama.cpp-only today; unsupported paths (no embedded
150
+ /// chat template, the mistralrs backend, the cloud fallback leg) reject
151
+ /// tool-bearing requests rather than quietly generating without them.
152
+ public var tools: [XybridToolDefinition]
153
+
154
+ public init(
155
+ maxTokens: UInt32?,
156
+ temperature: Float?,
157
+ topP: Float?,
158
+ minP: Float?,
159
+ topK: UInt32?,
160
+ repetitionPenalty: Float?,
161
+ stopSequences: [String],
162
+ grammar: String?,
163
+ tools: [XybridToolDefinition]
164
+ ) {
165
+ self.maxTokens = maxTokens
166
+ self.temperature = temperature
167
+ self.topP = topP
168
+ self.minP = minP
169
+ self.topK = topK
170
+ self.repetitionPenalty = repetitionPenalty
171
+ self.stopSequences = stopSequences
172
+ self.grammar = grammar
173
+ self.tools = tools
174
+ }
175
+
176
+ @inlinable static func decode(from reader: inout WireReader) -> XybridGenerationConfig {
177
+ XybridGenerationConfig(
178
+ maxTokens: reader.readOptional { reader in reader.readU32() },
179
+ temperature: reader.readOptional { reader in reader.readF32() },
180
+ topP: reader.readOptional { reader in reader.readF32() },
181
+ minP: reader.readOptional { reader in reader.readF32() },
182
+ topK: reader.readOptional { reader in reader.readU32() },
183
+ repetitionPenalty: reader.readOptional { reader in reader.readF32() },
184
+ stopSequences: reader.readArray { reader in reader.readString() },
185
+ grammar: reader.readOptional { reader in reader.readString() },
186
+ tools: reader.readArray { reader in XybridToolDefinition.decode(from: &reader) }
187
+ )
188
+ }
189
+
190
+ @inlinable func encode(to writer: inout WireWriter) {
191
+ writer.writeOptional(self.maxTokens) { writer, boltffiValue0 in writer.writeU32(boltffiValue0) }
192
+ writer.writeOptional(self.temperature) { writer, boltffiValue0 in writer.writeF32(boltffiValue0) }
193
+ writer.writeOptional(self.topP) { writer, boltffiValue0 in writer.writeF32(boltffiValue0) }
194
+ writer.writeOptional(self.minP) { writer, boltffiValue0 in writer.writeF32(boltffiValue0) }
195
+ writer.writeOptional(self.topK) { writer, boltffiValue0 in writer.writeU32(boltffiValue0) }
196
+ writer.writeOptional(self.repetitionPenalty) { writer, boltffiValue0 in writer.writeF32(boltffiValue0) }
197
+ writer.writeArray(self.stopSequences) { writer, boltffiValue0 in writer.writeString(boltffiValue0) }
198
+ writer.writeOptional(self.grammar) { writer, boltffiValue0 in writer.writeString(boltffiValue0) }
199
+ writer.writeArray(self.tools) { writer, boltffiValue0 in boltffiValue0.encode(to: &writer) }
200
+ }
201
+ }
202
+
203
+ public struct XybridRunOptions: Hashable, Equatable, Sendable {
204
+ public var generationConfig: XybridGenerationConfig?
205
+ public var abortOn: [XybridAbortSignal]
206
+ public var fallbackToCloud: Bool
207
+ public var maxGraceTokens: UInt32
208
+ public var correlationId: String?
209
+
210
+ public init(
211
+ generationConfig: XybridGenerationConfig?,
212
+ abortOn: [XybridAbortSignal],
213
+ fallbackToCloud: Bool,
214
+ maxGraceTokens: UInt32,
215
+ correlationId: String?
216
+ ) {
217
+ self.generationConfig = generationConfig
218
+ self.abortOn = abortOn
219
+ self.fallbackToCloud = fallbackToCloud
220
+ self.maxGraceTokens = maxGraceTokens
221
+ self.correlationId = correlationId
222
+ }
223
+
224
+ @inlinable static func decode(from reader: inout WireReader) -> XybridRunOptions {
225
+ XybridRunOptions(
226
+ generationConfig: reader.readOptional { reader in XybridGenerationConfig.decode(from: &reader) },
227
+ abortOn: reader.readArray { reader in XybridAbortSignal(rawValue: reader.readI32())! },
228
+ fallbackToCloud: reader.readBool(),
229
+ maxGraceTokens: reader.readU32(),
230
+ correlationId: reader.readOptional { reader in reader.readString() }
231
+ )
232
+ }
233
+
234
+ @inlinable func encode(to writer: inout WireWriter) {
235
+ writer.writeOptional(self.generationConfig) { writer, boltffiValue0 in boltffiValue0.encode(to: &writer) }
236
+ writer.writeArray(self.abortOn) { writer, boltffiValue0 in writer.writeI32(boltffiValue0.rawValue) }
237
+ writer.writeBool(self.fallbackToCloud)
238
+ writer.writeU32(self.maxGraceTokens)
239
+ writer.writeOptional(self.correlationId) { writer, boltffiValue0 in writer.writeString(boltffiValue0) }
240
+ }
241
+ }
242
+
243
+ public struct XybridStageLatency: Hashable, Equatable, Sendable {
244
+ public var stageId: String
245
+ public var latencyMs: UInt32
246
+
247
+ public init(stageId: String, latencyMs: UInt32) {
248
+ self.stageId = stageId
249
+ self.latencyMs = latencyMs
250
+ }
251
+
252
+ @inlinable static func decode(from reader: inout WireReader) -> XybridStageLatency {
253
+ XybridStageLatency(stageId: reader.readString(), latencyMs: reader.readU32())
254
+ }
255
+
256
+ @inlinable func encode(to writer: inout WireWriter) {
257
+ writer.writeString(self.stageId)
258
+ writer.writeU32(self.latencyMs)
259
+ }
260
+ }
261
+
262
+ public struct XybridInferenceMetrics: Hashable, Equatable, Sendable {
263
+ public var totalMs: UInt32
264
+ public var ttftMs: UInt32?
265
+ public var tokensPerSecond: Float?
266
+ public var prefillTps: Float?
267
+ public var decodeTps: Float?
268
+ public var tokensOut: UInt32?
269
+ public var stageLatenciesMs: [XybridStageLatency]
270
+
271
+ public init(
272
+ totalMs: UInt32,
273
+ ttftMs: UInt32?,
274
+ tokensPerSecond: Float?,
275
+ prefillTps: Float?,
276
+ decodeTps: Float?,
277
+ tokensOut: UInt32?,
278
+ stageLatenciesMs: [XybridStageLatency]
279
+ ) {
280
+ self.totalMs = totalMs
281
+ self.ttftMs = ttftMs
282
+ self.tokensPerSecond = tokensPerSecond
283
+ self.prefillTps = prefillTps
284
+ self.decodeTps = decodeTps
285
+ self.tokensOut = tokensOut
286
+ self.stageLatenciesMs = stageLatenciesMs
287
+ }
288
+
289
+ @inlinable static func decode(from reader: inout WireReader) -> XybridInferenceMetrics {
290
+ XybridInferenceMetrics(
291
+ totalMs: reader.readU32(),
292
+ ttftMs: reader.readOptional { reader in reader.readU32() },
293
+ tokensPerSecond: reader.readOptional { reader in reader.readF32() },
294
+ prefillTps: reader.readOptional { reader in reader.readF32() },
295
+ decodeTps: reader.readOptional { reader in reader.readF32() },
296
+ tokensOut: reader.readOptional { reader in reader.readU32() },
297
+ stageLatenciesMs: reader.readArray { reader in XybridStageLatency.decode(from: &reader) }
298
+ )
299
+ }
300
+
301
+ @inlinable func encode(to writer: inout WireWriter) {
302
+ writer.writeU32(self.totalMs)
303
+ writer.writeOptional(self.ttftMs) { writer, boltffiValue0 in writer.writeU32(boltffiValue0) }
304
+ writer.writeOptional(self.tokensPerSecond) { writer, boltffiValue0 in writer.writeF32(boltffiValue0) }
305
+ writer.writeOptional(self.prefillTps) { writer, boltffiValue0 in writer.writeF32(boltffiValue0) }
306
+ writer.writeOptional(self.decodeTps) { writer, boltffiValue0 in writer.writeF32(boltffiValue0) }
307
+ writer.writeOptional(self.tokensOut) { writer, boltffiValue0 in writer.writeU32(boltffiValue0) }
308
+ writer.writeArray(self.stageLatenciesMs) { writer, boltffiValue0 in boltffiValue0.encode(to: &writer) }
309
+ }
310
+ }
311
+
312
+ /// Inference output. Named `XybridResult` (not `XybridInferenceResult`)
313
+ /// to match the existing uniffi-generated Kotlin/Swift name — the iOS
314
+ /// example references `XybridResult` directly.
315
+ public struct XybridResult: Hashable, Equatable, Sendable {
316
+ public var envelope: XybridEnvelope
317
+ public var outputType: XybridOutputType
318
+ public var modelId: String
319
+ public var latencyMs: UInt32
320
+ /// Where the answer actually came from. Cloud fallback keeps `model_id`
321
+ /// identical on both legs, so this is the only way to tell them apart.
322
+ public var executionTarget: XybridExecutionTarget
323
+ public var metrics: XybridInferenceMetrics
324
+ /// Tool calls the model emitted this turn. Empty unless the request
325
+ /// offered tools via [`XybridGenerationConfig::tools`].
326
+ /// `#[data]` PODs serialize by field order across the FFI boundary.
327
+ public var toolCalls: [XybridToolCall]
328
+ /// Model reasoning emitted separately from the final answer text.
329
+ /// Appended last because `#[data]` fields serialize in declaration order.
330
+ public var reasoningContent: String?
331
+
332
+ public init(
333
+ envelope: XybridEnvelope,
334
+ outputType: XybridOutputType,
335
+ modelId: String,
336
+ latencyMs: UInt32,
337
+ executionTarget: XybridExecutionTarget,
338
+ metrics: XybridInferenceMetrics,
339
+ toolCalls: [XybridToolCall],
340
+ reasoningContent: String? = nil
341
+ ) {
342
+ self.envelope = envelope
343
+ self.outputType = outputType
344
+ self.modelId = modelId
345
+ self.latencyMs = latencyMs
346
+ self.executionTarget = executionTarget
347
+ self.metrics = metrics
348
+ self.toolCalls = toolCalls
349
+ self.reasoningContent = reasoningContent
350
+ }
351
+
352
+ @inlinable static func decode(from reader: inout WireReader) -> XybridResult {
353
+ let envelope = XybridEnvelope.decode(from: &reader)
354
+ let outputType = XybridOutputType(rawValue: reader.readI32())!
355
+ let modelId = reader.readString()
356
+ let latencyMs = reader.readU32()
357
+ let executionTarget = XybridExecutionTarget(rawValue: reader.readI32())!
358
+ let metrics = XybridInferenceMetrics.decode(from: &reader)
359
+ let toolCalls = reader.readArray { reader in XybridToolCall.decode(from: &reader) }
360
+ let reasoningContent = reader.position < reader.data.count
361
+ ? reader.readOptional { reader in reader.readString() }
362
+ : envelope.metadata.first { $0.key == "reasoning_content" }?.value
363
+ return XybridResult(
364
+ envelope: envelope,
365
+ outputType: outputType,
366
+ modelId: modelId,
367
+ latencyMs: latencyMs,
368
+ executionTarget: executionTarget,
369
+ metrics: metrics,
370
+ toolCalls: toolCalls,
371
+ reasoningContent: reasoningContent
372
+ )
373
+ }
374
+
375
+ @inlinable func encode(to writer: inout WireWriter) {
376
+ self.envelope.encode(to: &writer)
377
+ writer.writeI32(self.outputType.rawValue)
378
+ writer.writeString(self.modelId)
379
+ writer.writeU32(self.latencyMs)
380
+ writer.writeI32(self.executionTarget.rawValue)
381
+ self.metrics.encode(to: &writer)
382
+ writer.writeArray(self.toolCalls) { writer, boltffiValue0 in boltffiValue0.encode(to: &writer) }
383
+ writer.writeOptional(self.reasoningContent) { writer, boltffiValue0 in writer.writeString(boltffiValue0) }
384
+ }
385
+ }
386
+
387
+ /// Download progress, bytes and state in one consistent read.
388
+ ///
389
+ /// `progress` is aggregated across every artifact the model needs (weights
390
+ /// plus companions such as a vision projector), never moves backwards, and
391
+ /// reaches 1.0 only alongside `Ready`. `totalBytes` is null when the source
392
+ /// declares no size — a Hugging Face repo, or a registry entry without one —
393
+ /// in which case `downloadedBytes` is still exact and `progress` is coarser.
394
+ ///
395
+ /// Derives `Copy` because it is carried as a stream item.
396
+ public struct XybridDownloadStatus: Hashable, Equatable, Sendable {
397
+ public var state: XybridDownloadState
398
+ /// 0.0..=1.0.
399
+ public var progress: Float
400
+ /// Bytes written so far, across every artifact.
401
+ public var downloadedBytes: UInt64
402
+ /// Declared total across every artifact, or null when unknown.
403
+ public var totalBytes: UInt64?
404
+
405
+ public init(
406
+ state: XybridDownloadState,
407
+ progress: Float,
408
+ downloadedBytes: UInt64,
409
+ totalBytes: UInt64?
410
+ ) {
411
+ self.state = state
412
+ self.progress = progress
413
+ self.downloadedBytes = downloadedBytes
414
+ self.totalBytes = totalBytes
415
+ }
416
+
417
+ @inlinable static func decode(from reader: inout WireReader) -> XybridDownloadStatus {
418
+ XybridDownloadStatus(
419
+ state: XybridDownloadState(rawValue: reader.readI32())!,
420
+ progress: reader.readF32(),
421
+ downloadedBytes: reader.readU64(),
422
+ totalBytes: reader.readOptional { reader in reader.readU64() }
423
+ )
424
+ }
425
+
426
+ @inlinable func encode(to writer: inout WireWriter) {
427
+ writer.writeI32(self.state.rawValue)
428
+ writer.writeF32(self.progress)
429
+ writer.writeU64(self.downloadedBytes)
430
+ writer.writeOptional(self.totalBytes) { writer, boltffiValue0 in writer.writeU64(boltffiValue0) }
431
+ }
432
+ }
433
+
434
+ /// What one stage of a pipeline run produced.
435
+ public struct XybridStageResult: Hashable, Equatable, Sendable {
436
+ /// Stage identifier from the pipeline YAML (`id:`), or the model ID when
437
+ /// the stage declares none. Matches [`XybridPipeline::stage_names`].
438
+ public var stageId: String
439
+ /// This stage's output, which is also the next stage's input — the
440
+ /// transcript of an ASR stage, the reply of an LLM stage.
441
+ public var envelope: XybridEnvelope
442
+ public var outputType: XybridOutputType
443
+ public var latencyMs: UInt32
444
+ /// Where this stage ran. Stages of one pipeline can run in different
445
+ /// places.
446
+ public var executionTarget: XybridExecutionTarget
447
+ /// Generation figures (TTFT, tokens per second) when this stage is a
448
+ /// language model; `total_ms` is the stage latency.
449
+ public var metrics: XybridInferenceMetrics
450
+
451
+ public init(
452
+ stageId: String,
453
+ envelope: XybridEnvelope,
454
+ outputType: XybridOutputType,
455
+ latencyMs: UInt32,
456
+ executionTarget: XybridExecutionTarget,
457
+ metrics: XybridInferenceMetrics
458
+ ) {
459
+ self.stageId = stageId
460
+ self.envelope = envelope
461
+ self.outputType = outputType
462
+ self.latencyMs = latencyMs
463
+ self.executionTarget = executionTarget
464
+ self.metrics = metrics
465
+ }
466
+
467
+ @inlinable static func decode(from reader: inout WireReader) -> XybridStageResult {
468
+ XybridStageResult(
469
+ stageId: reader.readString(),
470
+ envelope: XybridEnvelope.decode(from: &reader),
471
+ outputType: XybridOutputType(rawValue: reader.readI32())!,
472
+ latencyMs: reader.readU32(),
473
+ executionTarget: XybridExecutionTarget(rawValue: reader.readI32())!,
474
+ metrics: XybridInferenceMetrics.decode(from: &reader)
475
+ )
476
+ }
477
+
478
+ @inlinable func encode(to writer: inout WireWriter) {
479
+ writer.writeString(self.stageId)
480
+ self.envelope.encode(to: &writer)
481
+ writer.writeI32(self.outputType.rawValue)
482
+ writer.writeU32(self.latencyMs)
483
+ writer.writeI32(self.executionTarget.rawValue)
484
+ self.metrics.encode(to: &writer)
485
+ }
486
+ }
487
+
488
+ /// Result of [`XybridPipeline::run`]: the final output plus every stage's own
489
+ /// output, so a voice pipeline can show the transcript and the reply as well
490
+ /// as play the audio.
491
+ public struct XybridPipelineResult: Hashable, Equatable, Sendable {
492
+ /// The final stage's output — the same envelope as the last entry of
493
+ /// `stages`.
494
+ public var envelope: XybridEnvelope
495
+ public var outputType: XybridOutputType
496
+ /// Wall-clock time of the whole run.
497
+ public var latencyMs: UInt32
498
+ /// Every executed stage, in order.
499
+ public var stages: [XybridStageResult]
500
+
501
+ public init(
502
+ envelope: XybridEnvelope,
503
+ outputType: XybridOutputType,
504
+ latencyMs: UInt32,
505
+ stages: [XybridStageResult]
506
+ ) {
507
+ self.envelope = envelope
508
+ self.outputType = outputType
509
+ self.latencyMs = latencyMs
510
+ self.stages = stages
511
+ }
512
+
513
+ @inlinable static func decode(from reader: inout WireReader) -> XybridPipelineResult {
514
+ XybridPipelineResult(
515
+ envelope: XybridEnvelope.decode(from: &reader),
516
+ outputType: XybridOutputType(rawValue: reader.readI32())!,
517
+ latencyMs: reader.readU32(),
518
+ stages: reader.readArray { reader in XybridStageResult.decode(from: &reader) }
519
+ )
520
+ }
521
+
522
+ @inlinable func encode(to writer: inout WireWriter) {
523
+ self.envelope.encode(to: &writer)
524
+ writer.writeI32(self.outputType.rawValue)
525
+ writer.writeU32(self.latencyMs)
526
+ writer.writeArray(self.stages) { writer, boltffiValue0 in boltffiValue0.encode(to: &writer) }
527
+ }
528
+ }
529
+
530
+ public struct XybridStreamToken: Hashable, Equatable, Sendable {
531
+ public var token: String
532
+ public var tokenId: Int64?
533
+ public var index: UInt64
534
+ public var cumulativeText: String
535
+ /// `"tool_calls"` when the turn ended on a parseable tool-call block.
536
+ public var finishReason: String?
537
+ /// Tool calls parsed from the completed turn — populated on the
538
+ /// **terminal** token only (the one carrying `finish_reason`).
539
+ ///
540
+ /// Tool-call blocks are suppressed from the emitted stream, so there is
541
+ /// nothing in the token text to parse: a streaming caller halts here,
542
+ /// runs the tools, then continues the turn by streaming a
543
+ /// [`tool_results_envelope`] through the same call. Empty on every
544
+ /// mid-stream token and on turns that emitted no call.
545
+ public var toolCalls: [XybridToolCall]
546
+ /// The completed turn's raw output text, tool-call block included — pass
547
+ /// it to [`tool_results_envelope`] as `prior_assistant_text`.
548
+ ///
549
+ /// Present only alongside a non-empty [`Self::tool_calls`]. Not the same
550
+ /// as `cumulative_text`, which reports the *emitted* text with the
551
+ /// protocol blocks suppressed — which is why this field exists at all.
552
+ public var rawText: String?
553
+
554
+ public init(
555
+ token: String,
556
+ tokenId: Int64?,
557
+ index: UInt64,
558
+ cumulativeText: String,
559
+ finishReason: String?,
560
+ toolCalls: [XybridToolCall],
561
+ rawText: String?
562
+ ) {
563
+ self.token = token
564
+ self.tokenId = tokenId
565
+ self.index = index
566
+ self.cumulativeText = cumulativeText
567
+ self.finishReason = finishReason
568
+ self.toolCalls = toolCalls
569
+ self.rawText = rawText
570
+ }
571
+
572
+ @inlinable static func decode(from reader: inout WireReader) -> XybridStreamToken {
573
+ XybridStreamToken(
574
+ token: reader.readString(),
575
+ tokenId: reader.readOptional { reader in reader.readI64() },
576
+ index: reader.readU64(),
577
+ cumulativeText: reader.readString(),
578
+ finishReason: reader.readOptional { reader in reader.readString() },
579
+ toolCalls: reader.readArray { reader in XybridToolCall.decode(from: &reader) },
580
+ rawText: reader.readOptional { reader in reader.readString() }
581
+ )
582
+ }
583
+
584
+ @inlinable func encode(to writer: inout WireWriter) {
585
+ writer.writeString(self.token)
586
+ writer.writeOptional(self.tokenId) { writer, boltffiValue0 in writer.writeI64(boltffiValue0) }
587
+ writer.writeU64(self.index)
588
+ writer.writeString(self.cumulativeText)
589
+ writer.writeOptional(self.finishReason) { writer, boltffiValue0 in writer.writeString(boltffiValue0) }
590
+ writer.writeArray(self.toolCalls) { writer, boltffiValue0 in boltffiValue0.encode(to: &writer) }
591
+ writer.writeOptional(self.rawText) { writer, boltffiValue0 in writer.writeString(boltffiValue0) }
592
+ }
593
+ }
594
+
595
+ /// One pull from a streaming inference session.
596
+ ///
597
+ /// This is a flat record instead of a data-carrying enum because the pinned
598
+ /// C# generator cannot lower that enum shape reliably. `kind` selects the one
599
+ /// populated payload: `token` for `Token`, none for `Complete`. A `Complete`
600
+ /// event is followed by [`XybridModel::stream_result`] to retrieve the final
601
+ /// result. Inference failures are returned as typed [`XybridError`] values by
602
+ /// [`XybridModel::stream_next`].
603
+ public struct XybridStreamEvent: Hashable, Equatable, Sendable {
604
+ public var kind: XybridStreamEventKind
605
+ public var token: XybridStreamToken?
606
+
607
+ public init(kind: XybridStreamEventKind, token: XybridStreamToken?) {
608
+ self.kind = kind
609
+ self.token = token
610
+ }
611
+
612
+ @inlinable static func decode(from reader: inout WireReader) -> XybridStreamEvent {
613
+ XybridStreamEvent(kind: XybridStreamEventKind(rawValue: reader.readI32())!, token: reader.readOptional { reader in XybridStreamToken.decode(from: &reader) })
614
+ }
615
+
616
+ @inlinable func encode(to writer: inout WireWriter) {
617
+ writer.writeI32(self.kind.rawValue)
618
+ writer.writeOptional(self.token) { writer, boltffiValue0 in boltffiValue0.encode(to: &writer) }
619
+ }
620
+ }
621
+
622
+ public struct XybridVoiceInfo: Hashable, Equatable, Sendable {
623
+ public var id: String
624
+ public var name: String
625
+ public var gender: String?
626
+ public var language: String?
627
+ public var style: String?
628
+
629
+ public init(
630
+ id: String,
631
+ name: String,
632
+ gender: String?,
633
+ language: String?,
634
+ style: String?
635
+ ) {
636
+ self.id = id
637
+ self.name = name
638
+ self.gender = gender
639
+ self.language = language
640
+ self.style = style
641
+ }
642
+
643
+ @inlinable static func decode(from reader: inout WireReader) -> XybridVoiceInfo {
644
+ XybridVoiceInfo(
645
+ id: reader.readString(),
646
+ name: reader.readString(),
647
+ gender: reader.readOptional { reader in reader.readString() },
648
+ language: reader.readOptional { reader in reader.readString() },
649
+ style: reader.readOptional { reader in reader.readString() }
650
+ )
651
+ }
652
+
653
+ @inlinable func encode(to writer: inout WireWriter) {
654
+ writer.writeString(self.id)
655
+ writer.writeString(self.name)
656
+ writer.writeOptional(self.gender) { writer, boltffiValue0 in writer.writeString(boltffiValue0) }
657
+ writer.writeOptional(self.language) { writer, boltffiValue0 in writer.writeString(boltffiValue0) }
658
+ writer.writeOptional(self.style) { writer, boltffiValue0 in writer.writeString(boltffiValue0) }
659
+ }
660
+ }
661
+
662
+ public struct XybridCacheEntry: Hashable, Equatable, Sendable {
663
+ public var modelId: String
664
+ public var location: XybridCacheEntryLocation
665
+ public var path: String
666
+ public var sizeBytes: UInt64
667
+
668
+ public init(
669
+ modelId: String,
670
+ location: XybridCacheEntryLocation,
671
+ path: String,
672
+ sizeBytes: UInt64
673
+ ) {
674
+ self.modelId = modelId
675
+ self.location = location
676
+ self.path = path
677
+ self.sizeBytes = sizeBytes
678
+ }
679
+
680
+ @inlinable static func decode(from reader: inout WireReader) -> XybridCacheEntry {
681
+ XybridCacheEntry(
682
+ modelId: reader.readString(),
683
+ location: XybridCacheEntryLocation(rawValue: reader.readI32())!,
684
+ path: reader.readString(),
685
+ sizeBytes: reader.readU64()
686
+ )
687
+ }
688
+
689
+ @inlinable func encode(to writer: inout WireWriter) {
690
+ writer.writeString(self.modelId)
691
+ writer.writeI32(self.location.rawValue)
692
+ writer.writeString(self.path)
693
+ writer.writeU64(self.sizeBytes)
694
+ }
695
+ }
696
+
697
+ public struct XybridCacheStatus: Hashable, Equatable, Sendable {
698
+ public var totalSizeBytes: UInt64
699
+ public var entryCount: UInt32
700
+ public var modelCount: UInt32
701
+ public var extractedModelCount: UInt32
702
+ public var cacheRoot: String
703
+
704
+ public init(
705
+ totalSizeBytes: UInt64,
706
+ entryCount: UInt32,
707
+ modelCount: UInt32,
708
+ extractedModelCount: UInt32,
709
+ cacheRoot: String
710
+ ) {
711
+ self.totalSizeBytes = totalSizeBytes
712
+ self.entryCount = entryCount
713
+ self.modelCount = modelCount
714
+ self.extractedModelCount = extractedModelCount
715
+ self.cacheRoot = cacheRoot
716
+ }
717
+
718
+ @inlinable static func decode(from reader: inout WireReader) -> XybridCacheStatus {
719
+ XybridCacheStatus(
720
+ totalSizeBytes: reader.readU64(),
721
+ entryCount: reader.readU32(),
722
+ modelCount: reader.readU32(),
723
+ extractedModelCount: reader.readU32(),
724
+ cacheRoot: reader.readString()
725
+ )
726
+ }
727
+
728
+ @inlinable func encode(to writer: inout WireWriter) {
729
+ writer.writeU64(self.totalSizeBytes)
730
+ writer.writeU32(self.entryCount)
731
+ writer.writeU32(self.modelCount)
732
+ writer.writeU32(self.extractedModelCount)
733
+ writer.writeString(self.cacheRoot)
734
+ }
735
+ }
736
+
737
+ /// Configuration for a live ASR session.
738
+ ///
739
+ /// The model is not named here — it comes from the loaded `XybridModel` the
740
+ /// session is opened on. This only configures *how* the audio is chunked.
741
+ public struct XybridStreamingConfig: Hashable, Equatable, Sendable {
742
+ /// Sample rate of the audio you will feed. Must be 16000; the ASR
743
+ /// backends are fixed there, so anything else is rejected rather than
744
+ /// silently resampled.
745
+ public var sampleRate: UInt32
746
+ /// Voice-activity-detection mode.
747
+ public var vad: XybridVadMode
748
+ /// VAD sensitivity, 0.0–1.0. Ignored when `vad` is `Off`.
749
+ public var vadThreshold: Float
750
+ /// Language hint (e.g. `"en"`); null uses the model default.
751
+ public var language: String?
752
+ /// Whisper encoder context in mel frames; null uses the model default.
753
+ public var audioCtx: UInt32?
754
+
755
+ public init(
756
+ sampleRate: UInt32,
757
+ vad: XybridVadMode,
758
+ vadThreshold: Float,
759
+ language: String?,
760
+ audioCtx: UInt32?
761
+ ) {
762
+ self.sampleRate = sampleRate
763
+ self.vad = vad
764
+ self.vadThreshold = vadThreshold
765
+ self.language = language
766
+ self.audioCtx = audioCtx
767
+ }
768
+
769
+ @inlinable static func decode(from reader: inout WireReader) -> XybridStreamingConfig {
770
+ XybridStreamingConfig(
771
+ sampleRate: reader.readU32(),
772
+ vad: XybridVadMode.decode(from: &reader),
773
+ vadThreshold: reader.readF32(),
774
+ language: reader.readOptional { reader in reader.readString() },
775
+ audioCtx: reader.readOptional { reader in reader.readU32() }
776
+ )
777
+ }
778
+
779
+ @inlinable func encode(to writer: inout WireWriter) {
780
+ writer.writeU32(self.sampleRate)
781
+ self.vad.encode(to: &writer)
782
+ writer.writeF32(self.vadThreshold)
783
+ writer.writeOptional(self.language) { writer, boltffiValue0 in writer.writeString(boltffiValue0) }
784
+ writer.writeOptional(self.audioCtx) { writer, boltffiValue0 in writer.writeU32(boltffiValue0) }
785
+ }
786
+ }
787
+
788
+ /// A partial transcript emitted while audio is streaming.
789
+ public struct XybridPartialResult: Hashable, Equatable, Sendable {
790
+ /// Best-effort transcript so far. Cumulative, not a delta — render it in
791
+ /// place of the previous partial rather than appending.
792
+ public var text: String
793
+ /// `true` once this span is committed and will not change.
794
+ public var isStable: Bool
795
+ /// Monotonic chunk sequence number this result corresponds to.
796
+ public var chunkSequence: UInt64
797
+ /// Audio covered so far, in milliseconds.
798
+ public var audioDurationMs: UInt64
799
+
800
+ public init(
801
+ text: String,
802
+ isStable: Bool,
803
+ chunkSequence: UInt64,
804
+ audioDurationMs: UInt64
805
+ ) {
806
+ self.text = text
807
+ self.isStable = isStable
808
+ self.chunkSequence = chunkSequence
809
+ self.audioDurationMs = audioDurationMs
810
+ }
811
+
812
+ @inlinable static func decode(from reader: inout WireReader) -> XybridPartialResult {
813
+ XybridPartialResult(
814
+ text: reader.readString(),
815
+ isStable: reader.readBool(),
816
+ chunkSequence: reader.readU64(),
817
+ audioDurationMs: reader.readU64()
818
+ )
819
+ }
820
+
821
+ @inlinable func encode(to writer: inout WireWriter) {
822
+ writer.writeString(self.text)
823
+ writer.writeBool(self.isStable)
824
+ writer.writeU64(self.chunkSequence)
825
+ writer.writeU64(self.audioDurationMs)
826
+ }
827
+ }
828
+
829
+ /// Errors surfaced across the FFI boundary. Variants mirror
830
+ /// [`facade::Error`] — the facade owns the SDK→FFI translation; this enum
831
+ /// only re-decorates it for the BoltFFI generator (proc macros must live
832
+ /// on the type definition).
833
+ ///
834
+ /// Named `XybridError` (not `Error`) so the emitted Swift type doesn't
835
+ /// shadow / collide with Swift's stdlib `Error` protocol, and so the
836
+ /// Kotlin sealed-hierarchy name matches the existing uniffi consumer
837
+ /// expectations.
838
+ ///
839
+ /// **Variant order is part of the wire contract.** BoltFFI encodes `#[error]`
840
+ /// (and `#[data]`) enums by ordinal tag, so reordering or inserting a variant
841
+ /// renumbers every variant after it and breaks already-built foreign clients.
842
+ /// Only ever append at the tail, and keep this order in lockstep with
843
+ /// [`facade::Error`] and its `code()` table.
844
+ public enum XybridError: Hashable, Equatable, Sendable, Error {
845
+ case modelNotFound(id: String)
846
+ case directoryNotFound(path: String)
847
+ case metadataNotFound(path: String)
848
+ case metadataInvalid(message: String)
849
+ case loadError(message: String)
850
+ case inferenceError(message: String)
851
+ case abortedForCloudFallback(reason: String)
852
+ case streamingNotSupported
853
+ case notLoaded
854
+ case configError(message: String)
855
+ case networkError(message: String)
856
+ case offline(message: String)
857
+ case ioError(message: String)
858
+ case cacheError(message: String)
859
+ case pipelineError(message: String)
860
+ case circuitOpen(message: String)
861
+ case rateLimited(retryAfterSecs: UInt64)
862
+ case timeout(timeoutMs: UInt64)
863
+ case missingArtifact(message: String)
864
+ case unsupportedModelCapability(message: String)
865
+ case unsupportedBackendCapability(message: String)
866
+ case invalidImage(message: String)
867
+ /// The host called `cancel` — today, on a model download.
868
+ case cancelled(message: String)
869
+
870
+ @inlinable static func decode(from reader: inout WireReader) -> XybridError {
871
+ let tag = reader.readU32()
872
+ switch tag {
873
+ case 0:
874
+ return .modelNotFound(id: reader.readString())
875
+ case 1:
876
+ return .directoryNotFound(path: reader.readString())
877
+ case 2:
878
+ return .metadataNotFound(path: reader.readString())
879
+ case 3:
880
+ return .metadataInvalid(message: reader.readString())
881
+ case 4:
882
+ return .loadError(message: reader.readString())
883
+ case 5:
884
+ return .inferenceError(message: reader.readString())
885
+ case 6:
886
+ return .abortedForCloudFallback(reason: reader.readString())
887
+ case 7:
888
+ return .streamingNotSupported
889
+ case 8:
890
+ return .notLoaded
891
+ case 9:
892
+ return .configError(message: reader.readString())
893
+ case 10:
894
+ return .networkError(message: reader.readString())
895
+ case 11:
896
+ return .offline(message: reader.readString())
897
+ case 12:
898
+ return .ioError(message: reader.readString())
899
+ case 13:
900
+ return .cacheError(message: reader.readString())
901
+ case 14:
902
+ return .pipelineError(message: reader.readString())
903
+ case 15:
904
+ return .circuitOpen(message: reader.readString())
905
+ case 16:
906
+ return .rateLimited(retryAfterSecs: reader.readU64())
907
+ case 17:
908
+ return .timeout(timeoutMs: reader.readU64())
909
+ case 18:
910
+ return .missingArtifact(message: reader.readString())
911
+ case 19:
912
+ return .unsupportedModelCapability(message: reader.readString())
913
+ case 20:
914
+ return .unsupportedBackendCapability(message: reader.readString())
915
+ case 21:
916
+ return .invalidImage(message: reader.readString())
917
+ case 22:
918
+ return .cancelled(message: reader.readString())
919
+ default:
920
+ fatalError("Invalid XybridError tag: \(tag)")
921
+ }
922
+ }
923
+
924
+ @inlinable func encode(to writer: inout WireWriter) {
925
+ switch self {
926
+ case let .modelNotFound(id):
927
+ writer.writeU32(0)
928
+ writer.writeString(id)
929
+ case let .directoryNotFound(path):
930
+ writer.writeU32(1)
931
+ writer.writeString(path)
932
+ case let .metadataNotFound(path):
933
+ writer.writeU32(2)
934
+ writer.writeString(path)
935
+ case let .metadataInvalid(message):
936
+ writer.writeU32(3)
937
+ writer.writeString(message)
938
+ case let .loadError(message):
939
+ writer.writeU32(4)
940
+ writer.writeString(message)
941
+ case let .inferenceError(message):
942
+ writer.writeU32(5)
943
+ writer.writeString(message)
944
+ case let .abortedForCloudFallback(reason):
945
+ writer.writeU32(6)
946
+ writer.writeString(reason)
947
+ case .streamingNotSupported:
948
+ writer.writeU32(7)
949
+ case .notLoaded:
950
+ writer.writeU32(8)
951
+ case let .configError(message):
952
+ writer.writeU32(9)
953
+ writer.writeString(message)
954
+ case let .networkError(message):
955
+ writer.writeU32(10)
956
+ writer.writeString(message)
957
+ case let .offline(message):
958
+ writer.writeU32(11)
959
+ writer.writeString(message)
960
+ case let .ioError(message):
961
+ writer.writeU32(12)
962
+ writer.writeString(message)
963
+ case let .cacheError(message):
964
+ writer.writeU32(13)
965
+ writer.writeString(message)
966
+ case let .pipelineError(message):
967
+ writer.writeU32(14)
968
+ writer.writeString(message)
969
+ case let .circuitOpen(message):
970
+ writer.writeU32(15)
971
+ writer.writeString(message)
972
+ case let .rateLimited(retryAfterSecs):
973
+ writer.writeU32(16)
974
+ writer.writeU64(retryAfterSecs)
975
+ case let .timeout(timeoutMs):
976
+ writer.writeU32(17)
977
+ writer.writeU64(timeoutMs)
978
+ case let .missingArtifact(message):
979
+ writer.writeU32(18)
980
+ writer.writeString(message)
981
+ case let .unsupportedModelCapability(message):
982
+ writer.writeU32(19)
983
+ writer.writeString(message)
984
+ case let .unsupportedBackendCapability(message):
985
+ writer.writeU32(20)
986
+ writer.writeString(message)
987
+ case let .invalidImage(message):
988
+ writer.writeU32(21)
989
+ writer.writeString(message)
990
+ case let .cancelled(message):
991
+ writer.writeU32(22)
992
+ writer.writeString(message)
993
+ }
994
+ }
995
+ }
996
+
997
+ public enum XybridEnvelopeKind: Hashable, Equatable, Sendable {
998
+ case text(text: String)
999
+ case audio(bytes: Data)
1000
+ case embedding(values: [Float])
1001
+ case image(bytes: Data, format: String)
1002
+ case multiPart(parts: [XybridEnvelope])
1003
+
1004
+ @inlinable static func decode(from reader: inout WireReader) -> XybridEnvelopeKind {
1005
+ let tag = reader.readU32()
1006
+ switch tag {
1007
+ case 0:
1008
+ return .text(text: reader.readString())
1009
+ case 1:
1010
+ return .audio(bytes: reader.readBytes())
1011
+ case 2:
1012
+ return .embedding(values: reader.readArray { reader in reader.readF32() })
1013
+ case 3:
1014
+ return .image(bytes: reader.readBytes(), format: reader.readString())
1015
+ case 4:
1016
+ return .multiPart(parts: reader.readArray { reader in XybridEnvelope.decode(from: &reader) })
1017
+ default:
1018
+ fatalError("Invalid XybridEnvelopeKind tag: \(tag)")
1019
+ }
1020
+ }
1021
+
1022
+ @inlinable func encode(to writer: inout WireWriter) {
1023
+ switch self {
1024
+ case let .text(text):
1025
+ writer.writeU32(0)
1026
+ writer.writeString(text)
1027
+ case let .audio(bytes):
1028
+ writer.writeU32(1)
1029
+ writer.writeBytes(bytes)
1030
+ case let .embedding(values):
1031
+ writer.writeU32(2)
1032
+ writer.writeArray(values) { writer, boltffiValue0 in writer.writeF32(boltffiValue0) }
1033
+ case let .image(bytes, format):
1034
+ writer.writeU32(3)
1035
+ writer.writeBytes(bytes)
1036
+ writer.writeString(format)
1037
+ case let .multiPart(parts):
1038
+ writer.writeU32(4)
1039
+ writer.writeArray(parts) { writer, boltffiValue0 in boltffiValue0.encode(to: &writer) }
1040
+ }
1041
+ }
1042
+ }
1043
+
1044
+ public enum XybridMessageRole: Int32, Hashable, Sendable, CaseIterable {
1045
+ case system = 0
1046
+ case user = 1
1047
+ case assistant = 2
1048
+
1049
+ @usableFromInline init(fromC c: Int32) {
1050
+ self = XybridMessageRole(rawValue: c)!
1051
+ }
1052
+
1053
+ @usableFromInline var cValue: Int32 {
1054
+ rawValue
1055
+ }
1056
+ }
1057
+
1058
+ public enum XybridAbortSignal: Int32, Hashable, Sendable, CaseIterable {
1059
+ case memoryPressureWarn = 0
1060
+ case memoryPressureCritical = 1
1061
+ case thermalHot = 2
1062
+ case thermalCritical = 3
1063
+
1064
+ @usableFromInline init(fromC c: Int32) {
1065
+ self = XybridAbortSignal(rawValue: c)!
1066
+ }
1067
+
1068
+ @usableFromInline var cValue: Int32 {
1069
+ rawValue
1070
+ }
1071
+ }
1072
+
1073
+ public enum XybridOutputType: Int32, Hashable, Sendable, CaseIterable {
1074
+ case text = 0
1075
+ case audio = 1
1076
+ case embedding = 2
1077
+ case unknown = 3
1078
+
1079
+ @usableFromInline init(fromC c: Int32) {
1080
+ self = XybridOutputType(rawValue: c)!
1081
+ }
1082
+
1083
+ @usableFromInline var cValue: Int32 {
1084
+ rawValue
1085
+ }
1086
+ }
1087
+
1088
+ /// Where a result was produced — observed fact, not a routing preference.
1089
+ public enum XybridExecutionTarget: Int32, Hashable, Sendable, CaseIterable {
1090
+ case local = 0
1091
+ case cloud = 1
1092
+
1093
+ @usableFromInline init(fromC c: Int32) {
1094
+ self = XybridExecutionTarget(rawValue: c)!
1095
+ }
1096
+
1097
+ @usableFromInline var cValue: Int32 {
1098
+ rawValue
1099
+ }
1100
+ }
1101
+
1102
+ /// Lifecycle of a model download — a standalone [`XybridDownload`] or
1103
+ /// the background download behind a speculative load.
1104
+ public enum XybridDownloadState: Int32, Hashable, Sendable, CaseIterable {
1105
+ case downloading = 0
1106
+ case ready = 1
1107
+ /// Download failed; for a speculative load the cloud keeps serving and
1108
+ /// `isLoaded` never flips.
1109
+ case failed = 2
1110
+ /// The host called `cancel`.
1111
+ case cancelled = 3
1112
+
1113
+ @usableFromInline init(fromC c: Int32) {
1114
+ self = XybridDownloadState(rawValue: c)!
1115
+ }
1116
+
1117
+ @usableFromInline var cValue: Int32 {
1118
+ rawValue
1119
+ }
1120
+ }
1121
+
1122
+ public enum XybridStreamEventKind: Int32, Hashable, Sendable, CaseIterable {
1123
+ case token = 0
1124
+ case complete = 1
1125
+
1126
+ @usableFromInline init(fromC c: Int32) {
1127
+ self = XybridStreamEventKind(rawValue: c)!
1128
+ }
1129
+
1130
+ @usableFromInline var cValue: Int32 {
1131
+ rawValue
1132
+ }
1133
+ }
1134
+
1135
+ public enum XybridCacheEntryLocation: Int32, Hashable, Sendable, CaseIterable {
1136
+ case registry = 0
1137
+ case extracted = 1
1138
+ case huggingFace = 2
1139
+ case huggingFaceHub = 3
1140
+
1141
+ @usableFromInline init(fromC c: Int32) {
1142
+ self = XybridCacheEntryLocation(rawValue: c)!
1143
+ }
1144
+
1145
+ @usableFromInline var cValue: Int32 {
1146
+ rawValue
1147
+ }
1148
+ }
1149
+
1150
+ public enum XybridThermalState: Int32, Hashable, Sendable, CaseIterable {
1151
+ case normal = 0
1152
+ case warm = 1
1153
+ case hot = 2
1154
+ case critical = 3
1155
+
1156
+ @usableFromInline init(fromC c: Int32) {
1157
+ self = XybridThermalState(rawValue: c)!
1158
+ }
1159
+
1160
+ @usableFromInline var cValue: Int32 {
1161
+ rawValue
1162
+ }
1163
+ }
1164
+
1165
+ /// How voice-activity detection (VAD) chunking is resolved for a session.
1166
+ ///
1167
+ /// There is deliberately no "on, with the default model" variant: nothing
1168
+ /// ships a bundled Silero model, and the core handles VAD-enabled-without-a-
1169
+ /// directory by warning and silently falling back to fixed-window chunking.
1170
+ /// Enabling VAD therefore requires naming a directory.
1171
+ public enum XybridVadMode: Hashable, Equatable, Sendable {
1172
+ /// Fixed time-window chunking; no voice-activity detection.
1173
+ case off
1174
+ /// VAD on, using the Silero model in this directory, which must contain a
1175
+ /// `model.onnx`.
1176
+ case enabled(modelDir: String)
1177
+
1178
+ @inlinable static func decode(from reader: inout WireReader) -> XybridVadMode {
1179
+ let tag = reader.readU32()
1180
+ switch tag {
1181
+ case 0:
1182
+ return .off
1183
+ case 1:
1184
+ return .enabled(modelDir: reader.readString())
1185
+ default:
1186
+ fatalError("Invalid XybridVadMode tag: \(tag)")
1187
+ }
1188
+ }
1189
+
1190
+ @inlinable func encode(to writer: inout WireWriter) {
1191
+ switch self {
1192
+ case .off:
1193
+ writer.writeU32(0)
1194
+ case let .enabled(modelDir):
1195
+ writer.writeU32(1)
1196
+ writer.writeString(modelDir)
1197
+ }
1198
+ }
1199
+ }
1200
+
1201
+ public final class XybridDownload {
1202
+ @usableFromInline let handle: UInt64
1203
+
1204
+ @usableFromInline init(handle: UInt64) {
1205
+ self.handle = handle
1206
+ }
1207
+
1208
+ deinit {
1209
+ boltffi_release_class_xybrid_bolt_xybrid_download(handle)
1210
+ }
1211
+
1212
+ /// Start downloading a registry model. Returns immediately.
1213
+ public init(fromRegistry id: String) {
1214
+ let boltffiIdBytes = boltffiEncode { boltffiIdWriter in boltffiIdWriter.writeString(id) }
1215
+ let boltffiHandle = boltffiIdBytes.withUnsafeBufferPointer { boltffiIdBuffer in
1216
+ return boltffi_init_class_xybrid_bolt_xybrid_download_from_registry(boltffiIdBuffer.baseAddress!, UInt(boltffiIdBuffer.count))
1217
+ }
1218
+ self.handle = boltffiHandle
1219
+ }
1220
+
1221
+ /// Start downloading a registry model resolved for a specific platform.
1222
+ public init(fromRegistryWithPlatform id: String, platform: String) {
1223
+ let boltffiIdBytes = boltffiEncode { boltffiIdWriter in boltffiIdWriter.writeString(id) }
1224
+ let boltffiHandle = boltffiIdBytes.withUnsafeBufferPointer { boltffiIdBuffer in
1225
+ let boltffiPlatformBytes = boltffiEncode { boltffiPlatformWriter in boltffiPlatformWriter.writeString(platform) }
1226
+ return boltffiPlatformBytes.withUnsafeBufferPointer { boltffiPlatformBuffer in
1227
+ return boltffi_init_class_xybrid_bolt_xybrid_download_from_registry_with_platform(
1228
+ boltffiIdBuffer.baseAddress!,
1229
+ UInt(boltffiIdBuffer.count),
1230
+ boltffiPlatformBuffer.baseAddress!,
1231
+ UInt(boltffiPlatformBuffer.count)
1232
+ )
1233
+ }
1234
+ }
1235
+ self.handle = boltffiHandle
1236
+ }
1237
+
1238
+ /// Current snapshot. Never blocks — safe from a UI thread or a per-frame
1239
+ /// render loop.
1240
+ public func status() -> XybridDownloadStatus {
1241
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_download_status(self.handle)
1242
+ defer { boltffi_free_buf(boltffiResult) }
1243
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridDownloadStatus.decode(from: &boltffiReader) }
1244
+ }
1245
+
1246
+ /// Whether the download reached a terminal state.
1247
+ public func isFinished() -> Bool {
1248
+ return boltffi_method_class_xybrid_bolt_xybrid_download_is_finished(self.handle)
1249
+ }
1250
+
1251
+ /// The failure message once the download ended in `Failed` or
1252
+ /// `Cancelled`; null otherwise. The stream carries the terminal *state*,
1253
+ /// this carries the reason.
1254
+ public func error() -> String? {
1255
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_download_error(self.handle)
1256
+ defer { boltffi_free_buf(boltffiResult) }
1257
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readOptional { boltffiReader in boltffiReader.readString() } }
1258
+ }
1259
+
1260
+ /// Ask the download to stop. Takes effect within one chunk read, discards
1261
+ /// the partial file, and moves the status to `Cancelled`. Idempotent, and
1262
+ /// a no-op once the download is terminal.
1263
+ public func cancel() {
1264
+ boltffi_method_class_xybrid_bolt_xybrid_download_cancel(self.handle)
1265
+ }
1266
+ }
1267
+
1268
+ public final class XybridStreamingSession {
1269
+ @usableFromInline let handle: UInt64
1270
+
1271
+ @usableFromInline init(handle: UInt64) {
1272
+ self.handle = handle
1273
+ }
1274
+
1275
+ deinit {
1276
+ boltffi_release_class_xybrid_bolt_xybrid_streaming_session(handle)
1277
+ }
1278
+
1279
+ /// Open a session on an already-loaded ASR model.
1280
+ ///
1281
+ /// Starts a worker thread and warms the weights, so the first spoken
1282
+ /// words do not pay the cold-start cost. Returns an error for a model
1283
+ /// that does not support streaming, or a sample rate other than 16000.
1284
+ public init(forModel model: XybridModel, config: XybridStreamingConfig) throws {
1285
+ let boltffiConfigBytes = boltffiEncode { boltffiConfigWriter in config.encode(to: &boltffiConfigWriter) }
1286
+ let boltffiHandle = try boltffiConfigBytes.withUnsafeBufferPointer { boltffiConfigBuffer in
1287
+ var boltffiResult: UInt64 = UInt64()
1288
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_streaming_session_for_model(
1289
+ model.handle,
1290
+ boltffiConfigBuffer.baseAddress!,
1291
+ UInt(boltffiConfigBuffer.count),
1292
+ &boltffiResult
1293
+ )
1294
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1295
+ defer { boltffi_free_buf(boltffiError) }
1296
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1297
+ }
1298
+ return boltffiResult
1299
+ }
1300
+ self.handle = boltffiHandle
1301
+ }
1302
+
1303
+ /// Feed PCM f32 mono 16 kHz samples.
1304
+ ///
1305
+ /// Hands the buffer to the worker and returns; transcription happens
1306
+ /// there, never on the caller's thread. Blocks only when the queue is
1307
+ /// full, which back-pressures a producer feeding faster than the model
1308
+ /// can keep up.
1309
+ public func feed(samples: [Float]) throws {
1310
+ try samples.withUnsafeBufferPointer { boltffiSamplesBuffer in
1311
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_streaming_session_feed(self.handle, boltffiSamplesBuffer.baseAddress, UInt(boltffiSamplesBuffer.count))
1312
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1313
+ defer { boltffi_free_buf(boltffiError) }
1314
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1315
+ }
1316
+ }
1317
+ }
1318
+
1319
+ /// Finalize: drain buffered audio and return the complete transcript.
1320
+ ///
1321
+ /// The session is over afterwards — `feed` fails and the partial stream
1322
+ /// closes. Blocks until the last chunk is transcribed, so call it off the
1323
+ /// UI thread.
1324
+ public func flush() throws -> String {
1325
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
1326
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_streaming_session_flush(self.handle, &boltffiResult)
1327
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1328
+ defer { boltffi_free_buf(boltffiError) }
1329
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1330
+ }
1331
+ defer { boltffi_free_buf(boltffiResult) }
1332
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
1333
+ }
1334
+
1335
+ /// Reset to transcribe fresh audio without reloading the model.
1336
+ public func reset() throws {
1337
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_streaming_session_reset(self.handle)
1338
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1339
+ defer { boltffi_free_buf(boltffiError) }
1340
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1341
+ }
1342
+ }
1343
+
1344
+ /// Stop the session and release the model, discarding buffered audio.
1345
+ ///
1346
+ /// Idempotent. Use [`Self::flush`] when you want the transcript — this is
1347
+ /// the "user walked away" path. Named `cancel` rather than `close`
1348
+ /// because BoltFFI already gives every handle a generated `close()` for
1349
+ /// the host's disposal idiom.
1350
+ public func cancel() {
1351
+ boltffi_method_class_xybrid_bolt_xybrid_streaming_session_cancel(self.handle)
1352
+ }
1353
+
1354
+ /// Whether the session is still accepting audio.
1355
+ public func isRunning() -> Bool {
1356
+ return boltffi_method_class_xybrid_bolt_xybrid_streaming_session_is_running(self.handle)
1357
+ }
1358
+ }
1359
+
1360
+ public final class XybridCancellationToken {
1361
+ @usableFromInline let handle: UInt64
1362
+
1363
+ @usableFromInline init(handle: UInt64) {
1364
+ self.handle = handle
1365
+ }
1366
+
1367
+ deinit {
1368
+ boltffi_release_class_xybrid_bolt_xybrid_cancellation_token(handle)
1369
+ }
1370
+
1371
+ /// Create a fresh, un-cancelled token.
1372
+ public init() {
1373
+ self.handle = boltffi_init_class_xybrid_bolt_xybrid_cancellation_token_new()
1374
+ }
1375
+
1376
+ /// Request cancellation. Idempotent, and safe to call from any thread.
1377
+ public func cancel() {
1378
+ boltffi_method_class_xybrid_bolt_xybrid_cancellation_token_cancel(self.handle)
1379
+ }
1380
+
1381
+ /// Whether [`Self::cancel`] has been called on this token.
1382
+ public func isCancelled() -> Bool {
1383
+ return boltffi_method_class_xybrid_bolt_xybrid_cancellation_token_is_cancelled(self.handle)
1384
+ }
1385
+ }
1386
+
1387
+ public final class XybridModel {
1388
+ @usableFromInline let handle: UInt64
1389
+
1390
+ @usableFromInline init(handle: UInt64) {
1391
+ self.handle = handle
1392
+ }
1393
+
1394
+ deinit {
1395
+ boltffi_release_class_xybrid_bolt_xybrid_model(handle)
1396
+ }
1397
+
1398
+ /// Load from the xybrid registry. Recommended path.
1399
+ public init(fromRegistry id: String) throws {
1400
+ let boltffiIdBytes = boltffiEncode { boltffiIdWriter in boltffiIdWriter.writeString(id) }
1401
+ let boltffiHandle = try boltffiIdBytes.withUnsafeBufferPointer { boltffiIdBuffer in
1402
+ var boltffiResult: UInt64 = UInt64()
1403
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_model_from_registry(boltffiIdBuffer.baseAddress!, UInt(boltffiIdBuffer.count), &boltffiResult)
1404
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1405
+ defer { boltffi_free_buf(boltffiError) }
1406
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1407
+ }
1408
+ return boltffiResult
1409
+ }
1410
+ self.handle = boltffiHandle
1411
+ }
1412
+
1413
+ /// Load from the registry, serving from the cloud gateway while the weights
1414
+ /// download in the background.
1415
+ ///
1416
+ /// Returns almost immediately instead of blocking on the download. Requires
1417
+ /// a resolvable API key and an uncached model; otherwise it behaves exactly
1418
+ /// like `from_registry`. Poll `download_status` for progress and
1419
+ /// `is_cloud_serving` to know which leg is answering. LLM/chat models only.
1420
+ public init(fromRegistrySpeculative id: String) throws {
1421
+ let boltffiIdBytes = boltffiEncode { boltffiIdWriter in boltffiIdWriter.writeString(id) }
1422
+ let boltffiHandle = try boltffiIdBytes.withUnsafeBufferPointer { boltffiIdBuffer in
1423
+ var boltffiResult: UInt64 = UInt64()
1424
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_model_from_registry_speculative(boltffiIdBuffer.baseAddress!, UInt(boltffiIdBuffer.count), &boltffiResult)
1425
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1426
+ defer { boltffi_free_buf(boltffiError) }
1427
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1428
+ }
1429
+ return boltffiResult
1430
+ }
1431
+ self.handle = boltffiHandle
1432
+ }
1433
+
1434
+ /// Load from a local model directory (must contain `model_metadata.json`).
1435
+ public init(fromDirectory path: String) throws {
1436
+ let boltffiPathBytes = boltffiEncode { boltffiPathWriter in boltffiPathWriter.writeString(path) }
1437
+ let boltffiHandle = try boltffiPathBytes.withUnsafeBufferPointer { boltffiPathBuffer in
1438
+ var boltffiResult: UInt64 = UInt64()
1439
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_model_from_directory(boltffiPathBuffer.baseAddress!, UInt(boltffiPathBuffer.count), &boltffiResult)
1440
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1441
+ defer { boltffi_free_buf(boltffiError) }
1442
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1443
+ }
1444
+ return boltffiResult
1445
+ }
1446
+ self.handle = boltffiHandle
1447
+ }
1448
+
1449
+ /// Load from a local `.xyb` bundle.
1450
+ public init(fromBundle path: String) throws {
1451
+ let boltffiPathBytes = boltffiEncode { boltffiPathWriter in boltffiPathWriter.writeString(path) }
1452
+ let boltffiHandle = try boltffiPathBytes.withUnsafeBufferPointer { boltffiPathBuffer in
1453
+ var boltffiResult: UInt64 = UInt64()
1454
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_model_from_bundle(boltffiPathBuffer.baseAddress!, UInt(boltffiPathBuffer.count), &boltffiResult)
1455
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1456
+ defer { boltffi_free_buf(boltffiError) }
1457
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1458
+ }
1459
+ return boltffiResult
1460
+ }
1461
+ self.handle = boltffiHandle
1462
+ }
1463
+
1464
+ /// Resolve and load from a HuggingFace repo (`org/repo` or `org/repo:variant`).
1465
+ public init(fromHuggingface repo: String) throws {
1466
+ let boltffiRepoBytes = boltffiEncode { boltffiRepoWriter in boltffiRepoWriter.writeString(repo) }
1467
+ let boltffiHandle = try boltffiRepoBytes.withUnsafeBufferPointer { boltffiRepoBuffer in
1468
+ var boltffiResult: UInt64 = UInt64()
1469
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_model_from_huggingface(boltffiRepoBuffer.baseAddress!, UInt(boltffiRepoBuffer.count), &boltffiResult)
1470
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1471
+ defer { boltffi_free_buf(boltffiError) }
1472
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1473
+ }
1474
+ return boltffiResult
1475
+ }
1476
+ self.handle = boltffiHandle
1477
+ }
1478
+
1479
+ /// Resolve and load a HuggingFace repository pinned to a revision.
1480
+ public init(fromHuggingfaceWithRevision repo: String, revision: String) throws {
1481
+ let boltffiRepoBytes = boltffiEncode { boltffiRepoWriter in boltffiRepoWriter.writeString(repo) }
1482
+ let boltffiHandle = try boltffiRepoBytes.withUnsafeBufferPointer { boltffiRepoBuffer in
1483
+ let boltffiRevisionBytes = boltffiEncode { boltffiRevisionWriter in boltffiRevisionWriter.writeString(revision) }
1484
+ return try boltffiRevisionBytes.withUnsafeBufferPointer { boltffiRevisionBuffer in
1485
+ var boltffiResult: UInt64 = UInt64()
1486
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_model_from_huggingface_with_revision(
1487
+ boltffiRepoBuffer.baseAddress!,
1488
+ UInt(boltffiRepoBuffer.count),
1489
+ boltffiRevisionBuffer.baseAddress!,
1490
+ UInt(boltffiRevisionBuffer.count),
1491
+ &boltffiResult
1492
+ )
1493
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1494
+ defer { boltffi_free_buf(boltffiError) }
1495
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1496
+ }
1497
+ return boltffiResult
1498
+ }
1499
+ }
1500
+ self.handle = boltffiHandle
1501
+ }
1502
+
1503
+ /// Load from a raw GGUF file, auto-generating `model_metadata.json` from the
1504
+ /// GGUF header (written next to the file if absent).
1505
+ public init(fromModelFile path: String) throws {
1506
+ let boltffiPathBytes = boltffiEncode { boltffiPathWriter in boltffiPathWriter.writeString(path) }
1507
+ let boltffiHandle = try boltffiPathBytes.withUnsafeBufferPointer { boltffiPathBuffer in
1508
+ var boltffiResult: UInt64 = UInt64()
1509
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_model_from_model_file(boltffiPathBuffer.baseAddress!, UInt(boltffiPathBuffer.count), &boltffiResult)
1510
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1511
+ defer { boltffi_free_buf(boltffiError) }
1512
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1513
+ }
1514
+ return boltffiResult
1515
+ }
1516
+ self.handle = boltffiHandle
1517
+ }
1518
+
1519
+ public func modelId() -> String {
1520
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_model_model_id(self.handle)
1521
+ defer { boltffi_free_buf(boltffiResult) }
1522
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
1523
+ }
1524
+
1525
+ public func version() -> String {
1526
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_model_version(self.handle)
1527
+ defer { boltffi_free_buf(boltffiResult) }
1528
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
1529
+ }
1530
+
1531
+ public func outputType() -> XybridOutputType {
1532
+ return XybridOutputType(fromC: boltffi_method_class_xybrid_bolt_xybrid_model_output_type(self.handle))
1533
+ }
1534
+
1535
+ public func isLoaded() -> Bool {
1536
+ return boltffi_method_class_xybrid_bolt_xybrid_model_is_loaded(self.handle)
1537
+ }
1538
+
1539
+ /// Whether runs are currently answered by the cloud because the local
1540
+ /// weights are not ready yet. `false` for ordinary local models.
1541
+ public func isCloudServing() -> Bool {
1542
+ return boltffi_method_class_xybrid_bolt_xybrid_model_is_cloud_serving(self.handle)
1543
+ }
1544
+
1545
+ /// Download progress + state in one read — poll this to drive a progress
1546
+ /// bar. Reports `Ready` at 1.0 for an ordinary local model, so hosts need
1547
+ /// no special case.
1548
+ public func downloadStatus() -> XybridDownloadStatus {
1549
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_model_download_status(self.handle)
1550
+ defer { boltffi_free_buf(boltffiResult) }
1551
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridDownloadStatus.decode(from: &boltffiReader) }
1552
+ }
1553
+
1554
+ /// Block until the download finishes or `timeout_ms` elapses, then report
1555
+ /// the status. Call it off the UI thread (the same place `from_registry` is
1556
+ /// already called). `timeout_ms = 0` makes it a non-blocking read.
1557
+ public func awaitDownload(timeoutMs: UInt64) -> XybridDownloadStatus {
1558
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_model_await_download(self.handle, timeoutMs)
1559
+ defer { boltffi_free_buf(boltffiResult) }
1560
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridDownloadStatus.decode(from: &boltffiReader) }
1561
+ }
1562
+
1563
+ public func supportsStreaming() -> Bool {
1564
+ return boltffi_method_class_xybrid_bolt_xybrid_model_supports_streaming(self.handle)
1565
+ }
1566
+
1567
+ /// Whether this model emits true token-by-token output.
1568
+ public func supportsTokenStreaming() -> Bool {
1569
+ return boltffi_method_class_xybrid_bolt_xybrid_model_supports_token_streaming(self.handle)
1570
+ }
1571
+
1572
+ /// Return the model's resolved generation defaults.
1573
+ public func defaultGenerationConfig() -> XybridGenerationConfig {
1574
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_model_default_generation_config(self.handle)
1575
+ defer { boltffi_free_buf(boltffiResult) }
1576
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridGenerationConfig.decode(from: &boltffiReader) }
1577
+ }
1578
+
1579
+ public func isLlm() -> Bool {
1580
+ return boltffi_method_class_xybrid_bolt_xybrid_model_is_llm(self.handle)
1581
+ }
1582
+
1583
+ /// Whether the model bundle declares local tool-calling support.
1584
+ ///
1585
+ /// Advisory tri-state: `null` means the bundle says nothing, so the host
1586
+ /// cannot tell. Gate tool UI on it; enforcement stays at run time — a
1587
+ /// tools-bearing request against a model whose chat template has no tool
1588
+ /// support fails as invalid input regardless of what this reports.
1589
+ public func supportsToolCalling() -> Bool? {
1590
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_model_supports_tool_calling(self.handle)
1591
+ defer { boltffi_free_buf(boltffiResult) }
1592
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readOptional { reader in reader.readBool() } }
1593
+ }
1594
+
1595
+ public func hasVoices() -> Bool {
1596
+ return boltffi_method_class_xybrid_bolt_xybrid_model_has_voices(self.handle)
1597
+ }
1598
+
1599
+ public func voices() -> [XybridVoiceInfo] {
1600
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_model_voices(self.handle)
1601
+ defer { boltffi_free_buf(boltffiResult) }
1602
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readArray { boltffiReader in XybridVoiceInfo.decode(from: &boltffiReader) } }
1603
+ }
1604
+
1605
+ public func defaultVoice() -> XybridVoiceInfo? {
1606
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_model_default_voice(self.handle)
1607
+ defer { boltffi_free_buf(boltffiResult) }
1608
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readOptional { boltffiReader in XybridVoiceInfo.decode(from: &boltffiReader) } }
1609
+ }
1610
+
1611
+ public func voice(voiceId: String) -> XybridVoiceInfo? {
1612
+ let boltffiVoiceIdBytes = boltffiEncode { boltffiVoiceIdWriter in boltffiVoiceIdWriter.writeString(voiceId) }
1613
+ return boltffiVoiceIdBytes.withUnsafeBufferPointer { boltffiVoiceIdBuffer in
1614
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_model_voice(self.handle, boltffiVoiceIdBuffer.baseAddress!, UInt(boltffiVoiceIdBuffer.count))
1615
+ defer { boltffi_free_buf(boltffiResult) }
1616
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readOptional { boltffiReader in XybridVoiceInfo.decode(from: &boltffiReader) } }
1617
+ }
1618
+ }
1619
+
1620
+ /// Run inference, optionally with [`XybridRunOptions`] (generation config,
1621
+ /// abort signals, cloud-fallback). Pass `None` for the model's defaults.
1622
+ ///
1623
+ /// The hand-written wrappers add a one-arg `run(envelope)` convenience that
1624
+ /// forwards `None`, so simple call sites stay ergonomic.
1625
+ /// Pass a [`XybridCancellationToken`] to keep a stop button on the run;
1626
+ /// `None` means the run cannot be cancelled.
1627
+ public func run(envelope: XybridEnvelope, options: XybridRunOptions?, cancel: XybridCancellationToken) throws -> XybridResult {
1628
+ let boltffiEnvelopeBytes = boltffiEncode { boltffiEnvelopeWriter in envelope.encode(to: &boltffiEnvelopeWriter) }
1629
+ return try boltffiEnvelopeBytes.withUnsafeBufferPointer { boltffiEnvelopeBuffer in
1630
+ let boltffiOptionsBytes = boltffiEncode { boltffiOptionsWriter in boltffiOptionsWriter.writeOptional(options) { boltffiOptionsWriter, boltffiValue0 in boltffiValue0.encode(to: &boltffiOptionsWriter) } }
1631
+ return try boltffiOptionsBytes.withUnsafeBufferPointer { boltffiOptionsBuffer in
1632
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
1633
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_model_run(
1634
+ self.handle,
1635
+ boltffiEnvelopeBuffer.baseAddress!,
1636
+ UInt(boltffiEnvelopeBuffer.count),
1637
+ boltffiOptionsBuffer.baseAddress!,
1638
+ UInt(boltffiOptionsBuffer.count),
1639
+ cancel.handle,
1640
+ &boltffiResult
1641
+ )
1642
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1643
+ defer { boltffi_free_buf(boltffiError) }
1644
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1645
+ }
1646
+ defer { boltffi_free_buf(boltffiResult) }
1647
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridResult.decode(from: &boltffiReader) }
1648
+ }
1649
+ }
1650
+ }
1651
+
1652
+ /// Start token streaming and return a model-scoped session identifier.
1653
+ ///
1654
+ /// The identifier remains valid until the final result is taken, an error
1655
+ /// is returned, or [`Self::stream_close`] is called.
1656
+ /// Pass a [`XybridCancellationToken`] to keep a stop button on the run;
1657
+ /// `None` means the run cannot be cancelled.
1658
+ public func runStream(envelope: XybridEnvelope, options: XybridRunOptions?, cancel: XybridCancellationToken) throws -> UInt64 {
1659
+ let boltffiEnvelopeBytes = boltffiEncode { boltffiEnvelopeWriter in envelope.encode(to: &boltffiEnvelopeWriter) }
1660
+ return try boltffiEnvelopeBytes.withUnsafeBufferPointer { boltffiEnvelopeBuffer in
1661
+ let boltffiOptionsBytes = boltffiEncode { boltffiOptionsWriter in boltffiOptionsWriter.writeOptional(options) { boltffiOptionsWriter, boltffiValue0 in boltffiValue0.encode(to: &boltffiOptionsWriter) } }
1662
+ return try boltffiOptionsBytes.withUnsafeBufferPointer { boltffiOptionsBuffer in
1663
+ var boltffiResult: UInt64 = UInt64()
1664
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_model_run_stream(
1665
+ self.handle,
1666
+ boltffiEnvelopeBuffer.baseAddress!,
1667
+ UInt(boltffiEnvelopeBuffer.count),
1668
+ boltffiOptionsBuffer.baseAddress!,
1669
+ UInt(boltffiOptionsBuffer.count),
1670
+ cancel.handle,
1671
+ &boltffiResult
1672
+ )
1673
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1674
+ defer { boltffi_free_buf(boltffiError) }
1675
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1676
+ }
1677
+ return boltffiResult
1678
+ }
1679
+ }
1680
+ }
1681
+
1682
+ /// Block until the next item for `stream_id` is ready.
1683
+ public func streamNext(streamId: UInt64) throws -> XybridStreamEvent {
1684
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
1685
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_model_stream_next(self.handle, streamId, &boltffiResult)
1686
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1687
+ defer { boltffi_free_buf(boltffiError) }
1688
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1689
+ }
1690
+ defer { boltffi_free_buf(boltffiResult) }
1691
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridStreamEvent.decode(from: &boltffiReader) }
1692
+ }
1693
+
1694
+ /// Take the final result after receiving a `Complete` event.
1695
+ public func streamResult(streamId: UInt64) throws -> XybridResult {
1696
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
1697
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_model_stream_result(self.handle, streamId, &boltffiResult)
1698
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1699
+ defer { boltffi_free_buf(boltffiError) }
1700
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1701
+ }
1702
+ defer { boltffi_free_buf(boltffiResult) }
1703
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridResult.decode(from: &boltffiReader) }
1704
+ }
1705
+
1706
+ /// Forget a streaming session.
1707
+ public func streamClose(streamId: UInt64) {
1708
+ boltffi_method_class_xybrid_bolt_xybrid_model_stream_close(self.handle, streamId)
1709
+ }
1710
+
1711
+ /// Run inference seeded with a conversation `context` (multi-turn chat).
1712
+ ///
1713
+ /// Only the generation config from `options` is applied — abort signals and
1714
+ /// cloud fallback are not wired on the context path (matches the facade's
1715
+ /// `run_with_context`).
1716
+ /// Pass a [`XybridCancellationToken`] to keep a stop button on the run;
1717
+ /// `None` means the run cannot be cancelled.
1718
+ ///
1719
+ /// Routes through the facade's options path, so abort signals and cloud
1720
+ /// fallback on `options` are honoured rather than dropped.
1721
+ public func runWithContext(
1722
+ envelope: XybridEnvelope,
1723
+ context: XybridConversationContext,
1724
+ options: XybridRunOptions?,
1725
+ cancel: XybridCancellationToken
1726
+ ) throws -> XybridResult {
1727
+ let boltffiEnvelopeBytes = boltffiEncode { boltffiEnvelopeWriter in envelope.encode(to: &boltffiEnvelopeWriter) }
1728
+ return try boltffiEnvelopeBytes.withUnsafeBufferPointer { boltffiEnvelopeBuffer in
1729
+ let boltffiOptionsBytes = boltffiEncode { boltffiOptionsWriter in boltffiOptionsWriter.writeOptional(options) { boltffiOptionsWriter, boltffiValue0 in boltffiValue0.encode(to: &boltffiOptionsWriter) } }
1730
+ return try boltffiOptionsBytes.withUnsafeBufferPointer { boltffiOptionsBuffer in
1731
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
1732
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_model_run_with_context(
1733
+ self.handle,
1734
+ boltffiEnvelopeBuffer.baseAddress!,
1735
+ UInt(boltffiEnvelopeBuffer.count),
1736
+ context.handle,
1737
+ boltffiOptionsBuffer.baseAddress!,
1738
+ UInt(boltffiOptionsBuffer.count),
1739
+ cancel.handle,
1740
+ &boltffiResult
1741
+ )
1742
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1743
+ defer { boltffi_free_buf(boltffiError) }
1744
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1745
+ }
1746
+ defer { boltffi_free_buf(boltffiResult) }
1747
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridResult.decode(from: &boltffiReader) }
1748
+ }
1749
+ }
1750
+ }
1751
+
1752
+ /// Start context-aware token streaming; returns a model-scoped session id.
1753
+ /// The pull protocol is identical to [`Self::run_stream`]
1754
+ /// (`stream_next` / `stream_result` / `stream_close`).
1755
+ /// Pass a [`XybridCancellationToken`] to keep a stop button on the run;
1756
+ /// `None` means the run cannot be cancelled.
1757
+ public func runStreamWithContext(
1758
+ envelope: XybridEnvelope,
1759
+ context: XybridConversationContext,
1760
+ options: XybridRunOptions?,
1761
+ cancel: XybridCancellationToken
1762
+ ) throws -> UInt64 {
1763
+ let boltffiEnvelopeBytes = boltffiEncode { boltffiEnvelopeWriter in envelope.encode(to: &boltffiEnvelopeWriter) }
1764
+ return try boltffiEnvelopeBytes.withUnsafeBufferPointer { boltffiEnvelopeBuffer in
1765
+ let boltffiOptionsBytes = boltffiEncode { boltffiOptionsWriter in boltffiOptionsWriter.writeOptional(options) { boltffiOptionsWriter, boltffiValue0 in boltffiValue0.encode(to: &boltffiOptionsWriter) } }
1766
+ return try boltffiOptionsBytes.withUnsafeBufferPointer { boltffiOptionsBuffer in
1767
+ var boltffiResult: UInt64 = UInt64()
1768
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_model_run_stream_with_context(
1769
+ self.handle,
1770
+ boltffiEnvelopeBuffer.baseAddress!,
1771
+ UInt(boltffiEnvelopeBuffer.count),
1772
+ context.handle,
1773
+ boltffiOptionsBuffer.baseAddress!,
1774
+ UInt(boltffiOptionsBuffer.count),
1775
+ cancel.handle,
1776
+ &boltffiResult
1777
+ )
1778
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1779
+ defer { boltffi_free_buf(boltffiError) }
1780
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1781
+ }
1782
+ return boltffiResult
1783
+ }
1784
+ }
1785
+ }
1786
+
1787
+ public func warmup() throws {
1788
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_model_warmup(self.handle)
1789
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1790
+ defer { boltffi_free_buf(boltffiError) }
1791
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1792
+ }
1793
+ }
1794
+
1795
+ public func unload() throws {
1796
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_model_unload(self.handle)
1797
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1798
+ defer { boltffi_free_buf(boltffiError) }
1799
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1800
+ }
1801
+ }
1802
+ }
1803
+
1804
+ public final class XybridPipeline {
1805
+ @usableFromInline let handle: UInt64
1806
+
1807
+ @usableFromInline init(handle: UInt64) {
1808
+ self.handle = handle
1809
+ }
1810
+
1811
+ deinit {
1812
+ boltffi_release_class_xybrid_bolt_xybrid_pipeline(handle)
1813
+ }
1814
+
1815
+ /// Parse and load a pipeline from YAML content.
1816
+ public init(fromYaml yaml: String) throws {
1817
+ let boltffiYamlBytes = boltffiEncode { boltffiYamlWriter in boltffiYamlWriter.writeString(yaml) }
1818
+ let boltffiHandle = try boltffiYamlBytes.withUnsafeBufferPointer { boltffiYamlBuffer in
1819
+ var boltffiResult: UInt64 = UInt64()
1820
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_pipeline_from_yaml(boltffiYamlBuffer.baseAddress!, UInt(boltffiYamlBuffer.count), &boltffiResult)
1821
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1822
+ defer { boltffi_free_buf(boltffiError) }
1823
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1824
+ }
1825
+ return boltffiResult
1826
+ }
1827
+ self.handle = boltffiHandle
1828
+ }
1829
+
1830
+ /// Read, parse, and load a pipeline from a YAML file.
1831
+ public init(fromFile path: String) throws {
1832
+ let boltffiPathBytes = boltffiEncode { boltffiPathWriter in boltffiPathWriter.writeString(path) }
1833
+ let boltffiHandle = try boltffiPathBytes.withUnsafeBufferPointer { boltffiPathBuffer in
1834
+ var boltffiResult: UInt64 = UInt64()
1835
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_pipeline_from_file(boltffiPathBuffer.baseAddress!, UInt(boltffiPathBuffer.count), &boltffiResult)
1836
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1837
+ defer { boltffi_free_buf(boltffiError) }
1838
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1839
+ }
1840
+ return boltffiResult
1841
+ }
1842
+ self.handle = boltffiHandle
1843
+ }
1844
+
1845
+ /// Load a pipeline bundle.
1846
+ public init(fromBundle path: String) throws {
1847
+ let boltffiPathBytes = boltffiEncode { boltffiPathWriter in boltffiPathWriter.writeString(path) }
1848
+ let boltffiHandle = try boltffiPathBytes.withUnsafeBufferPointer { boltffiPathBuffer in
1849
+ var boltffiResult: UInt64 = UInt64()
1850
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_pipeline_from_bundle(boltffiPathBuffer.baseAddress!, UInt(boltffiPathBuffer.count), &boltffiResult)
1851
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1852
+ defer { boltffi_free_buf(boltffiError) }
1853
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1854
+ }
1855
+ return boltffiResult
1856
+ }
1857
+ self.handle = boltffiHandle
1858
+ }
1859
+
1860
+ /// Execute every stage, downloading any missing models first, and return
1861
+ /// each stage's output alongside the final one.
1862
+ ///
1863
+ /// Of `options`, only `correlation_id` applies to a pipeline run. Setting
1864
+ /// `generation_config` or `abort_on` fails with `ConfigError` rather than
1865
+ /// being ignored; per-stage generation settings belong in the YAML.
1866
+ public func run(envelope: XybridEnvelope, options: XybridRunOptions?) throws -> XybridPipelineResult {
1867
+ let boltffiEnvelopeBytes = boltffiEncode { boltffiEnvelopeWriter in envelope.encode(to: &boltffiEnvelopeWriter) }
1868
+ return try boltffiEnvelopeBytes.withUnsafeBufferPointer { boltffiEnvelopeBuffer in
1869
+ let boltffiOptionsBytes = boltffiEncode { boltffiOptionsWriter in boltffiOptionsWriter.writeOptional(options) { boltffiOptionsWriter, boltffiValue0 in boltffiValue0.encode(to: &boltffiOptionsWriter) } }
1870
+ return try boltffiOptionsBytes.withUnsafeBufferPointer { boltffiOptionsBuffer in
1871
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
1872
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_pipeline_run(
1873
+ self.handle,
1874
+ boltffiEnvelopeBuffer.baseAddress!,
1875
+ UInt(boltffiEnvelopeBuffer.count),
1876
+ boltffiOptionsBuffer.baseAddress!,
1877
+ UInt(boltffiOptionsBuffer.count),
1878
+ &boltffiResult
1879
+ )
1880
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1881
+ defer { boltffi_free_buf(boltffiError) }
1882
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1883
+ }
1884
+ defer { boltffi_free_buf(boltffiResult) }
1885
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridPipelineResult.decode(from: &boltffiReader) }
1886
+ }
1887
+ }
1888
+ }
1889
+
1890
+ /// Pipeline name from the YAML definition, if present.
1891
+ public func name() -> String? {
1892
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_pipeline_name(self.handle)
1893
+ defer { boltffi_free_buf(boltffiResult) }
1894
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readOptional { boltffiReader in boltffiReader.readString() } }
1895
+ }
1896
+
1897
+ /// Stage identifiers in execution order.
1898
+ public func stageNames() -> [String] {
1899
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_pipeline_stage_names(self.handle)
1900
+ defer { boltffi_free_buf(boltffiResult) }
1901
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readArray { boltffiReader in boltffiReader.readString() } }
1902
+ }
1903
+
1904
+ /// Number of stages in the pipeline.
1905
+ public func stageCount() -> UInt32 {
1906
+ return boltffi_method_class_xybrid_bolt_xybrid_pipeline_stage_count(self.handle)
1907
+ }
1908
+ }
1909
+
1910
+ public final class XybridConversationContext {
1911
+ @usableFromInline let handle: UInt64
1912
+
1913
+ @usableFromInline init(handle: UInt64) {
1914
+ self.handle = handle
1915
+ }
1916
+
1917
+ deinit {
1918
+ boltffi_release_class_xybrid_bolt_xybrid_conversation_context(handle)
1919
+ }
1920
+
1921
+ /// Create an empty conversation context (fresh id).
1922
+ public init() {
1923
+ self.handle = boltffi_init_class_xybrid_bolt_xybrid_conversation_context_new()
1924
+ }
1925
+
1926
+ /// Create a context with a caller-supplied id (for telemetry correlation
1927
+ /// across turns).
1928
+ public init(withId id: String) {
1929
+ let boltffiIdBytes = boltffiEncode { boltffiIdWriter in boltffiIdWriter.writeString(id) }
1930
+ let boltffiHandle = boltffiIdBytes.withUnsafeBufferPointer { boltffiIdBuffer in
1931
+ return boltffi_init_class_xybrid_bolt_xybrid_conversation_context_with_id(boltffiIdBuffer.baseAddress!, UInt(boltffiIdBuffer.count))
1932
+ }
1933
+ self.handle = boltffiHandle
1934
+ }
1935
+
1936
+ /// Append a turn — typically a user or assistant message envelope.
1937
+ public func push(envelope: XybridEnvelope) throws {
1938
+ let boltffiEnvelopeBytes = boltffiEncode { boltffiEnvelopeWriter in envelope.encode(to: &boltffiEnvelopeWriter) }
1939
+ try boltffiEnvelopeBytes.withUnsafeBufferPointer { boltffiEnvelopeBuffer -> Void in
1940
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_conversation_context_push(self.handle, boltffiEnvelopeBuffer.baseAddress!, UInt(boltffiEnvelopeBuffer.count))
1941
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1942
+ defer { boltffi_free_buf(boltffiError) }
1943
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1944
+ }
1945
+ }
1946
+ }
1947
+
1948
+ /// Set the persistent system-prompt envelope (survives [`clear`](Self::clear)).
1949
+ public func setSystem(envelope: XybridEnvelope) throws {
1950
+ let boltffiEnvelopeBytes = boltffiEncode { boltffiEnvelopeWriter in envelope.encode(to: &boltffiEnvelopeWriter) }
1951
+ try boltffiEnvelopeBytes.withUnsafeBufferPointer { boltffiEnvelopeBuffer -> Void in
1952
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_conversation_context_set_system(self.handle, boltffiEnvelopeBuffer.baseAddress!, UInt(boltffiEnvelopeBuffer.count))
1953
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
1954
+ defer { boltffi_free_buf(boltffiError) }
1955
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
1956
+ }
1957
+ }
1958
+ }
1959
+
1960
+ /// Drop the history; the system envelope (if any) is preserved.
1961
+ public func clear() {
1962
+ boltffi_method_class_xybrid_bolt_xybrid_conversation_context_clear(self.handle)
1963
+ }
1964
+
1965
+ /// The context id.
1966
+ public func id() -> String {
1967
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_conversation_context_id(self.handle)
1968
+ defer { boltffi_free_buf(boltffiResult) }
1969
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
1970
+ }
1971
+
1972
+ /// Number of history turns (excludes the system envelope).
1973
+ public func historyLen() -> UInt32 {
1974
+ return boltffi_method_class_xybrid_bolt_xybrid_conversation_context_history_len(self.handle)
1975
+ }
1976
+
1977
+ /// Return history turns, excluding the persistent system envelope.
1978
+ public func history() -> [XybridEnvelope] {
1979
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_conversation_context_history(self.handle)
1980
+ defer { boltffi_free_buf(boltffiResult) }
1981
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readArray { boltffiReader in XybridEnvelope.decode(from: &boltffiReader) } }
1982
+ }
1983
+
1984
+ /// Whether a persistent system-prompt envelope is set.
1985
+ public func hasSystem() -> Bool {
1986
+ return boltffi_method_class_xybrid_bolt_xybrid_conversation_context_has_system(self.handle)
1987
+ }
1988
+
1989
+ /// Set the max history length before FIFO pruning.
1990
+ public func setMaxHistoryLen(len: UInt32) {
1991
+ boltffi_method_class_xybrid_bolt_xybrid_conversation_context_set_max_history_len(self.handle, len)
1992
+ }
1993
+ }
1994
+
1995
+ public final class XybridTelemetryConfig {
1996
+ @usableFromInline let handle: UInt64
1997
+
1998
+ @usableFromInline init(handle: UInt64) {
1999
+ self.handle = handle
2000
+ }
2001
+
2002
+ deinit {
2003
+ boltffi_release_class_xybrid_bolt_xybrid_telemetry_config(handle)
2004
+ }
2005
+
2006
+ /// A new config bound to the default ingest endpoint and the given API key.
2007
+ public init(apiKey: String) {
2008
+ let boltffiApiKeyBytes = boltffiEncode { boltffiApiKeyWriter in boltffiApiKeyWriter.writeString(apiKey) }
2009
+ let boltffiHandle = boltffiApiKeyBytes.withUnsafeBufferPointer { boltffiApiKeyBuffer in
2010
+ return boltffi_init_class_xybrid_bolt_xybrid_telemetry_config_new(boltffiApiKeyBuffer.baseAddress!, UInt(boltffiApiKeyBuffer.count))
2011
+ }
2012
+ self.handle = boltffiHandle
2013
+ }
2014
+
2015
+ /// Override the ingest endpoint (self-hosted collector / non-prod).
2016
+ public func setEndpoint(endpoint: String) {
2017
+ let boltffiEndpointBytes = boltffiEncode { boltffiEndpointWriter in boltffiEndpointWriter.writeString(endpoint) }
2018
+ boltffiEndpointBytes.withUnsafeBufferPointer { boltffiEndpointBuffer -> Void in
2019
+ boltffi_method_class_xybrid_bolt_xybrid_telemetry_config_set_endpoint(self.handle, boltffiEndpointBuffer.baseAddress!, UInt(boltffiEndpointBuffer.count))
2020
+ }
2021
+ }
2022
+
2023
+ /// Set the app version reported with every event.
2024
+ public func setAppVersion(version: String) {
2025
+ let boltffiVersionBytes = boltffiEncode { boltffiVersionWriter in boltffiVersionWriter.writeString(version) }
2026
+ boltffiVersionBytes.withUnsafeBufferPointer { boltffiVersionBuffer -> Void in
2027
+ boltffi_method_class_xybrid_bolt_xybrid_telemetry_config_set_app_version(self.handle, boltffiVersionBuffer.baseAddress!, UInt(boltffiVersionBuffer.count))
2028
+ }
2029
+ }
2030
+
2031
+ /// Set the human-friendly device label reported with every event.
2032
+ public func setDeviceLabel(label: String) {
2033
+ let boltffiLabelBytes = boltffiEncode { boltffiLabelWriter in boltffiLabelWriter.writeString(label) }
2034
+ boltffiLabelBytes.withUnsafeBufferPointer { boltffiLabelBuffer -> Void in
2035
+ boltffi_method_class_xybrid_bolt_xybrid_telemetry_config_set_device_label(self.handle, boltffiLabelBuffer.baseAddress!, UInt(boltffiLabelBuffer.count))
2036
+ }
2037
+ }
2038
+
2039
+ /// Attach an app-provided device attribute (stored under `device.custom`).
2040
+ public func setDeviceAttribute(key: String, value: String) {
2041
+ let boltffiKeyBytes = boltffiEncode { boltffiKeyWriter in boltffiKeyWriter.writeString(key) }
2042
+ boltffiKeyBytes.withUnsafeBufferPointer { boltffiKeyBuffer -> Void in
2043
+ let boltffiValueBytes = boltffiEncode { boltffiValueWriter in boltffiValueWriter.writeString(value) }
2044
+ boltffiValueBytes.withUnsafeBufferPointer { boltffiValueBuffer -> Void in
2045
+ boltffi_method_class_xybrid_bolt_xybrid_telemetry_config_set_device_attribute(
2046
+ self.handle,
2047
+ boltffiKeyBuffer.baseAddress!,
2048
+ UInt(boltffiKeyBuffer.count),
2049
+ boltffiValueBuffer.baseAddress!,
2050
+ UInt(boltffiValueBuffer.count)
2051
+ )
2052
+ }
2053
+ }
2054
+ }
2055
+
2056
+ /// Set the number of events buffered before a flush.
2057
+ public func setBatchSize(batchSize: UInt32) {
2058
+ boltffi_method_class_xybrid_bolt_xybrid_telemetry_config_set_batch_size(self.handle, batchSize)
2059
+ }
2060
+
2061
+ /// Set the background flush interval, in seconds.
2062
+ public func setFlushIntervalSecs(secs: UInt32) {
2063
+ boltffi_method_class_xybrid_bolt_xybrid_telemetry_config_set_flush_interval_secs(self.handle, secs)
2064
+ }
2065
+
2066
+ /// Start the process-global telemetry exporter from this config.
2067
+ ///
2068
+ /// Consumes the config: subsequent setters no-op and a second `init` on the
2069
+ /// same handle errors. Modeled as a method (not a free `telemetry_init`)
2070
+ /// because boltffi 0.25.3 drops free functions that take a handle
2071
+ /// parameter, but lowers a handle self-method fine (same reason the
2072
+ /// generated `run` lives on `XybridModel`).
2073
+ ///
2074
+ /// # Errors
2075
+ /// Errors if this config was already consumed, or if telemetry is already
2076
+ /// initialized without an intervening [`telemetry_shutdown`].
2077
+ public func `init`() throws {
2078
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_telemetry_config_init(self.handle)
2079
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2080
+ defer { boltffi_free_buf(boltffiError) }
2081
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2082
+ }
2083
+ }
2084
+ }
2085
+
2086
+ public final class XybridBundle {
2087
+ @usableFromInline let handle: UInt64
2088
+
2089
+ @usableFromInline init(handle: UInt64) {
2090
+ self.handle = handle
2091
+ }
2092
+
2093
+ deinit {
2094
+ boltffi_release_class_xybrid_bolt_xybrid_bundle(handle)
2095
+ }
2096
+
2097
+ /// Open and parse a `.xyb` bundle (decompress zstd, parse tar, validate the
2098
+ /// manifest).
2099
+ public init(`open` path: String) throws {
2100
+ let boltffiPathBytes = boltffiEncode { boltffiPathWriter in boltffiPathWriter.writeString(path) }
2101
+ let boltffiHandle = try boltffiPathBytes.withUnsafeBufferPointer { boltffiPathBuffer in
2102
+ var boltffiResult: UInt64 = UInt64()
2103
+ let boltffiError = boltffi_init_class_xybrid_bolt_xybrid_bundle_open(boltffiPathBuffer.baseAddress!, UInt(boltffiPathBuffer.count), &boltffiResult)
2104
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2105
+ defer { boltffi_free_buf(boltffiError) }
2106
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2107
+ }
2108
+ return boltffiResult
2109
+ }
2110
+ self.handle = boltffiHandle
2111
+ }
2112
+
2113
+ /// The model identifier from the manifest.
2114
+ public func modelId() -> String {
2115
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_bundle_model_id(self.handle)
2116
+ defer { boltffi_free_buf(boltffiResult) }
2117
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
2118
+ }
2119
+
2120
+ /// The version string from the manifest.
2121
+ public func version() -> String {
2122
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_bundle_version(self.handle)
2123
+ defer { boltffi_free_buf(boltffiResult) }
2124
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
2125
+ }
2126
+
2127
+ /// The target platform from the manifest.
2128
+ public func target() -> String {
2129
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_bundle_target(self.handle)
2130
+ defer { boltffi_free_buf(boltffiResult) }
2131
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
2132
+ }
2133
+
2134
+ /// The SHA-256 hash from the manifest.
2135
+ public func hash() -> String {
2136
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_bundle_hash(self.handle)
2137
+ defer { boltffi_free_buf(boltffiResult) }
2138
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
2139
+ }
2140
+
2141
+ /// Whether the bundle carries a `model_metadata.json`.
2142
+ public func hasMetadata() -> Bool {
2143
+ return boltffi_method_class_xybrid_bolt_xybrid_bundle_has_metadata(self.handle)
2144
+ }
2145
+
2146
+ /// Number of files in the bundle (excludes `manifest.json`).
2147
+ public func fileCount() -> UInt32 {
2148
+ return boltffi_method_class_xybrid_bolt_xybrid_bundle_file_count(self.handle)
2149
+ }
2150
+
2151
+ /// The file name at `index`, or `None` if out of bounds.
2152
+ public func fileName(index: UInt32) -> String? {
2153
+ let boltffiResult = boltffi_method_class_xybrid_bolt_xybrid_bundle_file_name(self.handle, index)
2154
+ defer { boltffi_free_buf(boltffiResult) }
2155
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readOptional { boltffiReader in boltffiReader.readString() } }
2156
+ }
2157
+
2158
+ /// The full bundle manifest serialized as JSON.
2159
+ public func manifestJson() throws -> String {
2160
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
2161
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_bundle_manifest_json(self.handle, &boltffiResult)
2162
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2163
+ defer { boltffi_free_buf(boltffiError) }
2164
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2165
+ }
2166
+ defer { boltffi_free_buf(boltffiResult) }
2167
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
2168
+ }
2169
+
2170
+ /// The `model_metadata.json` contents, or `None` if the bundle has none.
2171
+ public func metadataJson() throws -> String? {
2172
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
2173
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_bundle_metadata_json(self.handle, &boltffiResult)
2174
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2175
+ defer { boltffi_free_buf(boltffiError) }
2176
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2177
+ }
2178
+ defer { boltffi_free_buf(boltffiResult) }
2179
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readOptional { boltffiReader in boltffiReader.readString() } }
2180
+ }
2181
+
2182
+ /// Extract every bundle file to `output_dir` (created if absent).
2183
+ public func extract(outputDir: String) throws {
2184
+ let boltffiOutputDirBytes = boltffiEncode { boltffiOutputDirWriter in boltffiOutputDirWriter.writeString(outputDir) }
2185
+ try boltffiOutputDirBytes.withUnsafeBufferPointer { boltffiOutputDirBuffer -> Void in
2186
+ let boltffiError = boltffi_method_class_xybrid_bolt_xybrid_bundle_extract(self.handle, boltffiOutputDirBuffer.baseAddress!, UInt(boltffiOutputDirBuffer.count))
2187
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2188
+ defer { boltffi_free_buf(boltffiError) }
2189
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2190
+ }
2191
+ }
2192
+ }
2193
+ }
2194
+
2195
+ /// Build the continuation envelope for the turn after the model asked for
2196
+ /// tools.
2197
+ ///
2198
+ /// One `run` is one model turn, so the loop lives in your code: run a
2199
+ /// tools-bearing request, execute every [`XybridToolCall`] it returns, then
2200
+ /// run this envelope to feed the outcomes back. Pass the same tools on the
2201
+ /// continuation's [`XybridGenerationConfig`] as on the original turn.
2202
+ ///
2203
+ /// A free function rather than a constructor because `XybridEnvelope` is a
2204
+ /// `#[data]` record, not a handle type — records carry no methods across the
2205
+ /// generated bindings.
2206
+ public func toolResultsEnvelope(userText: String, priorAssistantText: String, results: [XybridToolResult]) throws -> XybridEnvelope {
2207
+ let boltffiUserTextBytes = boltffiEncode { boltffiUserTextWriter in boltffiUserTextWriter.writeString(userText) }
2208
+ return try boltffiUserTextBytes.withUnsafeBufferPointer { boltffiUserTextBuffer in
2209
+ let boltffiPriorAssistantTextBytes = boltffiEncode { boltffiPriorAssistantTextWriter in boltffiPriorAssistantTextWriter.writeString(priorAssistantText) }
2210
+ return try boltffiPriorAssistantTextBytes.withUnsafeBufferPointer { boltffiPriorAssistantTextBuffer in
2211
+ let boltffiResultsBytes = boltffiEncode { boltffiResultsWriter in boltffiResultsWriter.writeArray(results) { boltffiResultsWriter, boltffiValue0 in boltffiValue0.encode(to: &boltffiResultsWriter) } }
2212
+ return try boltffiResultsBytes.withUnsafeBufferPointer { boltffiResultsBuffer in
2213
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
2214
+ let boltffiError = boltffi_function_xybrid_bolt_tool_results_envelope(
2215
+ boltffiUserTextBuffer.baseAddress!,
2216
+ UInt(boltffiUserTextBuffer.count),
2217
+ boltffiPriorAssistantTextBuffer.baseAddress!,
2218
+ UInt(boltffiPriorAssistantTextBuffer.count),
2219
+ boltffiResultsBuffer.baseAddress!,
2220
+ UInt(boltffiResultsBuffer.count),
2221
+ &boltffiResult
2222
+ )
2223
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2224
+ defer { boltffi_free_buf(boltffiError) }
2225
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2226
+ }
2227
+ defer { boltffi_free_buf(boltffiResult) }
2228
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridEnvelope.decode(from: &boltffiReader) }
2229
+ }
2230
+ }
2231
+ }
2232
+ }
2233
+
2234
+ /// Convert a JSON Schema (as a JSON string) into a GBNF grammar for
2235
+ /// [`XybridGenerationConfig::grammar`]. Fails on invalid JSON or schema
2236
+ /// constructs outside the supported subset.
2237
+ public func jsonSchemaToGbnf(schemaJson: String) throws -> String {
2238
+ let boltffiSchemaJsonBytes = boltffiEncode { boltffiSchemaJsonWriter in boltffiSchemaJsonWriter.writeString(schemaJson) }
2239
+ return try boltffiSchemaJsonBytes.withUnsafeBufferPointer { boltffiSchemaJsonBuffer in
2240
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
2241
+ let boltffiError = boltffi_function_xybrid_bolt_json_schema_to_gbnf(boltffiSchemaJsonBuffer.baseAddress!, UInt(boltffiSchemaJsonBuffer.count), &boltffiResult)
2242
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2243
+ defer { boltffi_free_buf(boltffiError) }
2244
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2245
+ }
2246
+ defer { boltffi_free_buf(boltffiResult) }
2247
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
2248
+ }
2249
+ }
2250
+
2251
+ public func setThermalState(state: XybridThermalState) {
2252
+ boltffi_function_xybrid_bolt_set_thermal_state(state.cValue)
2253
+ }
2254
+
2255
+ public func clearThermalState() {
2256
+ boltffi_function_xybrid_bolt_clear_thermal_state()
2257
+ }
2258
+
2259
+ public func setBatteryLevel(percent: UInt8) {
2260
+ boltffi_function_xybrid_bolt_set_battery_level(percent)
2261
+ }
2262
+
2263
+ public func clearBatteryLevel() {
2264
+ boltffi_function_xybrid_bolt_clear_battery_level()
2265
+ }
2266
+
2267
+ /// One-stop SDK initialization: API key + gateway/ingest URL overrides in
2268
+ /// one call. Delegates to [`facade::configure_runtime`]; blank strings are
2269
+ /// treated as absent. This is the canonical init the Swift
2270
+ /// `Xybrid.initialize(apiKey:gatewayUrl:ingestUrl:)` and Kotlin
2271
+ /// `Xybrid.init(context, apiKey, gatewayUrl, ingestUrl)` wrappers call.
2272
+ public func configureRuntime(apiKey: String?, gatewayUrl: String?, ingestUrl: String?) {
2273
+ let boltffiApiKeyBytes = boltffiEncode { boltffiApiKeyWriter in boltffiApiKeyWriter.writeOptional(apiKey) { boltffiApiKeyWriter, boltffiValue0 in boltffiApiKeyWriter.writeString(boltffiValue0) } }
2274
+ boltffiApiKeyBytes.withUnsafeBufferPointer { boltffiApiKeyBuffer -> Void in
2275
+ let boltffiGatewayUrlBytes = boltffiEncode { boltffiGatewayUrlWriter in boltffiGatewayUrlWriter.writeOptional(gatewayUrl) { boltffiGatewayUrlWriter, boltffiValue0 in boltffiGatewayUrlWriter.writeString(boltffiValue0) } }
2276
+ boltffiGatewayUrlBytes.withUnsafeBufferPointer { boltffiGatewayUrlBuffer -> Void in
2277
+ let boltffiIngestUrlBytes = boltffiEncode { boltffiIngestUrlWriter in boltffiIngestUrlWriter.writeOptional(ingestUrl) { boltffiIngestUrlWriter, boltffiValue0 in boltffiIngestUrlWriter.writeString(boltffiValue0) } }
2278
+ boltffiIngestUrlBytes.withUnsafeBufferPointer { boltffiIngestUrlBuffer -> Void in
2279
+ boltffi_function_xybrid_bolt_configure_runtime(
2280
+ boltffiApiKeyBuffer.baseAddress!,
2281
+ UInt(boltffiApiKeyBuffer.count),
2282
+ boltffiGatewayUrlBuffer.baseAddress!,
2283
+ UInt(boltffiGatewayUrlBuffer.count),
2284
+ boltffiIngestUrlBuffer.baseAddress!,
2285
+ UInt(boltffiIngestUrlBuffer.count)
2286
+ )
2287
+ }
2288
+ }
2289
+ }
2290
+ }
2291
+
2292
+ public func initSdkCacheDir(cacheDir: String) {
2293
+ let boltffiCacheDirBytes = boltffiEncode { boltffiCacheDirWriter in boltffiCacheDirWriter.writeString(cacheDir) }
2294
+ boltffiCacheDirBytes.withUnsafeBufferPointer { boltffiCacheDirBuffer -> Void in
2295
+ boltffi_function_xybrid_bolt_init_sdk_cache_dir(boltffiCacheDirBuffer.baseAddress!, UInt(boltffiCacheDirBuffer.count))
2296
+ }
2297
+ }
2298
+
2299
+ /// Returns aggregate storage usage across every managed model-cache location.
2300
+ public func cacheStatus() throws -> XybridCacheStatus {
2301
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
2302
+ let boltffiError = boltffi_function_xybrid_bolt_cache_status(&boltffiResult)
2303
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2304
+ defer { boltffi_free_buf(boltffiError) }
2305
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2306
+ }
2307
+ defer { boltffi_free_buf(boltffiResult) }
2308
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in XybridCacheStatus.decode(from: &boltffiReader) }
2309
+ }
2310
+
2311
+ /// Lists every physical model entry occupying managed cache storage.
2312
+ public func cacheEntries() throws -> [XybridCacheEntry] {
2313
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
2314
+ let boltffiError = boltffi_function_xybrid_bolt_cache_entries(&boltffiResult)
2315
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2316
+ defer { boltffi_free_buf(boltffiError) }
2317
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2318
+ }
2319
+ defer { boltffi_free_buf(boltffiResult) }
2320
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readArray { boltffiReader in XybridCacheEntry.decode(from: &boltffiReader) } }
2321
+ }
2322
+
2323
+ /// Returns whether a model occupies any managed cache entry.
2324
+ public func cacheIsModelCached(modelId: String) throws -> Bool {
2325
+ let boltffiModelIdBytes = boltffiEncode { boltffiModelIdWriter in boltffiModelIdWriter.writeString(modelId) }
2326
+ return try boltffiModelIdBytes.withUnsafeBufferPointer { boltffiModelIdBuffer in
2327
+ var boltffiResult: Bool = Bool()
2328
+ let boltffiError = boltffi_function_xybrid_bolt_cache_is_model_cached(boltffiModelIdBuffer.baseAddress!, UInt(boltffiModelIdBuffer.count), &boltffiResult)
2329
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2330
+ defer { boltffi_free_buf(boltffiError) }
2331
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2332
+ }
2333
+ return boltffiResult
2334
+ }
2335
+ }
2336
+
2337
+ /// Resolves the preferred local cache path for a model, if present.
2338
+ public func cacheModelPath(modelId: String) throws -> String? {
2339
+ let boltffiModelIdBytes = boltffiEncode { boltffiModelIdWriter in boltffiModelIdWriter.writeString(modelId) }
2340
+ return try boltffiModelIdBytes.withUnsafeBufferPointer { boltffiModelIdBuffer in
2341
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
2342
+ let boltffiError = boltffi_function_xybrid_bolt_cache_model_path(boltffiModelIdBuffer.baseAddress!, UInt(boltffiModelIdBuffer.count), &boltffiResult)
2343
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2344
+ defer { boltffi_free_buf(boltffiError) }
2345
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2346
+ }
2347
+ defer { boltffi_free_buf(boltffiResult) }
2348
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readOptional { boltffiReader in boltffiReader.readString() } }
2349
+ }
2350
+ }
2351
+
2352
+ /// Lists model IDs extracted, validated, and ready to run offline.
2353
+ public func cacheListExtractedModelIds() throws -> [String] {
2354
+ var boltffiResult: FfiBuf_u8 = FfiBuf_u8()
2355
+ let boltffiError = boltffi_function_xybrid_bolt_cache_list_extracted_model_ids(&boltffiResult)
2356
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2357
+ defer { boltffi_free_buf(boltffiError) }
2358
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2359
+ }
2360
+ defer { boltffi_free_buf(boltffiResult) }
2361
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readArray { boltffiReader in boltffiReader.readString() } }
2362
+ }
2363
+
2364
+ /// Removes every managed cache entry for one model.
2365
+ ///
2366
+ /// Do not call concurrently with a load of the same model.
2367
+ public func cacheRemoveModel(modelId: String) throws -> UInt32 {
2368
+ let boltffiModelIdBytes = boltffiEncode { boltffiModelIdWriter in boltffiModelIdWriter.writeString(modelId) }
2369
+ return try boltffiModelIdBytes.withUnsafeBufferPointer { boltffiModelIdBuffer in
2370
+ var boltffiResult: UInt32 = UInt32()
2371
+ let boltffiError = boltffi_function_xybrid_bolt_cache_remove_model(boltffiModelIdBuffer.baseAddress!, UInt(boltffiModelIdBuffer.count), &boltffiResult)
2372
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2373
+ defer { boltffi_free_buf(boltffiError) }
2374
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2375
+ }
2376
+ return boltffiResult
2377
+ }
2378
+ }
2379
+
2380
+ /// Clears all managed model-cache storage.
2381
+ ///
2382
+ /// Do not call concurrently with any model load.
2383
+ public func cacheClear() throws -> UInt32 {
2384
+ var boltffiResult: UInt32 = UInt32()
2385
+ let boltffiError = boltffi_function_xybrid_bolt_cache_clear(&boltffiResult)
2386
+ if boltffiError.ptr != nil || Int(boltffiError.len) != 0 {
2387
+ defer { boltffi_free_buf(boltffiError) }
2388
+ throw boltffiDecodeOwnedBuf(boltffiError.ptr, Int(boltffiError.len)) { boltffiErrorReader in XybridError.decode(from: &boltffiErrorReader) }
2389
+ }
2390
+ return boltffiResult
2391
+ }
2392
+
2393
+ public func setBinding(binding: String) {
2394
+ let boltffiBindingBytes = boltffiEncode { boltffiBindingWriter in boltffiBindingWriter.writeString(binding) }
2395
+ boltffiBindingBytes.withUnsafeBufferPointer { boltffiBindingBuffer -> Void in
2396
+ boltffi_function_xybrid_bolt_set_binding(boltffiBindingBuffer.baseAddress!, UInt(boltffiBindingBuffer.count))
2397
+ }
2398
+ }
2399
+
2400
+ public func setApiKey(apiKey: String) {
2401
+ let boltffiApiKeyBytes = boltffiEncode { boltffiApiKeyWriter in boltffiApiKeyWriter.writeString(apiKey) }
2402
+ boltffiApiKeyBytes.withUnsafeBufferPointer { boltffiApiKeyBuffer -> Void in
2403
+ boltffi_function_xybrid_bolt_set_api_key(boltffiApiKeyBuffer.baseAddress!, UInt(boltffiApiKeyBuffer.count))
2404
+ }
2405
+ }
2406
+
2407
+ public func setProviderApiKey(provider: String, apiKey: String) {
2408
+ let boltffiProviderBytes = boltffiEncode { boltffiProviderWriter in boltffiProviderWriter.writeString(provider) }
2409
+ boltffiProviderBytes.withUnsafeBufferPointer { boltffiProviderBuffer -> Void in
2410
+ let boltffiApiKeyBytes = boltffiEncode { boltffiApiKeyWriter in boltffiApiKeyWriter.writeString(apiKey) }
2411
+ boltffiApiKeyBytes.withUnsafeBufferPointer { boltffiApiKeyBuffer -> Void in
2412
+ boltffi_function_xybrid_bolt_set_provider_api_key(
2413
+ boltffiProviderBuffer.baseAddress!,
2414
+ UInt(boltffiProviderBuffer.count),
2415
+ boltffiApiKeyBuffer.baseAddress!,
2416
+ UInt(boltffiApiKeyBuffer.count)
2417
+ )
2418
+ }
2419
+ }
2420
+ }
2421
+
2422
+ /// Point the cloud gateway at a platform base URL (staging, self-hosted).
2423
+ /// Pass a bare base URL — the `/v1` suffix is applied internally.
2424
+ public func setPlatformUrl(url: String) {
2425
+ let boltffiUrlBytes = boltffiEncode { boltffiUrlWriter in boltffiUrlWriter.writeString(url) }
2426
+ boltffiUrlBytes.withUnsafeBufferPointer { boltffiUrlBuffer -> Void in
2427
+ boltffi_function_xybrid_bolt_set_platform_url(boltffiUrlBuffer.baseAddress!, UInt(boltffiUrlBuffer.count))
2428
+ }
2429
+ }
2430
+
2431
+ /// Enable speculative cloud fallback globally: a registry model that isn't
2432
+ /// downloaded yet is served from the gateway while the weights download.
2433
+ ///
2434
+ /// LLM/chat only — prefer `XybridModel.fromRegistrySpeculative` when the app
2435
+ /// also loads ASR/TTS models, which cannot be served this way.
2436
+ public func setSpeculativeCloud(enabled: Bool) {
2437
+ boltffi_function_xybrid_bolt_set_speculative_cloud(enabled)
2438
+ }
2439
+
2440
+ /// Whether a Xybrid gateway API key is resolvable (in-memory or env).
2441
+ public func hasApiKey() -> Bool {
2442
+ return boltffi_function_xybrid_bolt_has_api_key()
2443
+ }
2444
+
2445
+ /// Whether the global speculative-cloud default is on.
2446
+ public func isSpeculativeCloudEnabled() -> Bool {
2447
+ return boltffi_function_xybrid_bolt_is_speculative_cloud_enabled()
2448
+ }
2449
+
2450
+ /// Whether `XybridModel::from_registry_speculative(model_id)` would actually
2451
+ /// speculate: an API key resolves and the model is not already cached.
2452
+ ///
2453
+ /// Lets the hand-written Swift/Kotlin loader facades answer "will this
2454
+ /// speculate?" before loading. Never touches the network.
2455
+ public func willSpeculateForModel(modelId: String) -> Bool {
2456
+ let boltffiModelIdBytes = boltffiEncode { boltffiModelIdWriter in boltffiModelIdWriter.writeString(modelId) }
2457
+ return boltffiModelIdBytes.withUnsafeBufferPointer { boltffiModelIdBuffer in
2458
+ return boltffi_function_xybrid_bolt_will_speculate_for_model(boltffiModelIdBuffer.baseAddress!, UInt(boltffiModelIdBuffer.count))
2459
+ }
2460
+ }
2461
+
2462
+ /// The SDK version string (tracks `CARGO_PKG_VERSION`).
2463
+ public func version() -> String {
2464
+ let boltffiResult = boltffi_function_xybrid_bolt_version()
2465
+ defer { boltffi_free_buf(boltffiResult) }
2466
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
2467
+ }
2468
+
2469
+ /// Release every idle loaded model's memory; returns how many were released.
2470
+ ///
2471
+ /// Call this from the platform's low-memory hook (`didReceiveMemoryWarning`
2472
+ /// on iOS, `onTrimMemory` on Android). Models with a run in flight are
2473
+ /// skipped, and a released model reloads itself on next use — no reload call,
2474
+ /// no new error to handle.
2475
+ public func releaseMemory() -> UInt32 {
2476
+ return boltffi_function_xybrid_bolt_release_memory()
2477
+ }
2478
+
2479
+ /// Enable or disable automatic model release for subsequent loads.
2480
+ ///
2481
+ /// When enabled, loading a model under device memory pressure first releases
2482
+ /// least-recently-used idle models. Off by default; [`release_memory`] works
2483
+ /// either way.
2484
+ public func setAutoRelease(enabled: Bool) {
2485
+ boltffi_function_xybrid_bolt_set_auto_release(enabled)
2486
+ }
2487
+
2488
+ /// Whether automatic model release is enabled process-wide.
2489
+ public func isAutoReleaseEnabled() -> Bool {
2490
+ return boltffi_function_xybrid_bolt_is_auto_release_enabled()
2491
+ }
2492
+
2493
+ /// The SDK's default telemetry ingest endpoint (for display alongside a config).
2494
+ public func telemetryDefaultEndpoint() -> String {
2495
+ let boltffiResult = boltffi_function_xybrid_bolt_telemetry_default_endpoint()
2496
+ defer { boltffi_free_buf(boltffiResult) }
2497
+ return boltffiDecodeOwnedBuf(boltffiResult.ptr, Int(boltffiResult.len)) { boltffiReader in boltffiReader.readString() }
2498
+ }
2499
+
2500
+ /// Flush pending telemetry events. Safe before init / after shutdown.
2501
+ public func telemetryFlush() {
2502
+ boltffi_function_xybrid_bolt_telemetry_flush()
2503
+ }
2504
+
2505
+ /// Shut down the telemetry exporter. Idempotent.
2506
+ public func telemetryShutdown() {
2507
+ boltffi_function_xybrid_bolt_telemetry_shutdown()
2508
+ }
2509
+
2510
+
2511
+ extension XybridDownload {
2512
+ /// Pushed progress updates, closing once the download is terminal.
2513
+ ///
2514
+ /// Generated as an `AsyncStream` in Swift, a `Flow` in Kotlin, an
2515
+ /// `IAsyncEnumerable` in C# and a subscription object in Python.
2516
+ /// Cancelling the consuming task / scope / token unsubscribes; it does
2517
+ /// **not** cancel the download itself — call [`Self::cancel`] for that.
2518
+ ///
2519
+ /// The current snapshot is delivered first, so subscribing late still
2520
+ /// yields a frame, and a download that already finished closes at once
2521
+ /// instead of hanging.
2522
+ public func progress() -> _Concurrency.AsyncStream<XybridDownloadStatus> {
2523
+ _Concurrency.AsyncStream<XybridDownloadStatus>(bufferingPolicy: .unbounded) { continuation in
2524
+ let boltffiSubscription = boltffi_stream_xybrid_bolt_xybrid_download_progress_subscribe(self.handle)
2525
+ guard boltffiSubscription != 0 else {
2526
+ continuation.finish()
2527
+ return
2528
+ }
2529
+ let context = BoltFFIStreamContext<XybridDownloadStatus>(
2530
+ subscription: boltffiSubscription,
2531
+ batchSize: 16,
2532
+ readBatch: { subscription, batchSize in
2533
+ return boltffiReadWireStreamBatch(
2534
+ subscription: subscription,
2535
+ batchSize: batchSize,
2536
+ popBatch: boltffi_stream_xybrid_bolt_xybrid_download_progress_pop_batch,
2537
+ freeBuf: boltffi_free_buf
2538
+ ) { reader in
2539
+ let boltffiStreamCount = Int(reader.readU32())
2540
+ var boltffiStreamBatch = [XybridDownloadStatus]()
2541
+ boltffiStreamBatch.reserveCapacity(boltffiStreamCount)
2542
+ for _ in 0..<boltffiStreamCount {
2543
+ boltffiStreamBatch.append(XybridDownloadStatus.decode(from: &reader))
2544
+ }
2545
+ return boltffiStreamBatch
2546
+ }
2547
+ },
2548
+ poll: boltffi_stream_xybrid_bolt_xybrid_download_progress_poll,
2549
+ unsubscribe: boltffi_stream_xybrid_bolt_xybrid_download_progress_unsubscribe,
2550
+ free: boltffi_stream_xybrid_bolt_xybrid_download_progress_free,
2551
+ atomicCompareExchange: boltffi_atomic_u8_cas,
2552
+ yieldItem: { item in _ = continuation.yield(item) },
2553
+ finish: { continuation.finish() }
2554
+ )
2555
+ continuation.onTermination = { @Sendable _ in context.requestTermination() }
2556
+ context.start()
2557
+ }
2558
+ }
2559
+ }
2560
+
2561
+
2562
+ extension XybridStreamingSession {
2563
+ /// Pushed partial transcripts, closing once the session ends.
2564
+ ///
2565
+ /// Generated as an `AsyncStream` in Swift, a `Flow` in Kotlin, an
2566
+ /// `IAsyncEnumerable` in C# and an iterable subscription in Python.
2567
+ ///
2568
+ /// A partial produced before subscribing is delivered immediately, so
2569
+ /// audio fed before the stream is attached is never silently lost, and
2570
+ /// subscribing to a finished session closes at once instead of hanging.
2571
+ public func partials() -> _Concurrency.AsyncStream<XybridPartialResult> {
2572
+ _Concurrency.AsyncStream<XybridPartialResult>(bufferingPolicy: .unbounded) { continuation in
2573
+ let boltffiSubscription = boltffi_stream_xybrid_bolt_xybrid_streaming_session_partials_subscribe(self.handle)
2574
+ guard boltffiSubscription != 0 else {
2575
+ continuation.finish()
2576
+ return
2577
+ }
2578
+ let context = BoltFFIStreamContext<XybridPartialResult>(
2579
+ subscription: boltffiSubscription,
2580
+ batchSize: 16,
2581
+ readBatch: { subscription, batchSize in
2582
+ return boltffiReadWireStreamBatch(
2583
+ subscription: subscription,
2584
+ batchSize: batchSize,
2585
+ popBatch: boltffi_stream_xybrid_bolt_xybrid_streaming_session_partials_pop_batch,
2586
+ freeBuf: boltffi_free_buf
2587
+ ) { reader in
2588
+ let boltffiStreamCount = Int(reader.readU32())
2589
+ var boltffiStreamBatch = [XybridPartialResult]()
2590
+ boltffiStreamBatch.reserveCapacity(boltffiStreamCount)
2591
+ for _ in 0..<boltffiStreamCount {
2592
+ boltffiStreamBatch.append(XybridPartialResult.decode(from: &reader))
2593
+ }
2594
+ return boltffiStreamBatch
2595
+ }
2596
+ },
2597
+ poll: boltffi_stream_xybrid_bolt_xybrid_streaming_session_partials_poll,
2598
+ unsubscribe: boltffi_stream_xybrid_bolt_xybrid_streaming_session_partials_unsubscribe,
2599
+ free: boltffi_stream_xybrid_bolt_xybrid_streaming_session_partials_free,
2600
+ atomicCompareExchange: boltffi_atomic_u8_cas,
2601
+ yieldItem: { item in _ = continuation.yield(item) },
2602
+ finish: { continuation.finish() }
2603
+ )
2604
+ continuation.onTermination = { @Sendable _ in context.requestTermination() }
2605
+ context.start()
2606
+ }
2607
+ }
2608
+ }
2609
+
2610
+
2611
+ extension XybridModel {
2612
+ /// Pushed download updates for a speculatively-loaded model — the stream
2613
+ /// counterpart of [`Self::await_download`], and what issue #504 asks for.
2614
+ ///
2615
+ /// Emits the current snapshot first, then every update, then closes on
2616
+ /// the terminal state. An ordinary local model is already `Ready`, so its
2617
+ /// stream yields one frame and ends.
2618
+ public func downloadProgress() -> _Concurrency.AsyncStream<XybridDownloadStatus> {
2619
+ _Concurrency.AsyncStream<XybridDownloadStatus>(bufferingPolicy: .unbounded) { continuation in
2620
+ let boltffiSubscription = boltffi_stream_xybrid_bolt_xybrid_model_download_progress_subscribe(self.handle)
2621
+ guard boltffiSubscription != 0 else {
2622
+ continuation.finish()
2623
+ return
2624
+ }
2625
+ let context = BoltFFIStreamContext<XybridDownloadStatus>(
2626
+ subscription: boltffiSubscription,
2627
+ batchSize: 16,
2628
+ readBatch: { subscription, batchSize in
2629
+ return boltffiReadWireStreamBatch(
2630
+ subscription: subscription,
2631
+ batchSize: batchSize,
2632
+ popBatch: boltffi_stream_xybrid_bolt_xybrid_model_download_progress_pop_batch,
2633
+ freeBuf: boltffi_free_buf
2634
+ ) { reader in
2635
+ let boltffiStreamCount = Int(reader.readU32())
2636
+ var boltffiStreamBatch = [XybridDownloadStatus]()
2637
+ boltffiStreamBatch.reserveCapacity(boltffiStreamCount)
2638
+ for _ in 0..<boltffiStreamCount {
2639
+ boltffiStreamBatch.append(XybridDownloadStatus.decode(from: &reader))
2640
+ }
2641
+ return boltffiStreamBatch
2642
+ }
2643
+ },
2644
+ poll: boltffi_stream_xybrid_bolt_xybrid_model_download_progress_poll,
2645
+ unsubscribe: boltffi_stream_xybrid_bolt_xybrid_model_download_progress_unsubscribe,
2646
+ free: boltffi_stream_xybrid_bolt_xybrid_model_download_progress_free,
2647
+ atomicCompareExchange: boltffi_atomic_u8_cas,
2648
+ yieldItem: { item in _ = continuation.yield(item) },
2649
+ finish: { continuation.finish() }
2650
+ )
2651
+ continuation.onTermination = { @Sendable _ in context.requestTermination() }
2652
+ context.start()
2653
+ }
2654
+ }
2655
+ }
2656
+
2657
+ private enum BoltFFIStreamPollResult: Int8 {
2658
+ case ready = 0
2659
+ case closed = 1
2660
+ }
2661
+
2662
+ @inline(__always)
2663
+ private func boltffiReadDirectStreamBatch<Element, Item>(
2664
+ subscription: UInt64,
2665
+ batchSize: UInt,
2666
+ popBatch: (UInt64, UnsafeMutablePointer<Element>?, UInt) -> UInt,
2667
+ mapItems: (UnsafeBufferPointer<Element>) -> [Item]
2668
+ ) -> [Item] {
2669
+ if batchSize == 0 {
2670
+ return []
2671
+ }
2672
+ let items = UnsafeMutablePointer<Element>.allocate(capacity: Int(batchSize))
2673
+ defer { items.deallocate() }
2674
+ let count = popBatch(subscription, items, batchSize)
2675
+ if count == 0 {
2676
+ return []
2677
+ }
2678
+ return mapItems(UnsafeBufferPointer(start: items, count: Int(count)))
2679
+ }
2680
+
2681
+ private class BoltFFIStreamPollContext: @unchecked Sendable {
2682
+ func handlePoll(_ result: Int8) {
2683
+ preconditionFailure("invalid BoltFFI stream poll context")
2684
+ }
2685
+ }
2686
+
2687
+ private let boltffiStreamPollCallback: @convention(c) (UInt64, Int8) -> Void = { data, result in
2688
+ Unmanaged<BoltFFIStreamPollContext>.fromOpaque(UnsafeRawPointer(bitPattern: UInt(data))!).takeRetainedValue().handlePoll(result)
2689
+ }
2690
+
2691
+ private final class BoltFFIStreamContext<Item>: BoltFFIStreamPollContext, @unchecked Sendable {
2692
+ private let subscription: UInt64
2693
+ private let batchSize: UInt
2694
+ private let readBatch: (UInt64, UInt) -> [Item]
2695
+ private let poll: (UInt64, UInt64, StreamContinuationCallback?) -> Void
2696
+ private let unsubscribe: (UInt64) -> Void
2697
+ private let free: (UInt64) -> Void
2698
+ private let atomicCompareExchange: (UnsafeMutablePointer<UInt8>?, UInt8, UInt8) -> Bool
2699
+ private let yieldItem: (Item) -> Void
2700
+ private let finish: () -> Void
2701
+ private var lifecycle = UInt8(0)
2702
+ private var processing = UInt8(0)
2703
+
2704
+ init(
2705
+ subscription: UInt64,
2706
+ batchSize: UInt,
2707
+ readBatch: @escaping (UInt64, UInt) -> [Item],
2708
+ poll: @escaping (UInt64, UInt64, StreamContinuationCallback?) -> Void,
2709
+ unsubscribe: @escaping (UInt64) -> Void,
2710
+ free: @escaping (UInt64) -> Void,
2711
+ atomicCompareExchange: @escaping (UnsafeMutablePointer<UInt8>?, UInt8, UInt8) -> Bool,
2712
+ yieldItem: @escaping (Item) -> Void,
2713
+ finish: @escaping () -> Void
2714
+ ) {
2715
+ self.subscription = subscription
2716
+ self.batchSize = batchSize
2717
+ self.readBatch = readBatch
2718
+ self.poll = poll
2719
+ self.unsubscribe = unsubscribe
2720
+ self.free = free
2721
+ self.atomicCompareExchange = atomicCompareExchange
2722
+ self.yieldItem = yieldItem
2723
+ self.finish = finish
2724
+ }
2725
+
2726
+ func start() {
2727
+ registerPoll()
2728
+ }
2729
+
2730
+ func requestTermination() {
2731
+ let started = withUnsafeMutablePointer(to: &lifecycle) {
2732
+ atomicCompareExchange($0, 0, 1)
2733
+ }
2734
+ if started {
2735
+ unsubscribe(subscription)
2736
+ _ = withUnsafeMutablePointer(to: &lifecycle) {
2737
+ atomicCompareExchange($0, 1, 2)
2738
+ }
2739
+ }
2740
+ finalizeIfIdle()
2741
+ }
2742
+
2743
+ private func registerPoll() {
2744
+ guard withUnsafeMutablePointer(to: &lifecycle, { atomicCompareExchange($0, 0, 0) }) else {
2745
+ finalizeIfIdle()
2746
+ return
2747
+ }
2748
+ let data = UInt64(UInt(bitPattern: Unmanaged.passRetained(self).toOpaque()))
2749
+ poll(subscription, data, boltffiStreamPollCallback)
2750
+ }
2751
+
2752
+ override func handlePoll(_ result: Int8) {
2753
+ guard withUnsafeMutablePointer(to: &processing, { atomicCompareExchange($0, 0, 1) }) else {
2754
+ finalizeIfIdle()
2755
+ return
2756
+ }
2757
+ let reschedule = processPoll(result)
2758
+ _ = withUnsafeMutablePointer(to: &processing) { atomicCompareExchange($0, 1, 0) }
2759
+ finalizeIfIdle()
2760
+ if reschedule {
2761
+ schedulePoll()
2762
+ }
2763
+ }
2764
+
2765
+ private func processPoll(_ result: Int8) -> Bool {
2766
+ guard withUnsafeMutablePointer(to: &lifecycle, { atomicCompareExchange($0, 0, 0) }) else {
2767
+ return false
2768
+ }
2769
+ drain()
2770
+ if result == BoltFFIStreamPollResult.closed.rawValue {
2771
+ requestTermination()
2772
+ return false
2773
+ }
2774
+ return withUnsafeMutablePointer(to: &lifecycle) { atomicCompareExchange($0, 0, 0) }
2775
+ }
2776
+
2777
+ private func drain() {
2778
+ while true {
2779
+ let items = readBatch(subscription, batchSize)
2780
+ if items.isEmpty {
2781
+ return
2782
+ }
2783
+ for item in items {
2784
+ yieldItem(item)
2785
+ }
2786
+ }
2787
+ }
2788
+
2789
+ private func schedulePoll() {
2790
+ _Concurrency.Task { [self] in
2791
+ await _Concurrency.Task.yield()
2792
+ registerPoll()
2793
+ }
2794
+ }
2795
+
2796
+ private func finalizeIfIdle() {
2797
+ guard withUnsafeMutablePointer(to: &processing, { atomicCompareExchange($0, 0, 0) }) else {
2798
+ return
2799
+ }
2800
+ guard withUnsafeMutablePointer(to: &lifecycle, { atomicCompareExchange($0, 2, 3) }) else {
2801
+ return
2802
+ }
2803
+ free(subscription)
2804
+ finish()
2805
+ }
2806
+ }
2807
+
2808
+ @usableFromInline struct WireReader {
2809
+ @usableFromInline let data: Data
2810
+ @usableFromInline var position: Int
2811
+
2812
+ @inlinable init(data: Data, position: Int = 0) {
2813
+ self.data = data
2814
+ self.position = position
2815
+ }
2816
+
2817
+ @inlinable init(ptr: UnsafePointer<UInt8>, len: Int) {
2818
+ self.data = Data(bytes: ptr, count: len)
2819
+ self.position = 0
2820
+ }
2821
+
2822
+ @inlinable mutating func readU8() -> UInt8 {
2823
+ let value = data[position]
2824
+ position += 1
2825
+ return value
2826
+ }
2827
+
2828
+ @inlinable mutating func readI8() -> Int8 {
2829
+ Int8(bitPattern: readU8())
2830
+ }
2831
+
2832
+ @inlinable mutating func readU16() -> UInt16 {
2833
+ let value = data.withUnsafeBytes { buffer in
2834
+ buffer.loadUnaligned(fromByteOffset: position, as: UInt16.self)
2835
+ }
2836
+ position += 2
2837
+ return UInt16(littleEndian: value)
2838
+ }
2839
+
2840
+ @inlinable mutating func readI16() -> Int16 {
2841
+ Int16(bitPattern: readU16())
2842
+ }
2843
+
2844
+ @inlinable mutating func readU32() -> UInt32 {
2845
+ let value = data.withUnsafeBytes { buffer in
2846
+ buffer.loadUnaligned(fromByteOffset: position, as: UInt32.self)
2847
+ }
2848
+ position += 4
2849
+ return UInt32(littleEndian: value)
2850
+ }
2851
+
2852
+ @inlinable mutating func readI32() -> Int32 {
2853
+ Int32(bitPattern: readU32())
2854
+ }
2855
+
2856
+ @inlinable mutating func readU64() -> UInt64 {
2857
+ let value = data.withUnsafeBytes { buffer in
2858
+ buffer.loadUnaligned(fromByteOffset: position, as: UInt64.self)
2859
+ }
2860
+ position += 8
2861
+ return UInt64(littleEndian: value)
2862
+ }
2863
+
2864
+ @inlinable mutating func readI64() -> Int64 {
2865
+ Int64(bitPattern: readU64())
2866
+ }
2867
+
2868
+ @inlinable mutating func readF32() -> Float {
2869
+ Float(bitPattern: readU32())
2870
+ }
2871
+
2872
+ @inlinable mutating func readF64() -> Double {
2873
+ Double(bitPattern: readU64())
2874
+ }
2875
+
2876
+ @inlinable mutating func readBool() -> Bool {
2877
+ readU8() != 0
2878
+ }
2879
+
2880
+ @inlinable mutating func readString() -> String {
2881
+ let length = Int(readU32())
2882
+ guard length > 0 else { return "" }
2883
+ let start = position
2884
+ position += length
2885
+ return data.withUnsafeBytes { bytes in
2886
+ let pointer = bytes.baseAddress!.advanced(by: start).assumingMemoryBound(to: UInt8.self)
2887
+ let buffer = UnsafeBufferPointer(start: pointer, count: length)
2888
+ return String(decoding: buffer, as: UTF8.self)
2889
+ }
2890
+ }
2891
+
2892
+ @inlinable mutating func readBytes() -> Data {
2893
+ let length = Int(readU32())
2894
+ guard length > 0 else { return Data() }
2895
+ let start = position
2896
+ position += length
2897
+ return Data(data[start..<(start + length)])
2898
+ }
2899
+
2900
+ @inlinable mutating func readDuration() -> TimeInterval {
2901
+ let seconds = readU64()
2902
+ let nanoseconds = readU32()
2903
+ return Double(seconds) + (Double(nanoseconds) / 1.0e9)
2904
+ }
2905
+
2906
+ @inlinable mutating func readTimestamp() -> Date {
2907
+ let seconds = readI64()
2908
+ let nanoseconds = readU32()
2909
+ let delta = Double(seconds) + (Double(nanoseconds) / 1.0e9)
2910
+ return Date(timeIntervalSince1970: delta)
2911
+ }
2912
+
2913
+ @inlinable mutating func readUuid() -> UUID {
2914
+ let hi = readU64()
2915
+ let lo = readU64()
2916
+ var uuid: uuid_t = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
2917
+ var hiBe = hi.bigEndian
2918
+ var loBe = lo.bigEndian
2919
+ withUnsafeMutableBytes(of: &uuid) { uuidBytes in
2920
+ Swift.withUnsafeBytes(of: &hiBe) { uuidBytes[0..<8].copyBytes(from: $0) }
2921
+ Swift.withUnsafeBytes(of: &loBe) { uuidBytes[8..<16].copyBytes(from: $0) }
2922
+ }
2923
+ return UUID(uuid: uuid)
2924
+ }
2925
+
2926
+ @inlinable mutating func readUrl() -> URL {
2927
+ guard let url = URL(string: readString()) else { fatalError("Invalid URL") }
2928
+ return url
2929
+ }
2930
+
2931
+ @inlinable mutating func readOptional<T>(_ body: (inout WireReader) -> T) -> T? {
2932
+ switch readU8() {
2933
+ case 0:
2934
+ return nil
2935
+ case 1:
2936
+ return body(&self)
2937
+ default:
2938
+ fatalError("Invalid optional wire tag")
2939
+ }
2940
+ }
2941
+
2942
+ @inlinable mutating func readSequence<T>(_ body: (inout WireReader) -> T) -> [T] {
2943
+ let count = Int(readU32())
2944
+ guard count > 0 else { return [] }
2945
+ var result = [T]()
2946
+ result.reserveCapacity(count)
2947
+ for _ in 0..<count {
2948
+ result.append(body(&self))
2949
+ }
2950
+ return result
2951
+ }
2952
+
2953
+ @inlinable mutating func readArray<T>(_ body: (inout WireReader) -> T) -> [T] {
2954
+ readSequence(body)
2955
+ }
2956
+
2957
+ @inlinable mutating func readResult<Success, Failure: Swift.Error>(
2958
+ _ success: (inout WireReader) -> Success,
2959
+ _ failure: (inout WireReader) -> Failure
2960
+ ) -> Swift.Result<Success, Failure> {
2961
+ switch readU8() {
2962
+ case 0:
2963
+ return .success(success(&self))
2964
+ case 1:
2965
+ return .failure(failure(&self))
2966
+ default:
2967
+ fatalError("Invalid result wire tag")
2968
+ }
2969
+ }
2970
+
2971
+ @inlinable mutating func readMap<K: Hashable, V>(
2972
+ _ key: (inout WireReader) -> K,
2973
+ _ value: (inout WireReader) -> V
2974
+ ) -> [K: V] {
2975
+ let count = Int(readU32())
2976
+ var result = [K: V]()
2977
+ result.reserveCapacity(count)
2978
+ for _ in 0..<count {
2979
+ let decodedKey = key(&self)
2980
+ let decodedValue = value(&self)
2981
+ if result.updateValue(decodedValue, forKey: decodedKey) != nil {
2982
+ fatalError("Duplicate map key")
2983
+ }
2984
+ }
2985
+ return result
2986
+ }
2987
+ }
2988
+
2989
+ @usableFromInline struct WireWriter {
2990
+ @usableFromInline var data: Data
2991
+
2992
+ @inlinable init(capacity: Int = 64) {
2993
+ self.data = Data(capacity: capacity)
2994
+ }
2995
+
2996
+ @inlinable mutating func writeU8(_ value: UInt8) {
2997
+ data.append(value)
2998
+ }
2999
+
3000
+ @inlinable mutating func writeI8(_ value: Int8) {
3001
+ writeU8(UInt8(bitPattern: value))
3002
+ }
3003
+
3004
+ @inlinable mutating func writeU16(_ value: UInt16) {
3005
+ var value = value.littleEndian
3006
+ Swift.withUnsafeBytes(of: &value) { bytes in
3007
+ data.append(contentsOf: bytes)
3008
+ }
3009
+ }
3010
+
3011
+ @inlinable mutating func writeI16(_ value: Int16) {
3012
+ writeU16(UInt16(bitPattern: value))
3013
+ }
3014
+
3015
+ @inlinable mutating func writeU32(_ value: UInt32) {
3016
+ var value = value.littleEndian
3017
+ Swift.withUnsafeBytes(of: &value) { bytes in
3018
+ data.append(contentsOf: bytes)
3019
+ }
3020
+ }
3021
+
3022
+ @inlinable mutating func writeI32(_ value: Int32) {
3023
+ writeU32(UInt32(bitPattern: value))
3024
+ }
3025
+
3026
+ @inlinable mutating func writeU64(_ value: UInt64) {
3027
+ var value = value.littleEndian
3028
+ Swift.withUnsafeBytes(of: &value) { bytes in
3029
+ data.append(contentsOf: bytes)
3030
+ }
3031
+ }
3032
+
3033
+ @inlinable mutating func writeI64(_ value: Int64) {
3034
+ writeU64(UInt64(bitPattern: value))
3035
+ }
3036
+
3037
+ @inlinable mutating func writeF32(_ value: Float) {
3038
+ writeU32(value.bitPattern)
3039
+ }
3040
+
3041
+ @inlinable mutating func writeF64(_ value: Double) {
3042
+ writeU64(value.bitPattern)
3043
+ }
3044
+
3045
+ @inlinable mutating func writeBool(_ value: Bool) {
3046
+ writeU8(value ? 1 : 0)
3047
+ }
3048
+
3049
+ @inlinable mutating func writeString(_ value: String) {
3050
+ var value = value
3051
+ value.withUTF8 { utf8 in
3052
+ writeU32(UInt32(utf8.count))
3053
+ data.append(contentsOf: utf8)
3054
+ }
3055
+ }
3056
+
3057
+ @inlinable mutating func writeBytes(_ value: Data) {
3058
+ writeU32(UInt32(value.count))
3059
+ data.append(contentsOf: value)
3060
+ }
3061
+
3062
+ @inlinable mutating func writeDuration(_ value: TimeInterval) {
3063
+ if value.rounded(.down) > Double(Int64.max) { fatalError("Duration overflow") }
3064
+ if value < 0 { fatalError("Invalid duration") }
3065
+ let seconds = UInt64(value)
3066
+ let nanoseconds = UInt32((value - Double(seconds)) * 1.0e9)
3067
+ writeU64(seconds)
3068
+ writeU32(nanoseconds)
3069
+ }
3070
+
3071
+ @inlinable mutating func writeTimestamp(_ value: Date) {
3072
+ let delta = value.timeIntervalSince1970
3073
+ let secondsDouble = delta.rounded(.down)
3074
+ if secondsDouble < Double(Int64.min) || secondsDouble > Double(Int64.max) { fatalError("Timestamp overflow") }
3075
+ let seconds = Int64(secondsDouble)
3076
+ let nanoseconds = UInt32((delta - secondsDouble) * 1.0e9)
3077
+ writeI64(seconds)
3078
+ writeU32(nanoseconds)
3079
+ }
3080
+
3081
+ @inlinable mutating func writeUuid(_ value: UUID) {
3082
+ var uuid = value.uuid
3083
+ let (hi, lo) = Swift.withUnsafeBytes(of: &uuid) { raw -> (UInt64, UInt64) in
3084
+ let hiBe = raw.loadUnaligned(fromByteOffset: 0, as: UInt64.self)
3085
+ let loBe = raw.loadUnaligned(fromByteOffset: 8, as: UInt64.self)
3086
+ return (UInt64(bigEndian: hiBe), UInt64(bigEndian: loBe))
3087
+ }
3088
+ writeU64(hi)
3089
+ writeU64(lo)
3090
+ }
3091
+
3092
+ @inlinable mutating func writeUrl(_ value: URL) {
3093
+ writeString(value.absoluteString)
3094
+ }
3095
+
3096
+ @inlinable mutating func writeOptional<T>(_ value: T?, _ body: (inout WireWriter, T) -> Void) {
3097
+ switch value {
3098
+ case .some(let value):
3099
+ writeU8(1)
3100
+ body(&self, value)
3101
+ case .none:
3102
+ writeU8(0)
3103
+ }
3104
+ }
3105
+
3106
+ @inlinable mutating func writeSequence<T>(_ value: [T], _ body: (inout WireWriter, T) -> Void) {
3107
+ writeU32(UInt32(value.count))
3108
+ for item in value {
3109
+ body(&self, item)
3110
+ }
3111
+ }
3112
+
3113
+ @inlinable mutating func writeArray<T>(_ value: [T], _ body: (inout WireWriter, T) -> Void) {
3114
+ writeSequence(value, body)
3115
+ }
3116
+
3117
+ @inlinable mutating func writeResult<Success, Failure: Swift.Error>(
3118
+ _ value: Swift.Result<Success, Failure>,
3119
+ _ success: (inout WireWriter, Success) -> Void,
3120
+ _ failure: (inout WireWriter, Failure) -> Void
3121
+ ) {
3122
+ switch value {
3123
+ case .success(let value):
3124
+ writeU8(0)
3125
+ success(&self, value)
3126
+ case .failure(let value):
3127
+ writeU8(1)
3128
+ failure(&self, value)
3129
+ }
3130
+ }
3131
+
3132
+ @inlinable mutating func writeMap<K: Hashable, V>(
3133
+ _ value: [K: V],
3134
+ _ key: (inout WireWriter, K) -> Void,
3135
+ _ writeValue: (inout WireWriter, V) -> Void
3136
+ ) {
3137
+ writeU32(UInt32(value.count))
3138
+ for entry in value {
3139
+ key(&self, entry.key)
3140
+ writeValue(&self, entry.value)
3141
+ }
3142
+ }
3143
+
3144
+ @inlinable func finalize() -> Data {
3145
+ data
3146
+ }
3147
+ }
3148
+
3149
+ @inlinable func boltffiDecodeOwnedBuf<T>(
3150
+ _ pointer: UnsafePointer<UInt8>?,
3151
+ _ length: Int,
3152
+ _ decode: (inout WireReader) -> T
3153
+ ) -> T {
3154
+ var reader = WireReader(ptr: pointer!, len: length)
3155
+ return decode(&reader)
3156
+ }
3157
+
3158
+ @inlinable func boltffiEncode(_ body: (inout WireWriter) -> Void) -> [UInt8] {
3159
+ var writer = WireWriter()
3160
+ body(&writer)
3161
+ return [UInt8](writer.finalize())
3162
+ }
3163
+
3164
+ /// Reserved marker for an error that does not match a callback's declared error type.
3165
+ private let boltffiUnexpectedCallbackErrorMarker = Array("BOLTFFI_CALLBACK".utf8)
3166
+
3167
+ /// Version of the unexpected callback error payload written by this generated binding.
3168
+ private let boltffiUnexpectedCallbackErrorVersion: UInt8 = 1
3169
+
3170
+ /// Encodes an unexpected host-language callback error for conversion by the Rust error owner.
3171
+ ///
3172
+ /// Declared callback errors keep their normal wire representation. The marker lets Rust detect this
3173
+ /// fallback before decoding the payload as the declared error type.
3174
+ func boltffiEncodeUnexpectedCallbackError(_ error: Error) -> [UInt8] {
3175
+ var writer = WireWriter()
3176
+ writer.data.append(contentsOf: boltffiUnexpectedCallbackErrorMarker)
3177
+ writer.writeU8(boltffiUnexpectedCallbackErrorVersion)
3178
+ writer.writeString(String(describing: error))
3179
+ return [UInt8](writer.finalize())
3180
+ }
3181
+
3182
+ @inline(__always)
3183
+ private func boltffiReadWireStreamBatch<Item>(
3184
+ subscription: UInt64,
3185
+ batchSize: UInt,
3186
+ popBatch: (UInt64, UInt) -> FfiBuf_u8,
3187
+ freeBuf: (FfiBuf_u8) -> Void,
3188
+ decodeItems: (inout WireReader) -> [Item]
3189
+ ) -> [Item] {
3190
+ let buffer = popBatch(subscription, batchSize)
3191
+ defer { freeBuf(buffer) }
3192
+ guard buffer.len > 0, let pointer = buffer.ptr else {
3193
+ return []
3194
+ }
3195
+ var reader = WireReader(ptr: pointer, len: Int(buffer.len))
3196
+ return decodeItems(&reader)
3197
+ }
3198
+