@ondeinference / onde / commits / a2d0f13

update .agents

Seto Elkahfi committed Apr 2, 2026 at 17:02 UTC a2d0f139e515a430d953e519aaa172750e102c0d
6 files changed +508
.agents/AGENTS.md new
+506
@@ -0,0 +1,506 @@
1 +# Onde Inference — AGENTS.md
2 +
3 +> **AI agent reference for the `onde` repository and the `onde-swift` Swift SDK.**
4 +> Keep this file accurate as the codebase evolves.
5 +
6 +---
7 +
8 +## What Is Onde?
9 +
10 +Onde is an **on-device LLM inference SDK** targeting Apple silicon (iOS, tvOS, macOS) plus Android, Windows, and Linux. It wraps [mistral.rs](https://github.com/EricLBuehler/mistral.rs) behind a clean, platform-aware API — automatic model selection, HuggingFace Hub downloads, cache management, and GPU acceleration via Metal.
11 +
12 +- **Website:** https://ondeinference.com
13 +- **Rust crate:** https://crates.io/crates/onde
14 +- **Swift package:** https://github.com/ondeinference/onde-swift
15 +- **In production:** [Splitfire AB apps on the Apple AppStore](https://apps.apple.com/se/developer/splitfire-ab/id1831430993)
16 +
17 +---
18 +
19 +## Repository Layout
20 +
21 +```
22 +onde/
23 +├── src/
24 +│ ├── lib.rs # Crate root — uniffi::setup_scaffolding!()
25 +│ ├── hf_cache.rs # HuggingFace Hub cache: list, download, delete, diagnose, repair
26 +│ └── inference/
27 +│ ├── mod.rs # Module exports + top-level re-exports
28 +│ ├── engine.rs # ChatEngine — Rust-native API (generics, mpsc)
29 +│ ├── ffi.rs # OndeChatEngine — UniFFI Object (FFI-safe, Arc-wrapped)
30 +│ ├── models.rs # Model ID constants + SupportedModelInfo metadata
31 +│ ├── token.rs # HF token resolution: build-time literal vs cache file
32 +│ └── types.rs # All shared types: ChatMessage, SamplingConfig, InferenceResult, etc.
33 +├── sdk/
34 +│ ├── Onde/ # Generated Swift package output (git-ignored; built by CI)
35 +│ ├── gem/ # Ruby native extension (Magnus)
36 +│ ├── kotlin/ # Kotlin/Android bindings
37 +│ └── python/ # Python bindings (maturin + uniffi)
38 +├── generated/ # UniFFI-generated headers and Swift glue (git-ignored)
39 +├── uniffi-bindgen/ # Standalone bindgen binary crate (pinned uniffi =0.31.0)
40 +├── .github/
41 +│ ├── workflows/
42 +│ │ └── build-swift-xcframework.yml # CI: builds + uploads OndeFramework.xcframework
43 +│ └── scripts/
44 +│ └── build-swift-xcframework.sh # Local/CI XCFramework assembly script
45 +├── .cargo/config.toml # Target-specific rustflags (fp16, linker overrides)
46 +├── Cargo.toml # Platform-conditional mistralrs deps
47 +├── build.rs # tvOS ___chkstk_darwin assembly stub
48 +├── tvos_chkstk.s # No-op arm64 stub for missing tvOS symbol
49 +├── uniffi.toml # UniFFI binding config
50 +└── docs/
51 + ├── dev.md # Developer guide (build, architecture, platform table)
52 + ├── swift-package.md # Swift API reference + XCFramework build steps
53 + ├── distribution.md # Release process for all registries
54 + └── ruby-gem.md # Ruby gem API reference
55 +```
56 +
57 +```
58 +onde-swift/ # Swift Package Manager wrapper repo
59 +├── Package.swift # Declares OndeFramework.xcframework binary target
60 +└── Sources/
61 + └── Onde/
62 + └── onde.swift # UniFFI-generated Swift glue (do NOT edit manually)
63 +```
64 +
65 +---
66 +
67 +## Architecture
68 +
69 +### Layer Diagram
70 +
71 +```
72 +┌──────────────────────────────────────────────────────────┐
73 +│ Swift (iOS / tvOS / macOS) Rust app / CLI / server │
74 +│ import Onde use onde::inference::* │
75 +└──────────┬───────────────────────────────┬───────────────┘
76 + │ UniFFI FFI bindings │ Direct Rust API
77 + ▼ ▼
78 +┌──────────────────────────┐ ┌─────────────────────────────┐
79 +│ OndeChatEngine │ │ ChatEngine │
80 +│ (ffi.rs — uniffi::Object│──│ (engine.rs — Rust-native) │
81 +│ FFI-safe, Arc<Self>) │ │ generics, mpsc channels │
82 +└──────────┬───────────────┘ └─────────────┬───────────────┘
83 + │ │
84 + └────────────────┬───────────────┘
85 + ▼
86 + ┌────────────────────────┐
87 + │ mistralrs::Model │
88 + │ GgufModelBuilder │
89 + │ Metal / CUDA / CPU │
90 + └────────────────────────┘
91 +```
92 +
93 +### Key Design Rules
94 +
95 +1. **`ChatEngine`** (`engine.rs`) owns all Rust-idiomatic logic: `impl Into<String>`, `tokio::sync::mpsc::Receiver`, etc. Never add UniFFI annotations here.
96 +2. **`OndeChatEngine`** (`ffi.rs`) is a thin `Arc`-wrapped UniFFI `Object` with concrete, FFI-safe signatures. It delegates every call to the inner `ChatEngine`.
97 +3. **`StreamChunkListener`** is a `#[uniffi::export(callback_interface)]` trait — it must be passed as a parameter to **free functions** (not Object methods) because UniFFI 0.31 does not support callback_interface on Object methods.
98 +4. **Free functions** in `ffi.rs` (e.g. `stream_chat_message`, `default_model_config`, `user_message`) are exported with `#[uniffi::export]` and generate top-level Swift functions.
99 +5. **`uniffi::setup_scaffolding!()`** lives in `lib.rs`. Never call `uniffi::generate_scaffolding!()` from `build.rs` for the proc-macro path.
100 +
101 +---
102 +
103 +## Platform Support
104 +
105 +| Platform | `target_os` | GPU Backend | Default Model | mistralrs features |
106 +| -------- | ----------- | ----------------- | --------------------- | -------------------|
107 +| macOS | `macos` | Metal | Qwen 2.5 3B (~1.93 GB) | `["metal"]` |
108 +| iOS | `ios` | Metal | Qwen 2.5 1.5B (~941 MB)| `["metal"]` |
109 +| tvOS | `tvos` | Metal | Qwen 2.5 1.5B (~941 MB)| `["metal"]` |
110 +| Android | `android` | CPU (candle) | Qwen 2.5 1.5B (~941 MB)| `[]` + hf-hub |
111 +| Windows | `windows` | CPU (CUDA in CI) | Qwen 2.5 3B (~1.93 GB) | `[]` |
112 +| Linux | `linux` | CPU (CUDA in CI) | Qwen 2.5 3B (~1.93 GB) | `[]` |
113 +
114 +- **iOS / tvOS memory constraint:** iOS gives apps ~2–3 GB; the 3B model (~1.93 GB) causes OOM on iPhone 16e (8 GB RAM). Always default to the 1.5B on iOS/tvOS/Android.
115 +- **tvOS tier-3 target:** requires `cargo +nightly -Z build-std`. Stable toolchain cannot build tvOS targets.
116 +- **Android `hf_hub`:** `dirs::home_dir()` panics in the Android sandbox. `hf-hub` is added as an explicit dep on Android so `HF_HOME` can be seeded programmatically via `hf_hub::api::tokio::ApiBuilder`.
117 +
118 +---
119 +
120 +## Supported Models
121 +
122 +All model constants live in `src/inference/models.rs`. When adding a new model:
123 +
124 +1. Add `pub const` entries for the HF repo ID, GGUF filename, and (on Android) `TOK_MODEL_ID`.
125 +2. Add the repo ID to `SUPPORTED_MODELS` so `list_local_hf_models` filters it.
126 +3. Add a `SupportedModelInfo` entry to `SUPPORTED_MODEL_INFO` with accurate `expected_size_bytes` (from HF API `siblings[].size`).
127 +4. Add a constructor to `GgufModelConfig` in `engine.rs` (see `qwen25_1_5b`, `qwen25_3b` for pattern).
128 +5. Export a free function in `ffi.rs` (see `qwen25_1_5b_config`, `qwen25_3b_config`).
129 +
130 +### Current Models
131 +
132 +| Model | Repo | File | Size | Platforms |
133 +|-------|------|------|------|-----------|
134 +| Qwen 2.5 1.5B (GGUF Q4_K_M) | `bartowski/Qwen2.5-1.5B-Instruct-GGUF` | `Qwen2.5-1.5B-Instruct-Q4_K_M.gguf` | ~941 MB | iOS, tvOS, Android (default) |
135 +| Qwen 2.5 3B (GGUF Q4_K_M) | `bartowski/Qwen2.5-3B-Instruct-GGUF` | `Qwen2.5-3B-Instruct-Q4_K_M.gguf` | ~1.93 GB | macOS, Windows, Linux (default) |
136 +| Qwen 2.5 Coder 1.5B (GGUF) | `bartowski/Qwen2.5-Coder-1.5B-Instruct-GGUF` | `Qwen2.5-Coder-1.5B-Instruct-Q4_K_M.gguf` | ~941 MB | All platforms |
137 +| Qwen 2.5 Coder 3B (GGUF) | `bartowski/Qwen2.5-Coder-3B-Instruct-GGUF` | `Qwen2.5-Coder-3B-Instruct-Q4_K_M.gguf` | ~1.93 GB | macOS |
138 +| Qwen 2.5 Coder 7B (ISQ) | `Qwen/Qwen2.5-Coder-7B-Instruct` | safetensors (ISQ in-situ) | ~8 GB | macOS (ISQ pipeline) |
139 +
140 +---
141 +
142 +## Key Types (Rust ↔ Swift)
143 +
144 +| Rust Type | Swift Type | Notes |
145 +|-----------|------------|-------|
146 +| `ChatRole` | `enum ChatRole` | `.system`, `.user`, `.assistant` |
147 +| `ChatMessage` | `struct ChatMessage` | `role: ChatRole`, `content: String` |
148 +| `SamplingConfig` | `struct SamplingConfig` | All fields `Optional` |
149 +| `GgufModelConfig` | `struct GgufModelConfig` | `modelId`, `files`, `tokModelId?`, `displayName`, `approxMemory` |
150 +| `IsqModelConfig` | `struct IsqModelConfig` | `modelId`, `isqBits: UInt8`, `displayName`, `approxMemory` |
151 +| `InferenceResult` | `struct InferenceResult` | `text`, `durationSecs`, `durationDisplay`, `finishReason` |
152 +| `StreamChunk` | `struct StreamChunk` | `delta`, `done`, `finishReason?` |
153 +| `EngineStatus` | `enum EngineStatus` | `.unloaded`, `.loading`, `.ready`, `.generating`, `.error` |
154 +| `EngineInfo` | `struct EngineInfo` | `status`, `modelName?`, `approxMemory?`, `historyLength: UInt64` |
155 +| `InferenceError` | `enum InferenceError: Error` | `noModelLoaded`, `alreadyLoaded`, `modelBuild`, `inference`, `cancelled`, `other` |
156 +| `OndeChatEngine` | `class OndeChatEngine` | Thread-safe; `Arc`-backed; constructed with `OndeChatEngine()` |
157 +| `StreamChunkListener` | `protocol StreamChunkListener` | Implement `onChunk(chunk:) -> Bool` |
158 +
159 +---
160 +
161 +## HuggingFace Token
162 +
163 +`src/inference/token.rs` resolves the HF token in priority order:
164 +
165 +1. **Build-time literal** (`HF_TOKEN` env var baked in via `option_env!`) — required for iOS/tvOS (no filesystem token possible).
166 +2. **On-disk cache** (`~/.cache/huggingface/token`) — works on macOS after `mistralrs login`.
167 +
168 +Set `HF_TOKEN` before building:
169 +
170 +```bash
171 +export HF_TOKEN=hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
172 +cargo build --release
173 +```
174 +
175 +On sandboxed platforms, your app's setup function must also configure `HF_HOME`, `HF_HUB_CACHE`, and `TMPDIR` to point inside the app container before any `OndeChatEngine` method is called. See `docs/swift-package.md` for the full `setupInferenceEnvironment()` Swift snippet.
176 +
177 +---
178 +
179 +## Building (Rust)
180 +
181 +### Prerequisites
182 +
183 +```bash
184 +# Stable toolchain (macOS, iOS, Android, Windows, Linux)
185 +rustup toolchain install stable
186 +
187 +# Nightly toolchain (tvOS tier-3 targets only)
188 +rustup toolchain install nightly
189 +rustup component add rust-src --toolchain nightly
190 +
191 +# Apple targets (stable)
192 +rustup target add aarch64-apple-ios aarch64-apple-ios-sim
193 +rustup target add aarch64-apple-darwin x86_64-apple-darwin
194 +```
195 +
196 +### Commands
197 +
198 +```bash
199 +# Verify compilation (macOS host)
200 +cargo check
201 +
202 +# Run all tests
203 +cargo test
204 +
205 +# Run inference module tests only
206 +cargo test inference::
207 +
208 +# Run FFI wrapper tests only
209 +cargo test inference::ffi::
210 +
211 +# Clippy lint
212 +cargo clippy
213 +
214 +# Format
215 +cargo fmt
216 +```
217 +
218 +### tvOS
219 +
220 +tvOS targets require nightly and `-Z build-std`:
221 +
222 +```bash
223 +cargo +nightly rustc -Z build-std \
224 + --target aarch64-apple-tvos --release --lib --crate-type staticlib
225 +```
226 +
227 +---
228 +
229 +## Building the Swift XCFramework
230 +
231 +The XCFramework bundles staticlibs for iOS device, iOS simulator, tvOS device, tvOS simulator, and macOS into a single distributable that `Package.swift` references.
232 +
233 +### Quick build (local or CI)
234 +
235 +```bash
236 +.github/scripts/build-swift-xcframework.sh
237 +```
238 +
239 +Output lands in `dist/swift/`:
240 +- `OndeFramework.xcframework/` — the framework tree
241 +- `OndeFramework.xcframework.zip` — zipped for SPM remote binary
242 +- `OndeFramework.checksum.txt` — SHA-256 for `Package.swift`
243 +- `version.txt` — semver from `Cargo.toml`
244 +- `Package/Sources/Onde/onde.swift` — generated UniFFI Swift glue
245 +
246 +### Manual steps
247 +
248 +```bash
249 +# 1. Build the uniffi-bindgen binary (pinned to uniffi =0.31.0)
250 +cargo build --manifest-path uniffi-bindgen/Cargo.toml --release
251 +BINDGEN=uniffi-bindgen/target/release/uniffi-bindgen
252 +
253 +# 2. Compile staticlibs per target (use +1.92.0 or current stable)
254 +cargo +stable rustc --target aarch64-apple-ios --release --lib --crate-type staticlib
255 +cargo +stable rustc --target aarch64-apple-ios-sim --release --lib --crate-type staticlib
256 +cargo +stable rustc --target aarch64-apple-darwin --release --lib --crate-type staticlib
257 +cargo +nightly rustc -Z build-std --target aarch64-apple-tvos --release --lib --crate-type staticlib
258 +cargo +nightly rustc -Z build-std --target aarch64-apple-tvos-sim --release --lib --crate-type staticlib
259 +
260 +# 3. Generate Swift bindings from the iOS arm64 slice
261 +$BINDGEN generate target/aarch64-apple-ios/release/libonde.a \
262 + --language swift \
263 + --out-dir sdk/Onde/Sources/Onde \
264 + --config uniffi.toml
265 +
266 +# 4. Assemble the XCFramework (see build-swift-xcframework.sh for full xcodebuild invocation)
267 +```
268 +
269 +### Updating `onde-swift` after a new release
270 +
271 +1. Run the build script and collect `OndeFramework.xcframework.zip` + checksum.
272 +2. Create a GitHub Release on the `onde` repo and attach the zip.
273 +3. In `onde-swift/Package.swift`, update the `.binaryTarget` to use the new `url:` and `checksum:`.
274 +4. Commit, tag (`0.x.y`), and push `onde-swift`.
275 +
276 +---
277 +
278 +## Swift SDK (`onde-swift`)
279 +
280 +### Package.swift structure
281 +
282 +```
283 +onde-swift/
284 +├── Package.swift # Declares OndeFramework binary target + Onde wrapper target
285 +└── Sources/Onde/
286 + └── onde.swift # UniFFI-generated glue (NEVER edit manually)
287 +```
288 +
289 +`Package.swift` has two targets:
290 +
291 +- **`OndeFramework`** — `.binaryTarget` pointing at `OndeFramework.xcframework` (local path during dev, remote URL+checksum for distribution).
292 +- **`Onde`** — `.target` depending on `OndeFramework`, used as the public import name in Swift.
293 +
294 +### Swift API Quick Reference
295 +
296 +```swift
297 +import Onde
298 +
299 +// Create engine
300 +let engine = OndeChatEngine()
301 +
302 +// Load model (platform-aware default)
303 +let elapsed = try await engine.loadDefaultModel(
304 + systemPrompt: "You are a helpful assistant.",
305 + sampling: nil
306 +)
307 +
308 +// Multi-turn chat
309 +let result = try await engine.sendMessage(message: "Hello!")
310 +print(result.text)
311 +
312 +// Streaming (free function — callback_interface constraint in UniFFI 0.31)
313 +class Handler: StreamChunkListener {
314 + func onChunk(chunk: StreamChunk) -> Bool {
315 + print(chunk.delta, terminator: "")
316 + return !chunk.done
317 + }
318 +}
319 +try await streamChatMessage(engine: engine, message: "Tell me a story.", listener: Handler())
320 +
321 +// One-shot (does NOT modify conversation history)
322 +let enhanced = try await engine.generate(
323 + messages: [userMessage(content: "Expand: a cat in space")],
324 + sampling: deterministicSamplingConfig()
325 +)
326 +
327 +// Status
328 +let info = await engine.info() // EngineInfo
329 +
330 +// History management
331 +let history = await engine.history()
332 +let removed = await engine.clearHistory()
333 +await engine.pushHistory(message: userMessage(content: "..."))
334 +
335 +// Cleanup
336 +await engine.unloadModel()
337 +```
338 +
339 +### Free Functions
340 +
341 +| Function | Returns | Notes |
342 +|----------|---------|-------|
343 +| `defaultModelConfig()` | `GgufModelConfig` | Platform-aware (1.5B on iOS/tvOS, 3B on macOS) |
344 +| `qwen251_5bConfig()` | `GgufModelConfig` | Forces 1.5B regardless of platform |
345 +| `qwen253bConfig()` | `GgufModelConfig` | Forces 3B regardless of platform |
346 +| `defaultSamplingConfig()` | `SamplingConfig` | temp=0.7, top_p=0.95, max_tokens=512 |
347 +| `deterministicSamplingConfig()` | `SamplingConfig` | temp=0.0, greedy |
348 +| `mobileSamplingConfig()` | `SamplingConfig` | temp=0.7, max_tokens=128 |
349 +| `systemMessage(content:)` | `ChatMessage` | `.system` role |
350 +| `userMessage(content:)` | `ChatMessage` | `.user` role |
351 +| `assistantMessage(content:)` | `ChatMessage` | `.assistant` role |
352 +| `streamChatMessage(engine:message:listener:)` | `async throws` | Streaming via callback |
353 +
354 +---
355 +
356 +## UniFFI Conventions
357 +
358 +- **Version pin:** `uniffi = "=0.31.0"` everywhere — the `onde` crate, `uniffi-bindgen/`, and `[build-dependencies]` must all use the **same** version. Mixing versions causes bindgen panics.
359 +- **`uniffi::setup_scaffolding!()`** in `lib.rs` — proc-macro approach, no UDL file needed.
360 +- **Object methods** use `#[uniffi::export]` on the `impl OndeChatEngine` block.
361 +- **Callback interfaces** (`StreamChunkListener`) must be parameters of **free functions** only, not Object methods.
362 +- **`Arc<Self>`** is the return type for `#[uniffi::constructor]`. UniFFI automatically handles this.
363 +- **Async:** all async exported methods use `tokio` runtime (enabled via `uniffi = { features = ["tokio"] }`).
364 +- **`uniffi.toml`**: lives at the crate root. Adjust renaming or namespace settings there before regenerating.
365 +
366 +---
367 +
368 +## HuggingFace Cache (`hf_cache.rs`)
369 +
370 +The `hf_cache` module manages the on-device model cache, exposed via UniFFI to Swift / Kotlin as-needed.
371 +
372 +Key public functions:
373 +
374 +| Function | Description |
375 +|----------|-------------|
376 +| `list_local_hf_models()` | List downloaded models that Onde supports |
377 +| `list_supported_hf_models()` | All supported models with download status |
378 +| `download_model(model_id, progress_callback)` | Download a model with progress reporting |
379 +| `delete_local_hf_model(model_id)` | Remove a model from the local cache |
380 +| `diagnose_hf_cache()` | Inspect the cache for corruption |
381 +| `repair_hf_cache_symlinks()` | Fix broken symlinks in the HF cache layout |
382 +| `model_cache_path(model_id)` | Resolve the filesystem path for a model |
383 +| `clean_stale_lock_files()` | Remove leftover `.lock` files from interrupted downloads |
384 +
385 +`ModelDownloadProgress` carries `downloaded_bytes`, `total_bytes`, `progress` (0.0–1.0), and `done`.
386 +
387 +---
388 +
389 +## Sampling Presets
390 +
391 +| Preset | `temperature` | `top_p` | `max_tokens` | Use Case |
392 +|--------|--------------|---------|--------------|----------|
393 +| `SamplingConfig::default()` | 0.7 | 0.95 | 512 | General creative chat |
394 +| `SamplingConfig::deterministic()` | 0.0 | — | 512 | Reproducible / coding |
395 +| `SamplingConfig::mobile()` | 0.7 | 0.95 | 128 | Memory/latency constrained |
396 +| `SamplingConfig::coding()` | 0.0 | — | 512 | Code generation |
397 +| `SamplingConfig::coding_mobile()` | 0.0 | — | 128 | Code on mobile |
398 +
399 +---
400 +
401 +## Testing
402 +
403 +```bash
404 +# All tests (requires a macOS host with Metal)
405 +cargo test
406 +
407 +# Unit tests only (no model downloads)
408 +cargo test inference::
409 +cargo test inference::ffi::
410 +cargo test inference::types::
411 +
412 +# hf_cache module
413 +cargo test hf_cache::
414 +
415 +# Clippy (treat warnings as errors)
416 +cargo clippy -- -D warnings
417 +```
418 +
419 +Tests that require model downloads are integration tests and not run by default in CI. Unit tests verify:
420 +- Type constructors and `Display` implementations
421 +- `SamplingConfig` presets
422 +- `GgufModelConfig` constructors and `platform_default()` routing
423 +- `OndeChatEngine` lifecycle: `new()` starts unloaded, `send_message` without model returns `InferenceError::NoModelLoaded`, `clear_history` on empty returns 0, `unload_model` on empty returns `nil`.
424 +
425 +---
426 +
427 +## Known Issues
428 +
429 +### `___chkstk_darwin` linker error on tvOS
430 +
431 +`aws-lc-sys` (transitive via `reqwest → rustls → aws-lc-rs`) references `___chkstk_darwin`, a stack probing symbol that tvOS does not export. `build.rs` compiles `tvos_chkstk.s` (a no-op `ret` stub) via the `cc` crate to satisfy the linker. **Do not delete `tvos_chkstk.s`.**
432 +
433 +Affects: tvOS only. macOS, iOS, Android, Windows, Linux are unaffected.
434 +
435 +### Metal Toolchain missing (Xcode 26+)
436 +
437 +On Xcode 26+, the Metal compiler is a separate download. If you see empty `.metallib` files (92 bytes) or `"Error while loading function: fused_glu_float"` at runtime:
438 +
439 +```bash
440 +xcodebuild -downloadComponent MetalToolchain
441 +cargo clean -p mistralrs-quant
442 +cargo check
443 +```
444 +
445 +### Android `home_dir` sandbox panic
446 +
447 +`dirs::home_dir()` panics in the Android sandbox. The crate-level `Cargo.toml` adds `hf-hub` as an explicit Android dependency so `HF_HOME` can be seeded via `ApiBuilder` before any hub request. Never call `home::home_dir()` or `dirs::home_dir()` on Android.
448 +
449 +---
450 +
451 +## Code Conventions
452 +
453 +### Rust
454 +
455 +- **Error handling:** `anyhow` for application-level errors inside `engine.rs`; `thiserror` for the `InferenceError` enum (exported via UniFFI). Never `.unwrap()` or `.expect()` in non-test code.
456 +- **Async:** `tokio` runtime. All async functions in `OndeChatEngine` are `pub async fn`.
457 +- **Logging:** `log` crate macros (`log::debug!`, `log::info!`, `log::warn!`, `log::error!`). No `println!` in library code.
458 +- **Platform gating:** use `#[cfg(target_os = "...")]` blocks. Match `Cargo.toml`'s target-conditional dependency sections.
459 +- **Re-exports:** `mistralrs`, `hf_hub`, and `mistralrs_core` are re-exported from `lib.rs` for downstream Rust consumers. Keep these re-exports in sync with what's actually available per platform.
460 +- **No `mod.rs`:** use named files (`inference/engine.rs`) not `inference/mod.rs` — except that `inference/mod.rs` exists and is the intentional module root for the `inference` module.
461 +
462 +### Swift / SDK
463 +
464 +- **Never manually edit `onde-swift/Sources/Onde/onde.swift`** — it is generated by `uniffi-bindgen`. Regenerate by running the build script.
465 +- **`Package.swift` binary target path** must point to the correct `.xcframework` location: local path during development, remote URL+checksum for published releases.
466 +- **iOS/tvOS sandbox:** always call `setupInferenceEnvironment()` at app launch before any `OndeChatEngine` call.
467 +
468 +---
469 +
470 +## CI / Release Workflow
471 +
472 +### `build-swift-xcframework.yml`
473 +
474 +Triggered on `workflow_dispatch` or on published GitHub Releases. Runs on `macos-15`.
475 +
476 +Steps:
477 +1. Install stable Rust (`1.92.0`) with iOS/macOS targets.
478 +2. Install nightly Rust with `rust-src` component and tvOS targets.
479 +3. Run `build-swift-xcframework.sh`.
480 +4. Upload artifacts: zip, checksum, version, generated `onde.swift`.
481 +5. On release: attach zip and checksum to the GitHub Release.
482 +
483 +### Release Process (end-to-end)
484 +
485 +1. Bump version in `onde/Cargo.toml`.
486 +2. Tag and push `onde` repo → CI builds and attaches `OndeFramework.xcframework.zip`.
487 +3. In `onde-swift`, update `Package.swift` with new `url:` and `checksum:`.
488 +4. Commit, tag, push `onde-swift` → Swift Package Index indexes it automatically.
489 +5. `cargo publish` to crates.io.
490 +
491 +---
492 +
493 +## Distribution Registry Summary
494 +
495 +| Registry | Name | Import |
496 +|----------|------|--------|
497 +| crates.io | `onde` | `onde = "0.x"` |
498 +| Swift Package Index | `onde-swift` (org: `ondeinference`) | `import Onde` |
499 +| PyPI | `onde-inference` | `import onde_inference` |
500 +| RubyGems | `onde-inference` | `require 'onde'` |
501 +| Maven Central | `com.ondeinference:onde-inference` | Gradle `implementation(...)` |
502 +
503 +---
504 +
505 +*This file is for AI agent and human developer reference. Update it when the architecture, API surface, supported models, or toolchain requirements change.*
506 +```
.agents/skills/humanizer/SKILL.md renamed
.agents/skills/sdk-swift-package/SKILL.md renamed
.agents/skills/uniffi-rs/SKILL.md renamed
AGENTS.md new
+1
@@ -0,0 +1 @@
1 +.agents/AGENTS.md
\ No newline at end of file
CLAUDE.md new
+1
@@ -0,0 +1 @@
1 +.agents/AGENTS.md
\ No newline at end of file