Watch
1
0
Fork
You've already forked Seyyed_arc
0
forked from hesabix/arc
Seyyed_arc/hesabixUI/hesabix_ui/lib/models/ai_models.dart
2026-08-21 14:14:21 +00:00

723 lines
20 KiB
Dart
Executable file

import 'package:flutter/foundation.dart';
/// مدل‌های مربوط به سیستم AI
class AIConfig {
final int? id;
final String provider; // 'openai', 'anthropic', 'local'
final String modelName;
final String? apiBaseUrl;
final bool isActive;
/// اگر false: tools به provider نمی‌رود (gateway بدون tool calling)
final bool functionCallingEnabled;
final int maxTokens;
final double temperature;
final DateTime? createdAt;
final DateTime? updatedAt;
AIConfig({
this.id,
required this.provider,
required this.modelName,
this.apiBaseUrl,
this.isActive = false,
this.functionCallingEnabled = true,
this.maxTokens = 2000,
this.temperature = 0.7,
this.createdAt,
this.updatedAt,
});
factory AIConfig.fromJson(Map<String, dynamic> json) {
return AIConfig(
id: json['id'] as int?,
provider: json['provider'] as String,
modelName: json['model_name'] as String,
apiBaseUrl: json['api_base_url'] as String?,
isActive: json['is_active'] as bool? ?? false,
functionCallingEnabled: json['function_calling_enabled'] as bool? ?? true,
maxTokens: json['max_tokens'] as int? ?? 2000,
temperature: (json['temperature'] as num?)?.toDouble() ?? 0.7,
createdAt: json['created_at'] != null
? DateTime.parse(json['created_at'] as String)
: null,
updatedAt: json['updated_at'] != null
? DateTime.parse(json['updated_at'] as String)
: null,
);
}
Map<String, dynamic> toJson() {
return {
if (id != null) 'id': id,
'provider': provider,
'model_name': modelName,
if (apiBaseUrl != null) 'api_base_url': apiBaseUrl,
'is_active': isActive,
'function_calling_enabled': functionCallingEnabled,
'max_tokens': maxTokens,
'temperature': temperature,
};
}
}
enum AIPlanType {
free,
subscription,
payAsGo,
hybrid,
byok;
String get value {
switch (this) {
case AIPlanType.free:
return 'free';
case AIPlanType.subscription:
return 'subscription';
case AIPlanType.payAsGo:
return 'pay_as_go';
case AIPlanType.hybrid:
return 'hybrid';
case AIPlanType.byok:
return 'byok';
}
}
static AIPlanType fromString(String value) {
switch (value) {
case 'free':
return AIPlanType.free;
case 'subscription':
return AIPlanType.subscription;
case 'pay_as_go':
return AIPlanType.payAsGo;
case 'hybrid':
return AIPlanType.hybrid;
case 'byok':
return AIPlanType.byok;
default:
return AIPlanType.free;
}
}
}
class AIPlan {
final int? id;
final String code;
final String name;
final String? description;
final AIPlanType planType;
final Map<String, dynamic> pricingConfig;
final int? tokensLimit;
final int? monthlyTokensLimit;
final bool isActive;
final bool autoRenew;
final DateTime? createdAt;
final DateTime? updatedAt;
AIPlan({
this.id,
required this.code,
required this.name,
this.description,
required this.planType,
required this.pricingConfig,
this.tokensLimit,
this.monthlyTokensLimit,
this.isActive = true,
this.autoRenew = false,
this.createdAt,
this.updatedAt,
});
factory AIPlan.fromJson(Map<String, dynamic> json) {
return AIPlan(
id: json['id'] as int?,
code: json['code'] as String,
name: json['name'] as String,
description: json['description'] as String?,
planType: AIPlanType.fromString(json['plan_type'] as String),
pricingConfig: Map<String, dynamic>.from(
json['pricing_config'] as Map? ?? {},
),
tokensLimit: json['tokens_limit'] as int?,
monthlyTokensLimit: json['monthly_tokens_limit'] as int?,
isActive: json['is_active'] as bool? ?? true,
autoRenew: json['auto_renew'] as bool? ?? false,
createdAt: json['created_at'] != null
? DateTime.parse(json['created_at'] as String)
: null,
updatedAt: json['updated_at'] != null
? DateTime.parse(json['updated_at'] as String)
: null,
);
}
Map<String, dynamic> toJson() {
return {
if (id != null) 'id': id,
'code': code,
'name': name,
if (description != null) 'description': description,
'plan_type': planType.value,
'pricing_config': pricingConfig,
if (tokensLimit != null) 'tokens_limit': tokensLimit,
if (monthlyTokensLimit != null) 'monthly_tokens_limit': monthlyTokensLimit,
'is_active': isActive,
'auto_renew': autoRenew,
};
}
}
class UserAISubscription {
final int? id;
final int userId;
final int? businessId;
final int planId;
final AIPlan? plan;
final String? preferredModelCode;
final int tokensUsed;
final int? tokensLimit;
final bool isActive;
final DateTime? periodStart;
final DateTime? periodEnd;
final DateTime? expiresAt;
final DateTime? lastResetAt;
final DateTime? createdAt;
final DateTime? updatedAt;
UserAISubscription({
this.id,
required this.userId,
this.businessId,
required this.planId,
this.plan,
this.preferredModelCode,
this.tokensUsed = 0,
this.tokensLimit,
this.isActive = true,
this.periodStart,
this.periodEnd,
this.expiresAt,
this.lastResetAt,
this.createdAt,
this.updatedAt,
});
factory UserAISubscription.fromJson(Map<String, dynamic> json) {
return UserAISubscription(
id: json['id'] as int?,
userId: json['user_id'] as int,
businessId: json['business_id'] as int?,
planId: json['plan_id'] as int,
plan: json['plan'] != null
? AIPlan.fromJson(json['plan'] as Map<String, dynamic>)
: null,
preferredModelCode: json['preferred_model_code'] as String?,
tokensUsed: json['tokens_used'] as int? ?? 0,
tokensLimit: json['tokens_limit'] as int?,
isActive: json['is_active'] as bool? ?? true,
periodStart: json['period_start'] != null
? DateTime.parse(json['period_start'] as String)
: null,
periodEnd: json['period_end'] != null
? DateTime.parse(json['period_end'] as String)
: null,
expiresAt: json['expires_at'] != null
? DateTime.parse(json['expires_at'] as String)
: null,
lastResetAt: json['last_reset_at'] != null
? DateTime.parse(json['last_reset_at'] as String)
: null,
createdAt: json['created_at'] != null
? DateTime.parse(json['created_at'] as String)
: null,
updatedAt: json['updated_at'] != null
? DateTime.parse(json['updated_at'] as String)
: null,
);
}
/// true وقتی سقف معین داریم (مثلاً اشتراک ماهانه با monthly_tokens).
bool get hasTokenCap => tokensLimit != null && tokensLimit! > 0;
/// null یعنی بدون سقف معین (مثلاً pay-as-go یا مقدار نامحدود).
int? get remainingTokens {
if (!hasTokenCap) return null;
return (tokensLimit! - tokensUsed).clamp(0, tokensLimit!);
}
}
class AIChatSession {
final int? id;
final int userId;
final int? businessId;
final String title;
final String executionMode;
final DateTime? createdAt;
final DateTime? updatedAt;
AIChatSession({
this.id,
required this.userId,
this.businessId,
required this.title,
this.executionMode = 'analyzer',
this.createdAt,
this.updatedAt,
});
factory AIChatSession.fromJson(Map<String, dynamic> json) {
return AIChatSession(
id: json['id'] as int?,
userId: json['user_id'] as int,
businessId: json['business_id'] as int?,
title: json['title'] as String,
executionMode: (json['execution_mode'] as String?) ?? 'analyzer',
createdAt: json['created_at'] != null
? DateTime.parse(json['created_at'] as String)
: null,
updatedAt: json['updated_at'] != null
? DateTime.parse(json['updated_at'] as String)
: null,
);
}
Map<String, dynamic> toJson() {
return {
if (id != null) 'id': id,
'user_id': userId,
if (businessId != null) 'business_id': businessId,
'title': title,
'execution_mode': executionMode,
};
}
AIChatSession copyWith({
int? id,
int? userId,
int? businessId,
String? title,
String? executionMode,
DateTime? createdAt,
DateTime? updatedAt,
}) {
return AIChatSession(
id: id ?? this.id,
userId: userId ?? this.userId,
businessId: businessId ?? this.businessId,
title: title ?? this.title,
executionMode: executionMode ?? this.executionMode,
createdAt: createdAt ?? this.createdAt,
updatedAt: updatedAt ?? this.updatedAt,
);
}
}
class AIChatActiveRun {
final String runId;
final String status;
final bool live;
final bool canContinue;
final int lastEventId;
final String? stopReason;
final String? stopMessageFa;
const AIChatActiveRun({
required this.runId,
required this.status,
this.live = false,
this.canContinue = false,
this.lastEventId = 0,
this.stopReason,
this.stopMessageFa,
});
factory AIChatActiveRun.fromJson(Map<String, dynamic> json) {
return AIChatActiveRun(
runId: json['run_id']?.toString() ?? '',
status: json['status']?.toString() ?? '',
live: json['live'] as bool? ?? false,
canContinue: json['can_continue'] as bool? ?? false,
lastEventId: (json['last_event_id'] as num?)?.toInt() ?? 0,
stopReason: json['stop_reason'] as String?,
stopMessageFa: json['stop_message_fa'] as String?,
);
}
bool get isGenerating => live || status == 'running';
}
enum MessageRole {
user,
assistant,
system;
String get value {
switch (this) {
case MessageRole.user:
return 'user';
case MessageRole.assistant:
return 'assistant';
case MessageRole.system:
return 'system';
}
}
static MessageRole fromString(String value) {
switch (value) {
case 'user':
return MessageRole.user;
case 'assistant':
return MessageRole.assistant;
case 'system':
return MessageRole.system;
default:
return MessageRole.user;
}
}
}
class AIChatMessage {
final int? id;
final int sessionId;
final MessageRole role;
final String content;
final Object? functionCalls;
final Object? functionResults;
final int tokensUsed;
final DateTime? createdAt;
AIChatMessage({
this.id,
required this.sessionId,
required this.role,
required this.content,
this.functionCalls,
this.functionResults,
this.tokensUsed = 0,
this.createdAt,
});
factory AIChatMessage.fromJson(Map<String, dynamic> json) {
return AIChatMessage(
id: json['id'] as int?,
sessionId: json['session_id'] as int,
role: MessageRole.fromString(json['role'] as String),
content: json['content'] as String,
functionCalls: json['function_calls'],
functionResults: json['function_results'],
tokensUsed: json['tokens_used'] as int? ?? 0,
createdAt: json['created_at'] != null
? DateTime.parse(json['created_at'] as String)
: null,
);
}
Map<String, dynamic> toJson() {
return {
if (id != null) 'id': id,
'session_id': sessionId,
'role': role.value,
'content': content,
if (functionCalls != null) 'function_calls': functionCalls,
if (functionResults != null) 'function_results': functionResults,
'tokens_used': tokensUsed,
};
}
}
class AIUsageStats {
final Map<String, dynamic> period;
final Map<String, dynamic> total;
final List<Map<String, dynamic>> daily;
final List<Map<String, dynamic>> byModel;
AIUsageStats({
required this.period,
required this.total,
required this.daily,
required this.byModel,
});
factory AIUsageStats.fromJson(Map<String, dynamic> json) {
// ignore: avoid_print
if (kDebugMode) {
print('[AIUsageStats] fromJson - Input JSON: $json');
}
try {
final period = json['period'] != null
? Map<String, dynamic>.from(json['period'] as Map)
: <String, dynamic>{};
if (kDebugMode) {
print('[AIUsageStats] fromJson - period: $period');
}
final total = json['total'] != null
? Map<String, dynamic>.from(json['total'] as Map)
: <String, dynamic>{
'total_tokens': 0,
'input_tokens': 0,
'output_tokens': 0,
'total_cost': 0.0,
'total_requests': 0,
};
if (kDebugMode) {
print('[AIUsageStats] fromJson - total: $total');
}
final dailyRaw = json['daily'];
final daily = dailyRaw != null && dailyRaw is List
? dailyRaw
.map((e) {
try {
return e is Map<String, dynamic> ? Map<String, dynamic>.from(e) : <String, dynamic>{};
} catch (err) {
if (kDebugMode) {
print('[AIUsageStats] fromJson - Error parsing daily entry: $e, error: $err');
}
return <String, dynamic>{};
}
})
.toList()
: <Map<String, dynamic>>[];
if (kDebugMode) {
print('[AIUsageStats] fromJson - daily: $daily (length: ${daily.length})');
}
final byModelRaw = json['by_model'];
final byModel = byModelRaw != null && byModelRaw is List
? byModelRaw
.map((e) {
try {
return e is Map<String, dynamic> ? Map<String, dynamic>.from(e) : <String, dynamic>{};
} catch (err) {
if (kDebugMode) {
print('[AIUsageStats] fromJson - Error parsing byModel entry: $e, error: $err');
}
return <String, dynamic>{};
}
})
.toList()
: <Map<String, dynamic>>[];
if (kDebugMode) {
print('[AIUsageStats] fromJson - byModel: $byModel (length: ${byModel.length})');
}
final result = AIUsageStats(
period: period,
total: total,
daily: daily,
byModel: byModel,
);
if (kDebugMode) {
print('[AIUsageStats] fromJson - Successfully created AIUsageStats');
}
return result;
} catch (e, stackTrace) {
// ignore: avoid_print
if (kDebugMode) {
print('[AIUsageStats] fromJson - Error: $e');
print('[AIUsageStats] fromJson - StackTrace: $stackTrace');
}
rethrow;
}
}
}
class AIUsageLog {
final int? id;
final String provider;
final String model;
final int inputTokens;
final int outputTokens;
final double cost;
final String paymentMethod;
final DateTime? createdAt;
AIUsageLog({
this.id,
required this.provider,
required this.model,
required this.inputTokens,
required this.outputTokens,
required this.cost,
required this.paymentMethod,
this.createdAt,
});
factory AIUsageLog.fromJson(Map<String, dynamic> json) {
return AIUsageLog(
id: json['id'] as int?,
provider: json['provider'] as String,
model: json['model'] as String,
inputTokens: json['input_tokens'] as int,
outputTokens: json['output_tokens'] as int,
cost: (json['cost'] as num).toDouble(),
paymentMethod: json['payment_method'] as String,
createdAt: json['created_at'] != null
? DateTime.parse(json['created_at'] as String)
: null,
);
}
int get totalTokens => inputTokens + outputTokens;
}
/// کاتالوگ مدل AI قابل انتخاب توسط کاربر
class AIModelCatalogItem {
final int? id;
final String code;
final String displayName;
final String? description;
final String provider;
final String modelId;
final String? tier;
final bool supportsTools;
final int maxTokensDefault;
final bool supportsReasoning;
final String? reasoningEffort;
final bool isActive;
final bool isDefault;
final Map<String, dynamic>? pricing;
final double? estimatedCostPer1kTokens;
final String? pricingHint;
final bool isAuto;
AIModelCatalogItem({
this.id,
required this.code,
required this.displayName,
this.description,
required this.provider,
required this.modelId,
this.tier,
this.supportsTools = true,
this.maxTokensDefault = 4000,
this.supportsReasoning = false,
this.reasoningEffort,
this.isActive = true,
this.isDefault = false,
this.pricing,
this.estimatedCostPer1kTokens,
this.pricingHint,
this.isAuto = false,
});
factory AIModelCatalogItem.fromJson(Map<String, dynamic> json) {
return AIModelCatalogItem(
id: json['id'] as int?,
code: json['code'] as String,
displayName: json['display_name'] as String? ?? json['code'] as String,
description: json['description'] as String?,
provider: json['provider'] as String? ?? 'openai',
modelId: json['model_id'] as String? ?? json['code'] as String,
tier: json['tier'] as String?,
supportsTools: json['supports_tools'] as bool? ?? true,
maxTokensDefault: json['max_tokens_default'] as int? ?? 4000,
supportsReasoning: json['supports_reasoning'] as bool? ?? false,
reasoningEffort: json['reasoning_effort'] as String?,
isActive: json['is_active'] as bool? ?? true,
isDefault: json['is_default'] as bool? ?? false,
pricing: json['pricing'] is Map
? Map<String, dynamic>.from(json['pricing'] as Map)
: null,
estimatedCostPer1kTokens:
(json['estimated_cost_per_1k_tokens'] as num?)?.toDouble(),
pricingHint: json['pricing_hint'] as String?,
isAuto: json['is_auto'] as bool? ?? json['code'] == 'auto',
);
}
Map<String, dynamic> toJson() {
return {
if (id != null) 'id': id,
'code': code,
'display_name': displayName,
if (description != null) 'description': description,
'provider': provider,
'model_id': modelId,
if (tier != null) 'tier': tier,
'supports_tools': supportsTools,
'max_tokens_default': maxTokensDefault,
'supports_reasoning': supportsReasoning,
if (reasoningEffort != null) 'reasoning_effort': reasoningEffort,
'is_active': isActive,
'sort_order': 0,
};
}
}
class AIPrompt {
final int? id;
final String? promptKey;
final String role; // 'admin', 'operator', 'user'
final String? promptType; // 'system', 'user'
final String? category;
final String? title;
final String content;
final int? userId;
final bool isDefault;
final String? source; // 'database' | 'fallback'
final DateTime? createdAt;
final DateTime? updatedAt;
AIPrompt({
this.id,
this.promptKey,
required this.role,
this.promptType,
this.category,
this.title,
required this.content,
this.userId,
this.isDefault = false,
this.source,
this.createdAt,
this.updatedAt,
});
factory AIPrompt.fromJson(Map<String, dynamic> json) {
return AIPrompt(
id: json['id'] as int?,
promptKey: json['prompt_key'] as String?,
role: json['role'] as String,
promptType: json['prompt_type'] as String?,
category: json['category'] as String?,
title: json['title'] as String?,
content: json['content'] as String,
userId: json['user_id'] as int?,
isDefault: json['is_default'] as bool? ?? false,
source: json['source'] as String?,
createdAt: json['created_at'] != null
? DateTime.parse(json['created_at'] as String)
: null,
updatedAt: json['updated_at'] != null
? DateTime.parse(json['updated_at'] as String)
: null,
);
}
Map<String, dynamic> toJson() {
return {
if (id != null) 'id': id,
if (promptKey != null) 'prompt_key': promptKey,
'role': role,
if (promptType != null) 'prompt_type': promptType,
if (category != null) 'category': category,
if (title != null) 'title': title,
'content': content,
if (userId != null) 'user_id': userId,
'is_default': isDefault,
if (source != null) 'source': source,
};
}
}