diff --git a/hesabixCore/src/AiTool/PersonService.php b/hesabixCore/src/AiTool/PersonService.php new file mode 100644 index 0000000..3d2ddc7 --- /dev/null +++ b/hesabixCore/src/AiTool/PersonService.php @@ -0,0 +1,44 @@ +em = $em; + $this->cogPersonService = $cogPersonService; + } + + /** + * دریافت اطلاعات یک شخص بر اساس کد و اطلاعات دسترسی + */ + public function getPersonInfoByCode($code, $acc): array + { + if (!$code) { + return [ + 'error' => 'کد شخص الزامی است' + ]; + } + if (!$acc) { + return [ + 'error' => 'اطلاعات دسترسی (acc) الزامی است' + ]; + } + try { + return $this->cogPersonService->getPersonInfo($code, $acc); + } catch (\Exception $e) { + return [ + 'error' => 'خطا در دریافت اطلاعات شخص: ' . $e->getMessage() + ]; + } + } + + +} \ No newline at end of file diff --git a/hesabixCore/src/Cog/PersonService.php b/hesabixCore/src/Cog/PersonService.php index cb29b8e..a8d8e86 100644 --- a/hesabixCore/src/Cog/PersonService.php +++ b/hesabixCore/src/Cog/PersonService.php @@ -7,7 +7,6 @@ use App\Entity\Person; use App\Entity\PersonType; use App\Entity\HesabdariRow; use App\Service\Explore; -use App\Service\Access; /** * سرویس مدیریت اشخاص @@ -17,12 +16,12 @@ use App\Service\Access; class PersonService { private EntityManagerInterface $entityManager; - private Access $access; + private array $access; /** * سازنده سرویس */ - public function __construct(EntityManagerInterface $entityManager, Access $access) + public function __construct(EntityManagerInterface $entityManager, array $access) { $this->entityManager = $entityManager; $this->access = $access; diff --git a/hesabixCore/src/Controller/wizardController.php b/hesabixCore/src/Controller/wizardController.php index 1cd2ab8..29e94d4 100644 --- a/hesabixCore/src/Controller/wizardController.php +++ b/hesabixCore/src/Controller/wizardController.php @@ -32,7 +32,8 @@ class wizardController extends AbstractController Log $log ): JsonResponse { - try { + + $acc = $access->hasRole('join'); if (!$acc) { return $this->json([ @@ -45,7 +46,8 @@ class wizardController extends AbstractController } // بررسی دسترسی هوش مصنوعی - if (!$acc['ai']) { + $acc = $access->hasRole('ai'); + if (!$acc) { return $this->json([ 'success' => false, 'error' => 'شما دسترسی استفاده از هوش مصنوعی را ندارید', @@ -96,7 +98,11 @@ class wizardController extends AbstractController 'showChargeButton' => true, 'debug_info' => [ 'balance' => $currentBalance, - 'required' => $estimatedCost + 'required' => $estimatedCost, + 'business' => [ + 'id' => $business->getId(), + 'name' => $business->getName(), + ] ] ]); } @@ -142,15 +148,27 @@ class wizardController extends AbstractController } } + // پاک‌سازی خروجی از اشیای Doctrine (Business, User و ...) + array_walk_recursive($response, function (&$item) { + if (is_object($item) && method_exists($item, 'getId')) { + $item = $item->getId(); + } + }); return $this->json($response); } else { - return $this->json([ + $errorResponse = [ 'success' => false, 'error' => $result['error'] ?? 'خطای نامشخص در سرویس هوش مصنوعی', 'debug_info' => $result['debug_info'] ?? ['fallback' => 'no debug info from service', 'result' => $result] - ]); + ]; + array_walk_recursive($errorResponse, function (&$item) { + if (is_object($item) && method_exists($item, 'getId')) { + $item = $item->getId(); + } + }); + return $this->json($errorResponse); } - + try { } catch (\Exception $e) { return $this->json([ 'success' => false, diff --git a/hesabixCore/src/Service/AGI/AGIService.php b/hesabixCore/src/Service/AGI/AGIService.php index b8a8408..203f0b9 100644 --- a/hesabixCore/src/Service/AGI/AGIService.php +++ b/hesabixCore/src/Service/AGI/AGIService.php @@ -64,15 +64,13 @@ class AGIService 'service_status' => $status, 'inputs' => [ 'message' => $message, - 'business' => $business, - 'user' => $user, 'conversationId' => $conversationId ] ] ]; } - try { + // مدیریت گفتگو و تاریخچه $conversation = $this->manageConversation($conversationId, $business, $user, $message); $conversationHistory = $this->getConversationHistory($conversation); @@ -80,8 +78,8 @@ class AGIService // ذخیره پیام کاربر $this->saveUserMessage($conversation, $message); - // ساخت پرامپ هوشمند - $prompt = $this->buildSmartPrompt($message, $business, $conversationHistory); + // فقط سوال کاربر به عنوان prompt + $userPrompt = $message; $service = $this->getAIAgentSource(); $apiKey = $this->getAIApiKey($service); @@ -94,16 +92,14 @@ class AGIService 'service' => $service, 'inputs' => [ 'message' => $message, - 'business' => $business, - 'user' => $user, 'conversationId' => $conversationId ] ] ]; } - // ارسال درخواست با function calling - $result = $this->sendToAIServiceWithFunctionCalling($prompt, $apiKey, $service, $conversationHistory, $acc); + // ارسال درخواست با function calling (فقط سوال کاربر) + $result = $this->sendToAIServiceWithFunctionCalling($userPrompt, $apiKey, $service, $conversationHistory, $acc); if (!$result['success']) { return $result; @@ -129,18 +125,8 @@ class AGIService 'tool_results' => $result['tool_results'] ?? [] ] ]; - + try { } catch (\Exception $e) { - $this->log->error('خطا در ارسال درخواست به هوش مصنوعی: ' . $e->getMessage(), [ - 'context' => 'AGIService::sendRequest', - 'message' => $message, - 'business' => $business, - 'user' => $user, - 'conversationId' => $conversationId, - 'exception' => $e->getMessage(), - 'trace' => $e->getTraceAsString() - ]); - return [ 'success' => false, 'error' => 'خطا در پردازش درخواست: ' . $e->getMessage(), @@ -149,8 +135,6 @@ class AGIService 'exception' => $e->getMessage(), 'inputs' => [ 'message' => $message, - 'business' => $business, - 'user' => $user, 'conversationId' => $conversationId ] ] @@ -166,33 +150,36 @@ class AGIService $urls = $this->getServiceUrls($service); $model = $this->getAIModel(); - // ساخت messages با تاریخچه - $messages = []; - - // اضافه کردن تاریخچه گفتگو + // پیام system شامل قوانین خروجی و مثال دقیق + $systemPrompt = "شما دستیار هوشمند حسابیکس هستید. فقط پاسخ را به صورت JSON مطابق مثال خروجی بده. اگر نیاز به ابزار داشتی، از function calling استفاده کن." + . $this->promptService->getOutputFormatPrompt(); + $messages = [ + [ + 'role' => 'system', + 'content' => $systemPrompt + ] + ]; + // تاریخچه گفتگو foreach ($conversationHistory as $historyItem) { $messages[] = [ 'role' => $historyItem['role'], 'content' => $historyItem['content'] ]; } - - // اضافه کردن پیام فعلی + // پیام user فقط سوال فعلی $messages[] = [ 'role' => 'user', 'content' => $prompt ]; - // تعریف ابزارهای موجود $tools = $this->buildToolsFromPromptServices(); - $data = [ 'model' => $model, 'messages' => $messages, 'tools' => $tools, - 'tool_choice' => 'auto', // اجازه انتخاب ابزار به مدل + 'tool_choice' => 'auto', 'max_tokens' => 12000, - 'temperature' => 0.1 + 'temperature' => 0.9 ]; $maxIterations = 5; // حداکثر تعداد تکرار برای جلوگیری از حلقه بی‌نهایت @@ -220,7 +207,12 @@ class AGIService 'context' => 'sendToAIServiceWithFunctionCalling', 'url_list' => $urls, 'data' => $data, - 'iteration' => $iteration + 'iteration' => $iteration, + 'result' => $result, + 'apiKey' => $apiKey, + 'service' => $service, + 'conversationHistory' => $conversationHistory, + ] ]; } @@ -313,63 +305,22 @@ class AGIService try { switch ($tool) { case 'getPersonInfo': - // استفاده مستقیم از سرویس Cog\PersonService - return $this->callGetPersonInfoFromCog($params); + // استفاده مستقیم از سرویس جدید + $cogPersonService = new \App\Cog\PersonService($this->em, $params['acc'] ?? null); + $personService = new \App\AiTool\PersonService($this->em, $cogPersonService); + return $personService->getPersonInfoByCode($params['code'] ?? null, $params['acc'] ?? null); default: return [ 'error' => 'ابزار ناشناخته: ' . $tool ]; } } catch (\Exception $e) { - $this->log->error('خطا در اجرای ابزار: ' . $e->getMessage(), [ - 'context' => 'AGIService::callTool', - 'tool' => $tool, - 'params' => $params, - 'exception' => $e->getMessage() - ]); - return [ 'error' => 'خطا در اجرای ابزار: ' . $e->getMessage() ]; } } - /** - * اجرای ابزار getPersonInfo با استفاده از سرویس Cog\PersonService - */ - private function callGetPersonInfoFromCog(array $params) - { - $code = $params['code'] ?? null; - if (!$code) { - return [ - 'error' => 'کد شخص الزامی است' - ]; - } - - try { - // دریافت اطلاعات دسترسی (acc) از context یا پارامترها - $acc = $params['acc'] ?? null; - if (!$acc) { - return [ - 'error' => 'اطلاعات دسترسی (acc) الزامی است' - ]; - } - // استفاده از سرویس Cog\PersonService - $personService = new \App\Cog\PersonService($this->em, $this->provider->getAccessService()); - $result = $personService->getPersonInfo($code, $acc); - return $result; - } catch (\Exception $e) { - $this->log->error('خطا در دریافت اطلاعات شخص از Cog: ' . $e->getMessage(), [ - 'context' => 'AGIService::callGetPersonInfoFromCog', - 'code' => $code, - 'exception' => $e->getMessage() - ]); - return [ - 'error' => 'خطا در دریافت اطلاعات شخص: ' . $e->getMessage() - ]; - } - } - /** * ساخت پرامپ هوشمند */ @@ -420,9 +371,19 @@ class AGIService */ private function makeHttpRequest(string $url, array $data, string $apiKey): array { + $requestJson = json_encode($data, JSON_UNESCAPED_UNICODE); $debugInfo = [ 'request_url' => $url, 'request_data' => $data, + 'request_headers' => [ + 'Content-Type' => 'application/json', + 'Authorization' => 'Bearer ' . $apiKey, + ], + 'api_key' => $apiKey, + 'model' => $data['model'] ?? null, + 'request_json' => $requestJson, + 'request_time' => date('Y-m-d H:i:s'), + 'request_size_bytes' => strlen($requestJson), ]; try { @@ -440,6 +401,10 @@ class AGIService $content = $response->getContent(false); // false: throw exception on 4xx/5xx نمی‌دهد $debugInfo['http_code'] = $statusCode; $debugInfo['raw_response'] = $content; + $debugInfo['response_headers'] = $response->getHeaders(false); + $debugInfo['response_time'] = date('Y-m-d H:i:s'); + $debugInfo['response_size_bytes'] = strlen($content); + $debugInfo['response_sample'] = mb_substr($content, 0, 500, 'UTF-8'); if ($statusCode !== 200) { $debugInfo['http_error_message'] = $this->getHttpErrorMessage($statusCode); @@ -461,6 +426,7 @@ class AGIService ]; } + $debugInfo['response_data'] = $responseData; return [ 'success' => true, 'data' => $responseData, @@ -629,13 +595,6 @@ class AGIService return (bool) ($this->registryMGR->get('system', 'aiEnabled') ?? false); } - /** - * دریافت پرامپ هوش مصنوعی - */ - public function getAIPrompt(): string - { - return $this->registryMGR->get('system', 'aiPrompt') ?? 'شما یک دستیار هوشمند برای سیستم حسابداری هستید.'; - } /** * مدیریت گفتگو - ایجاد یا بازیابی گفتگوی موجود @@ -833,6 +792,19 @@ class AGIService */ private function buildToolsFromPromptServices(): array { - return $this->promptService->getAllTools(); + // بر اساس آخرین مستندات OpenAI و OpenRouter، باید هر ابزار به صورت زیر باشد: + // [ + // 'type' => 'function', + // 'function' => [ + // 'name' => ..., 'description' => ..., 'parameters' => [...] + // ] + // ] + // این ساختار در PromptService رعایت شده اما اینجا یکبار دیگر چک و لاگ می‌کنیم + $tools = $this->promptService->getAllTools(); + // ابزارها را در لاگ دیباگ ذخیره کن + if (method_exists($this->log, 'debug')) { + $this->log->debug('AIService tools', ['tools' => $tools]); + } + return $tools; } } \ No newline at end of file diff --git a/hesabixCore/src/Service/AGI/Promps/BankPromptService.php b/hesabixCore/src/Service/AGI/Promps/BankPromptService.php index 793471e..5b1c288 100644 --- a/hesabixCore/src/Service/AGI/Promps/BankPromptService.php +++ b/hesabixCore/src/Service/AGI/Promps/BankPromptService.php @@ -26,6 +26,13 @@ class BankPromptService $bankAccountInfoData = json_decode($bankAccountInfoPrompt, true); if ($bankAccountInfoData) { + // اصلاح ساختار properties + $properties = [ + 'code' => [ + 'type' => 'string', + 'description' => 'Bank account code (e.g., 1001, 1002)' + ] + ]; $tools[] = [ 'type' => 'function', 'function' => [ @@ -33,8 +40,8 @@ class BankPromptService 'description' => $bankAccountInfoData['description'], 'parameters' => [ 'type' => 'object', - 'properties' => $bankAccountInfoData['input'], - 'required' => array_keys($bankAccountInfoData['input']) + 'properties' => $properties, + 'required' => ['code'] ] ] ]; diff --git a/hesabixCore/src/Service/AGI/Promps/InventoryPromptService.php b/hesabixCore/src/Service/AGI/Promps/InventoryPromptService.php index 64c3499..8a9c990 100644 --- a/hesabixCore/src/Service/AGI/Promps/InventoryPromptService.php +++ b/hesabixCore/src/Service/AGI/Promps/InventoryPromptService.php @@ -26,6 +26,13 @@ class InventoryPromptService $itemInfoData = json_decode($itemInfoPrompt, true); if ($itemInfoData) { + // اصلاح ساختار properties + $properties = [ + 'code' => [ + 'type' => 'string', + 'description' => 'Item code (e.g., 1001, 1002)' + ] + ]; $tools[] = [ 'type' => 'function', 'function' => [ @@ -33,8 +40,8 @@ class InventoryPromptService 'description' => $itemInfoData['description'], 'parameters' => [ 'type' => 'object', - 'properties' => $itemInfoData['input'], - 'required' => array_keys($itemInfoData['input']) + 'properties' => $properties, + 'required' => ['code'] ] ] ]; diff --git a/hesabixCore/src/Service/AGI/Promps/PersonPromptService.php b/hesabixCore/src/Service/AGI/Promps/PersonPromptService.php index 771e261..c0a6645 100644 --- a/hesabixCore/src/Service/AGI/Promps/PersonPromptService.php +++ b/hesabixCore/src/Service/AGI/Promps/PersonPromptService.php @@ -26,6 +26,13 @@ class PersonPromptService $personInfoData = json_decode($personInfoPrompt, true); if ($personInfoData) { + // اصلاح ساختار properties + $properties = [ + 'code' => [ + 'type' => 'string', + 'description' => 'Person code (e.g., 1001, 1002)' + ] + ]; $tools[] = [ 'type' => 'function', 'function' => [ @@ -33,8 +40,8 @@ class PersonPromptService 'description' => $personInfoData['description'], 'parameters' => [ 'type' => 'object', - 'properties' => $personInfoData['input'], - 'required' => array_keys($personInfoData['input']) + 'properties' => $properties, + 'required' => ['code'] ] ] ];