Skip to content

Commit 50be070

Browse files
committed
refactor: streamline AI request handling and introduce Lead helper for data mapping
2 parents 908fe04 + 80c8f42 commit 50be070

File tree

5 files changed

+172
-180
lines changed

5 files changed

+172
-180
lines changed

packages/Webkul/Admin/src/Http/Controllers/Lead/LeadController.php

Lines changed: 3 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use Illuminate\Http\RedirectResponse;
88
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
99
use Illuminate\Support\Facades\Event;
10-
use Illuminate\Support\Facades\Validator;
1110
use Illuminate\View\View;
1211
use Prettus\Repository\Criteria\RequestCriteria;
1312
use Webkul\Admin\DataGrids\Lead\LeadDataGrid;
@@ -20,6 +19,7 @@
2019
use Webkul\Attribute\Repositories\AttributeRepository;
2120
use Webkul\Contact\Repositories\PersonRepository;
2221
use Webkul\DataGrid\Enums\DateRangeOptionEnum;
22+
use Webkul\Lead\Helpers\Lead;
2323
use Webkul\Lead\Repositories\LeadRepository;
2424
use Webkul\Lead\Repositories\PipelineRepository;
2525
use Webkul\Lead\Repositories\ProductRepository;
@@ -30,6 +30,7 @@
3030
use Webkul\Tag\Repositories\TagRepository;
3131
use Webkul\User\Repositories\UserRepository;
3232

