| 1 | # Siti AI — visionOS app |
| 2 | |
| 3 | A native SwiftUI chat app for visionOS, built on [Ed Swift](https://github.com/ondeinference/ed-swift), the agent SDK over the same on-device Onde inference engine that powers the Tauri app at the repo root (through `ed-agent`). This is a separate native client, not a Tauri build target; Tauri does not support visionOS. |
| 4 | |
| 5 | Project layout and build-target conventions (window sizing, `xros`/`xrsimulator` platform settings, App Group entitlements, solid-image-stack app icon) follow the `SplitFireAIVision` target in the sibling `splitfire` repo (`../splitfire/frontend/xcode/splitfire/SplitFireAI.xcodeproj`). The chat engine wiring (streaming bridge, model lifecycle, conversation persistence) uses Ed's `stream`, `load(gguf:)` and `restoreHistory`. |
| 6 | |
| 7 | ## Setup |
| 8 | |
| 9 | Install [XcodeGen](https://github.com/yonaskolb/XcodeGen) if you don't have it: |
| 10 | |
| 11 | ``` |
| 12 | brew install xcodegen |
| 13 | ``` |
| 14 | |
| 15 | Generate the Xcode project and open it: |
| 16 | |
| 17 | ``` |
| 18 | cd xcode |
| 19 | xcodegen generate |
| 20 | open SitiVision.xcodeproj |
| 21 | ``` |
| 22 | |
| 23 | Set your development team under Signing & Capabilities (defaults to `2TQF86ZACD`, the same team as the Tauri app's iOS build — see `../src-tauri/tauri.conf.json`), pick a Vision Pro simulator or device, hit Run. |
| 24 | |
| 25 | ### Developing against a local Ed Swift checkout |
| 26 | |
| 27 | `project.yml` pulls `Ed` from the published [ed-swift](https://github.com/ondeinference/ed-swift) releases (`from: 1.1.0`), the same way `src-tauri/Cargo.toml` pins `ed-agent` from crates.io. To try an unreleased change, point the package at a sibling `onde-ed-swift` checkout instead: |
| 28 | |
| 29 | ```yaml |
| 30 | packages: |
| 31 | Ed: |
| 32 | path: ../../onde-ed-swift |
| 33 | ``` |
| 34 | |
| 35 | Then build the visionOS slice in that checkout with `make visionos`. It needs the sibling `onde-ed` Rust workspace (`ED_REPO` defaults to `../onde-ed`) and a nightly Rust toolchain with `-Z build-std`, since visionOS is a tier-3 target. |
| 36 | |
| 37 | ## What it does |
| 38 | |
| 39 | On first launch, it downloads and loads the platform-default model (Qwen 2.5 3B on visionOS, same tier as macOS) and streams replies token-by-token. Conversations persist locally between launches. The model cache lives in the shared `group.com.ondeinference.apps` App Group, so a model already downloaded by the Tauri macOS/iOS build of Siti is reused here instead of being fetched twice. |
| 40 | |
| 41 | Current MVP feature set: |
| 42 | |
| 43 | - token-by-token streaming with a stop button |
| 44 | - local conversation persistence between launches |
| 45 | - model settings sheet for system prompt + sampling preset |
| 46 | - toolbar actions to reload the model or clear the conversation |
| 47 | - prompt suggestions when the conversation is empty |
| 48 | |
| 49 | Not yet done: a real visionOS-native layout (ornaments, volumes, spatial windows) — this MVP is a single glass `WindowGroup`, functionally equivalent to the iOS/macOS chat view. Immersive-space exploration is a good next step once the 2D chat flow is verified end-to-end. |
| 50 | |
| 51 | ## Files |
| 52 | |
| 53 | | File | What's in it | |
| 54 | |------|---------------| |
| 55 | | `SitiVisionApp.swift` | Entry point. Calls `setupInferenceEnvironment()` at launch, sizes the default window. | |
| 56 | | `ContentView.swift` | The chat UI: header, empty state, message list, input bar, reload/clear actions, settings sheet. | |
| 57 | | `ChatViewModel.swift` | Bridges UniFFI streaming into Swift concurrency, manages model lifecycle, restores history, persists conversation. | |
| 58 | | `SitiDefaults.swift` | Siti's system prompt (mirrors `../src-tauri/src/chat/mod.rs`'s `CHAT_SYSTEM_PROMPT`), sampling presets, prompt suggestions. | |
| 59 | | `InferenceEnvironment.swift` | Points `HF_HOME`/`TMPDIR` at the shared App Group container so downloads land somewhere visionOS allows and are shared with the Tauri build. | |
| 60 | | `SitiVision.entitlements` | Generated by `xcodegen generate` from `project.yml`'s `entitlements.properties` — do not hand-edit, it's overwritten on every generate. | |
| 61 | | `project.yml` | XcodeGen spec. `SitiVision.xcodeproj` is generated from this, not checked in. | |