Skip to content

Commit 7df8df2

Browse files
author
sylar
committed
feat: support to build for windows and add proxy when do http request or download modals
1 parent 094d569 commit 7df8df2

125 files changed

Lines changed: 9582 additions & 786 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ iOSInjectionProject/
7878
# macOS
7979
.DS_Store
8080

81+
# Git worktrees
82+
.worktrees/
83+
8184
# IDE - IntelliJ IDEA / Android Studio
8285
# User-specific stuff
8386
.idea/workspace.xml

docs/building.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,43 @@ cd sdk/runanywhere-kotlin && ./scripts/build-kotlin.sh --setup
142142
```bash
143143
./gradlew cleanAll && ./gradlew buildAll
144144
```
145+
146+
## Windows Voice Validation
147+
148+
For Windows ONNX voice support, build the native backends first, then build the
149+
Flutter Windows example.
150+
151+
```powershell
152+
cd sdk\runanywhere-commons
153+
cmd /c scripts\build-windows.bat all --clean
154+
155+
cd ..\..\examples\flutter\RunAnywhereAI
156+
New-Item -ItemType Directory -Force -Path build\native_assets\windows | Out-Null
157+
fvm flutter build windows
158+
```
159+
160+
Notes:
161+
162+
- `scripts\build-windows.bat onnx` and `scripts\build-windows.bat all` will
163+
automatically download Sherpa-ONNX Windows prebuilts into
164+
`sdk\runanywhere-commons\third_party\sherpa-onnx-windows`.
165+
- The final Windows runner directory should contain:
166+
- `rac_backend_onnx.dll`
167+
- `onnxruntime.dll`
168+
- `onnxruntime_providers_shared.dll`
169+
- `sherpa-onnx-c-api.dll`
170+
- companion runtime DLLs staged by the ONNX plugin
171+
172+
## Flutter Example Windows Vision
173+
174+
The Flutter example Vision page now supports Windows through the `camera_windows`
175+
plugin while continuing to use the standard `camera` API in the app code.
176+
177+
Notes:
178+
179+
- The project pins `camera_windows` to a Dart 3.3-compatible version because the
180+
current repository workflow uses `fvm flutter` on the 3.19 line.
181+
- Live mode still uses repeated still captures, matching the current Flutter
182+
Vision implementation rather than raw frame streaming.
183+
- Mobile-specific camera controls such as torch, exposure point, and focus point
184+
may remain unavailable on Windows camera devices.

examples/flutter/RunAnywhereAI/.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ ios/Podfile.lock
5555
# Platforms not supported (mobile-only app)
5656
linux/
5757
macos/
58-
windows/
5958
web/
6059

6160
# Duplicate Kotlin DSL build files (using Groovy .gradle)

examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public static void registerWith(@NonNull FlutterEngine flutterEngine) {
103103
try {
104104
flutterEngine.getPlugins().add(new com.tekartik.sqflite.SqflitePlugin());
105105
} catch (Exception e) {
106-
Log.e(TAG, "Error registering plugin sqflite_android, com.tekartik.sqflite.SqflitePlugin", e);
106+
Log.e(TAG, "Error registering plugin sqflite, com.tekartik.sqflite.SqflitePlugin", e);
107107
}
108108
try {
109109
flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin());

examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,10 @@
6666
@import permission_handler_apple;
6767
#endif
6868

69-
#if __has_include(<record_ios/RecordIosPlugin.h>)
70-
#import <record_ios/RecordIosPlugin.h>
69+
#if __has_include(<record_darwin/RecordPlugin.h>)
70+
#import <record_darwin/RecordPlugin.h>
7171
#else
72-
@import record_ios;
72+
@import record_darwin;
7373
#endif
7474

7575
#if __has_include(<runanywhere/RunAnywherePlugin.h>)
@@ -102,10 +102,10 @@
102102
@import shared_preferences_foundation;
103103
#endif
104104

105-
#if __has_include(<sqflite_darwin/SqflitePlugin.h>)
106-
#import <sqflite_darwin/SqflitePlugin.h>
105+
#if __has_include(<sqflite/SqflitePlugin.h>)
106+
#import <sqflite/SqflitePlugin.h>
107107
#else
108-
@import sqflite_darwin;
108+
@import sqflite;
109109
#endif
110110

111111
#if __has_include(<url_launcher_ios/URLLauncherPlugin.h>)
@@ -127,7 +127,7 @@ + (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {
127127
[FPPPackageInfoPlusPlugin registerWithRegistrar:[registry registrarForPlugin:@"FPPPackageInfoPlusPlugin"]];
128128
[PathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"PathProviderPlugin"]];
129129
[PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]];
130-
[RecordIosPlugin registerWithRegistrar:[registry registrarForPlugin:@"RecordIosPlugin"]];
130+
[RecordPlugin registerWithRegistrar:[registry registrarForPlugin:@"RecordPlugin"]];
131131
[RunAnywherePlugin registerWithRegistrar:[registry registrarForPlugin:@"RunAnywherePlugin"]];
132132
[GeniePlugin registerWithRegistrar:[registry registrarForPlugin:@"GeniePlugin"]];
133133
[LlamaCppPlugin registerWithRegistrar:[registry registrarForPlugin:@"LlamaCppPlugin"]];

examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@ import 'dart:async';
22

33
import 'package:flutter/material.dart';
44
import 'package:provider/provider.dart';
5+
import 'package:runanywhere/core/types/npu_chip.dart';
6+
import 'package:runanywhere/public/extensions/runanywhere_device.dart';
57
import 'package:runanywhere/public/extensions/rag_module.dart';
68
import 'package:runanywhere/runanywhere.dart';
79
import 'package:runanywhere_ai/app/content_view.dart';
810
import 'package:runanywhere_ai/core/design_system/app_colors.dart';
911
import 'package:runanywhere_ai/core/design_system/app_spacing.dart';
12+
import 'package:runanywhere_ai/core/models/proxy_settings.dart';
13+
import 'package:runanywhere_ai/core/services/example_http_service.dart';
1014
import 'package:runanywhere_ai/core/services/model_manager.dart';
1115
import 'package:runanywhere_ai/core/utilities/constants.dart';
1216
import 'package:runanywhere_ai/core/utilities/keychain_helper.dart';
@@ -116,6 +120,13 @@ class _RunAnywhereAIAppState extends State<RunAnywhereAIApp> {
116120
'🔧 Environment: ${RunAnywhere.getCurrentEnvironment()?.description ?? "Unknown"}');
117121
debugPrint('📱 Services will initialize on first API call');
118122

123+
RunAnywhere.configureDownloadHttpClientFactory((uri) async {
124+
return ExampleHttpService.shared.createScopedHttpPackageClient(
125+
ProxyScope.download,
126+
uri,
127+
);
128+
});
129+
119130
// Refresh model manager state (runs model discovery)
120131
await ModelManager.shared.refresh();
121132

@@ -213,13 +224,37 @@ class _RunAnywhereAIAppState extends State<RunAnywhereAIApp> {
213224
// Models with per-chip availability
214225
const genieModels = [
215226
// Qwen3 4B — Gen 5 only
216-
(slug: 'qwen3-4b', name: 'Qwen3 4B', mem: 2800000000, quant: 'w4a16', chips: {NPUChip.snapdragon8EliteGen5}),
227+
(
228+
slug: 'qwen3-4b',
229+
name: 'Qwen3 4B',
230+
mem: 2800000000,
231+
quant: 'w4a16',
232+
chips: {NPUChip.snapdragon8EliteGen5}
233+
),
217234
// Llama 3.2 1B Instruct — both chips
218-
(slug: 'llama3.2-1b-instruct', name: 'Llama 3.2 1B Instruct', mem: 1200000000, quant: 'w4a16', chips: {NPUChip.snapdragon8Elite, NPUChip.snapdragon8EliteGen5}),
235+
(
236+
slug: 'llama3.2-1b-instruct',
237+
name: 'Llama 3.2 1B Instruct',
238+
mem: 1200000000,
239+
quant: 'w4a16',
240+
chips: {NPUChip.snapdragon8Elite, NPUChip.snapdragon8EliteGen5}
241+
),
219242
// SEA-LION v3.5 8B Instruct — both chips
220-
(slug: 'sea-lion3.5-8b-instruct', name: 'SEA-LION v3.5 8B Instruct', mem: 4800000000, quant: 'w4a16', chips: {NPUChip.snapdragon8Elite, NPUChip.snapdragon8EliteGen5}),
243+
(
244+
slug: 'sea-lion3.5-8b-instruct',
245+
name: 'SEA-LION v3.5 8B Instruct',
246+
mem: 4800000000,
247+
quant: 'w4a16',
248+
chips: {NPUChip.snapdragon8Elite, NPUChip.snapdragon8EliteGen5}
249+
),
221250
// Qwen 2.5 7B Instruct — 8elite only, w8a16 quant
222-
(slug: 'qwen2.5-7b-instruct', name: 'Qwen 2.5 7B Instruct', mem: 4200000000, quant: 'w8a16', chips: {NPUChip.snapdragon8Elite}),
251+
(
252+
slug: 'qwen2.5-7b-instruct',
253+
name: 'Qwen 2.5 7B Instruct',
254+
mem: 4200000000,
255+
quant: 'w8a16',
256+
chips: {NPUChip.snapdragon8Elite}
257+
),
223258
];
224259
for (final m in genieModels) {
225260
if (m.chips.contains(chip)) {
@@ -261,7 +296,8 @@ class _RunAnywhereAIAppState extends State<RunAnywhereAIApp> {
261296
RunAnywhere.registerModel(
262297
id: 'sherpa-onnx-whisper-tiny.en',
263298
name: 'Sherpa Whisper Tiny (ONNX)',
264-
url: Uri.parse('https://github.com/RunanywhereAI/sherpa-onnx/releases/download/runanywhere-models-v1/sherpa-onnx-whisper-tiny.en.tar.gz'),
299+
url: Uri.parse(
300+
'https://github.com/RunanywhereAI/sherpa-onnx/releases/download/runanywhere-models-v1/sherpa-onnx-whisper-tiny.en.tar.gz'),
265301
framework: InferenceFramework.onnx,
266302
modality: ModelCategory.speechRecognition,
267303
memoryRequirement: 75000000,
@@ -270,7 +306,8 @@ class _RunAnywhereAIAppState extends State<RunAnywhereAIApp> {
270306
RunAnywhere.registerModel(
271307
id: 'sherpa-onnx-whisper-small.en',
272308
name: 'Sherpa Whisper Small (ONNX)',
273-
url: Uri.parse('https://github.com/RunanywhereAI/sherpa-onnx/releases/download/runanywhere-models-v1/sherpa-onnx-whisper-small.en.tar.gz'),
309+
url: Uri.parse(
310+
'https://github.com/RunanywhereAI/sherpa-onnx/releases/download/runanywhere-models-v1/sherpa-onnx-whisper-small.en.tar.gz'),
274311
framework: InferenceFramework.onnx,
275312
modality: ModelCategory.speechRecognition,
276313
memoryRequirement: 250000000,
@@ -280,7 +317,8 @@ class _RunAnywhereAIAppState extends State<RunAnywhereAIApp> {
280317
RunAnywhere.registerModel(
281318
id: 'vits-piper-en_US-lessac-medium',
282319
name: 'Piper TTS (US English - Medium)',
283-
url: Uri.parse('https://github.com/RunanywhereAI/sherpa-onnx/releases/download/runanywhere-models-v1/vits-piper-en_US-lessac-medium.tar.gz'),
320+
url: Uri.parse(
321+
'https://github.com/RunanywhereAI/sherpa-onnx/releases/download/runanywhere-models-v1/vits-piper-en_US-lessac-medium.tar.gz'),
284322
framework: InferenceFramework.onnx,
285323
modality: ModelCategory.speechSynthesis,
286324
memoryRequirement: 65000000,
@@ -289,7 +327,8 @@ class _RunAnywhereAIAppState extends State<RunAnywhereAIApp> {
289327
RunAnywhere.registerModel(
290328
id: 'vits-piper-en_GB-alba-medium',
291329
name: 'Piper TTS (British English)',
292-
url: Uri.parse('https://github.com/RunanywhereAI/sherpa-onnx/releases/download/runanywhere-models-v1/vits-piper-en_GB-alba-medium.tar.gz'),
330+
url: Uri.parse(
331+
'https://github.com/RunanywhereAI/sherpa-onnx/releases/download/runanywhere-models-v1/vits-piper-en_GB-alba-medium.tar.gz'),
293332
framework: InferenceFramework.onnx,
294333
modality: ModelCategory.speechSynthesis,
295334
memoryRequirement: 65000000,
@@ -325,17 +364,17 @@ class _RunAnywhereAIAppState extends State<RunAnywhereAIApp> {
325364
// --- ONNX BACKEND (required for embeddings used by RAG) ---
326365
try {
327366
await Onnx.register();
328-
debugPrint('✅ ONNX backend registered (STT + TTS + VAD + Embeddings)');
367+
debugPrint('✅ ONNX backend registered (Windows optional)');
329368
} catch (e) {
330-
debugPrint('⚠️ ONNX backend not available: $e');
369+
debugPrint('⚠️ ONNX backend not available on this Windows build: $e');
331370
}
332371

333372
// --- RAG BACKEND ---
334373
try {
335374
await RAGModule.register();
336375
debugPrint('✅ RAG backend registered');
337376
} catch (e) {
338-
debugPrint('⚠️ RAG backend not available (RAG features disabled): $e');
377+
debugPrint('⚠️ RAG backend not available on this Windows build: $e');
339378
}
340379

341380
debugPrint('🎉 All modules and models registered');

examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import 'package:flutter/material.dart';
22

3+
extension ColorCompatibility on Color {
4+
Color withValues({double? alpha}) {
5+
if (alpha != null) {
6+
return withOpacity(alpha.clamp(0.0, 1.0));
7+
}
8+
return this;
9+
}
10+
}
11+
312
/// App Colors (mirroring iOS AppColors.swift)
413
class AppColors {
514
// MARK: - Semantic Colors
@@ -26,7 +35,7 @@ class AppColors {
2635
static Color backgroundTertiary(BuildContext context) =>
2736
Theme.of(context).colorScheme.surface;
2837
static Color backgroundGrouped(BuildContext context) =>
29-
Theme.of(context).colorScheme.surfaceContainerHighest;
38+
Theme.of(context).colorScheme.surfaceVariant;
3039
static Color backgroundGray5(BuildContext context) =>
3140
Theme.of(context).brightness == Brightness.dark
3241
? Colors.grey.shade800
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:runanywhere_ai/core/design_system/app_spacing.dart';
3+
4+
class UnsupportedFeatureView extends StatelessWidget {
5+
final String title;
6+
final String message;
7+
8+
const UnsupportedFeatureView({
9+
super.key,
10+
required this.title,
11+
required this.message,
12+
});
13+
14+
@override
15+
Widget build(BuildContext context) {
16+
return Center(
17+
child: Padding(
18+
padding: const EdgeInsets.all(AppSpacing.large),
19+
child: Column(
20+
mainAxisSize: MainAxisSize.min,
21+
children: [
22+
const Icon(Icons.info_outline, size: 40),
23+
const SizedBox(height: AppSpacing.mediumLarge),
24+
Text(title, style: Theme.of(context).textTheme.titleLarge),
25+
const SizedBox(height: AppSpacing.smallMedium),
26+
Text(message, textAlign: TextAlign.center),
27+
],
28+
),
29+
),
30+
);
31+
}
32+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
enum ProxyScope {
2+
general('Network Proxy'),
3+
download('Download Proxy');
4+
5+
final String displayName;
6+
7+
const ProxyScope(this.displayName);
8+
}
9+
10+
enum ProxyScheme {
11+
http('HTTP'),
12+
https('HTTPS'),
13+
socks5('SOCKS5');
14+
15+
final String displayName;
16+
17+
const ProxyScheme(this.displayName);
18+
19+
String get wireValue => name;
20+
21+
static ProxyScheme fromWireValue(String? value) {
22+
return ProxyScheme.values.firstWhere(
23+
(scheme) => scheme.wireValue == value,
24+
orElse: () => ProxyScheme.http,
25+
);
26+
}
27+
}
28+
29+
class ProxySettings {
30+
final bool enabled;
31+
final ProxyScheme scheme;
32+
final String host;
33+
final int? port;
34+
final String username;
35+
final String password;
36+
final bool bypassLocal;
37+
38+
const ProxySettings({
39+
this.enabled = false,
40+
this.scheme = ProxyScheme.http,
41+
this.host = '',
42+
this.port,
43+
this.username = '',
44+
this.password = '',
45+
this.bypassLocal = true,
46+
});
47+
48+
bool get hasCredentials => username.isNotEmpty || password.isNotEmpty;
49+
50+
bool get isComplete => !enabled || (host.trim().isNotEmpty && port != null);
51+
52+
ProxySettings copyWith({
53+
bool? enabled,
54+
ProxyScheme? scheme,
55+
String? host,
56+
int? port,
57+
String? username,
58+
String? password,
59+
bool? bypassLocal,
60+
}) {
61+
return ProxySettings(
62+
enabled: enabled ?? this.enabled,
63+
scheme: scheme ?? this.scheme,
64+
host: host ?? this.host,
65+
port: port ?? this.port,
66+
username: username ?? this.username,
67+
password: password ?? this.password,
68+
bypassLocal: bypassLocal ?? this.bypassLocal,
69+
);
70+
}
71+
}

0 commit comments

Comments
 (0)