33+
3334
class LeadController extends Controller
3435
{
3536
/**
@@ -650,7 +651,7 @@ public function createByAI(LeadForm $request)
650651
], 400);
651652
}
652653

653-
$leadData = $this->mapAIDataToLead($extractedData);
654+
$leadData = Lead::mapAIDataToLead($extractedData);
654655

655656
if (
656657
! empty($leadData['status'])
@@ -665,109 +666,6 @@ public function createByAI(LeadForm $request)
665666
return self::leadCreate($leadData);
666667
}
667668

668-
/**
669-
* Mapped the receive Extracted AI data.
670-
*/
671-
private function mapAIDataToLead($aiData)
672-
{
673-
$model = core()->getConfigData('general.magic_ai.settings.model');
674-
675-
if (str_contains($model, 'gemini-1.5-flash')) {
676-
$content = $aiData['candidates'][0]['content']['parts'][0]['text'] ?? '';
677-
} else {
678-
$content = $aiData['choices'][0]['message']['content'] ?? '';
679-
}
680-
681-
$content = strip_tags($content);
682-
683-
preg_match('/\{.*\}/s', $content, $matches);
684-
685-
$jsonString = $matches[0] ?? null;
686-
687-
if (! $jsonString) {
688-
return [
689-
'status' => 'error',
690-
'message' => trans('admin::app.leads.file.invalid-response'),
691-
];
692-
}
693-
694-
$finalData = json_decode($jsonString);
695-
696-
if (json_last_error() !== JSON_ERROR_NONE) {
697-
return [
698-
'status' => 'error',
699-
'message' => trans('admin::app.leads.file.invalid-format'),
700-
];
701-
}
702-
703-
try {
704-
$this->validateLeadData($finalData);
705-
706-
$data = [
707-
'status' => 1,
708-
'title' => $finalData->title ?? 'N/A',
709-
'description' => $finalData->description ?? null,
710-
'lead_source_id' => 1,
711-
'lead_type_id' => 1,
712-
'lead_value' => $finalData->lead_value ?? 0,
713-
'person' => [
714-
'name' => $finalData->person->name ?? 'Unknown',
715-
'emails' => [
716-
[
717-
'value' => $finalData->person->emails->value ?? null,
718-
'label' => $finalData->person->emails->label ?? 'work',
719-
],
720-
],
721-
'contact_numbers' => [
722-
[
723-
'value' => $finalData->person->contact_numbers->value ?? null,
724-
'label' => $finalData->person->contact_numbers->label ?? 'work',
725-
],
726-
],
727-
'entity_type' => 'persons',
728-
],
729-
'entity_type' => 'leads',
730-
];
731-
732-
$validatedData = app(LeadForm::class)->validated();
733-
734-
return array_merge($validatedData, $data);
735-
} catch (\Exception $e) {
736-
return [
737-
'status' => 'error',
738-
'message' => $e->getMessage(),
739-
];
740-
}
741-
}
742-
743-
/**
744-
* Validate the lead data.
745-
*/
746-
private function validateLeadData($data)
747-
{
748-
$dataArray = json_decode(json_encode($data), true);
749-
750-
$validator = Validator::make($dataArray, [
751-
'title' => 'required|string|max:255',
752-
'lead_value' => 'required|numeric|min:0',
753-
'person.name' => 'required|string|max:255',
754-
'person.emails.value' => 'required|email',
755-
'person.contact_numbers.value' => 'required|string|max:20',
756-
]);
757-
758-
if ($validator->fails()) {
759-
throw new \Illuminate\Validation\ValidationException(
760-
$validator,
761-
response()->json([
762-
'status' => 'error',
763-
'message' => $validator->errors()->getMessages(),
764-
], 400)
765-
);
766-
}
767-
768-
return $data;
769-
}
770-
771669
/**
772670
* Create lead independent entity.
773671
*/
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
namespace Webkul\Lead\Helpers;
4+
5+
use Illuminate\Support\Facades\Validator;
6+
use Webkul\Admin\Http\Requests\LeadForm;
7+
8+
class Lead
9+
{
10+
/**
11+
* Const Variable of GEMINI_MODEL.
12+
*/
13+
const GEMINI_MODEL = 'gemini-1.5-flash';
14+
15+
/**
16+
* Const Variable of LEAD_ENTITY.
17+
*/
18+
const LEAD_ENTITY = 'leads';
19+
20+
/**
21+
* Const Variable of PERSON_ENTITY.
22+
*/
23+
const PERSON_ENTITY = 'persons';
24+
25+
const OPEN_AI_MODEL_URL = 'https://api.openai.com/v1/chat/completions';
26+
27+
/**
28+
* Mapped the receive Extracted AI data.
29+
*/
30+
public static function mapAIDataToLead($aiData)
31+
{
32+
$isGeminiModel = str_contains(core()->getConfigData('general.magic_ai.settings.model'), self::GEMINI_MODEL);
33+
34+
$content = $isGeminiModel ? $aiData['candidates'][0]['content']['parts'][0]['text'] : $aiData['choices'][0]['message']['content'];
35+
36+
$content = strip_tags($content);
37+
38+
preg_match('/\{.*\}/s', $content, $matches);
39+
40+
if (! $jsonString = $matches[0] ?? null) {
41+
return [
42+
'status' => 'error',
43+
'message' => trans('admin::app.leads.file.invalid-response'),
44+
];
45+
}
46+
47+
$finalData = json_decode($jsonString);
48+
49+
if (json_last_error() !== JSON_ERROR_NONE) {
50+
return [
51+
'status' => 'error',
52+
'message' => trans('admin::app.leads.file.invalid-format'),
53+
];
54+
}
55+
56+
try {
57+
self::validateLeadData($finalData);
58+
59+
$validatedData = app(LeadForm::class)->validated();
60+
61+
return array_merge($validatedData, self::prepareLeadData($finalData));
62+
} catch (\Exception $e) {
63+
return [
64+
'status' => 'error',
65+
'message' => $e->getMessage(),
66+
];
67+
}
68+
}
69+
70+
/**
71+
* Validate the lead data.
72+
*/
73+
private static function validateLeadData($data)
74+
{
75+
$dataArray = json_decode(json_encode($data), true);
76+
77+
$validator = Validator::make($dataArray, [
78+
'title' => 'required|string|max:255',
79+
'lead_value' => 'required|numeric|min:0',
80+
'person.name' => 'required|string|max:255',
81+
'person.emails.value' => 'required|email',
82+
'person.contact_numbers.value' => 'required|string|max:20',
83+
]);
84+
85+
if ($validator->fails()) {
86+
throw new \Illuminate\Validation\ValidationException(
87+
$validator,
88+
response()->json([
89+
'status' => 'error',
90+
'message' => $validator->errors()->getMessages(),
91+
], 400)
92+
);
93+
}
94+
95+
return $data;
96+
}
97+
98+
private static function prepareLeadData($finalData)
99+
{
100+
return [
101+
'status' => 1,
102+
'title' => $finalData->title ?? 'N/A',
103+
'description' => $finalData->description ?? null,
104+
'lead_source_id' => 1,
105+
'lead_type_id' => 1,
106+
'lead_value' => $finalData->lead_value ?? 0,
107+
'person' => [
108+
'name' => $finalData->person->name ?? 'Unknown',
109+
'emails' => [
110+
[
111+
'value' => $finalData->person->emails->value ?? null,
112+
'label' => $finalData->person->emails->label ?? 'work',
113+
],
114+
],
115+
'contact_numbers' => [
116+
[
117+
'value' => $finalData->person->contact_numbers->value ?? null,
118+
'label' => $finalData->person->contact_numbers->label ?? 'work',
119+
],
120+
],
121+
'entity_type' => self::PERSON_ENTITY,
122+
],
123+
'entity_type' => self::LEAD_ENTITY,
124+
];
125+
}
126+
}

packages/Webkul/Lead/src/Services/GeminiService.php

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,42 +13,24 @@ public static function ask($prompt, $model, $apiKey)
1313
{
1414
$url = "https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$apiKey}";
1515

16-
return self::curlRequest($url, self::prepareRequestData($prompt));
16+
return self::sendHttpRequest($url, self::prepareLeadExtractionRequestData($prompt));
1717
}
1818

1919
/**
2020
* Request data to AI using Curl API.
2121
*/
22-
private static function curlRequest($url, array $data)
22+
private static function sendHttpRequest($url, array $data)
2323
{
2424
try {
25-
$ch = curl_init($url);
25+
$response = \Http::withHeaders([
26+
'Content-Type' => 'application/json',
27+
])->post($url, $data);
2628

27-
curl_setopt_array($ch, [
28-
CURLOPT_RETURNTRANSFER => true,
29-
CURLOPT_POST => true,
30-
CURLOPT_POSTFIELDS => json_encode($data),
31-
CURLOPT_HTTPHEADER => [
32-
'Content-Type: application/json',
33-
],
34-
]);
35-
36-
$response = curl_exec($ch);
37-
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
38-
39-
if (curl_errno($ch)) {
40-
throw new Exception('cURL Error: '.curl_error($ch));
41-
}
42-
43-
curl_close($ch);
44-
45-
$decodedResponse = json_decode($response, true);
46-
47-
if ($httpCode !== 200 || isset($decodedResponse['error'])) {
48-
throw new Exception('LLM API Error: '.($decodedResponse['error']['message'] ?? 'Unknown error'));
29+
if ($response->failed()) {
30+
throw new Exception($response->json('error.message'));
4931
}
5032

51-
return $decodedResponse;
33+
return $response->json();
5234
} catch (Exception $e) {
5335
return ['error' => $e->getMessage()];
5436
}
@@ -57,7 +39,7 @@ private static function curlRequest($url, array $data)
5739
/**
5840
* Prepare request data for AI.
5941
*/
60-
private static function prepareRequestData($prompt)
42+
private static function prepareLeadExtractionRequestData($prompt)
6143
{
6244
return [
6345
'contents' => [

packages/Webkul/Lead/src/Services/LeadService.php

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44

55
use Exception;
66
use Smalot\PdfParser\Parser;
7+
use Webkul\Lead\Helpers\Lead;
78

89
class LeadService
910
{
1011
/**
1112
* Const variable
1213
*/
13-
const GEMINI_MODEL = 'gemini-1.5-flash';
14-
1514
const OPEN_AI_MODEL_URL = 'https://api.openai.com/v1/chat/completions';
1615

1716
/**
@@ -22,13 +21,11 @@ public static function extractDataFromPdf($pdfPath)
2221
try {
2322
$parser = new Parser;
2423

25-
$pdfText = trim($parser->parseFile($pdfPath)->getText());
26-
27-
if (empty($pdfText)) {
24+
if (empty($pdfText = trim($parser->parseFile($pdfPath)->getText()))) {
2825
throw new Exception('PDF content is empty or could not be extracted.');
2926
}
3027

31-
return self::sendLLMRequest($pdfText);
28+
return self::processPromptWithAI($pdfText);
3229
} catch (Exception $e) {
3330
return ['error' => $e->getMessage()];
3431
}
@@ -37,7 +34,7 @@ public static function extractDataFromPdf($pdfPath)
3734
/**
3835
* Send a request to the LLM API.
3936
*/
40-
private static function sendLLMRequest($prompt)
37+
private static function processPromptWithAI($prompt)
4138
{
4239
$model = core()->getConfigData('general.magic_ai.settings.model');
4340
$apiKey = core()->getConfigData('general.magic_ai.settings.api_key');
@@ -46,7 +43,7 @@ private static function sendLLMRequest($prompt)
4643
return ['error' => 'Missing API key or model configuration.'];
4744
}
4845

49-
if (str_contains($model, self::GEMINI_MODEL)) {
46+
if (str_contains($model, Lead::GEMINI_MODEL)) {
5047
return GeminiService::ask($prompt, $model, $apiKey);
5148
} else {
5249
return OpenAIService::ask($prompt, $model);

0 commit comments

Comments
 (0)