progress in wizard

This commit is contained in:
Hesabix 2025-07-17 23:00:04 +00:00
parent 574bdd1a5b
commit 186229c848
5 changed files with 1520 additions and 136 deletions

View file

@ -459,6 +459,17 @@ class AdminController extends AbstractController
$resp['cardToShebaFee'] = $registryMGR->get('system', key: 'cardToShebaFee');
$resp['enableAccountToSheba'] = $registryMGR->get('system', key: 'enableAccountToSheba');
$resp['accountToShebaFee'] = $registryMGR->get('system', key: 'accountToShebaFee');
// تنظیمات جادوگر هوش مصنوعی
$resp['aiEnabled'] = $registryMGR->get('system', key: 'aiEnabled');
$resp['aiAgentSource'] = $registryMGR->get('system', key: 'aiAgentSource');
$resp['aiModel'] = $registryMGR->get('system', key: 'aiModel');
$resp['aiApiKey'] = $registryMGR->get('system', key: 'aiApiKey');
$resp['localModelAddress'] = $registryMGR->get('system', key: 'localModelAddress');
$resp['inputTokenPrice'] = $registryMGR->get('system', key: 'inputTokenPrice');
$resp['outputTokenPrice'] = $registryMGR->get('system', key: 'outputTokenPrice');
$resp['aiPrompt'] = $registryMGR->get('system', key: 'aiPrompt');
return $this->json($resp);
}
@ -492,6 +503,25 @@ class AdminController extends AbstractController
$registryMGR->update('system', 'cardToShebaFee', $params['cardToShebaFee']);
$registryMGR->update('system', 'enableAccountToSheba', $params['enableAccountToSheba']);
$registryMGR->update('system', 'accountToShebaFee', $params['accountToShebaFee']);
// ذخیره تنظیمات جادوگر هوش مصنوعی
if (array_key_exists('aiEnabled', $params))
$registryMGR->update('system', 'aiEnabled', $params['aiEnabled']);
if (array_key_exists('aiAgentSource', $params))
$registryMGR->update('system', 'aiAgentSource', $params['aiAgentSource']);
if (array_key_exists('aiModel', $params))
$registryMGR->update('system', 'aiModel', $params['aiModel']);
if (array_key_exists('aiApiKey', $params))
$registryMGR->update('system', 'aiApiKey', $params['aiApiKey']);
if (array_key_exists('localModelAddress', $params))
$registryMGR->update('system', 'localModelAddress', $params['localModelAddress']);
if (array_key_exists('inputTokenPrice', $params))
$registryMGR->update('system', 'inputTokenPrice', $params['inputTokenPrice']);
if (array_key_exists('outputTokenPrice', $params))
$registryMGR->update('system', 'outputTokenPrice', $params['outputTokenPrice']);
if (array_key_exists('aiPrompt', $params))
$registryMGR->update('system', 'aiPrompt', $params['aiPrompt']);
$entityManager->persist($item);
$entityManager->flush();
return $this->json(['result' => 1]);

View file

@ -0,0 +1,192 @@
<?php
namespace App\Controller;
use App\Service\AIService;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\JsonResponse;
class wizardController extends AbstractController
{
private AIService $aiService;
public function __construct(AIService $aiService)
{
$this->aiService = $aiService;
}
#[Route('/api/wizard/talk', name: 'wizard_talk', methods: ['POST'])]
public function wizard_talk(Request $request): JsonResponse
{
try {
$params = json_decode($request->getContent(), true) ?? [];
if (!isset($params['message']) || empty($params['message'])) {
return $this->json([
'success' => false,
'error' => 'پیام الزامی است'
]);
}
$message = $params['message'];
$options = $params['options'] ?? [];
// بررسی فعال بودن هوش مصنوعی
if (!$this->aiService->isAIEnabled()) {
return $this->json([
'success' => false,
'error' => 'سرویس هوش مصنوعی غیرفعال است'
]);
}
// ارسال درخواست به سرویس هوش مصنوعی
$result = $this->aiService->sendRequest($message, $options);
if ($result['success']) {
$response = [
'success' => true,
'response' => $result['response'],
'model' => $result['model'] ?? null,
'usage' => $result['usage'] ?? null
];
// محاسبه هزینه در صورت وجود اطلاعات usage
if (isset($result['usage'])) {
$cost = $this->aiService->calculateCost($result['usage']);
$response['cost'] = $cost;
}
return $this->json($response);
} else {
return $this->json([
'success' => false,
'error' => $result['error']
]);
}
} catch (\Exception $e) {
return $this->json([
'success' => false,
'error' => 'خطا در پردازش درخواست: ' . $e->getMessage()
]);
}
}
#[Route('/api/wizard/status', name: 'wizard_status', methods: ['GET'])]
public function wizard_status(): JsonResponse
{
try {
$isEnabled = $this->aiService->isAIEnabled();
$agentSource = $this->aiService->getAIAgentSource();
$model = $this->aiService->getAIModel();
$apiKey = $this->aiService->getAIApiKey();
// بررسی وضعیت کامل
$status = 'available';
$message = 'سرویس هوش مصنوعی در دسترس است';
if (!$isEnabled) {
$status = 'disabled';
$message = 'سرویس هوش مصنوعی غیرفعال است';
} elseif (empty($apiKey)) {
$status = 'no_api_key';
$message = 'کلید API تنظیم نشده است';
}
return $this->json([
'success' => true,
'status' => $status,
'agent_source' => $agentSource,
'model' => $model,
'message' => $message,
'debug_info' => [
'ai_enabled' => $isEnabled,
'has_api_key' => !empty($apiKey),
'agent_source' => $agentSource,
'model' => $model
]
]);
} catch (\Exception $e) {
return $this->json([
'success' => false,
'error' => 'خطا در بررسی وضعیت: ' . $e->getMessage()
]);
}
}
#[Route('/api/wizard/models', name: 'wizard_models', methods: ['GET'])]
public function wizard_models(): JsonResponse
{
try {
$agentSource = $this->aiService->getAIAgentSource();
$currentModel = $this->aiService->getAIModel();
// لیست مدل‌های موجود بر اساس منبع ایجنت
$models = [];
switch ($agentSource) {
case 'gapgpt':
$models = [
'gpt-4o' => 'GPT-4 Omni',
'gpt-4-turbo' => 'GPT-4 Turbo',
'gpt-3.5-turbo' => 'GPT-3.5 Turbo',
'claude-3-opus' => 'Claude 3 Opus',
'claude-3-sonnet' => 'Claude 3 Sonnet',
'gemini-pro' => 'Gemini Pro'
];
break;
case 'avalai':
$models = [
'gpt-4' => 'GPT-4',
'gpt-3.5-turbo' => 'GPT-3.5 Turbo',
'claude-3' => 'Claude 3',
'gemini-pro' => 'Gemini Pro'
];
break;
case 'local':
$models = [
'local-model' => 'مدل لوکال',
'custom-model' => 'مدل سفارشی'
];
break;
}
return $this->json([
'success' => true,
'models' => $models,
'current_model' => $currentModel,
'agent_source' => $agentSource
]);
} catch (\Exception $e) {
return $this->json([
'success' => false,
'error' => 'خطا در دریافت مدل‌ها: ' . $e->getMessage()
]);
}
}
#[Route('/api/wizard/settings', name: 'wizard_settings', methods: ['GET'])]
public function wizard_settings(): JsonResponse
{
try {
return $this->json([
'success' => true,
'settings' => [
'aiEnabled' => $this->aiService->isAIEnabled(),
'aiAgentSource' => $this->aiService->getAIAgentSource(),
'aiModel' => $this->aiService->getAIModel(),
'inputTokenPrice' => $this->aiService->getInputTokenPrice(),
'outputTokenPrice' => $this->aiService->getOutputTokenPrice(),
'aiPrompt' => $this->aiService->getAIPrompt()
]
]);
} catch (\Exception $e) {
return $this->json([
'success' => false,
'error' => 'خطا در دریافت تنظیمات: ' . $e->getMessage()
]);
}
}
}

View file

@ -0,0 +1,377 @@
<?php
namespace App\Service;
use App\Service\registryMGR;
use Exception;
use Symfony\Component\HttpFoundation\JsonResponse;
class AIService
{
private registryMGR $registryMGR;
public function __construct(registryMGR $registryMGR)
{
$this->registryMGR = $registryMGR;
}
/**
* ارسال درخواست به سرویس هوش مصنوعی
*/
public function sendRequest(string $message, array $options = []): array
{
try {
// بررسی فعال بودن هوش مصنوعی
if (!$this->isAIEnabled()) {
return [
'success' => false,
'error' => 'سرویس هوش مصنوعی غیرفعال است'
];
}
// ترکیب پرامپ با پیام کاربر
$enhancedMessage = $this->combinePromptWithMessage($message);
$agentSource = $this->getAIAgentSource();
switch ($agentSource) {
case 'gapgpt':
return $this->sendToGapGPT($enhancedMessage, $options);
case 'avalai':
return $this->sendToAvalAI($enhancedMessage, $options);
case 'local':
return $this->sendToLocalModel($enhancedMessage, $options);
default:
return [
'success' => false,
'error' => 'سرویس هوش مصنوعی نامعتبر است'
];
}
} catch (Exception $e) {
return [
'success' => false,
'error' => 'خطا در ارتباط با سرویس هوش مصنوعی: ' . $e->getMessage()
];
}
}
/**
* ترکیب پرامپ با پیام کاربر
*/
private function combinePromptWithMessage(string $message): string
{
$prompt = $this->getAIPrompt();
if (empty($prompt)) {
return $message;
}
// اگر پرامپ وجود دارد، آن را با پیام کاربر ترکیب کن
return $prompt . "\n\nسوال کاربر: " . $message;
}
/**
* ارسال درخواست به GapGPT
*/
private function sendToGapGPT(string $message, array $options = []): array
{
$apiKey = $this->getAIApiKey();
if (empty($apiKey)) {
return [
'success' => false,
'error' => 'کلید API GapGPT تنظیم نشده است'
];
}
$url = 'https://api.gapgpt.app/v1/chat/completions';
$model = $options['model'] ?? $this->getAIModel();
$data = [
'model' => $model,
'messages' => [
[
'role' => 'user',
'content' => $message
]
]
];
// اضافه کردن تنظیمات اختیاری
if (isset($options['temperature'])) {
$data['temperature'] = $options['temperature'];
}
if (isset($options['max_tokens'])) {
$data['max_tokens'] = $options['max_tokens'];
}
$headers = [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey
];
$response = $this->makeHttpRequest($url, $data, $headers);
if ($response['success']) {
return [
'success' => true,
'response' => $response['data']['choices'][0]['message']['content'] ?? '',
'usage' => $response['data']['usage'] ?? null,
'model' => $response['data']['model'] ?? $model
];
}
return [
'success' => false,
'error' => $response['error'] ?? 'خطا در ارتباط با GapGPT'
];
}
/**
* ارسال درخواست به AvalAI
*/
private function sendToAvalAI(string $message, array $options = []): array
{
$apiKey = $this->getAIApiKey();
if (empty($apiKey)) {
return [
'success' => false,
'error' => 'کلید API AvalAI تنظیم نشده است'
];
}
$url = 'https://api.avalai.ir/v1/chat/completions';
$model = $options['model'] ?? $this->getAIModel();
$data = [
'model' => $model,
'messages' => [
[
'role' => 'user',
'content' => $message
]
]
];
// اضافه کردن تنظیمات اختیاری
if (isset($options['temperature'])) {
$data['temperature'] = $options['temperature'];
}
if (isset($options['max_tokens'])) {
$data['max_tokens'] = $options['max_tokens'];
}
$headers = [
'Content-Type: application/json',
'Authorization: Bearer ' . $apiKey
];
$response = $this->makeHttpRequest($url, $data, $headers);
if ($response['success']) {
return [
'success' => true,
'response' => $response['data']['choices'][0]['message']['content'] ?? '',
'usage' => $response['data']['usage'] ?? null,
'model' => $response['data']['model'] ?? $model
];
}
return [
'success' => false,
'error' => $response['error'] ?? 'خطا در ارتباط با AvalAI'
];
}
/**
* ارسال درخواست به مدل لوکال
*/
private function sendToLocalModel(string $message, array $options = []): array
{
$localAddress = $this->getLocalModelAddress();
if (empty($localAddress)) {
return [
'success' => false,
'error' => 'آدرس مدل لوکال تنظیم نشده است'
];
}
$url = rtrim($localAddress, '/') . '/v1/chat/completions';
$model = $options['model'] ?? 'local-model';
$data = [
'model' => $model,
'messages' => [
[
'role' => 'user',
'content' => $message
]
]
];
// اضافه کردن تنظیمات اختیاری
if (isset($options['temperature'])) {
$data['temperature'] = $options['temperature'];
}
if (isset($options['max_tokens'])) {
$data['max_tokens'] = $options['max_tokens'];
}
$headers = [
'Content-Type: application/json'
];
// اضافه کردن کلید API در صورت وجود
$apiKey = $this->getAIApiKey();
if (!empty($apiKey)) {
$headers[] = 'Authorization: Bearer ' . $apiKey;
}
$response = $this->makeHttpRequest($url, $data, $headers);
if ($response['success']) {
return [
'success' => true,
'response' => $response['data']['choices'][0]['message']['content'] ?? '',
'usage' => $response['data']['usage'] ?? null,
'model' => $response['data']['model'] ?? $model
];
}
return [
'success' => false,
'error' => $response['error'] ?? 'خطا در ارتباط با مدل لوکال'
];
}
/**
* انجام درخواست HTTP
*/
private function makeHttpRequest(string $url, array $data, array $headers): array
{
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => $headers,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_FOLLOWLOCATION => true
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
return [
'success' => false,
'error' => 'خطا در cURL: ' . $error
];
}
if ($httpCode !== 200) {
return [
'success' => false,
'error' => 'خطای HTTP: ' . $httpCode . ' - ' . $response
];
}
$responseData = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return [
'success' => false,
'error' => 'خطا در پردازش پاسخ JSON'
];
}
return [
'success' => true,
'data' => $responseData
];
}
/**
* بررسی فعال بودن هوش مصنوعی
*/
public function isAIEnabled(): bool
{
return $this->registryMGR->get('system', 'aiEnabled') === '1';
}
/**
* دریافت منبع ایجنت هوش مصنوعی
*/
public function getAIAgentSource(): string
{
return $this->registryMGR->get('system', 'aiAgentSource') ?: 'gapgpt';
}
/**
* دریافت مدل هوش مصنوعی
*/
public function getAIModel(): string
{
return $this->registryMGR->get('system', 'aiModel') ?: 'gpt-4o';
}
/**
* دریافت کلید API هوش مصنوعی
*/
public function getAIApiKey(): string
{
return $this->registryMGR->get('system', 'aiApiKey') ?: '';
}
/**
* دریافت آدرس مدل لوکال
*/
public function getLocalModelAddress(): string
{
return $this->registryMGR->get('system', 'localModelAddress') ?: '';
}
/**
* دریافت قیمت توکن ورودی
*/
public function getInputTokenPrice(): float
{
return (float) ($this->registryMGR->get('system', 'inputTokenPrice') ?: 0);
}
/**
* دریافت قیمت توکن خروجی
*/
public function getOutputTokenPrice(): float
{
return (float) ($this->registryMGR->get('system', 'outputTokenPrice') ?: 0);
}
/**
* دریافت پرامپ هوش مصنوعی
*/
public function getAIPrompt(): string
{
return $this->registryMGR->get('system', 'aiPrompt') ?: '';
}
/**
* محاسبه هزینه بر اساس تعداد توکن‌ها
*/
public function calculateCost(array $usage): float
{
$inputTokenPrice = (float) ($this->registryMGR->get('system', 'inputTokenPrice') ?: 0);
$outputTokenPrice = (float) ($this->registryMGR->get('system', 'outputTokenPrice') ?: 0);
$inputTokens = $usage['prompt_tokens'] ?? 0;
$outputTokens = $usage['completion_tokens'] ?? 0;
$inputCost = ($inputTokens / 1000) * $inputTokenPrice;
$outputCost = ($outputTokens / 1000) * $outputTokenPrice;
return $inputCost + $outputCost;
}
}

