chore: initial release
This commit is contained in:
23
lib/core/models/chat_message.dart
Normal file
23
lib/core/models/chat_message.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'chat_message.freezed.dart';
|
||||
part 'chat_message.g.dart';
|
||||
|
||||
@freezed
|
||||
sealed class ChatMessage with _$ChatMessage {
|
||||
const factory ChatMessage({
|
||||
required String id,
|
||||
required String role, // 'user', 'assistant', 'system'
|
||||
required String content,
|
||||
required DateTime timestamp,
|
||||
String? model,
|
||||
@Default(false) bool isStreaming,
|
||||
List<String>? attachmentIds,
|
||||
Map<String, dynamic>? metadata,
|
||||
List<Map<String, dynamic>>? sources,
|
||||
Map<String, dynamic>? usage,
|
||||
}) = _ChatMessage;
|
||||
|
||||
factory ChatMessage.fromJson(Map<String, dynamic> json) =>
|
||||
_$ChatMessageFromJson(json);
|
||||
}
|
||||
27
lib/core/models/conversation.dart
Normal file
27
lib/core/models/conversation.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'chat_message.dart';
|
||||
|
||||
part 'conversation.freezed.dart';
|
||||
part 'conversation.g.dart';
|
||||
|
||||
@freezed
|
||||
sealed class Conversation with _$Conversation {
|
||||
const factory Conversation({
|
||||
required String id,
|
||||
required String title,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
String? model,
|
||||
String? systemPrompt,
|
||||
@Default([]) List<ChatMessage> messages,
|
||||
@Default({}) Map<String, dynamic> metadata,
|
||||
@Default(false) bool pinned,
|
||||
@Default(false) bool archived,
|
||||
String? shareId,
|
||||
String? folderId,
|
||||
@Default([]) List<String> tags,
|
||||
}) = _Conversation;
|
||||
|
||||
factory Conversation.fromJson(Map<String, dynamic> json) =>
|
||||
_$ConversationFromJson(json);
|
||||
}
|
||||
23
lib/core/models/file_info.dart
Normal file
23
lib/core/models/file_info.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'file_info.freezed.dart';
|
||||
part 'file_info.g.dart';
|
||||
|
||||
@freezed
|
||||
sealed class FileInfo with _$FileInfo {
|
||||
const factory FileInfo({
|
||||
required String id,
|
||||
required String filename,
|
||||
required String originalFilename,
|
||||
required int size,
|
||||
required String mimeType,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
String? userId,
|
||||
String? hash,
|
||||
Map<String, dynamic>? metadata,
|
||||
}) = _FileInfo;
|
||||
|
||||
factory FileInfo.fromJson(Map<String, dynamic> json) =>
|
||||
_$FileInfoFromJson(json);
|
||||
}
|
||||
41
lib/core/models/folder.dart
Normal file
41
lib/core/models/folder.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'folder.freezed.dart';
|
||||
part 'folder.g.dart';
|
||||
|
||||
// Timestamp converter for Unix timestamps
|
||||
class TimestampConverter implements JsonConverter<DateTime, dynamic> {
|
||||
const TimestampConverter();
|
||||
|
||||
@override
|
||||
DateTime fromJson(dynamic json) {
|
||||
if (json is String) {
|
||||
return DateTime.parse(json);
|
||||
} else if (json is int) {
|
||||
return DateTime.fromMillisecondsSinceEpoch(json * 1000);
|
||||
} else {
|
||||
throw ArgumentError('Invalid date format: $json');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
dynamic toJson(DateTime object) {
|
||||
return object.millisecondsSinceEpoch ~/ 1000;
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class Folder with _$Folder {
|
||||
const factory Folder({
|
||||
required String id,
|
||||
required String name,
|
||||
@TimestampConverter() required DateTime createdAt,
|
||||
@TimestampConverter() required DateTime updatedAt,
|
||||
String? parentId,
|
||||
@Default([]) List<String> conversationIds,
|
||||
@Default([]) List<Folder> subfolders,
|
||||
@Default({}) Map<String, dynamic> metadata,
|
||||
}) = _Folder;
|
||||
|
||||
factory Folder.fromJson(Map<String, dynamic> json) => _$FolderFromJson(json);
|
||||
}
|
||||
35
lib/core/models/knowledge_base.dart
Normal file
35
lib/core/models/knowledge_base.dart
Normal file
@@ -0,0 +1,35 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'knowledge_base.freezed.dart';
|
||||
part 'knowledge_base.g.dart';
|
||||
|
||||
@freezed
|
||||
sealed class KnowledgeBase with _$KnowledgeBase {
|
||||
const factory KnowledgeBase({
|
||||
required String id,
|
||||
required String name,
|
||||
String? description,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
@Default(0) int itemCount,
|
||||
@Default({}) Map<String, dynamic> metadata,
|
||||
}) = _KnowledgeBase;
|
||||
|
||||
factory KnowledgeBase.fromJson(Map<String, dynamic> json) =>
|
||||
_$KnowledgeBaseFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class KnowledgeBaseItem with _$KnowledgeBaseItem {
|
||||
const factory KnowledgeBaseItem({
|
||||
required String id,
|
||||
required String content,
|
||||
String? title,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
@Default({}) Map<String, dynamic> metadata,
|
||||
}) = _KnowledgeBaseItem;
|
||||
|
||||
factory KnowledgeBaseItem.fromJson(Map<String, dynamic> json) =>
|
||||
_$KnowledgeBaseItemFromJson(json);
|
||||
}
|
||||
93
lib/core/models/model.dart
Normal file
93
lib/core/models/model.dart
Normal file
@@ -0,0 +1,93 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'model.freezed.dart';
|
||||
|
||||
@freezed
|
||||
sealed class Model with _$Model {
|
||||
const Model._();
|
||||
|
||||
const factory Model({
|
||||
required String id,
|
||||
required String name,
|
||||
String? description,
|
||||
@Default(false) bool isMultimodal,
|
||||
@Default(false) bool supportsStreaming,
|
||||
@Default(false) bool supportsRAG,
|
||||
Map<String, dynamic>? capabilities,
|
||||
Map<String, dynamic>? metadata,
|
||||
List<String>? supportedParameters,
|
||||
}) = _Model;
|
||||
|
||||
factory Model.fromJson(Map<String, dynamic> json) {
|
||||
// Handle different response formats from OpenWebUI
|
||||
|
||||
// Extract architecture info for capabilities
|
||||
final architecture = json['architecture'] as Map<String, dynamic>?;
|
||||
final modality = architecture?['modality'] as String?;
|
||||
final inputModalities = architecture?['input_modalities'] as List?;
|
||||
|
||||
// Determine if multimodal based on architecture
|
||||
final isMultimodal =
|
||||
modality?.contains('image') == true ||
|
||||
inputModalities?.contains('image') == true;
|
||||
|
||||
// Extract supported parameters robustly (top-level or nested under provider keys)
|
||||
List? supportedParams =
|
||||
(json['supported_parameters'] as List?) ??
|
||||
(json['supportedParameters'] as List?);
|
||||
|
||||
if (supportedParams == null) {
|
||||
const providerKeys = [
|
||||
'openai',
|
||||
'anthropic',
|
||||
'google',
|
||||
'meta',
|
||||
'mistral',
|
||||
'cohere',
|
||||
'xai',
|
||||
'perplexity',
|
||||
'deepseek',
|
||||
'groq',
|
||||
];
|
||||
for (final key in providerKeys) {
|
||||
final provider = json[key] as Map<String, dynamic>?;
|
||||
final list =
|
||||
(provider?['supported_parameters'] as List?) ??
|
||||
(provider?['supportedParameters'] as List?);
|
||||
if (list != null) {
|
||||
supportedParams = list;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine streaming support from supported parameters if known
|
||||
final supportsStreaming = supportedParams?.contains('stream') ?? true;
|
||||
|
||||
// Convert supported parameters to List<String> if present
|
||||
final supportedParamsList = supportedParams
|
||||
?.map((e) => e.toString())
|
||||
.toList();
|
||||
|
||||
return Model(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String?,
|
||||
isMultimodal: isMultimodal,
|
||||
supportsStreaming: supportsStreaming,
|
||||
supportsRAG: json['supportsRAG'] as bool? ?? false,
|
||||
supportedParameters: supportedParamsList,
|
||||
capabilities: {
|
||||
'architecture': architecture,
|
||||
'pricing': json['pricing'],
|
||||
'context_length': json['context_length'],
|
||||
'supported_parameters': supportedParamsList ?? supportedParams,
|
||||
},
|
||||
metadata: {
|
||||
'canonical_slug': json['canonical_slug'],
|
||||
'created': json['created'],
|
||||
'connection_type': json['connection_type'],
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
19
lib/core/models/server_config.dart
Normal file
19
lib/core/models/server_config.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'server_config.freezed.dart';
|
||||
part 'server_config.g.dart';
|
||||
|
||||
@freezed
|
||||
sealed class ServerConfig with _$ServerConfig {
|
||||
const factory ServerConfig({
|
||||
required String id,
|
||||
required String name,
|
||||
required String url,
|
||||
String? apiKey,
|
||||
DateTime? lastConnected,
|
||||
@Default(false) bool isActive,
|
||||
}) = _ServerConfig;
|
||||
|
||||
factory ServerConfig.fromJson(Map<String, dynamic> json) =>
|
||||
_$ServerConfigFromJson(json);
|
||||
}
|
||||
33
lib/core/models/user.dart
Normal file
33
lib/core/models/user.dart
Normal file
@@ -0,0 +1,33 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'user.freezed.dart';
|
||||
|
||||
@freezed
|
||||
sealed class User with _$User {
|
||||
const User._();
|
||||
|
||||
const factory User({
|
||||
required String id,
|
||||
required String username,
|
||||
required String email,
|
||||
String? name,
|
||||
String? profileImage,
|
||||
required String role,
|
||||
@Default(true) bool isActive,
|
||||
}) = _User;
|
||||
|
||||
factory User.fromJson(Map<String, dynamic> json) {
|
||||
// Handle different field names from OpenWebUI API
|
||||
return User(
|
||||
id: json['id'] as String? ?? '',
|
||||
username: json['username'] as String? ?? json['name'] as String? ?? '',
|
||||
email: json['email'] as String? ?? '',
|
||||
name: json['name'] as String?,
|
||||
profileImage:
|
||||
json['profile_image_url'] as String? ??
|
||||
json['profileImage'] as String?,
|
||||
role: json['role'] as String? ?? 'user',
|
||||
isActive: json['is_active'] as bool? ?? json['isActive'] as bool? ?? true,
|
||||
);
|
||||
}
|
||||
}
|
||||
40
lib/core/models/user_settings.dart
Normal file
40
lib/core/models/user_settings.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'user_settings.freezed.dart';
|
||||
part 'user_settings.g.dart';
|
||||
|
||||
@freezed
|
||||
sealed class UserSettings with _$UserSettings {
|
||||
const factory UserSettings({
|
||||
// Chat preferences
|
||||
@Default(true) bool showReadReceipts,
|
||||
@Default(true) bool enableNotifications,
|
||||
@Default(false) bool enableSounds,
|
||||
@Default('auto') String theme, // 'light', 'dark', 'auto'
|
||||
// AI preferences
|
||||
@Default(0.7) double temperature,
|
||||
@Default(2048) int maxTokens,
|
||||
@Default(false) bool streamResponses,
|
||||
@Default(false) bool webSearchEnabled,
|
||||
|
||||
// Privacy settings
|
||||
@Default(true) bool saveConversations,
|
||||
@Default(false) bool shareUsageData,
|
||||
|
||||
// Interface preferences
|
||||
@Default('comfortable')
|
||||
String density, // 'compact', 'comfortable', 'spacious'
|
||||
@Default(14.0) double fontSize,
|
||||
@Default('en') String language,
|
||||
|
||||
// Accessibility settings
|
||||
@Default(false) bool reduceMotion,
|
||||
@Default(true) bool hapticFeedback,
|
||||
|
||||
// Advanced settings
|
||||
@Default({}) Map<String, dynamic> customSettings,
|
||||
}) = _UserSettings;
|
||||
|
||||
factory UserSettings.fromJson(Map<String, dynamic> json) =>
|
||||
_$UserSettingsFromJson(json);
|
||||
}
|
||||
Reference in New Issue
Block a user