forked from morrning/hesabixCore
progress in wizard
This commit is contained in:
parent
574bdd1a5b
commit
186229c848
|
@ -459,6 +459,17 @@ class AdminController extends AbstractController
|
||||||
$resp['cardToShebaFee'] = $registryMGR->get('system', key: 'cardToShebaFee');
|
$resp['cardToShebaFee'] = $registryMGR->get('system', key: 'cardToShebaFee');
|
||||||
$resp['enableAccountToSheba'] = $registryMGR->get('system', key: 'enableAccountToSheba');
|
$resp['enableAccountToSheba'] = $registryMGR->get('system', key: 'enableAccountToSheba');
|
||||||
$resp['accountToShebaFee'] = $registryMGR->get('system', key: 'accountToShebaFee');
|
$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);
|
return $this->json($resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,6 +503,25 @@ class AdminController extends AbstractController
|
||||||
$registryMGR->update('system', 'cardToShebaFee', $params['cardToShebaFee']);
|
$registryMGR->update('system', 'cardToShebaFee', $params['cardToShebaFee']);
|
||||||
$registryMGR->update('system', 'enableAccountToSheba', $params['enableAccountToSheba']);
|
$registryMGR->update('system', 'enableAccountToSheba', $params['enableAccountToSheba']);
|
||||||
$registryMGR->update('system', 'accountToShebaFee', $params['accountToShebaFee']);
|
$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->persist($item);
|
||||||
$entityManager->flush();
|
$entityManager->flush();
|
||||||
return $this->json(['result' => 1]);
|
return $this->json(['result' => 1]);
|
||||||
|
|
192
hesabixCore/src/Controller/wizardController.php
Normal file
192
hesabixCore/src/Controller/wizardController.php
Normal 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()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
377
hesabixCore/src/Service/AIService.php
Normal file
377
hesabixCore/src/Service/AIService.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,8 @@ export default defineComponent({
|
||||||
tabs: [
|
tabs: [
|
||||||
{ title: 'تنظیمات پایه', icon: 'mdi-cog' },
|
{ title: 'تنظیمات پایه', icon: 'mdi-cog' },
|
||||||
{ title: 'درگاههای پرداخت', icon: 'mdi-credit-card' },
|
{ title: 'درگاههای پرداخت', icon: 'mdi-credit-card' },
|
||||||
{ title: 'پنل استعلامات', icon: 'mdi-magnify' }
|
{ title: 'پنل استعلامات', icon: 'mdi-magnify' },
|
||||||
|
{ title: 'جادوگر هوش مصنوعی', icon: 'mdi-robot' }
|
||||||
],
|
],
|
||||||
gatepays: [
|
gatepays: [
|
||||||
{
|
{
|
||||||
|
@ -62,6 +63,79 @@ export default defineComponent({
|
||||||
accountToShebaFee: 0,
|
accountToShebaFee: 0,
|
||||||
},
|
},
|
||||||
loading: true,
|
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: {
|
methods: {
|
||||||
|
@ -80,6 +154,16 @@ export default defineComponent({
|
||||||
activeGateway: data.activeGateway || 'zarinpal',
|
activeGateway: data.activeGateway || 'zarinpal',
|
||||||
inquiryPanel: data.inquiryPanel || 'zohal'
|
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;
|
this.loading = false;
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -130,7 +214,20 @@ export default defineComponent({
|
||||||
return;
|
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;
|
this.loading = false;
|
||||||
if (resp.data.result == 1) {
|
if (resp.data.result == 1) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
|
@ -571,6 +668,307 @@ export default defineComponent({
|
||||||
</v-row>
|
</v-row>
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-window-item>
|
</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-window>
|
||||||
</v-card>
|
</v-card>
|
||||||
</v-container>
|
</v-container>
|
||||||
|
@ -646,4 +1044,88 @@ export default defineComponent({
|
||||||
.service-card:hover .v-icon {
|
.service-card:hover .v-icon {
|
||||||
transform: scale(1.1);
|
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>
|
</style>
|
|
@ -1,8 +1,95 @@
|
||||||
<template>
|
<template>
|
||||||
<v-toolbar color="toolbar" title="هوش مصنوعی حسابیکس">
|
<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>
|
</v-toolbar>
|
||||||
<div class="page-container">
|
<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">
|
<div class="content-container">
|
||||||
<v-card class="chat-container" elevation="0">
|
<v-card class="chat-container" elevation="0">
|
||||||
<div class="chat-box">
|
<div class="chat-box">
|
||||||
|
@ -17,51 +104,13 @@
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<template v-for="(message, index) in userMessages" :key="index">
|
||||||
<!-- پیام کاربر -->
|
<!-- پیام کاربر -->
|
||||||
<div class="message user-message" v-if="typeof message === 'string'">
|
<div class="message user-message" v-if="typeof message === 'string'">
|
||||||
<div class="message-content">
|
<div class="message-content">
|
||||||
<div class="message-text">{{ message }}</div>
|
<div class="message-text">{{ message }}</div>
|
||||||
|
<div class="message-time">{{ formatTime(new Date()) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<v-avatar color="grey lighten-2" size="36" class="ml-2">
|
<v-avatar color="grey lighten-2" size="36" class="ml-2">
|
||||||
<v-icon color="grey darken-1" size="20">mdi-account</v-icon>
|
<v-icon color="grey darken-1" size="20">mdi-account</v-icon>
|
||||||
|
@ -75,6 +124,7 @@
|
||||||
</v-avatar>
|
</v-avatar>
|
||||||
<div class="message-content" :class="{ 'details-message': message.isDetails }">
|
<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-text" v-html="message.text.replace(/\n/g, '<br>')"></div>
|
||||||
|
<div class="message-time">{{ formatTime(new Date()) }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -98,12 +148,53 @@
|
||||||
|
|
||||||
<!-- باکس ورودی پیام -->
|
<!-- باکس ورودی پیام -->
|
||||||
<div class="input-container">
|
<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
|
<v-textarea v-model="userMessage" placeholder="پیام خود را اینجا بنویسید..." rows="1" auto-grow hide-details
|
||||||
variant="plain" class="message-input" @keydown.enter.prevent="sendMessage"></v-textarea>
|
variant="plain" class="message-input" @keydown.enter.prevent="sendMessage"></v-textarea>
|
||||||
<v-btn color="#1a237e" icon :loading="isLoading" @click="sendMessage" class="send-button"
|
<v-btn color="#1a237e" icon :loading="isLoading" @click="sendMessage" class="send-button"
|
||||||
:disabled="!userMessage.trim()">
|
:disabled="!userMessage.trim()">
|
||||||
<v-icon>mdi-send</v-icon>
|
<v-icon>mdi-send</v-icon>
|
||||||
</v-btn>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</v-card>
|
</v-card>
|
||||||
|
@ -112,6 +203,8 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'WizardHome',
|
name: 'WizardHome',
|
||||||
data() {
|
data() {
|
||||||
|
@ -120,6 +213,14 @@ export default {
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
isTyping: true,
|
isTyping: true,
|
||||||
userMessages: [],
|
userMessages: [],
|
||||||
|
aiSettings: {
|
||||||
|
aiEnabled: false,
|
||||||
|
aiAgentSource: 'gapgpt',
|
||||||
|
aiModel: 'gpt-4o',
|
||||||
|
inputTokenPrice: 0,
|
||||||
|
outputTokenPrice: 0,
|
||||||
|
aiPrompt: ''
|
||||||
|
},
|
||||||
aiResponses: [
|
aiResponses: [
|
||||||
{
|
{
|
||||||
message: 'با عرض پوزش، در حال حاضر سختافزار پردازش داده متصل نشده است. لطفاً با پشتیبانی فنی تماس بگیرید تا در اسرع وقت مشکل را برطرف کنیم.',
|
message: 'با عرض پوزش، در حال حاضر سختافزار پردازش داده متصل نشده است. لطفاً با پشتیبانی فنی تماس بگیرید تا در اسرع وقت مشکل را برطرف کنیم.',
|
||||||
|
@ -145,45 +246,52 @@ export default {
|
||||||
welcomePatterns: [
|
welcomePatterns: [
|
||||||
{
|
{
|
||||||
welcome: 'سلام! 👋',
|
welcome: 'سلام! 👋',
|
||||||
thanks: 'از اینکه از هوش مصنوعی حسابیکس استفاده میکنید، بسیار خوشحالم! من یک هوش مصنوعی مستقل هستم که به صورت کامل در سرورهای داخلی حسابیکس میزبانی میشوم و نیازی به سرویسهای خارجی ندارم.'
|
thanks: 'به هوش مصنوعی حسابیکس خوش آمدید! من آماده کمک به شما هستم.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
welcome: 'درود! 🌟',
|
welcome: 'درود! 🌟',
|
||||||
thanks: 'به هوش مصنوعی حسابیکس خوش آمدید! من یک دستیار هوشمند مستقل هستم که به صورت کامل در سرورهای داخلی حسابیکس میزبانی میشوم و آماده خدمترسانی به شما هستم.'
|
thanks: 'خوشحالم که از هوش مصنوعی حسابیکس استفاده میکنید. چطور میتوانم کمک کنم؟'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
welcome: 'سلام و وقت بخیر! ✨',
|
welcome: 'سلام و وقت بخیر! ✨',
|
||||||
thanks: 'خوشحالم که از هوش مصنوعی حسابیکس استفاده میکنید. من یک دستیار هوشمند مستقل هستم که به صورت کامل در سرورهای داخلی حسابیکس میزبانی میشوم و میتوانم در زمینههای مختلف به شما کمک کنم.'
|
thanks: 'به عنوان دستیار هوشمند حسابیکس، آماده خدمترسانی به شما هستم.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
welcome: 'به حسابیکس خوش آمدید! 🚀',
|
welcome: 'به حسابیکس خوش آمدید! 🚀',
|
||||||
thanks: 'من هوش مصنوعی مستقل حسابیکس هستم که به صورت کامل در سرورهای داخلی میزبانی میشوم. خوشحالم که میتوانم به شما در استفاده از این نرمافزار کمک کنم.'
|
thanks: 'من هوش مصنوعی حسابیکس هستم. سؤال یا درخواست خود را بنویسید.'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
welcome: 'سلام! من دستیار هوشمند شما هستم 🤖',
|
welcome: 'سلام! من دستیار شما هستم 🤖',
|
||||||
thanks: 'به عنوان یک هوش مصنوعی مستقل که به صورت کامل در سرورهای داخلی حسابیکس میزبانی میشوم، آمادهام تا در هر زمینهای که نیاز دارید به شما کمک کنم.'
|
thanks: 'خوشحالم که میتوانم در استفاده از نرمافزار حسابیکس به شما کمک کنم.'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
selectedPattern: null,
|
selectedPattern: null,
|
||||||
capabilities: 'من میتوانم به شما در موارد زیر کمک کنم:',
|
|
||||||
capabilitiesList: [
|
|
||||||
'ساخت گزارشهای سفارشی با استفاده از هوش مصنوعی داخلی',
|
|
||||||
'ایجاد ماژولهای جدید بدون نیاز به کدنویسی',
|
|
||||||
'پاسخ به سؤالات شما درباره نرمافزار با استفاده از دانش داخلی',
|
|
||||||
'راهنمایی در استفاده از امکانات مختلف با هوش مصنوعی مستقل',
|
|
||||||
'تجزیه و تحلیل دادههای مالی با استفاده از الگوریتمهای داخلی',
|
|
||||||
'پیشبینی روندهای مالی با استفاده از هوش مصنوعی اختصاصی'
|
|
||||||
],
|
|
||||||
prompt: 'لطفاً سؤال یا درخواست خود را در باکس زیر بنویسید. من با استفاده از هوش مصنوعی مستقل خود، به شما کمک خواهم کرد.',
|
|
||||||
displayWelcome: '',
|
displayWelcome: '',
|
||||||
displayThanks: '',
|
suggestions: [
|
||||||
displayCapabilities: '',
|
'چگونه میتوانم گزارش مالی تهیه کنم؟',
|
||||||
displayCapabilitiesList: [],
|
'راهنمای استفاده از نرمافزار',
|
||||||
displayPrompt: ''
|
'چگونه فاکتور جدید ایجاد کنم؟',
|
||||||
|
'مشکلات رایج و راهحلها',
|
||||||
|
'تنظیمات سیستم'
|
||||||
|
],
|
||||||
|
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() {
|
async mounted() {
|
||||||
this.selectRandomPattern()
|
await this.loadAISettings()
|
||||||
|
await this.checkConnectionStatus()
|
||||||
await this.startTypingAnimation()
|
await this.startTypingAnimation()
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -199,70 +307,77 @@ export default {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.scrollToBottom()
|
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: {
|
methods: {
|
||||||
selectRandomPattern() {
|
async loadAISettings() {
|
||||||
const randomIndex = Math.floor(Math.random() * this.welcomePatterns.length)
|
try {
|
||||||
this.selectedPattern = this.welcomePatterns[randomIndex]
|
const response = await axios.get('/api/wizard/settings')
|
||||||
this.welcome = this.selectedPattern.welcome
|
if (response.data.success) {
|
||||||
this.thanks = this.selectedPattern.thanks
|
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() {
|
async startTypingAnimation() {
|
||||||
// تایپ پیام خوشآمدگویی
|
// تایپ یک پیام ساده
|
||||||
await this.typeText(this.welcome, (text) => {
|
await this.typeText('سلام! من دستیار هوشمند حسابیکس هستم. چطور میتوانم کمک کنم؟', (text) => {
|
||||||
this.displayWelcome = text
|
this.displayWelcome = text
|
||||||
}, 15)
|
}, 30)
|
||||||
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)
|
|
||||||
|
|
||||||
this.isTyping = false
|
this.isTyping = false
|
||||||
},
|
},
|
||||||
|
@ -280,28 +395,80 @@ export default {
|
||||||
async sendMessage() {
|
async sendMessage() {
|
||||||
if (!this.userMessage.trim()) return
|
if (!this.userMessage.trim()) return
|
||||||
|
|
||||||
|
// بررسی فعال بودن هوش مصنوعی
|
||||||
|
if (!this.aiSettings.aiEnabled) {
|
||||||
|
this.userMessages.push({
|
||||||
|
text: 'سرویس هوش مصنوعی غیرفعال است. لطفاً ابتدا آن را در تنظیمات سیستم فعال کنید.',
|
||||||
|
isAI: true,
|
||||||
|
isDetails: true
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const message = this.userMessage.trim()
|
const message = this.userMessage.trim()
|
||||||
this.userMessages.push(message)
|
this.userMessages.push(message)
|
||||||
this.userMessage = ''
|
this.userMessage = ''
|
||||||
this.isLoading = true
|
this.isLoading = true
|
||||||
|
|
||||||
// انتخاب پاسخ رندوم
|
try {
|
||||||
const randomResponse = this.aiResponses[Math.floor(Math.random() * this.aiResponses.length)]
|
// ارسال درخواست به بکاند
|
||||||
|
const response = await axios.post('/api/wizard/talk', {
|
||||||
|
message: message,
|
||||||
|
options: {
|
||||||
|
model: this.aiSettings.aiModel,
|
||||||
|
temperature: 0.7,
|
||||||
|
max_tokens: 1000
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// نمایش پاسخ اصلی
|
if (response.data.success) {
|
||||||
await this.delay(1000)
|
// نمایش پاسخ هوش مصنوعی
|
||||||
this.userMessages.push({
|
this.userMessages.push({
|
||||||
text: randomResponse.message,
|
text: response.data.response,
|
||||||
isAI: true
|
isAI: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// نمایش جزئیات
|
// نمایش اطلاعات اضافی در صورت وجود
|
||||||
await this.delay(500)
|
if (response.data.usage || response.data.cost) {
|
||||||
this.userMessages.push({
|
let detailsText = ''
|
||||||
text: randomResponse.details,
|
if (response.data.usage) {
|
||||||
isAI: true,
|
detailsText += `توکنهای ورودی: ${response.data.usage.prompt_tokens || 0}\n`
|
||||||
isDetails: true
|
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.isLoading = false
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
@ -316,6 +483,22 @@ export default {
|
||||||
behavior: 'smooth'
|
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;
|
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 {
|
.input-container {
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
@ -475,4 +686,96 @@ export default {
|
||||||
.messages-container::-webkit-scrollbar-thumb:hover {
|
.messages-container::-webkit-scrollbar-thumb:hover {
|
||||||
background: rgba(0, 0, 0, 0.3);
|
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>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue