main
dart 118 lines 4.25 KB
Raw
1 import 'package:dio/dio.dart';
2 import 'package:flutter_riverpod/flutter_riverpod.dart';
3 import 'package:localmind/core/models/enums.dart';
4 import 'package:localmind/core/providers/app_providers.dart';
5 import 'package:localmind/core/providers/service_providers.dart';
6 import 'package:localmind/features/chat/utils/image_upload_utils.dart';
7 import 'package:localmind/features/servers/data/models/server.dart';
8 import 'package:localmind/features/servers/providers/server_providers.dart';
9 import 'package:localmind/features/on_device/providers/on_device_providers.dart';
10 import 'package:localmind/features/on_device/data/on_device_chat_service.dart';
11 import 'package:localmind/features/on_device/data/on_device_gemma_service.dart';
12 import 'package:localmind/features/on_device/data/on_device_llama_service.dart';
13 import 'package:localmind/features/on_device/data/models/on_device_model.dart';
14 import 'package:localmind/features/on_device/data/on_device_mlx_service.dart';
15 import '../data/chat_service.dart';
16 import '../data/smart_reply_service.dart';
17 import '../data/title_generation_service.dart';
18
19 final chatServiceProvider = Provider<ChatService?>((ref) {
20 final server = ref.watch(activeServerProvider);
21 if (server == null) {
22 return null;
23 }
24 final loadedRuntime = ref.watch(
25 onDeviceEngineProvider.select((s) => s.loadedRuntime),
26 );
27 final settings = ref.watch(
28 settingsProvider.select(
29 (s) => (s.imageCompressionEnabled, s.imageCompressionLevel),
30 ),
31 );
32 final service = createChatServiceForServer(
33 server: server,
34 dio: ref.read(dioProvider),
35 onDeviceGemmaService: ref.read(onDeviceGemmaServiceProvider),
36 onDeviceLlamaService: ref.read(onDeviceLlamaServiceProvider),
37 onDeviceMlxService: ref.read(onDeviceMlxServiceProvider),
38 loadedOnDeviceRuntime: loadedRuntime,
39 imageCompressionEnabled: settings.$1,
40 imageCompressionLevel: settings.$2,
41 );
42
43 ref.onDispose(() {
44 final s = service;
45 if (s is OnDeviceChatService) {
46 s.dispose();
47 }
48 });
49
50 return service;
51 });
52
53 /// Builds the service that owns one reply. Remote services keep a single
54 /// cancel token, so every concurrent reply gets its own instance; on-device
55 /// replies share the one engine-backed [chatServiceProvider] service.
56 final chatServiceFactoryProvider = Provider<ChatService? Function(Server)>((
57 ref,
58 ) {
59 return (server) {
60 if (server.isOnDevice) return ref.read(chatServiceProvider);
61 final settings = ref.read(settingsProvider);
62 return createChatServiceForServer(
63 server: server,
64 dio: ref.read(dioProvider),
65 onDeviceGemmaService: ref.read(onDeviceGemmaServiceProvider),
66 onDeviceLlamaService: ref.read(onDeviceLlamaServiceProvider),
67 onDeviceMlxService: ref.read(onDeviceMlxServiceProvider),
68 imageCompressionEnabled: settings.imageCompressionEnabled,
69 imageCompressionLevel: settings.imageCompressionLevel,
70 );
71 };
72 });
73
74 ChatService createChatServiceForServer({
75 required Server server,
76 required Dio dio,
77 required OnDeviceGemmaService onDeviceGemmaService,
78 required OnDeviceLlamaService onDeviceLlamaService,
79 OnDeviceMlxService? onDeviceMlxService,
80 OnDeviceModelRuntime? loadedOnDeviceRuntime,
81 bool imageCompressionEnabled = true,
82 ImageCompressionLevel imageCompressionLevel = ImageCompressionLevel.medium,
83 }) {
84 if (server.isOnDevice) {
85 if (loadedOnDeviceRuntime == OnDeviceModelRuntime.llamaCpp) {
86 return OnDeviceLlamaChatService(onDeviceLlamaService);
87 }
88 if (loadedOnDeviceRuntime == OnDeviceModelRuntime.mlx &&
89 onDeviceMlxService != null) {
90 return OnDeviceMlxChatService(onDeviceMlxService);
91 }
92
93 return ChatService.forServer(
94 ServerType.onDevice,
95 dio,
96 onDeviceGemma: onDeviceGemmaService,
97 imageCompressionEnabled: imageCompressionEnabled,
98 imageCompressionLevel: imageCompressionLevel,
99 );
100 }
101
102 return ChatService.forServer(
103 server.type,
104 dio,
105 imageCompressionEnabled: imageCompressionEnabled,
106 imageCompressionLevel: imageCompressionLevel,
107 );
108 }
109
110 final smartReplyServiceProvider = Provider<SmartReplyService>((ref) {
111 final service = SmartReplyService();
112 ref.onDispose(() => service.dispose());
113 return service;
114 });
115
116 final titleGenerationServiceProvider = Provider<TitleGenerationService>((ref) {
117 return TitleGenerationService();
118 });