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['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]);
|
||||
|
|
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: [
|
||||
{ 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>
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue