@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.
- package/LICENSE +201 -0
- package/README.md +298 -0
- package/android/build.gradle +61 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/ai/xybrid/reactnative/XybridCodec.kt +481 -0
- package/android/src/main/java/ai/xybrid/reactnative/XybridHandles.kt +139 -0
- package/android/src/main/java/ai/xybrid/reactnative/XybridModule.kt +665 -0
- package/android/src/main/java/ai/xybrid/reactnative/XybridPackage.kt +27 -0
- package/ios/XybridCodec.swift +508 -0
- package/ios/XybridHandles.swift +191 -0
- package/ios/XybridModule.mm +494 -0
- package/ios/XybridModuleImpl.swift +887 -0
- package/ios/XybridSwift/Xybrid.swift +1401 -0
- package/ios/XybridSwift/xybrid_bolt.swift +3198 -0
- package/ios/xybrid_natives.rb +178 -0
- package/lib/commonjs/NativeXybrid.js +24 -0
- package/lib/commonjs/base64.js +45 -0
- package/lib/commonjs/cancel.js +35 -0
- package/lib/commonjs/context.js +67 -0
- package/lib/commonjs/envelope.js +79 -0
- package/lib/commonjs/errors.js +61 -0
- package/lib/commonjs/index.js +59 -0
- package/lib/commonjs/model.js +307 -0
- package/lib/commonjs/pipeline.js +55 -0
- package/lib/commonjs/presets.js +32 -0
- package/lib/commonjs/session.js +76 -0
- package/lib/commonjs/types.js +10 -0
- package/lib/commonjs/wire.js +285 -0
- package/lib/commonjs/xybrid.js +133 -0
- package/lib/module/NativeXybrid.js +22 -0
- package/lib/module/base64.js +41 -0
- package/lib/module/cancel.js +29 -0
- package/lib/module/context.js +60 -0
- package/lib/module/envelope.js +73 -0
- package/lib/module/errors.js +56 -0
- package/lib/module/index.js +36 -0
- package/lib/module/model.js +298 -0
- package/lib/module/pipeline.js +48 -0
- package/lib/module/presets.js +27 -0
- package/lib/module/session.js +69 -0
- package/lib/module/types.js +9 -0
- package/lib/module/wire.js +271 -0
- package/lib/module/xybrid.js +127 -0
- package/lib/typescript/NativeXybrid.d.ts +68 -0
- package/lib/typescript/NativeXybrid.d.ts.map +1 -0
- package/lib/typescript/base64.d.ts +5 -0
- package/lib/typescript/base64.d.ts.map +1 -0
- package/lib/typescript/cancel.d.ts +24 -0
- package/lib/typescript/cancel.d.ts.map +1 -0
- package/lib/typescript/context.d.ts +40 -0
- package/lib/typescript/context.d.ts.map +1 -0
- package/lib/typescript/envelope.d.ts +51 -0
- package/lib/typescript/envelope.d.ts.map +1 -0
- package/lib/typescript/errors.d.ts +12 -0
- package/lib/typescript/errors.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +28 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/model.d.ts +211 -0
- package/lib/typescript/model.d.ts.map +1 -0
- package/lib/typescript/pipeline.d.ts +35 -0
- package/lib/typescript/pipeline.d.ts.map +1 -0
- package/lib/typescript/presets.d.ts +21 -0
- package/lib/typescript/presets.d.ts.map +1 -0
- package/lib/typescript/session.d.ts +50 -0
- package/lib/typescript/session.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +313 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/wire.d.ts +130 -0
- package/lib/typescript/wire.d.ts.map +1 -0
- package/lib/typescript/xybrid.d.ts +89 -0
- package/lib/typescript/xybrid.d.ts.map +1 -0
- package/package.json +88 -0
- package/react-native-xybrid.podspec +50 -0
- package/src/NativeXybrid.ts +131 -0
- package/src/base64.ts +43 -0
- package/src/cancel.ts +47 -0
- package/src/context.ts +70 -0
- package/src/envelope.ts +115 -0
- package/src/errors.ts +67 -0
- package/src/index.ts +91 -0
- package/src/model.ts +408 -0
- package/src/pipeline.ts +66 -0
- package/src/presets.ts +36 -0
- package/src/session.ts +76 -0
- package/src/types.ts +362 -0
- package/src/wire.ts +408 -0
- 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
|
+
|