@xybrid/react-native 0.10.0-rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +298 -0
  3. package/android/build.gradle +61 -0
  4. package/android/src/main/AndroidManifest.xml +1 -0
  5. package/android/src/main/java/ai/xybrid/reactnative/XybridCodec.kt +481 -0
  6. package/android/src/main/java/ai/xybrid/reactnative/XybridHandles.kt +139 -0
  7. package/android/src/main/java/ai/xybrid/reactnative/XybridModule.kt +665 -0
  8. package/android/src/main/java/ai/xybrid/reactnative/XybridPackage.kt +27 -0
  9. package/ios/XybridCodec.swift +508 -0
  10. package/ios/XybridHandles.swift +191 -0
  11. package/ios/XybridModule.mm +494 -0
  12. package/ios/XybridModuleImpl.swift +887 -0
  13. package/ios/XybridSwift/Xybrid.swift +1401 -0
  14. package/ios/XybridSwift/xybrid_bolt.swift +3198 -0
  15. package/ios/xybrid_natives.rb +178 -0
  16. package/lib/commonjs/NativeXybrid.js +24 -0
  17. package/lib/commonjs/base64.js +45 -0
  18. package/lib/commonjs/cancel.js +35 -0
  19. package/lib/commonjs/context.js +67 -0
  20. package/lib/commonjs/envelope.js +79 -0
  21. package/lib/commonjs/errors.js +61 -0
  22. package/lib/commonjs/index.js +59 -0
  23. package/lib/commonjs/model.js +307 -0
  24. package/lib/commonjs/pipeline.js +55 -0
  25. package/lib/commonjs/presets.js +32 -0
  26. package/lib/commonjs/session.js +76 -0
  27. package/lib/commonjs/types.js +10 -0
  28. package/lib/commonjs/wire.js +285 -0
  29. package/lib/commonjs/xybrid.js +133 -0
  30. package/lib/module/NativeXybrid.js +22 -0
  31. package/lib/module/base64.js +41 -0
  32. package/lib/module/cancel.js +29 -0
  33. package/lib/module/context.js +60 -0
  34. package/lib/module/envelope.js +73 -0
  35. package/lib/module/errors.js +56 -0
  36. package/lib/module/index.js +36 -0
  37. package/lib/module/model.js +298 -0
  38. package/lib/module/pipeline.js +48 -0
  39. package/lib/module/presets.js +27 -0
  40. package/lib/module/session.js +69 -0
  41. package/lib/module/types.js +9 -0
  42. package/lib/module/wire.js +271 -0
  43. package/lib/module/xybrid.js +127 -0
  44. package/lib/typescript/NativeXybrid.d.ts +68 -0
  45. package/lib/typescript/NativeXybrid.d.ts.map +1 -0
  46. package/lib/typescript/base64.d.ts +5 -0
  47. package/lib/typescript/base64.d.ts.map +1 -0
  48. package/lib/typescript/cancel.d.ts +24 -0
  49. package/lib/typescript/cancel.d.ts.map +1 -0
  50. package/lib/typescript/context.d.ts +40 -0
  51. package/lib/typescript/context.d.ts.map +1 -0
  52. package/lib/typescript/envelope.d.ts +51 -0
  53. package/lib/typescript/envelope.d.ts.map +1 -0
  54. package/lib/typescript/errors.d.ts +12 -0
  55. package/lib/typescript/errors.d.ts.map +1 -0
  56. package/lib/typescript/index.d.ts +28 -0
  57. package/lib/typescript/index.d.ts.map +1 -0
  58. package/lib/typescript/model.d.ts +211 -0
  59. package/lib/typescript/model.d.ts.map +1 -0
  60. package/lib/typescript/pipeline.d.ts +35 -0
  61. package/lib/typescript/pipeline.d.ts.map +1 -0
  62. package/lib/typescript/presets.d.ts +21 -0
  63. package/lib/typescript/presets.d.ts.map +1 -0
  64. package/lib/typescript/session.d.ts +50 -0
  65. package/lib/typescript/session.d.ts.map +1 -0
  66. package/lib/typescript/types.d.ts +313 -0
  67. package/lib/typescript/types.d.ts.map +1 -0
  68. package/lib/typescript/wire.d.ts +130 -0
  69. package/lib/typescript/wire.d.ts.map +1 -0
  70. package/lib/typescript/xybrid.d.ts +89 -0
  71. package/lib/typescript/xybrid.d.ts.map +1 -0
  72. package/package.json +88 -0
  73. package/react-native-xybrid.podspec +50 -0
  74. package/src/NativeXybrid.ts +131 -0
  75. package/src/base64.ts +43 -0
  76. package/src/cancel.ts +47 -0
  77. package/src/context.ts +70 -0
  78. package/src/envelope.ts +115 -0
  79. package/src/errors.ts +67 -0
  80. package/src/index.ts +91 -0
  81. package/src/model.ts +408 -0
  82. package/src/pipeline.ts +66 -0
  83. package/src/presets.ts +36 -0
  84. package/src/session.ts +76 -0
  85. package/src/types.ts +362 -0
  86. package/src/wire.ts +408 -0
  87. package/src/xybrid.ts +176 -0
@@ -0,0 +1,178 @@
1
+ # Resolves the two iOS pieces the npm tarball does not carry by default, at
2
+ # `pod install` time (the podspec calls this while CocoaPods evaluates it):
3
+ #
4
+ # ios/Frameworks/XybridFFI.xcframework — the Rust core (~64 MB zipped).
5
+ # Downloaded from the GitHub Release for this package's version and
6
+ # checked against the SHA-256 in package.json, exactly like Package.swift's
7
+ # binaryTarget. Cached in ~/.xybrid/cache/xcframework, next to the SDK's
8
+ # other caches, so each version downloads once per machine.
9
+ # ios/XybridSwift/{Xybrid,xybrid_bolt}.swift — the Swift SDK sources. The
10
+ # published tarball includes them; in the xybrid monorepo they are synced
11
+ # from bindings/apple on every install.
12
+ #
13
+ # Where the XCFramework comes from, first match wins:
14
+ # 1. XYBRID_XCFRAMEWORK_PATH — an XybridFFI.xcframework directory or .zip
15
+ # 2. an ios/Frameworks/XybridFFI.xcframework you staged yourself
16
+ # 3. in the monorepo: <repo>/bazel-bin/bindings/apple/XybridFFI.xcframework.zip
17
+ # (a release download could not match the working tree's C ABI, so the
18
+ # monorepo never falls back to one)
19
+ # 4. otherwise: the pinned release asset (XYBRID_NATIVES_BASE_URL = mirror)
20
+ #
21
+ # Plain Ruby + curl/unzip/shasum from macOS: this runs inside `pod install`,
22
+ # so it must not need anything a stock Mac lacks.
23
+
24
+ require 'digest'
25
+ require 'fileutils'
26
+ require 'json'
27
+ require 'tmpdir'
28
+
29
+ module XybridNatives
30
+ FRAMEWORK = 'XybridFFI.xcframework'.freeze
31
+ STAMP = '.xybrid-natives'.freeze
32
+ SWIFT_SOURCES = %w[Xybrid.swift xybrid_bolt.swift].freeze
33
+ DEFAULT_BASE_URL = 'https://github.com/xybrid-ai/xybrid/releases/download'.freeze
34
+
35
+ module_function
36
+
37
+ # Make sure every native input exists; returns the framework path relative
38
+ # to the pod root, for `vendored_frameworks`.
39
+ def prepare!(root)
40
+ # Resolve symlinks: `file:` installs link node_modules to the source tree.
41
+ root = File.realpath(root)
42
+ package = JSON.parse(File.read(File.join(root, 'package.json')))
43
+ monorepo = File.expand_path('../..', root) if File.file?(File.join(root, '../apple/Sources/Xybrid/Xybrid.swift'))
44
+ ensure_swift_sources!(root, monorepo)
45
+ ensure_xcframework!(root, package, monorepo)
46
+ "ios/Frameworks/#{FRAMEWORK}"
47
+ end
48
+
49
+ def log(message)
50
+ if defined?(Pod::UI)
51
+ Pod::UI.puts("[@xybrid/react-native] #{message}")
52
+ else
53
+ puts("[@xybrid/react-native] #{message}")
54
+ end
55
+ end
56
+
57
+ def fail!(message)
58
+ raise(defined?(Pod::Informative) ? Pod::Informative : RuntimeError, "[@xybrid/react-native] #{message}")
59
+ end
60
+
61
+ def ensure_swift_sources!(root, monorepo)
62
+ staged = File.join(root, 'ios', 'XybridSwift')
63
+ if monorepo
64
+ # Always sync, so a stale copy can never drift from bindings/apple.
65
+ source = File.join(monorepo, 'bindings', 'apple', 'Sources', 'Xybrid')
66
+ FileUtils.mkdir_p(staged)
67
+ SWIFT_SOURCES.each do |name|
68
+ from = File.join(source, name)
69
+ to = File.join(staged, name)
70
+ FileUtils.cp(from, to) unless File.file?(to) && FileUtils.identical?(from, to)
71
+ end
72
+ return
73
+ end
74
+ return if SWIFT_SOURCES.all? { |name| File.file?(File.join(staged, name)) }
75
+
76
+ fail!('ios/XybridSwift is missing the Swift SDK sources; reinstall @xybrid/react-native from npm.')
77
+ end
78
+
79
+ def ensure_xcframework!(root, package, monorepo)
80
+ frameworks = File.join(root, 'ios', 'Frameworks')
81
+ target = File.join(frameworks, FRAMEWORK)
82
+ stamp = File.join(frameworks, STAMP)
83
+ installed = File.file?(stamp) ? File.read(stamp).strip : nil
84
+ version = package.fetch('version')
85
+
86
+ local = ENV['XYBRID_XCFRAMEWORK_PATH'].to_s.strip
87
+ local = File.expand_path(local) unless local.empty?
88
+ # A framework staged by hand (no stamp) is used as-is.
89
+ return if local.empty? && File.directory?(target) && installed.nil?
90
+
91
+ if local.empty? && monorepo
92
+ local = File.join(monorepo, 'bazel-bin', 'bindings', 'apple', 'XybridFFI.xcframework.zip')
93
+ unless File.exist?(local)
94
+ fail!('build the XCFramework first: `bazel build --config=ios //bindings/apple:XybridFFI` ' \
95
+ '(or set XYBRID_XCFRAMEWORK_PATH).')
96
+ end
97
+ end
98
+
99
+ unless local.empty?
100
+ fail!("XYBRID_XCFRAMEWORK_PATH does not exist: #{local}") unless File.exist?(local)
101
+ source = "local:#{local}:#{fingerprint(local)}"
102
+ return if File.directory?(target) && installed == source
103
+
104
+ replace!(frameworks, target) do
105
+ File.directory?(local) ? FileUtils.cp_r(local, target) : unzip!(local, frameworks)
106
+ end
107
+ File.write(stamp, "#{source}\n")
108
+ log("Using the XCFramework from #{local}")
109
+ return
110
+ end
111
+
112
+ sha256 = package.dig('xybrid', 'iosXcframeworkSha256').to_s.strip
113
+ fail!("no XCFramework checksum is pinned for #{version}; set XYBRID_XCFRAMEWORK_PATH.") if sha256.empty?
114
+ source = "release:#{version}:#{sha256}"
115
+ return if File.directory?(target) && installed == source
116
+
117
+ zip = cached_download!(version, sha256)
118
+ replace!(frameworks, target) { unzip!(zip, frameworks) }
119
+ File.write(stamp, "#{source}\n")
120
+ end
121
+
122
+ def replace!(frameworks, target)
123
+ FileUtils.rm_rf(target)
124
+ FileUtils.rm_f(File.join(frameworks, STAMP))
125
+ FileUtils.mkdir_p(frameworks)
126
+ yield
127
+ end
128
+
129
+ # Identifies a local XCFramework (directory or .zip) by every file's path,
130
+ # size and mtime to the nanosecond. A directory's own mtime only moves when
131
+ # its direct entries change, so rebuilding a library inside the framework
132
+ # would otherwise keep serving the stale copy.
133
+ def fingerprint(path)
134
+ files = File.directory?(path) ? Dir.glob(File.join(path, '**', '*'), File::FNM_DOTMATCH) : [path]
135
+ digest = Digest::SHA256.new
136
+ files.select { |file| File.file?(file) }.sort.each do |file|
137
+ stat = File.stat(file)
138
+ digest << "#{file.delete_prefix(path)}\0#{stat.size}\0#{stat.mtime.to_i}.#{stat.mtime.nsec}\n"
139
+ end
140
+ digest.hexdigest
141
+ end
142
+
143
+ def cached_download!(version, sha256)
144
+ cache = File.join(Dir.home, '.xybrid', 'cache', 'xcframework')
145
+ name = "XybridFFI-v#{version}.xcframework.zip"
146
+ zip = File.join(cache, name)
147
+ return zip if File.file?(zip) && Digest::SHA256.file(zip).hexdigest == sha256
148
+
149
+ base = ENV['XYBRID_NATIVES_BASE_URL'].to_s.strip
150
+ base = DEFAULT_BASE_URL if base.empty?
151
+ url = "#{base.chomp('/')}/v#{version}/#{name}"
152
+ FileUtils.mkdir_p(cache)
153
+ partial = "#{zip}.partial"
154
+ log("Downloading #{url}")
155
+ unless system('curl', '--fail', '--location', '--silent', '--show-error',
156
+ '--retry', '3', '--output', partial, url)
157
+ FileUtils.rm_f(partial)
158
+ fail!("could not download #{url}. Set XYBRID_NATIVES_BASE_URL to a mirror, or " \
159
+ 'XYBRID_XCFRAMEWORK_PATH to a local copy.')
160
+ end
161
+ actual = Digest::SHA256.file(partial).hexdigest
162
+ unless actual == sha256
163
+ FileUtils.rm_f(partial)
164
+ fail!("checksum mismatch for #{name}: expected #{sha256}, got #{actual}")
165
+ end
166
+ FileUtils.mv(partial, zip)
167
+ zip
168
+ end
169
+
170
+ def unzip!(zip, destination)
171
+ Dir.mktmpdir('xybrid-xcframework') do |scratch|
172
+ fail!("could not unzip #{zip}") unless system('unzip', '-q', '-o', zip, '-d', scratch)
173
+ extracted = Dir.glob(File.join(scratch, '**', FRAMEWORK)).min_by(&:length)
174
+ fail!("#{zip} does not contain #{FRAMEWORK}") unless extracted
175
+ FileUtils.mv(extracted, File.join(destination, FRAMEWORK))
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ // TurboModule spec consumed by React Native Codegen — the one contract both
3
+ // native shims are compiled against: Android extends the generated
4
+ // `NativeXybridSpec` class and iOS conforms to the generated
5
+ // `NativeXybridSpec` protocol (tests/spec-conformance.test.mjs checks the
6
+ // selectors without a Mac).
7
+ //
8
+ // Codegen accepts only `string`, `number`, `boolean`, `Object`, arrays and
9
+ // `Promise`s of those, and `| null`. Structured values therefore cross as
10
+ // plain `Object`s: the TS facade (src/index.ts) builds and narrows them, and
11
+ // the native codecs (ios/XybridCodec.swift, android/.../XybridCodec.kt)
12
+ // translate them to and from the bolt types.
13
+ //
14
+ // Native objects (models, conversation contexts, cancellation tokens, token
15
+ // streams, downloads, pipelines, live-ASR sessions) are opaque string handles
16
+ // held in one registry per platform. `dispose` frees any of them, and freeing
17
+ // a model also disposes the token streams started from it.
18
+ //
19
+ // Nothing here is public API. Keep method names distinct from NSObject /
20
+ // Java `Object` members (hence `dispose`, not `release`; `sdkVersion`, not
21
+ // `version`).
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ const react_native_1 = require("react-native");
24
+ exports.default = react_native_1.TurboModuleRegistry.getEnforcing('RNXybrid');
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ // Minimal base64 for the one binary payload JS produces itself: live-ASR PCM.
3
+ // Hermes has no Buffer, and `btoa` only takes binary strings, so encode the
4
+ // bytes directly. Every platform React Native runs on is little-endian, which
5
+ // is the byte order the native shims decode Float32 samples in.
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.bytesToBase64 = bytesToBase64;
8
+ exports.float32ToBase64 = float32ToBase64;
9
+ const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
10
+ /** Standard (RFC 4648, padded) base64 of `bytes`. */
11
+ function bytesToBase64(bytes) {
12
+ const chunks = [];
13
+ let chunk = '';
14
+ let i = 0;
15
+ for (; i + 2 < bytes.length; i += 3) {
16
+ const n = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
17
+ chunk +=
18
+ ALPHABET[(n >> 18) & 63] +
19
+ ALPHABET[(n >> 12) & 63] +
20
+ ALPHABET[(n >> 6) & 63] +
21
+ ALPHABET[n & 63];
22
+ // Flush periodically so long inputs don't build one enormous rope.
23
+ if (chunk.length >= 8192) {
24
+ chunks.push(chunk);
25
+ chunk = '';
26
+ }
27
+ }
28
+ const rest = bytes.length - i;
29
+ if (rest === 1) {
30
+ const n = bytes[i] << 16;
31
+ chunk += ALPHABET[(n >> 18) & 63] + ALPHABET[(n >> 12) & 63] + '==';
32
+ }
33
+ else if (rest === 2) {
34
+ const n = (bytes[i] << 16) | (bytes[i + 1] << 8);
35
+ chunk +=
36
+ ALPHABET[(n >> 18) & 63] + ALPHABET[(n >> 12) & 63] + ALPHABET[(n >> 6) & 63] + '=';
37
+ }
38
+ chunks.push(chunk);
39
+ return chunks.join('');
40
+ }
41
+ /** Base64 of Float32 PCM samples as little-endian bytes. */
42
+ function float32ToBase64(samples) {
43
+ const floats = samples instanceof Float32Array ? samples : Float32Array.from(samples);
44
+ return bytesToBase64(new Uint8Array(floats.buffer, floats.byteOffset, floats.byteLength));
45
+ }
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.bindCancel = bindCancel;
7
+ const NativeXybrid_1 = __importDefault(require("./NativeXybrid"));
8
+ const NO_SIGNAL = { token: null, release: () => Promise.resolve() };
9
+ /**
10
+ * Create a native stop button wired to `signal`. Aborting the signal cancels
11
+ * the token, which stops generation at the next token boundary. A signal
12
+ * that is already aborted cancels the token before the call starts, so the
13
+ * call rejects with `xybrid_cancelled` instead of running.
14
+ */
15
+ async function bindCancel(signal) {
16
+ if (!signal)
17
+ return NO_SIGNAL;
18
+ const token = await NativeXybrid_1.default.createCancelToken();
19
+ const onAbort = () => {
20
+ NativeXybrid_1.default.cancel(token).catch(() => { });
21
+ };
22
+ if (signal.aborted) {
23
+ await NativeXybrid_1.default.cancel(token);
24
+ }
25
+ else {
26
+ signal.addEventListener('abort', onAbort);
27
+ }
28
+ return {
29
+ token,
30
+ release: async () => {
31
+ signal.removeEventListener('abort', onAbort);
32
+ await NativeXybrid_1.default.dispose(token).catch(() => { });
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ConversationContext = void 0;
7
+ const NativeXybrid_1 = __importDefault(require("./NativeXybrid"));
8
+ const wire_1 = require("./wire");
9
+ /**
10
+ * Multi-turn conversation history, held natively. Pass it to
11
+ * `model.run(envelope, { context })` or `runStreaming`; inference reads the
12
+ * history but never changes it. The prompt you run is appended to the
13
+ * history for that run only, so push both turns *after* the run — pushing the
14
+ * prompt first would send it twice:
15
+ *
16
+ * ```ts
17
+ * const chat = await ConversationContext.create();
18
+ * await chat.setSystem('You are terse.');
19
+ *
20
+ * const question = Envelope.user('What is the capital of France?');
21
+ * const reply = await model.run(question, { context: chat });
22
+ * await chat.push(question);
23
+ * await chat.push(reply.envelope); // already tagged `assistant`
24
+ * ```
25
+ */
26
+ class ConversationContext {
27
+ constructor(
28
+ /** Opaque native handle. */
29
+ handle) {
30
+ this.handle = handle;
31
+ }
32
+ /** A new, empty conversation; `id` defaults to a fresh one (telemetry correlation). */
33
+ static async create(id) {
34
+ return new ConversationContext(await NativeXybrid_1.default.createContext(id ?? null));
35
+ }
36
+ /** Append a turn. Give it a `role` (see `Envelope.user` / `Envelope.assistant`). */
37
+ push(envelope) {
38
+ return NativeXybrid_1.default.contextPush(this.handle, (0, wire_1.toWireEnvelope)(envelope));
39
+ }
40
+ /** Set the persistent system prompt; it survives {@link clear}. */
41
+ setSystem(system) {
42
+ const envelope = typeof system === 'string' ? { kind: 'text', text: system, role: 'system' } : system;
43
+ return NativeXybrid_1.default.contextSetSystem(this.handle, (0, wire_1.toWireEnvelope)(envelope));
44
+ }
45
+ /** Drop the history; the system prompt is kept. */
46
+ clear() {
47
+ return NativeXybrid_1.default.contextClear(this.handle);
48
+ }
49
+ /** Id, history length and whether a system prompt is set. */
50
+ async info() {
51
+ return (await NativeXybrid_1.default.contextInfo(this.handle));
52
+ }
53
+ /** History turns in order, excluding the system prompt. */
54
+ async history() {
55
+ const wire = (await NativeXybrid_1.default.contextHistory(this.handle));
56
+ return wire.map(wire_1.fromWireEnvelope);
57
+ }
58
+ /** Cap the history; older turns are pruned first, immediately. */
59
+ setMaxHistoryLength(length) {
60
+ return NativeXybrid_1.default.contextSetMaxHistoryLength(this.handle, length);
61
+ }
62
+ /** Free the native context. Runs already started keep their snapshot. */
63
+ release() {
64
+ return NativeXybrid_1.default.dispose(this.handle);
65
+ }
66
+ }
67
+ exports.ConversationContext = ConversationContext;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Envelope = void 0;
7
+ const NativeXybrid_1 = __importDefault(require("./NativeXybrid"));
8
+ const wire_1 = require("./wire");
9
+ /**
10
+ * Envelope factories, named like the Swift and Kotlin `Envelope` helpers.
11
+ * Object literals (`{ kind: 'text', text }`) work just as well; these only
12
+ * save typing and apply the same defaults.
13
+ */
14
+ exports.Envelope = {
15
+ /** A text prompt, or text to speak with an optional TTS voice and speed. */
16
+ text(text, options = {}) {
17
+ return { kind: 'text', text, ...options };
18
+ },
19
+ /** A user turn for a conversation. */
20
+ user(text) {
21
+ return { kind: 'text', text, role: 'user' };
22
+ },
23
+ /** An assistant turn for a conversation (e.g. a model reply you keep). */
24
+ assistant(text) {
25
+ return { kind: 'text', text, role: 'assistant' };
26
+ },
27
+ /** A system prompt. */
28
+ system(text) {
29
+ return { kind: 'text', text, role: 'system' };
30
+ },
31
+ /** PCM/WAV audio, base64-encoded. Defaults to 16 kHz mono. */
32
+ audio(bytesBase64, options = {}) {
33
+ return {
34
+ kind: 'audio',
35
+ bytesBase64,
36
+ sampleRate: options.sampleRate ?? 16000,
37
+ channels: options.channels ?? 1,
38
+ };
39
+ },
40
+ /** An embedding vector. */
41
+ embedding(data) {
42
+ return { kind: 'embedding', data };
43
+ },
44
+ /**
45
+ * An encoded image for vision-language models. The bytes are validated
46
+ * natively at run time (`xybrid_invalid_image` on bad input).
47
+ */
48
+ image(bytesBase64, format) {
49
+ return { kind: 'image', bytesBase64, format: (0, wire_1.normalizeImageFormat)(format) };
50
+ },
51
+ /** A user message with image attachments, for vision-language models. */
52
+ userMessage(text, images = []) {
53
+ if (!images.every((image) => image?.kind === 'image')) {
54
+ throw new TypeError('Envelope.userMessage accepts only image envelopes');
55
+ }
56
+ return {
57
+ kind: 'multipart',
58
+ parts: [{ kind: 'text', text }, ...images],
59
+ role: 'user',
60
+ };
61
+ },
62
+ /**
63
+ * The continuation envelope for the turn after the model asked for tools.
64
+ *
65
+ * One `run` is one model turn, so the tool loop lives in your code: run a
66
+ * request carrying `generationConfig.tools`, execute every
67
+ * `result.toolCalls` entry, then run this envelope (with the same tools) to
68
+ * feed the outcomes back.
69
+ *
70
+ * @param userText the original user message of the turn being continued.
71
+ * @param priorAssistantText that turn's raw output — `result.text` from a
72
+ * batch run, or the terminal token's `rawText` when streaming.
73
+ * @param results tool outcomes, in call order.
74
+ */
75
+ async toolResults(userText, priorAssistantText, results) {
76
+ const wire = (await NativeXybrid_1.default.toolResultsEnvelope(userText, priorAssistantText, results.map(wire_1.toWireToolResult)));
77
+ return (0, wire_1.fromWireEnvelope)(wire);
78
+ },
79
+ };
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ // Every native rejection carries one of these codes as `error.code`. The
3
+ // SDK codes map 1:1 onto bolt's `XybridError` variants (same order as
4
+ // crates/xybrid-bolt/src/lib.rs); the last three are raised by the React
5
+ // Native layer itself.
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.XybridErrorCodes = void 0;
8
+ exports.isXybridError = isXybridError;
9
+ exports.isRetryable = isRetryable;
10
+ /** Stable error codes for `XybridError.code`. */
11
+ exports.XybridErrorCodes = [
12
+ 'xybrid_model_not_found',
13
+ 'xybrid_directory_not_found',
14
+ 'xybrid_metadata_not_found',
15
+ 'xybrid_metadata_invalid',
16
+ 'xybrid_load_error',
17
+ 'xybrid_inference_error',
18
+ 'xybrid_aborted_cloud_fallback',
19
+ 'xybrid_streaming_unsupported',
20
+ 'xybrid_not_loaded',
21
+ 'xybrid_config_error',
22
+ 'xybrid_network_error',
23
+ 'xybrid_offline',
24
+ 'xybrid_io_error',
25
+ 'xybrid_cache_error',
26
+ 'xybrid_pipeline_error',
27
+ 'xybrid_circuit_open',
28
+ 'xybrid_rate_limited',
29
+ 'xybrid_timeout',
30
+ 'xybrid_missing_artifact',
31
+ 'xybrid_unsupported_model_capability',
32
+ 'xybrid_unsupported_backend_capability',
33
+ 'xybrid_invalid_image',
34
+ 'xybrid_cancelled',
35
+ // React Native layer:
36
+ /** The handle was never issued, was released, or names another kind of object. */
37
+ 'xybrid_handle',
38
+ /** A JS argument could not be decoded (bad envelope, options, source, base64…). */
39
+ 'xybrid_invalid_argument',
40
+ /** Anything unexpected; the message carries the detail. */
41
+ 'xybrid_unknown',
42
+ ];
43
+ const CODES = new Set(exports.XybridErrorCodes);
44
+ /** Narrow an unknown rejection to a {@link XybridError}. */
45
+ function isXybridError(error) {
46
+ if (!(error instanceof Error))
47
+ return false;
48
+ const { code } = error;
49
+ return typeof code === 'string' && CODES.has(code);
50
+ }
51
+ // Mirrors `RetryableError::is_retryable` on the Rust `SdkError`.
52
+ const RETRYABLE = new Set([
53
+ 'xybrid_network_error',
54
+ 'xybrid_rate_limited',
55
+ 'xybrid_timeout',
56
+ 'xybrid_offline',
57
+ ]);
58
+ /** Whether retrying the same call later can succeed (network, rate limit, timeout, offline). */
59
+ function isRetryable(error) {
60
+ return isXybridError(error) && RETRYABLE.has(error.code);
61
+ }
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ // @xybrid/react-native — on-device (and optionally cloud) inference for React
3
+ // Native, backed by the same Rust SDK as the Swift, Kotlin, Flutter and Unity
4
+ // bindings.
5
+ //
6
+ // await Xybrid.initialize({ apiKey }); // optional
7
+ // const model = await ModelLoader.fromRegistry('qwen3.5-0.8b').load();
8
+ // const result = await model.run(Envelope.text('Hello'));
9
+ // await model.release();
10
+ var __importDefault = (this && this.__importDefault) || function (mod) {
11
+ return (mod && mod.__esModule) ? mod : { "default": mod };
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.float32ToBase64 = exports.bytesToBase64 = exports.isXybridError = exports.isRetryable = exports.XybridErrorCodes = exports.greedy = exports.creative = exports.GenerationConfigs = exports.Envelope = exports.StreamingSession = exports.Pipeline = exports.ConversationContext = exports.ModelLoader = exports.ModelDownload = exports.Model = exports.Xybrid = void 0;
15
+ exports.jsonSchemaToGbnf = jsonSchemaToGbnf;
16
+ const NativeXybrid_1 = __importDefault(require("./NativeXybrid"));
17
+ var xybrid_1 = require("./xybrid");
18
+ Object.defineProperty(exports, "Xybrid", { enumerable: true, get: function () { return xybrid_1.Xybrid; } });
19
+ var model_1 = require("./model");
20
+ Object.defineProperty(exports, "Model", { enumerable: true, get: function () { return model_1.Model; } });
21
+ Object.defineProperty(exports, "ModelDownload", { enumerable: true, get: function () { return model_1.ModelDownload; } });
22
+ Object.defineProperty(exports, "ModelLoader", { enumerable: true, get: function () { return model_1.ModelLoader; } });
23
+ var context_1 = require("./context");
24
+ Object.defineProperty(exports, "ConversationContext", { enumerable: true, get: function () { return context_1.ConversationContext; } });
25
+ var pipeline_1 = require("./pipeline");
26
+ Object.defineProperty(exports, "Pipeline", { enumerable: true, get: function () { return pipeline_1.Pipeline; } });
27
+ var session_1 = require("./session");
28
+ Object.defineProperty(exports, "StreamingSession", { enumerable: true, get: function () { return session_1.StreamingSession; } });
29
+ var envelope_1 = require("./envelope");
30
+ Object.defineProperty(exports, "Envelope", { enumerable: true, get: function () { return envelope_1.Envelope; } });
31
+ var presets_1 = require("./presets");
32
+ Object.defineProperty(exports, "GenerationConfigs", { enumerable: true, get: function () { return presets_1.GenerationConfigs; } });
33
+ Object.defineProperty(exports, "creative", { enumerable: true, get: function () { return presets_1.creative; } });
34
+ Object.defineProperty(exports, "greedy", { enumerable: true, get: function () { return presets_1.greedy; } });
35
+ var errors_1 = require("./errors");
36
+ Object.defineProperty(exports, "XybridErrorCodes", { enumerable: true, get: function () { return errors_1.XybridErrorCodes; } });
37
+ Object.defineProperty(exports, "isRetryable", { enumerable: true, get: function () { return errors_1.isRetryable; } });
38
+ Object.defineProperty(exports, "isXybridError", { enumerable: true, get: function () { return errors_1.isXybridError; } });
39
+ var base64_1 = require("./base64");
40
+ Object.defineProperty(exports, "bytesToBase64", { enumerable: true, get: function () { return base64_1.bytesToBase64; } });
41
+ Object.defineProperty(exports, "float32ToBase64", { enumerable: true, get: function () { return base64_1.float32ToBase64; } });
42
+ /**
43
+ * Convert a JSON Schema (object or JSON string) into a GBNF grammar for
44
+ * `generationConfig.grammar`, so a local LLM emits schema-valid JSON. Runs the
45
+ * same native converter every binding uses; rejects with
46
+ * `xybrid_config_error` on invalid JSON or an unsupported construct.
47
+ *
48
+ * ```ts
49
+ * const grammar = await jsonSchemaToGbnf({
50
+ * type: 'object',
51
+ * properties: { name: { type: 'string' } },
52
+ * required: ['name'],
53
+ * });
54
+ * const result = await model.run(prompt, { generationConfig: { grammar } });
55
+ * ```
56
+ */
57
+ function jsonSchemaToGbnf(schema) {
58
+ return NativeXybrid_1.default.jsonSchemaToGbnf(typeof schema === 'string' ? schema : JSON.stringify(schema));
59
+ }