View file

@ -11,7 +11,8 @@ export default defineComponent({
tabs: [
{ title: 'تنظیمات پایه', icon: 'mdi-cog' },
{ title: 'درگاه‌های پرداخت', icon: 'mdi-credit-card' },
{ title: 'پنل استعلامات', icon: 'mdi-magnify' }
{ title: 'پنل استعلامات', icon: 'mdi-magnify' },
{ title: 'جادوگر هوش مصنوعی', icon: 'mdi-robot' }
],
gatepays: [
{
@ -62,6 +63,79 @@ export default defineComponent({
accountToShebaFee: 0,
},
loading: true,
// افزودن ایجنتهای هوش مصنوعی
aiEnabled: true,
aiAgentSource: 'gapgpt',
aiModel: 'gpt-4o',
aiApiKey: '',
showApiKey: false,
localModelAddress: '',
inputTokenPrice: 0,
outputTokenPrice: 0,
aiPrompt: '',
aiAgentSources: [
{ title: 'GapGPT', value: 'gapgpt', subtitle: 'gapgpt.app' },
{ title: 'AvalAI', value: 'avalai', subtitle: 'avalai.ir' },
{ title: 'مدل لوکال', value: 'local', subtitle: 'local' },
],
aiModels: [
// OpenAI Models
{ title: 'GPT-4 Omni', value: 'gpt-4o', subtitle: 'OpenAI - $2.50/$1.25/$10.00', provider: 'OpenAI' },
{ title: 'GPT-4.5 Preview', value: 'gpt-4.5-preview', subtitle: 'OpenAI - $75.00/$37.50/$150.00', provider: 'OpenAI' },
{ title: 'GPT-4.1', value: 'gpt-4.1', subtitle: 'OpenAI - $2.00/$0.50/$8.00', provider: 'OpenAI' },
{ title: 'GPT-4.1 Mini', value: 'gpt-4.1-mini', subtitle: 'OpenAI - $0.40/$0.10/$1.60', provider: 'OpenAI' },
{ title: 'GPT-4.1 Nano', value: 'gpt-4.1-nano', subtitle: 'OpenAI - $0.10/$0.03/$0.40', provider: 'OpenAI' },
{ title: 'GPT-4o Mini', value: 'gpt-4o-mini', subtitle: 'OpenAI - $0.15/$0.07/$0.60', provider: 'OpenAI' },
{ title: 'GPT-4o Audio Preview', value: 'gpt-4o-audio-preview', subtitle: 'OpenAI - $2.50/-/$10.00', provider: 'OpenAI' },
{ title: 'ChatGPT-4o Latest', value: 'chatgpt-4o-latest', subtitle: 'OpenAI - $5.00/-/$15.00', provider: 'OpenAI' },
{ title: 'O1', value: 'o1', subtitle: 'OpenAI - $15.00/$7.50/$60.00', provider: 'OpenAI' },
{ title: 'O4 Mini', value: 'o4-mini', subtitle: 'OpenAI - $1.10/$0.28/$4.40', provider: 'OpenAI' },
{ title: 'O3', value: 'o3', subtitle: 'OpenAI - $2.00/$0.50/$8.00', provider: 'OpenAI' },
{ title: 'O3 Mini', value: 'o3-mini', subtitle: 'OpenAI - $1.10/$0.55/$4.40', provider: 'OpenAI' },
{ title: 'O3 Mini High', value: 'o3-mini-high', subtitle: 'OpenAI - $1.10/-/$4.40', provider: 'OpenAI' },
{ title: 'O3 Mini Low', value: 'o3-mini-low', subtitle: 'OpenAI - $1.10/-/$4.40', provider: 'OpenAI' },
{ title: 'O1 Mini', value: 'o1-mini', subtitle: 'OpenAI - $1.10/$0.55/$4.40', provider: 'OpenAI' },
// Anthropic Models
{ title: 'Claude Opus 4', value: 'claude-opus-4-20250514', subtitle: 'Anthropic - $15.00/-/$75.00', provider: 'Anthropic' },
{ title: 'Claude Sonnet 4', value: 'claude-sonnet-4-20250514', subtitle: 'Anthropic - $3.00/-/$15.00', provider: 'Anthropic' },
{ title: 'Claude 3.7 Sonnet', value: 'claude-3-7-sonnet-20250219', subtitle: 'Anthropic - $3.00/$0.30/$15.00', provider: 'Anthropic' },
{ title: 'Claude 3.7 Sonnet Thinking', value: 'claude-3-7-sonnet-20250219-thinking', subtitle: 'Anthropic - $3.00/$0.30/$15.00', provider: 'Anthropic' },
{ title: 'Claude 3.5 Sonnet', value: 'claude-3-5-sonnet-20241022', subtitle: 'Anthropic - $3.00/$0.30/$15.00', provider: 'Anthropic' },
// Google Models
{ title: 'Gemini 2.5 Pro', value: 'gemini-2.5-pro', subtitle: 'Google - $2.50/-/$20.00', provider: 'Google' },
{ title: 'Gemini 2.5 Flash', value: 'gemini-2.5-flash', subtitle: 'Google - $0.30/-/$2.50', provider: 'Google' },
{ title: 'Gemini 1.5 Pro', value: 'gemini-1.5-pro', subtitle: 'Google - $1.25/-/$5.00', provider: 'Google' },
{ title: 'Gemini 1.5 Pro Latest', value: 'gemini-1.5-pro-latest', subtitle: 'Google - $3.50/-/$14.00', provider: 'Google' },
{ title: 'Gemini 1.5 Pro 002', value: 'gemini-1.5-pro-002', subtitle: 'Google - $3.50/-/$14.00', provider: 'Google' },
{ title: 'Gemini Live 2.5 Flash Preview', value: 'gemini-live-2.5-flash-preview', subtitle: 'Google - $0.30/-/$1.20', provider: 'Google' },
{ title: 'Gemini 2.5 Flash Preview Native Audio Dialog', value: 'gemini-2.5-flash-preview-native-audio-dialog', subtitle: 'Google - $1.00/-/$4.00', provider: 'Google' },
{ title: 'Gemini 2.5 Flash Lite Preview', value: 'gemini-2.5-flash-lite-preview-06-17', subtitle: 'Google - $0.10/-/$0.40', provider: 'Google' },
{ title: 'Gemini 2.0 Flash Lite', value: 'gemini-2.0-flash-lite', subtitle: 'Google - $0.07/-/$0.30', provider: 'Google' },
{ title: 'Gemini 2.0 Flash Live', value: 'gemini-2.0-flash-live-001', subtitle: 'Google - $0.10/-/$0.40', provider: 'Google' },
{ title: 'Gemini 2.0 Flash Lite Preview', value: 'gemini-2.0-flash-lite-preview', subtitle: 'Google - $0.07/-/$0.30', provider: 'Google' },
{ title: 'Gemini 2.0 Flash', value: 'gemini-2.0-flash', subtitle: 'Google - $0.10/-/$0.40', provider: 'Google' },
{ title: 'Gemini 2.5 Flash Exp Native Audio Thinking Dialog', value: 'gemini-2.5-flash-exp-native-audio-thinking-dialog', subtitle: 'Google - $75.00/-/$625.00', provider: 'Google' },
{ title: 'Gemini 2.0 Flash Lite Preview 02-05', value: 'gemini-2.0-flash-lite-preview-02-05', subtitle: 'Google - $0.07/-/$0.30', provider: 'Google' },
{ title: 'Gemini 2.0 Flash Preview Image Generation', value: 'gemini-2.0-flash-preview-image-generation', subtitle: 'Google - $0.10/-/$0.40', provider: 'Google' },
{ title: 'Gemini 2.0 Flash Lite 001', value: 'gemini-2.0-flash-lite-001', subtitle: 'Google - $0.07/-/$0.30', provider: 'Google' },
{ title: 'Gemini 2.0 Flash 001', value: 'gemini-2.0-flash-001', subtitle: 'Google - $0.10/-/$0.40', provider: 'Google' },
// XAI Models
{ title: 'Grok-4', value: 'grok-4', subtitle: 'XAI - $3.00/-/$15.00', provider: 'XAI' },
{ title: 'Grok-3', value: 'grok-3', subtitle: 'XAI - $3.00/-/$15.00', provider: 'XAI' },
{ title: 'Grok-3 Mini', value: 'grok-3-mini', subtitle: 'XAI - $0.30/-/$1.50', provider: 'XAI' },
{ title: 'Grok-3 Fast', value: 'grok-3-fast', subtitle: 'XAI - $5.00/-/$25.00', provider: 'XAI' },
{ title: 'Grok-3 Mini Fast', value: 'grok-3-mini-fast', subtitle: 'XAI - $0.60/-/$4.00', provider: 'XAI' },
// Deepseek Models
{ title: 'Deepseek Reasoner', value: 'deepseek-reasoner', subtitle: 'Deepseek - $0.55/$0.14/$2.20', provider: 'Deepseek' },
{ title: 'Deepseek Chat', value: 'deepseek-chat', subtitle: 'Deepseek - $0.27/$0.07/$1.08', provider: 'Deepseek' },
// Alibaba Models
{ title: 'Qwen3 235B A22B', value: 'qwen3-235b-a22b', subtitle: 'Alibaba - $0.16/-/$0.48', provider: 'Alibaba' },
],
}
},
methods: {
@ -80,6 +154,16 @@ export default defineComponent({
activeGateway: data.activeGateway || 'zarinpal',
inquiryPanel: data.inquiryPanel || 'zohal'
};
// بازیابی تنظیمات جادوگر هوش مصنوعی
this.aiEnabled = data.aiEnabled === '1' || data.aiEnabled === true;
this.aiAgentSource = data.aiAgentSource || 'gapgpt';
this.aiModel = data.aiModel || 'gpt-4o';
this.aiApiKey = data.aiApiKey || '';
this.localModelAddress = data.localModelAddress || '';
this.inputTokenPrice = parseFloat(data.inputTokenPrice) || 0;
this.outputTokenPrice = parseFloat(data.outputTokenPrice) || 0;
this.aiPrompt = data.aiPrompt || '';
this.loading = false;
})
},
@ -130,7 +214,20 @@ export default defineComponent({
return;
}
axios.post('/api/admin/settings/system/info/save', this.systemInfo).then((resp) => {
// ترکیب تنظیمات سیستم با تنظیمات جادوگر هوش مصنوعی
const submitData = {
...this.systemInfo,
aiEnabled: this.aiEnabled,
aiAgentSource: this.aiAgentSource,
aiModel: this.aiModel,
aiApiKey: this.aiApiKey,
localModelAddress: this.localModelAddress,
inputTokenPrice: this.inputTokenPrice,
outputTokenPrice: this.outputTokenPrice,
aiPrompt: this.aiPrompt
};
axios.post('/api/admin/settings/system/info/save', submitData).then((resp) => {
this.loading = false;
if (resp.data.result == 1) {
Swal.fire({
@ -571,6 +668,307 @@ export default defineComponent({
</v-row>
</v-card-text>
</v-window-item>
<!-- تب چهارم: جادوگر هوش مصنوعی -->
<v-window-item :value="3">
<v-card-text class="pa-8">
<!-- هدر اصلی -->
<div class="d-flex align-center mb-8">
<div class="d-flex align-center bg-primary-lighten-5 pa-4 rounded-lg">
<v-icon size="32" color="primary" class="mr-3">mdi-robot</v-icon>
<div>
<h3 class="text-h5 font-weight-medium text-primary mb-1">جادوگر هوش مصنوعی</h3>
<p class="text-caption text-medium-emphasis mb-0">مدیریت و تنظیم سرویسهای هوش مصنوعی</p>
</div>
</div>
</div>
<!-- کارت فعال/غیرفعال کردن -->
<v-row class="mb-8">
<v-col cols="12">
<v-card
variant="outlined"
class="ai-status-card"
elevation="0"
:class="{ 'ai-status-active': aiEnabled, 'ai-status-inactive': !aiEnabled }"
>
<v-card-text class="pa-6">
<div class="d-flex align-center justify-space-between">
<div class="d-flex align-center">
<v-icon
size="40"
:color="aiEnabled ? 'success' : 'grey'"
class="mr-4"
>
{{ aiEnabled ? 'mdi-robot' : 'mdi-robot-off' }}
</v-icon>
<div>
<h4 class="text-h6 font-weight-medium mb-2">
{{ aiEnabled ? 'هوش مصنوعی فعال است' : 'هوش مصنوعی غیرفعال است' }}
</h4>
<p class="text-body-2 text-medium-emphasis mb-0">
{{ aiEnabled ? 'سرویس هوش مصنوعی در دسترس است' : 'سرویس هوش مصنوعی غیرفعال شده است' }}
</p>
</div>
</div>
<v-switch
v-model="aiEnabled"
:label="aiEnabled ? 'فعال' : 'غیرفعال'"
:color="aiEnabled ? 'success' : 'grey'"
hide-details="auto"
inset
density="comfortable"
class="ai-status-switch"
></v-switch>
</div>
</v-card-text>
</v-card>
</v-col>
</v-row>
<!-- تنظیمات ایجنت -->
<v-row class="mb-8" :class="{ 'opacity-50': !aiEnabled }">
<v-col cols="12">
<v-card variant="outlined" class="pa-6" elevation="0">
<v-card-title class="text-subtitle-1 font-weight-medium pb-4 d-flex align-center">
<v-icon start class="mr-2" color="info">mdi-cog</v-icon>
تنظیمات ایجنت
</v-card-title>
<v-card-text class="pa-0">
<v-row>
<v-col cols="12" md="6" lg="4">
<v-select
v-model="aiAgentSource"
:items="aiAgentSources"
item-title="title"
item-value="value"
label="انتخاب ایجنت هوش مصنوعی"
variant="outlined"
density="comfortable"
hide-details="auto"
prepend-inner-icon="mdi-robot"
:disabled="!aiEnabled"
>
<template #item="{ props, item }">
<v-list-item v-bind="props">
<template #title>
<div class="d-flex flex-column">
<span class="font-weight-medium">{{ item.raw.title }}</span>
<span class="text-caption text-medium-emphasis">{{ item.raw.subtitle }}</span>
</div>
</template>
</v-list-item>
</template>
</v-select>
<div class="text-caption text-medium-emphasis mt-2 d-flex align-center">
<v-icon size="16" class="mr-1">mdi-information</v-icon>
انتخاب سرویس هوش مصنوعی مورد نظر
</div>
</v-col>
<!-- فیلد کلید API -->
<v-col cols="12" md="6" lg="4">
<v-text-field
v-model="aiApiKey"
label="کلید API"
placeholder="کلید دسترسی به سرویس هوش مصنوعی"
variant="outlined"
density="comfortable"
hide-details="auto"
prepend-inner-icon="mdi-key"
:type="showApiKey ? 'text' : 'password'"
:disabled="!aiEnabled"
:rules="[v => !!v || 'کلید API الزامی است']"
>
<template #append-inner>
<v-btn
icon="mdi-eye"
variant="text"
size="small"
@click="showApiKey = !showApiKey"
:color="showApiKey ? 'primary' : 'grey'"
></v-btn>
</template>
</v-text-field>
<div class="text-caption text-medium-emphasis mt-2 d-flex align-center">
<v-icon size="16" class="mr-1">mdi-information</v-icon>
کلید دسترسی به سرویس هوش مصنوعی انتخاب شده
</div>
</v-col>
<!-- انتخاب مدل هوش مصنوعی -->
<v-col v-if="aiAgentSource !== 'local'" cols="12" md="6" lg="4">
<v-select
v-model="aiModel"
:items="aiModels"
item-title="title"
item-value="value"
label="انتخاب مدل هوش مصنوعی"
variant="outlined"
density="comfortable"
hide-details="auto"
prepend-inner-icon="mdi-brain"
:disabled="!aiEnabled"
>
<template #item="{ props, item }">
<v-list-item v-bind="props">
<template #title>
<div class="d-flex flex-column">
<span class="font-weight-medium">{{ item.raw.title }}</span>
<span class="text-caption text-medium-emphasis">{{ item.raw.subtitle }}</span>
</div>
</template>
</v-list-item>
</template>
</v-select>
<div class="text-caption text-medium-emphasis mt-2 d-flex align-center">
<v-icon size="16" class="mr-1">mdi-information</v-icon>
انتخاب مدل هوش مصنوعی مورد نظر
</div>
</v-col>
<!-- فیلد آدرس مدل لوکال -->
<v-col v-if="aiAgentSource === 'local'" cols="12" md="6" lg="4">
<v-text-field
v-model="localModelAddress"
label="آدرس مدل لوکال"
placeholder="مثال: http://localhost:8000"
variant="outlined"
density="comfortable"
hide-details="auto"
prepend-inner-icon="mdi-link"
:rules="[v => !!v || 'آدرس مدل لوکال الزامی است']"
:disabled="!aiEnabled"
></v-text-field>
<div class="text-caption text-medium-emphasis mt-2 d-flex align-center">
<v-icon size="16" class="mr-1">mdi-information</v-icon>
آدرس کامل مدل هوش مصنوعی لوکال
</div>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-col>
</v-row>
<!-- کارت پرامپ هوش مصنوعی -->
<v-row class="mb-8" :class="{ 'opacity-50': !aiEnabled }">
<v-col cols="12">
<v-card variant="outlined" class="pa-6" elevation="0">
<v-card-title class="text-subtitle-1 font-weight-medium pb-4 d-flex align-center">
<v-icon start class="mr-2" color="purple">mdi-message-text</v-icon>
پرامپ هوش مصنوعی
</v-card-title>
<v-card-text class="pa-0">
<v-textarea
v-model="aiPrompt"
label="دستورالعمل‌های هوش مصنوعی"
placeholder="مثال: شما یک دستیار هوشمند هستید که فقط در زمینه مالی و بانکی پاسخ می‌دهید. لطفاً پاسخ‌های خود را مختصر و مفید ارائه دهید..."
variant="outlined"
density="comfortable"
hide-details="auto"
rows="6"
auto-grow
:disabled="!aiEnabled"
prepend-inner-icon="mdi-robot"
></v-textarea>
<div class="text-caption text-medium-emphasis mt-3 d-flex align-center">
<v-icon size="16" class="mr-1">mdi-information</v-icon>
این متن قبل از هر سوال به هوش مصنوعی ارسال میشود تا رفتار و پاسخدهی آن را کنترل کند
</div>
</v-card-text>
</v-card>
</v-col>
</v-row>
<!-- کارتهای قیمتگذاری -->
<v-row class="mb-6" :class="{ 'opacity-50': !aiEnabled }">
<v-col cols="12">
<div class="d-flex align-center mb-6">
<div class="d-flex align-center bg-warning-lighten-5 pa-3 rounded-lg">
<v-icon size="24" color="warning" class="mr-2">mdi-currency-usd</v-icon>
<div>
<h4 class="text-h6 font-weight-medium text-warning mb-1">قیمتگذاری توکنها</h4>
<p class="text-caption text-medium-emphasis mb-0">تعیین قیمت برای استفاده از سرویس هوش مصنوعی</p>
</div>
</div>
</div>
<v-row>
<v-col cols="12" sm="12" md="6">
<v-card
variant="outlined"
class="token-price-card h-100"
elevation="0"
>
<div class="token-price-header bg-success-lighten-5 pa-4">
<div class="d-flex align-center">
<v-icon size="32" color="success" class="mr-3">mdi-arrow-down</v-icon>
<div>
<h5 class="text-subtitle-1 font-weight-medium text-success mb-1">توکن ورودی</h5>
<p class="text-caption text-medium-emphasis mb-0">قیمت هر 1000 توکن ورودی</p>
</div>
</div>
</div>
<v-card-text class="pa-4">
<v-text-field
v-model.number="inputTokenPrice"
label="قیمت (ریال)"
type="number"
min="0"
step="0.01"
prepend-inner-icon="mdi-currency-usd"
hide-details="auto"
suffix="ریال"
density="comfortable"
variant="outlined"
:rules="[v => v >= 0 || 'قیمت نمی‌تواند منفی باشد']"
:disabled="!aiEnabled"
></v-text-field>
</v-card-text>
</v-card>
</v-col>
<v-col cols="12" sm="12" md="6">
<v-card
variant="outlined"
class="token-price-card h-100"
elevation="0"
>
<div class="token-price-header bg-info-lighten-5 pa-4">
<div class="d-flex align-center">
<v-icon size="32" color="info" class="mr-3">mdi-arrow-up</v-icon>
<div>
<h5 class="text-subtitle-1 font-weight-medium text-info mb-1">توکن خروجی</h5>
<p class="text-caption text-medium-emphasis mb-0">قیمت هر 1000 توکن خروجی</p>
</div>
</div>
</div>
<v-card-text class="pa-4">
<v-text-field
v-model.number="outputTokenPrice"
label="قیمت (ریال)"
type="number"
min="0"
step="0.01"
prepend-inner-icon="mdi-currency-usd"
hide-details="auto"
suffix="ریال"
density="comfortable"
variant="outlined"
:rules="[v => v >= 0 || 'قیمت نمی‌تواند منفی باشد']"
:disabled="!aiEnabled"
></v-text-field>
</v-card-text>
</v-card>
</v-col>
</v-row>
</v-col>
</v-row>
</v-card-text>
</v-window-item>
</v-window>
</v-card>
</v-container>
@ -646,4 +1044,88 @@ export default defineComponent({
.service-card:hover .v-icon {
transform: scale(1.1);
}
/* استایل‌های جادوگر هوش مصنوعی */
.ai-status-card {
transition: all 0.3s ease;
border: 2px solid transparent;
position: relative;
overflow: hidden;
}
.ai-status-active {
border-color: var(--v-success-base);
background: linear-gradient(135deg, rgba(76, 175, 80, 0.05), rgba(76, 175, 80, 0.02));
}
.ai-status-inactive {
border-color: var(--v-grey-base);
background: linear-gradient(135deg, rgba(158, 158, 158, 0.05), rgba(158, 158, 158, 0.02));
}
.ai-status-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(90deg, var(--v-success-base), var(--v-info-base));
opacity: 0;
transition: opacity 0.3s ease;
}
.ai-status-active::before {
opacity: 1;
}
.ai-status-switch {
transform: scale(1.2);
}
.token-price-card {
transition: all 0.3s ease;
border: 2px solid transparent;
position: relative;
overflow: hidden;
}
.token-price-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1) !important;
}
.token-price-header {
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
position: relative;
}
.token-price-header::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.1), transparent);
}
/* انیمیشن برای کارت‌های قیمت */
.token-price-card .v-icon {
transition: transform 0.3s ease;
}
.token-price-card:hover .v-icon {
transform: scale(1.1);
}
/* بهبود ظاهر فیلدهای غیرفعال */
.opacity-50 {
transition: opacity 0.3s ease;
}
.opacity-50 .v-text-field--disabled,
.opacity-50 .v-select--disabled {
opacity: 0.4;
}
</style>

View file

@ -1,8 +1,95 @@
<template>
<v-toolbar color="toolbar" title="هوش مصنوعی حسابیکس">
<v-spacer></v-spacer>
<!-- نمایش وضعیت سرویس -->
<div class="d-flex align-center mr-4">
<v-chip
:color="aiSettings.aiEnabled ? 'success' : 'error'"
size="small"
variant="flat"
class="mr-2"
>
<v-icon start size="16">
{{ aiSettings.aiEnabled ? 'mdi-robot' : 'mdi-robot-off' }}
</v-icon>
{{ aiSettings.aiEnabled ? 'فعال' : 'غیرفعال' }}
</v-chip>
<span class="text-caption text-medium-emphasis">
{{ aiSettings.aiAgentSource.toUpperCase() }} - {{ aiSettings.aiModel }}
</span>
</div>
<!-- نمایش وضعیت اتصال -->
<div class="d-flex align-center mr-4">
<v-chip
:color="connectionStatus.color"
size="small"
variant="flat"
class="mr-2"
>
<v-icon start size="16">
{{ connectionStatus.icon }}
</v-icon>
{{ connectionStatus.text }}
</v-chip>
</div>
</v-toolbar>
<div class="page-container">
<!-- کارت اطلاعات قیمتگذاری -->
<div class="pricing-info" v-if="aiSettings.aiEnabled">
<v-card variant="outlined" class="ma-4" elevation="0">
<v-card-text class="pa-4">
<div class="d-flex align-center justify-space-between">
<div class="d-flex align-center">
<v-icon color="info" class="mr-2">mdi-currency-usd</v-icon>
<span class="text-subtitle-2">قیمتگذاری توکنها:</span>
</div>
<div class="d-flex align-center gap-4">
<div class="text-center">
<div class="text-caption text-medium-emphasis">ورودی</div>
<div class="text-body-2 font-weight-medium">{{ aiSettings.inputTokenPrice.toLocaleString('fa-IR') }} ریال</div>
</div>
<v-divider vertical></v-divider>
<div class="text-center">
<div class="text-caption text-medium-emphasis">خروجی</div>
<div class="text-body-2 font-weight-medium">{{ aiSettings.outputTokenPrice.toLocaleString('fa-IR') }} ریال</div>
</div>
<div class="text-caption text-medium-emphasis">(به ازای هر 1000 توکن)</div>
</div>
</div>
</v-card-text>
</v-card>
</div>
<!-- آمار استفاده -->
<div class="usage-stats" v-if="userMessages.length > 0">
<v-card variant="outlined" class="ma-4" elevation="0">
<v-card-text class="pa-4">
<div class="d-flex align-center justify-space-between">
<div class="d-flex align-center">
<v-icon color="success" class="mr-2">mdi-chart-line</v-icon>
<span class="text-subtitle-2">آمار استفاده:</span>
</div>
<div class="d-flex align-center gap-4">
<div class="text-center">
<div class="text-caption text-medium-emphasis">کل پیامها</div>
<div class="text-body-2 font-weight-medium">{{ userMessages.length }}</div>
</div>
<v-divider vertical></v-divider>
<div class="text-center">
<div class="text-caption text-medium-emphasis">پیامهای هوش مصنوعی</div>
<div class="text-body-2 font-weight-medium">{{ aiMessageCount }}</div>
</div>
<v-divider vertical></v-divider>
<div class="text-center">
<div class="text-caption text-medium-emphasis">پیامهای کاربر</div>
<div class="text-body-2 font-weight-medium">{{ userMessageCount }}</div>
</div>
</div>
</div>
</v-card-text>
</v-card>
</div>
<div class="content-container">
<v-card class="chat-container" elevation="0">
<div class="chat-box">
@ -17,51 +104,13 @@
</div>
</div>
<div class="message ai-message" v-if="displayThanks">
<v-avatar color="#1a237e" size="36" class="mr-2">
<v-icon color="white" size="20">mdi-robot</v-icon>
</v-avatar>
<div class="message-content">
<div class="message-text typing-text">{{ displayThanks }}</div>
</div>
</div>
<div class="message ai-message" v-if="displayCapabilities">
<v-avatar color="#1a237e" size="36" class="mr-2">
<v-icon color="white" size="20">mdi-robot</v-icon>
</v-avatar>
<div class="message-content">
<div class="message-text typing-text">{{ displayCapabilities }}</div>
</div>
</div>
<div class="message ai-message" v-for="(capability, index) in displayCapabilitiesList" :key="index">
<v-avatar color="#1a237e" size="36" class="mr-2">
<v-icon color="white" size="20">mdi-robot</v-icon>
</v-avatar>
<div class="message-content">
<div class="message-text typing-text">
<v-icon color="#1a237e" size="16" class="mr-2">mdi-check-circle</v-icon>
{{ capability }}
</div>
</div>
</div>
<div class="message ai-message" v-if="displayPrompt">
<v-avatar color="#1a237e" size="36" class="mr-2">
<v-icon color="white" size="20">mdi-robot</v-icon>
</v-avatar>
<div class="message-content">
<div class="message-text typing-text">{{ displayPrompt }}</div>
</div>
</div>
<!-- پیامهای کاربر و پاسخهای هوش مصنوعی -->
<template v-for="(message, index) in userMessages" :key="index">
<!-- پیام کاربر -->
<div class="message user-message" v-if="typeof message === 'string'">
<div class="message-content">
<div class="message-text">{{ message }}</div>
<div class="message-time">{{ formatTime(new Date()) }}</div>
</div>
<v-avatar color="grey lighten-2" size="36" class="ml-2">
<v-icon color="grey darken-1" size="20">mdi-account</v-icon>
@ -75,6 +124,7 @@
</v-avatar>
<div class="message-content" :class="{ 'details-message': message.isDetails }">
<div class="message-text" v-html="message.text.replace(/\n/g, '<br>')"></div>
<div class="message-time">{{ formatTime(new Date()) }}</div>
</div>
</div>
</template>
@ -98,12 +148,53 @@
<!-- باکس ورودی پیام -->
<div class="input-container">
<div class="d-flex align-center justify-space-between mb-2">
<v-btn
size="small"
variant="text"
color="grey"
@click="clearChat"
:disabled="userMessages.length === 0"
>
<v-icon start size="16">mdi-delete</v-icon>
پاک کردن تاریخچه
</v-btn>
<span class="text-caption text-medium-emphasis">
{{ userMessages.length }} پیام
</span>
</div>
<v-textarea v-model="userMessage" placeholder="پیام خود را اینجا بنویسید..." rows="1" auto-grow hide-details
variant="plain" class="message-input" @keydown.enter.prevent="sendMessage"></v-textarea>
<v-btn color="#1a237e" icon :loading="isLoading" @click="sendMessage" class="send-button"
:disabled="!userMessage.trim()">
<v-icon>mdi-send</v-icon>
</v-btn>
<!-- پیشنمایش پیام -->
<div class="message-preview" v-if="userMessage.trim()">
<div class="text-caption text-medium-emphasis mb-1">پیشنمایش:</div>
<div class="preview-content">
{{ userMessage }}
</div>
</div>
</div>
<!-- پیشنهادات سوالات -->
<div class="suggestions-container" v-if="userMessages.length === 0">
<div class="text-caption text-medium-emphasis mb-2">پیشنهادات:</div>
<div class="d-flex flex-wrap gap-2">
<v-chip
v-for="suggestion in suggestions"
:key="suggestion"
size="small"
variant="outlined"
@click="useSuggestion(suggestion)"
class="suggestion-chip"
>
{{ suggestion }}
</v-chip>
</div>
</div>
</div>
</v-card>
@ -112,6 +203,8 @@
</template>
<script>
import axios from 'axios'
export default {
name: 'WizardHome',
data() {
@ -120,6 +213,14 @@ export default {
isLoading: false,
isTyping: true,
userMessages: [],
aiSettings: {
aiEnabled: false,
aiAgentSource: 'gapgpt',
aiModel: 'gpt-4o',
inputTokenPrice: 0,
outputTokenPrice: 0,
aiPrompt: ''
},
aiResponses: [
{
message: 'با عرض پوزش، در حال حاضر سخت‌افزار پردازش داده متصل نشده است. لطفاً با پشتیبانی فنی تماس بگیرید تا در اسرع وقت مشکل را برطرف کنیم.',
@ -145,45 +246,52 @@ export default {
welcomePatterns: [
{
welcome: 'سلام! 👋',
thanks: 'از اینکه از هوش مصنوعی حسابیکس استفاده می‌کنید، بسیار خوشحالم! من یک هوش مصنوعی مستقل هستم که به صورت کامل در سرورهای داخلی حسابیکس میزبانی می‌شوم و نیازی به سرویس‌های خارجی ندارم.'
thanks: 'به هوش مصنوعی حسابیکس خوش آمدید! من آماده کمک به شما هستم.'
},
{
welcome: 'درود! 🌟',
thanks: 'به هوش مصنوعی حسابیکس خوش آمدید! من یک دستیار هوشمند مستقل هستم که به صورت کامل در سرورهای داخلی حسابیکس میزبانی می‌شوم و آماده خدمت‌رسانی به شما هستم.'
thanks: 'خوشحالم که از هوش مصنوعی حسابیکس استفاده می‌کنید. چطور می‌توانم کمک کنم؟'
},
{
welcome: 'سلام و وقت بخیر! ✨',
thanks: 'خوشحالم که از هوش مصنوعی حسابیکس استفاده می‌کنید. من یک دستیار هوشمند مستقل هستم که به صورت کامل در سرورهای داخلی حسابیکس میزبانی می‌شوم و می‌توانم در زمینه‌های مختلف به شما کمک کنم.'
thanks: 'به عنوان دستیار هوشمند حسابیکس، آماده خدمت‌رسانی به شما هستم.'
},
{
welcome: 'به حسابیکس خوش آمدید! 🚀',
thanks: 'من هوش مصنوعی مستقل حسابیکس هستم که به صورت کامل در سرورهای داخلی میزبانی می‌شوم. خوشحالم که می‌توانم به شما در استفاده از این نرم‌افزار کمک کنم.'
thanks: 'من هوش مصنوعی حسابیکس هستم. سؤال یا درخواست خود را بنویسید.'
},
{
welcome: 'سلام! من دستیار هوشمند شما هستم 🤖',
thanks: 'به عنوان یک هوش مصنوعی مستقل که به صورت کامل در سرورهای داخلی حسابیکس میزبانی می‌شوم، آماده‌ام تا در هر زمینه‌ای که نیاز دارید به شما کمک کنم.'
welcome: 'سلام! من دستیار شما هستم 🤖',
thanks: 'خوشحالم که می‌توانم در استفاده از نرم‌افزار حسابیکس به شما کمک کنم.'
}
],
selectedPattern: null,
capabilities: 'من می‌توانم به شما در موارد زیر کمک کنم:',
capabilitiesList: [
'ساخت گزارش‌های سفارشی با استفاده از هوش مصنوعی داخلی',
'ایجاد ماژول‌های جدید بدون نیاز به کدنویسی',
'پاسخ به سؤالات شما درباره نرم‌افزار با استفاده از دانش داخلی',
'راهنمایی در استفاده از امکانات مختلف با هوش مصنوعی مستقل',
'تجزیه و تحلیل داده‌های مالی با استفاده از الگوریتم‌های داخلی',
'پیش‌بینی روندهای مالی با استفاده از هوش مصنوعی اختصاصی'
],
prompt: 'لطفاً سؤال یا درخواست خود را در باکس زیر بنویسید. من با استفاده از هوش مصنوعی مستقل خود، به شما کمک خواهم کرد.',
displayWelcome: '',
displayThanks: '',
displayCapabilities: '',
displayCapabilitiesList: [],
displayPrompt: ''
suggestions: [
'چگونه می‌توانم گزارش مالی تهیه کنم؟',
'راهنمای استفاده از نرم‌افزار',
'چگونه فاکتور جدید ایجاد کنم؟',
'مشکلات رایج و راه‌حل‌ها',
'تنظیمات سیستم'
],
connectionStatus: {
color: 'warning',
icon: 'mdi-clock',
text: 'در حال بررسی اتصال...'
}
}
},
computed: {
aiMessageCount() {
return this.userMessages.filter(msg => msg && msg.isAI).length
},
userMessageCount() {
return this.userMessages.filter(msg => typeof msg === 'string').length
}
},
async mounted() {
this.selectRandomPattern()
await this.loadAISettings()
await this.checkConnectionStatus()
await this.startTypingAnimation()
},
watch: {
@ -199,70 +307,77 @@ export default {
this.$nextTick(() => {
this.scrollToBottom()
})
},
displayThanks() {
this.$nextTick(() => {
this.scrollToBottom()
})
},
displayCapabilities() {
this.$nextTick(() => {
this.scrollToBottom()
})
},
displayCapabilitiesList: {
handler() {
this.$nextTick(() => {
this.scrollToBottom()
})
},
deep: true
},
displayPrompt() {
this.$nextTick(() => {
this.scrollToBottom()
})
}
},
methods: {
selectRandomPattern() {
const randomIndex = Math.floor(Math.random() * this.welcomePatterns.length)
this.selectedPattern = this.welcomePatterns[randomIndex]
this.welcome = this.selectedPattern.welcome
this.thanks = this.selectedPattern.thanks
async loadAISettings() {
try {
const response = await axios.get('/api/wizard/settings')
if (response.data.success) {
this.aiSettings = response.data.settings
}
} catch (error) {
console.error('Error loading AI settings:', error)
}
},
async checkConnectionStatus() {
try {
const response = await axios.get('/api/wizard/status')
console.log('Status response:', response.data) // اضافه کردن log
if (response.data.success) {
let color = 'success'
let icon = 'mdi-check-circle'
let text = 'اتصال به سرور موفق'
switch (response.data.status) {
case 'available':
color = 'success'
icon = 'mdi-check-circle'
text = 'اتصال به سرور موفق'
break
case 'disabled':
color = 'error'
icon = 'mdi-robot-off'
text = 'سرویس هوش مصنوعی غیرفعال'
break
case 'no_api_key':
color = 'warning'
icon = 'mdi-key-remove'
text = 'کلید API تنظیم نشده'
break
default:
color = 'error'
icon = 'mdi-alert-circle'
text = 'اتصال به سرور متصل نیست'
}
this.connectionStatus = {
color: color,
icon: icon,
text: text
}
console.log('Connection status updated:', this.connectionStatus) // اضافه کردن log
} else {
this.connectionStatus = {
color: 'error',
icon: 'mdi-alert-circle',
text: 'خطا در بررسی وضعیت اتصال'
}
}
} catch (error) {
console.error('Error checking connection status:', error)
this.connectionStatus = {
color: 'error',
icon: 'mdi-alert-circle',
text: 'خطا در ارتباط با سرور'
}
}
},
async startTypingAnimation() {
// تایپ پیام خوشآمدگویی
await this.typeText(this.welcome, (text) => {
// تایپ یک پیام ساده
await this.typeText('سلام! من دستیار هوشمند حسابیکس هستم. چطور می‌توانم کمک کنم؟', (text) => {
this.displayWelcome = text
}, 15)
await this.delay(100)
// تایپ پیام تشکر
await this.typeText(this.thanks, (text) => {
this.displayThanks = text
}, 15)
await this.delay(100)
// تایپ معرفی قابلیتها
await this.typeText(this.capabilities, (text) => {
this.displayCapabilities = text
}, 15)
await this.delay(100)
// تایپ لیست قابلیتها
for (const capability of this.capabilitiesList) {
this.displayCapabilitiesList.push('')
await this.typeText(capability, (text) => {
this.displayCapabilitiesList[this.displayCapabilitiesList.length - 1] = text
}, 15)
await this.delay(50)
}
// تایپ پیام نهایی
await this.typeText(this.prompt, (text) => {
this.displayPrompt = text
}, 15)
}, 30)
this.isTyping = false
},
@ -280,28 +395,80 @@ export default {
async sendMessage() {
if (!this.userMessage.trim()) return
// بررسی فعال بودن هوش مصنوعی
if (!this.aiSettings.aiEnabled) {
this.userMessages.push({
text: 'سرویس هوش مصنوعی غیرفعال است. لطفاً ابتدا آن را در تنظیمات سیستم فعال کنید.',
isAI: true,
isDetails: true
})
return
}
const message = this.userMessage.trim()
this.userMessages.push(message)
this.userMessage = ''
this.isLoading = true
// انتخاب پاسخ رندوم
const randomResponse = this.aiResponses[Math.floor(Math.random() * this.aiResponses.length)]
try {
// ارسال درخواست به بکاند
const response = await axios.post('/api/wizard/talk', {
message: message,
options: {
model: this.aiSettings.aiModel,
temperature: 0.7,
max_tokens: 1000
}
})
// نمایش پاسخ اصلی
await this.delay(1000)
this.userMessages.push({
text: randomResponse.message,
isAI: true
})
if (response.data.success) {
// نمایش پاسخ هوش مصنوعی
this.userMessages.push({
text: response.data.response,
isAI: true
})
// نمایش جزئیات
await this.delay(500)
this.userMessages.push({
text: randomResponse.details,
isAI: true,
isDetails: true
})
// نمایش اطلاعات اضافی در صورت وجود
if (response.data.usage || response.data.cost) {
let detailsText = ''
if (response.data.usage) {
detailsText += `توکن‌های ورودی: ${response.data.usage.prompt_tokens || 0}\n`
detailsText += `توکن‌های خروجی: ${response.data.usage.completion_tokens || 0}\n`
detailsText += `کل توکن‌ها: ${response.data.usage.total_tokens || 0}\n`
}
if (response.data.cost) {
detailsText += `هزینه: ${response.data.cost.toLocaleString('fa-IR')} ریال`
}
if (response.data.model) {
detailsText += `\nمدل: ${response.data.model}`
}
if (detailsText) {
this.userMessages.push({
text: detailsText,
isAI: true,
isDetails: true
})
}
}
} else {
// نمایش خطا
this.userMessages.push({
text: `خطا: ${response.data.error}`,
isAI: true,
isDetails: true
})
}
} catch (error) {
console.error('Error sending message:', error)
// نمایش خطای شبکه
this.userMessages.push({
text: 'خطا در ارتباط با سرور. لطفاً دوباره تلاش کنید.',
isAI: true,
isDetails: true
})
}
this.isLoading = false
this.$nextTick(() => {
@ -316,6 +483,22 @@ export default {
behavior: 'smooth'
})
}
},
clearChat() {
this.userMessages = []
this.displayWelcome = ''
this.isTyping = true
this.startTypingAnimation()
},
useSuggestion(suggestion) {
this.userMessage = suggestion
},
formatTime(date) {
return new Intl.DateTimeFormat('fa-IR', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
}).format(date)
}
}
}
@ -391,6 +574,34 @@ export default {
line-height: 1.5;
}
.message-time {
font-size: 0.75rem;
color: rgba(0, 0, 0, 0.5);
margin-top: 4px;
text-align: right;
}
.user-message .message-time {
color: rgba(255, 255, 255, 0.7);
}
/* استایل برای پیش‌نمایش پیام */
.message-preview {
margin-top: 8px;
padding: 8px;
background-color: #f5f5f5;
border-radius: 8px;
border: 1px solid #e0e0e0;
}
.preview-content {
font-size: 0.875rem;
color: #424242;
line-height: 1.4;
max-height: 60px;
overflow-y: auto;
}
.input-container {
padding: 16px;
background-color: white;
@ -475,4 +686,96 @@ export default {
.messages-container::-webkit-scrollbar-thumb:hover {
background: rgba(0, 0, 0, 0.3);
}
/* استایل برای کارت اطلاعات قیمت‌گذاری */
.pricing-info {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
.pricing-info .v-card {
border: 1px solid rgba(0, 0, 0, 0.1);
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
}
/* انیمیشن برای chip وضعیت */
.v-chip {
transition: all 0.3s ease;
}
.v-chip:hover {
transform: scale(1.05);
}
/* بهبود ظاهر toolbar */
.v-toolbar {
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
/* استایل برای پیام‌های خطا */
.message-content.error-message {
background-color: #ffebee !important;
border: 1px solid #ffcdd2;
color: #c62828;
}
/* استایل برای پیام‌های موفقیت */
.message-content.success-message {
background-color: #e8f5e8 !important;
border: 1px solid #c8e6c9;
color: #2e7d32;
}
/* استایل برای پیشنهادات */
.suggestions-container {
padding: 16px;
background-color: #f8f9fa;
border-top: 1px solid #e9ecef;
border-bottom: 1px solid #e9ecef;
}
.suggestion-chip {
cursor: pointer;
transition: all 0.3s ease;
background-color: white;
border: 1px solid #dee2e6;
margin: 2px;
}
.suggestion-chip:hover {
background-color: #e3f2fd !important;
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-color: #1a237e;
}
/* بهبود ظاهر input container */
.input-container {
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
border-top: 2px solid #e9ecef;
padding: 16px;
}
/* استایل برای آمار استفاده */
.usage-stats {
background: linear-gradient(135deg, #e8f5e8 0%, #c8e6c9 100%);
}
.usage-stats .v-card {
border: 1px solid rgba(0, 0, 0, 0.1);
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
}
/* انیمیشن برای کارت‌های آمار */
.usage-stats .v-card,
.pricing-info .v-card {
transition: all 0.3s ease;
}
.usage-stats .v-card:hover,
.pricing-info .v-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
</style>