feat(cache): Add lightweight in-memory cache with TTL and LRU eviction
This commit is contained in:
@@ -35,16 +35,27 @@ class BackendConfig {
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return <String, dynamic>{'enable_websocket': enableWebsocket};
|
||||
return <String, dynamic>{
|
||||
'enable_websocket': enableWebsocket,
|
||||
};
|
||||
}
|
||||
|
||||
static BackendConfig fromJson(Map<String, dynamic> json) {
|
||||
bool? enableWebsocket;
|
||||
final features = json['features'];
|
||||
if (features is Map<String, dynamic>) {
|
||||
final value = features['enable_websocket'];
|
||||
if (value is bool) {
|
||||
enableWebsocket = value;
|
||||
// Try canonical format first
|
||||
final value = json['enable_websocket'];
|
||||
if (value is bool) {
|
||||
enableWebsocket = value;
|
||||
}
|
||||
|
||||
// Fallback to nested format for backwards compatibility
|
||||
if (enableWebsocket == null) {
|
||||
final features = json['features'];
|
||||
if (features is Map<String, dynamic>) {
|
||||
final nestedValue = features['enable_websocket'];
|
||||
if (nestedValue is bool) {
|
||||
enableWebsocket = nestedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,18 @@ sealed class Model with _$Model {
|
||||
}) = _Model;
|
||||
|
||||
factory Model.fromJson(Map<String, dynamic> json) {
|
||||
final cachedIsMultimodal = switch (json['isMultimodal']) {
|
||||
final bool value => value,
|
||||
_ => json['is_multimodal'] is bool ? json['is_multimodal'] as bool : null,
|
||||
};
|
||||
final cachedSupportsStreaming = switch (json['supportsStreaming']) {
|
||||
final bool value => value,
|
||||
_ =>
|
||||
json['supports_streaming'] is bool
|
||||
? json['supports_streaming'] as bool
|
||||
: null,
|
||||
};
|
||||
|
||||
// Handle different response formats from OpenWebUI
|
||||
|
||||
// Extract architecture info for capabilities
|
||||
@@ -29,8 +41,9 @@ sealed class Model with _$Model {
|
||||
|
||||
// Determine if multimodal based on architecture
|
||||
final isMultimodal =
|
||||
modality?.contains('image') == true ||
|
||||
inputModalities?.contains('image') == true;
|
||||
cachedIsMultimodal ??
|
||||
(modality?.contains('image') == true ||
|
||||
inputModalities?.contains('image') == true);
|
||||
|
||||
// Extract supported parameters robustly (top-level or nested under provider keys)
|
||||
List? supportedParams =
|
||||
@@ -63,7 +76,8 @@ sealed class Model with _$Model {
|
||||
}
|
||||
|
||||
// Determine streaming support from supported parameters if known
|
||||
final supportsStreaming = supportedParams?.contains('stream') ?? true;
|
||||
final supportsStreaming =
|
||||
cachedSupportsStreaming ?? supportedParams?.contains('stream') ?? true;
|
||||
|
||||
// Convert supported parameters to List<String> if present
|
||||
final supportedParamsList = supportedParams
|
||||
@@ -154,4 +168,22 @@ sealed class Model with _$Model {
|
||||
toolIds: toolIds,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final data = <String, dynamic>{
|
||||
'id': id,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'isMultimodal': isMultimodal,
|
||||
'supportsStreaming': supportsStreaming,
|
||||
'supportsRAG': supportsRAG,
|
||||
'supported_parameters': supportedParameters,
|
||||
'capabilities': capabilities,
|
||||
'metadata': metadata,
|
||||
'architecture': capabilities?['architecture'],
|
||||
'toolIds': toolIds,
|
||||
};
|
||||
data.removeWhere((_, value) => value == null);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
23
lib/core/models/socket_transport_availability.dart
Normal file
23
lib/core/models/socket_transport_availability.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
class SocketTransportAvailability {
|
||||
const SocketTransportAvailability({
|
||||
required this.allowPolling,
|
||||
required this.allowWebsocketOnly,
|
||||
});
|
||||
|
||||
final bool allowPolling;
|
||||
final bool allowWebsocketOnly;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'allowPolling': allowPolling,
|
||||
'allowWebsocketOnly': allowWebsocketOnly,
|
||||
};
|
||||
}
|
||||
|
||||
factory SocketTransportAvailability.fromJson(Map<String, dynamic> json) {
|
||||
return SocketTransportAvailability(
|
||||
allowPolling: json['allowPolling'] == true,
|
||||
allowWebsocketOnly: json['allowWebsocketOnly'] == true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -23,4 +23,14 @@ sealed class Tool with _$Tool {
|
||||
meta: json['meta'] as Map<String, dynamic>?,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'user_id': userId,
|
||||
'meta': meta,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,4 +30,16 @@ sealed class User with _$User {
|
||||
isActive: json['is_active'] as bool? ?? json['isActive'] as bool? ?? true,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'username': username,
|
||||
'email': email,
|
||||
'name': name,
|
||||
'profile_image_url': profileImage,
|
||||
'role': role,
|
||||
'is_active': isActive,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user