Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 457 |
|
0.00% |
0 / 12 |
CRAP | |
0.00% |
0 / 1 |
FlyAIController | |
0.00% |
0 / 457 |
|
0.00% |
0 / 12 |
4160 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
checkSentences | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
rewrite | |
0.00% |
0 / 42 |
|
0.00% |
0 / 1 |
56 | |||
checkQuota | |
0.00% |
0 / 51 |
|
0.00% |
0 / 1 |
110 | |||
engage_generate | |
0.00% |
0 / 80 |
|
0.00% |
0 / 1 |
42 | |||
post_generate | |
0.00% |
0 / 98 |
|
0.00% |
0 / 1 |
110 | |||
save_custom_prompt | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
12 | |||
update_custom_prompt | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
6 | |||
delete_custom_prompt | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
12 | |||
getTypingSpeedConfigByFeature | |
0.00% |
0 / 31 |
|
0.00% |
0 / 1 |
132 | |||
convertStringToJson | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
youtube | |
0.00% |
0 / 47 |
|
0.00% |
0 / 1 |
56 |
1 | <?php |
2 | |
3 | namespace App\Http\Controllers\v2; |
4 | |
5 | use App\Helpers\Constants; |
6 | use Illuminate\Http\Request; |
7 | use App\Traits\SubscriptionTrait; |
8 | use Illuminate\Http\JsonResponse; |
9 | use App\Http\Controllers\Controller; |
10 | use App\Events\TrackFlyMsgAIUsageEvent; |
11 | use App\Helpers\FlyMSGLogger; |
12 | use App\Http\Models\FlyMsgAI\SavedPrompt; |
13 | use App\Http\Models\Parameter; |
14 | use App\Http\Models\PromptCompanyNewUpdate; |
15 | use App\Http\Models\PromptLanguage; |
16 | use App\Http\Models\PromptLengthOfPost; |
17 | use App\Http\Models\PromptPersonalMilestone; |
18 | use App\Http\Models\Prompts\CustomPrompts; |
19 | use App\Http\Models\Prompts\PromptModel; |
20 | use App\Http\Models\Prompts\PromptSetting; |
21 | use App\Http\Models\Prompts\PromptType; |
22 | use App\Http\Models\PromptTone; |
23 | use App\Http\Models\Setting; |
24 | use App\Http\Models\UserPersona; |
25 | use App\Http\Requests\FlyAI\EngageGenerateFormRequest; |
26 | use App\Http\Requests\FlyAI\PostGenerateFormRequest; |
27 | use App\Http\Requests\FlyAI\RewriteFormRequest; |
28 | use App\Http\Requests\FlyAI\SavePromptFormRequest; |
29 | use App\Services\FlyMsgAI\FlyMsgAIService; |
30 | use App\Services\FlyMsgAI\GeminiAPI; |
31 | use Carbon\Carbon; |
32 | use Illuminate\Http\Response; |
33 | use Illuminate\Support\Str; |
34 | use stdClass; |
35 | |
36 | class FlyAIController extends Controller |
37 | { |
38 | use SubscriptionTrait; |
39 | |
40 | public function __construct( |
41 | private readonly FlyMsgAIService $ai_service |
42 | ) {} |
43 | |
44 | public function checkSentences(Request $request): JsonResponse |
45 | { |
46 | $sentences = $request->sentences; |
47 | |
48 | if (!$sentences) { |
49 | return response()->json([ |
50 | 'status' => 'error', |
51 | 'message' => 'sentences field is required.', |
52 | ], 400); |
53 | } |
54 | |
55 | $rewrite = $this->ai_service->checkSentences($sentences); |
56 | |
57 | return response()->json([ |
58 | 'status' => 'success', |
59 | 'data' => $rewrite, |
60 | ]); |
61 | } |
62 | |
63 | public function rewrite(RewriteFormRequest $request): JsonResponse |
64 | { |
65 | $data = $request->validated(); |
66 | $user = $request->user(); |
67 | |
68 | $rewrite = $this->ai_service->rewrite($data); |
69 | |
70 | $prompt = "FlyMSG AI Rewrite: " . $data['action'] . "\n" . "Input: " . $data['input']; |
71 | if (isset($data['tone']) && !empty($data['tone'])) { |
72 | $tone = PromptTone::find($data['tone']); |
73 | if ($tone) { |
74 | $prompt .= "\nTone: " . $tone->name; |
75 | } |
76 | } |
77 | if (isset($data['language']) && !empty($data['language'])) { |
78 | $language = PromptLanguage::find($data['language']); |
79 | if ($language) { |
80 | $prompt .= "\nLanguage: " . $language->name; |
81 | } |
82 | } |
83 | |
84 | $quota = $this->getFlyAIQuota($user); |
85 | |
86 | TrackFlyMsgAIUsageEvent::dispatch( |
87 | $user, |
88 | $rewrite['output']['suggestion'], |
89 | $request->browser, |
90 | $data['action'], |
91 | $rewrite['prompt'], |
92 | $data['product'], |
93 | $user->id . '-' . Carbon::now()->timestamp, |
94 | [ |
95 | 'response' => $rewrite['output'], |
96 | 'prompt' => $rewrite['prompt'], |
97 | ], |
98 | $data['context'] ?? '' |
99 | ); |
100 | |
101 | $usedQuota = ($quota['used'] ?? 0) + 1; |
102 | $totalQuota = $quota['total'] ?? 0; |
103 | |
104 | return response()->json([ |
105 | 'status' => 'success', |
106 | 'data' => [ |
107 | 'input' => $data, |
108 | 'output' => $rewrite['output'], |
109 | 'quota' => [ |
110 | 'used' => $usedQuota, |
111 | 'total' => $totalQuota, |
112 | 'remaining' => max($totalQuota - $usedQuota, 0), |
113 | 'seconds_remaining_until_next_prompt_refill' => now()->diffInSeconds(now()->startOfDay()->addDay()) |
114 | ], |
115 | ], |
116 | ]); |
117 | } |
118 | |
119 | public function checkQuota(Request $request): JsonResponse |
120 | { |
121 | $feature = $request->feature; |
122 | |
123 | if (!$feature) { |
124 | return response()->json([ |
125 | 'status' => 'error', |
126 | 'message' => 'Feature is required.', |
127 | ], 400); |
128 | } |
129 | |
130 | $user = $request->user(); |
131 | |
132 | $order = []; |
133 | |
134 | $model = PromptModel::where('is_active', true)->latest()->first(); |
135 | $setting = PromptSetting::where('prompt_model_id', $model->id)->where('is_active', true)->where('feature', $feature)->latest()->first(); |
136 | $prompts = PromptType::where('prompt_setting_id', $setting->id) |
137 | ->where('is_active', true) |
138 | ->where('feature', $feature) |
139 | ->get(); |
140 | |
141 | $default_prompts = []; |
142 | foreach ($prompts as $prompt) { |
143 | $default_prompts[] = [ |
144 | 'id' => $prompt->id, |
145 | 'name' => $prompt->name, |
146 | 'prompt' => $prompt->mission ?? '', |
147 | 'feature' => $feature, |
148 | 'prompt_tone_id' => $prompt->prompt_tone_id, |
149 | ]; |
150 | } |
151 | |
152 | if (str_contains($feature, 'flyengage')) { |
153 | $order = ['curious', 'optimistic', 'thoughtful', 'custom']; |
154 | } elseif ($feature == "flypost") { |
155 | $order = ['thought leadership', 'company news', 'celebrate something', 'hiring', 'custom']; |
156 | } |
157 | |
158 | $default_prompts = collect($default_prompts)->sort(function ($a, $b) use ($order) { |
159 | $posA = array_search($a['name'], $order); |
160 | $posB = array_search($b['name'], $order); |
161 | |
162 | $posA = $posA === false ? count($order) : $posA; |
163 | $posB = $posB === false ? count($order) : $posB; |
164 | |
165 | return $posA - $posB; |
166 | }); |
167 | |
168 | $default_prompts = $default_prompts->values(); |
169 | |
170 | $quota = $this->getFlyAIQuota($user); |
171 | $promptsQuota = $this->getPromptQuota($user); |
172 | $savedPrompts = CustomPrompts::where('feature', $feature)->get(); |
173 | |
174 | $plan = $this->getCurrentPlan($user); |
175 | $show_upgrade_button = empty($user->company_id) && $plan->identifier !== 'sales-pro-monthly' && $plan->identifier !== 'sales-pro-yearly'; |
176 | |
177 | return response()->json([ |
178 | 'status' => 'success', |
179 | 'data' => [ |
180 | 'is_enterprise' => !empty($user->company_id) && $user->status !== 'Invited', |
181 | 'show_upgrade_button' => $show_upgrade_button, |
182 | 'quota' => $quota, |
183 | 'prompts_quota' => $promptsQuota, |
184 | 'default_prompts' => $default_prompts, |
185 | 'saved_prompts' => $savedPrompts, |
186 | ], |
187 | ]); |
188 | } |
189 | |
190 | public function engage_generate(EngageGenerateFormRequest $request): JsonResponse |
191 | { |
192 | $user = $request->user(); |
193 | $validated = $request->validated(); |
194 | |
195 | $prompt_id = $validated['prompt_id']; |
196 | $custom_prompt_id = $validated['custom_prompt_id'] ?? null; |
197 | $isReply = $validated['is_reply'] ?? false; |
198 | $context = $validated['context']; |
199 | $is_regenerate = $validated['is_regenerate'] ?? false; |
200 | $uniqueId = $validated['uniqueId'] ?? null; |
201 | $include_hashtags = $validated['include_hashtags']; |
202 | $include_emojis = $validated['include_emojis']; |
203 | $persona_id = $validated['persona_id'] ?? null; |
204 | $tone_id = $validated['tone_id'] ?? null; |
205 | $additional_instructions = $validated['additional_instructions'] ?? null; |
206 | |
207 | $currentSubscriptionPlan = $this->getCurrentPlan($user); |
208 | $total = Constants::CURRENT_SUBSCRIPTION_PLAN_IDENTIFIERS[$currentSubscriptionPlan->identifier]; |
209 | $used = $this->getQuotaUsed($user->id); |
210 | |
211 | if (!empty($custom_prompt_id)) { |
212 | $customPrompt = CustomPrompts::find($custom_prompt_id); |
213 | } |
214 | $prompt = PromptType::find($prompt_id); |
215 | if (!empty($tone_id)) { |
216 | $tone = PromptTone::find($tone_id); |
217 | } |
218 | |
219 | if (!empty($persona_id)) { |
220 | $persona = UserPersona::where('_id', $persona_id)->first(); |
221 | if ($persona) { |
222 | $tone = $persona->prompt_tone; |
223 | } |
224 | } |
225 | |
226 | $context['post']['content'] = strip_tags($context['post']['content']); |
227 | |
228 | $uniqueId = "https://www.linkedin.com/feed/update/{$uniqueId}"; |
229 | |
230 | $name = strtolower($prompt->name); |
231 | |
232 | try { |
233 | $aiResult = $this->ai_service->engageGenerate( |
234 | $name, |
235 | $context, |
236 | $uniqueId, |
237 | $user, |
238 | $include_hashtags, |
239 | $include_emojis, |
240 | $persona ?? null, |
241 | $tone ?? null, |
242 | $additional_instructions ?? null, |
243 | $customPrompt ?? null, |
244 | $is_regenerate, |
245 | $isReply |
246 | ); |
247 | $prompt_response = $this->ai_service->transform($aiResult['response']); |
248 | |
249 | TrackFlyMsgAIUsageEvent::dispatch( |
250 | $user, |
251 | $prompt_response, |
252 | $request->browser, |
253 | $prompt->name, |
254 | $prompt->mission . "\n" . $additional_instructions . "\n\n Post:\n\n " . $context['post']['content'], |
255 | 'flyengage', |
256 | $uniqueId, |
257 | $aiResult, |
258 | $context |
259 | ); |
260 | |
261 | $used++; |
262 | |
263 | $quota = [ |
264 | 'used' => $used, |
265 | 'total' => $total, |
266 | 'remaining' => max($total - $used, 0), |
267 | 'deleted' => $used >= $total, |
268 | ]; |
269 | |
270 | $words_per_minute_typing_speed_control = $this->getTypingSpeedConfigByFeature('flyengage', $user); |
271 | |
272 | return response()->json([ |
273 | 'status' => 'success', |
274 | 'data' => [ |
275 | 'prompt' => $prompt->mission . "\n" . $additional_instructions . "\n\n Post:\n\n ", |
276 | 'context' => $context, |
277 | 'prompt_response' => $prompt_response, |
278 | 'quota' => $quota, |
279 | 'typingConfig' => $words_per_minute_typing_speed_control, |
280 | ], |
281 | ]); |
282 | } catch (\Throwable $th) { |
283 | FlyMSGLogger::logError("generate", $th); |
284 | return response()->json([ |
285 | "status" => "error", |
286 | "data" => [ |
287 | "message" => "Something went wrong. " . $th->getMessage() |
288 | ] |
289 | ], Response::HTTP_INTERNAL_SERVER_ERROR); |
290 | } |
291 | } |
292 | |
293 | public function post_generate(PostGenerateFormRequest $request): JsonResponse |
294 | { |
295 | $user = $request->user(); |
296 | $validated = $request->validated(); |
297 | |
298 | $prompt_id = $validated['prompt_id']; |
299 | $custom_prompt_id = $validated['custom_prompt_id'] ?? null; |
300 | $persona_id = $validated['persona_id'] ?? null; |
301 | $topic = $validated['topic'] ?? null; |
302 | $insert_role = $validated['insert_role'] ?? null; |
303 | $prompt_company_new_update = $validated['prompt_company_new_update_id'] ?? null; |
304 | $prompt_personal_milestone = $validated['prompt_personal_milestone_id'] ?? null; |
305 | $tone_id = $validated['tone_id'] ?? null; |
306 | $prompt_language_id = $validated['prompt_language_id'] ?? null; |
307 | $length_of_post_id = $validated['length_of_post_id'] ?? null; |
308 | $youtube_url = $validated['youtube_url'] ?? null; |
309 | $blog_url = $validated['blog_url'] ?? null; |
310 | $include_hashtags = $validated['include_hashtags']; |
311 | $include_emojis = $validated['include_emojis']; |
312 | $additional_instructions = $validated['additional_instructions'] ?? null; |
313 | $is_regenerate = $validated['is_regenerate'] ?? false; |
314 | $uniqueId = $validated['uniqueId'] ?? null; |
315 | |
316 | $currentSubscriptionPlan = $this->getCurrentPlan($user); |
317 | $total = Constants::CURRENT_SUBSCRIPTION_PLAN_IDENTIFIERS[$currentSubscriptionPlan->identifier]; |
318 | $used = $this->getQuotaUsed($user->id); |
319 | |
320 | if (!empty($custom_prompt_id)) { |
321 | $customPrompt = CustomPrompts::find($custom_prompt_id); |
322 | } |
323 | $prompt = PromptType::find($prompt_id); |
324 | if (!empty($tone_id)) { |
325 | $tone = PromptTone::find($tone_id); |
326 | } |
327 | |
328 | // company news |
329 | if (!empty($prompt_company_new_update)) { |
330 | $promptCompanyNewUpdate = PromptCompanyNewUpdate::find($prompt_company_new_update); |
331 | } |
332 | // celebrate something |
333 | if (!empty($prompt_personal_milestone)) { |
334 | $promptPersonalMilestone = PromptPersonalMilestone::find($prompt_personal_milestone); |
335 | } |
336 | |
337 | if (!empty($length_of_post_id)) { |
338 | $lengthOfPost = PromptLengthOfPost::find($length_of_post_id); |
339 | } |
340 | |
341 | if (!empty($prompt_language_id)) { |
342 | $promptLanguage = PromptLanguage::find($prompt_language_id); |
343 | } |
344 | |
345 | if (!empty($persona_id)) { |
346 | $persona = UserPersona::where('_id', $persona_id)->first(); |
347 | if ($persona) { |
348 | $tone = $persona->prompt_tone; |
349 | } |
350 | } |
351 | |
352 | $uniqueId = str_slug($request->uniqueId); |
353 | |
354 | $name = strtolower($prompt->name); |
355 | |
356 | try { |
357 | $aiResult = $this->ai_service->postGenerate( |
358 | $name, |
359 | $youtube_url, |
360 | $blog_url, |
361 | $uniqueId, |
362 | $user, |
363 | $include_hashtags, |
364 | $include_emojis, |
365 | $promptLanguage, |
366 | $lengthOfPost, |
367 | $topic, |
368 | $insert_role, |
369 | $promptCompanyNewUpdate ?? null, |
370 | $promptPersonalMilestone ?? null, |
371 | $persona ?? null, |
372 | $tone ?? null, |
373 | $additional_instructions ?? null, |
374 | $customPrompt ?? null, |
375 | $is_regenerate |
376 | ); |
377 | $prompt_response = $this->ai_service->transform($aiResult['response']); |
378 | |
379 | TrackFlyMsgAIUsageEvent::dispatch( |
380 | $user, |
381 | $prompt_response, |
382 | $request->browser, |
383 | $prompt->name, |
384 | $prompt->mission . "\n" . $additional_instructions, |
385 | 'flypost', |
386 | $uniqueId, |
387 | $aiResult, |
388 | null |
389 | ); |
390 | |
391 | $used++; |
392 | |
393 | $quota = [ |
394 | 'used' => $used, |
395 | 'total' => $total, |
396 | 'remaining' => max($total - $used, 0), |
397 | 'deleted' => $used >= $total, |
398 | ]; |
399 | |
400 | $words_per_minute_typing_speed_control = $this->getTypingSpeedConfigByFeature('flypost', $user); |
401 | |
402 | return response()->json([ |
403 | 'status' => 'success', |
404 | 'data' => [ |
405 | 'prompt' => $prompt->mission . "\n" . $additional_instructions, |
406 | 'prompt_response' => $prompt_response, |
407 | 'quota' => $quota, |
408 | 'typingConfig' => $words_per_minute_typing_speed_control, |
409 | ], |
410 | ]); |
411 | } catch (\Throwable $th) { |
412 | FlyMSGLogger::logError("generate", $th); |
413 | return response()->json([ |
414 | "status" => "error", |
415 | "data" => [ |
416 | "message" => "Something went wrong. " . $th->getMessage() |
417 | ] |
418 | ], Response::HTTP_INTERNAL_SERVER_ERROR); |
419 | } |
420 | } |
421 | |
422 | public function save_custom_prompt(SavePromptFormRequest $request): JsonResponse |
423 | { |
424 | $validated = $request->validated(); |
425 | $user = $request->user(); |
426 | |
427 | $existsSameName = CustomPrompts::where('name', $validated['name'])->where('feature', $validated['feature'])->where('user_id', $user->id)->exists(); |
428 | |
429 | if ($existsSameName) { |
430 | return response()->json([ |
431 | 'status' => 'error', |
432 | 'message' => 'You already have a custom prompt with the same name', |
433 | ], 400); |
434 | } |
435 | |
436 | $existsDefaultSameName = PromptType::where('is_active', true)->where('name', $validated['name'])->where('feature', $validated['feature'])->exists(); |
437 | |
438 | if ($existsDefaultSameName) { |
439 | return response()->json([ |
440 | 'status' => 'error', |
441 | 'message' => 'You cannot use the same name as a default prompt', |
442 | ], 400); |
443 | } |
444 | |
445 | $customPrompt = new CustomPrompts(); |
446 | $customPrompt->user_id = $user->id; |
447 | $customPrompt->fill($validated); |
448 | $customPrompt->save(); |
449 | |
450 | $customPrompt = $customPrompt->fresh(); |
451 | |
452 | $quota = $this->getFlyAIQuota($user); |
453 | $promptsQuota = $this->getPromptQuota($user); |
454 | $savedPrompts = CustomPrompts::where('feature', $validated['feature'])->get(); |
455 | |
456 | return response()->json([ |
457 | 'status' => 'success', |
458 | 'data' => [ |
459 | 'success' => true, |
460 | 'prompt' => $customPrompt, |
461 | 'quota' => $quota, |
462 | 'prompts_quota' => $promptsQuota, |
463 | 'saved_prompts' => $savedPrompts, |
464 | ], |
465 | ]); |
466 | } |
467 | |
468 | public function update_custom_prompt(SavePromptFormRequest $request, CustomPrompts $customPrompt): JsonResponse |
469 | { |
470 | $validated = $request->validated(); |
471 | $user = $request->user(); |
472 | |
473 | $existsSameName = CustomPrompts::where('name', $validated['name']) |
474 | ->where('feature', $validated['feature']) |
475 | ->where('user_id', $user->id) |
476 | ->where('_id', '!=', $customPrompt->id) |
477 | ->exists(); |
478 | |
479 | if ($existsSameName) { |
480 | return response()->json([ |
481 | 'status' => 'error', |
482 | 'message' => 'You already have a custom prompt with the same name', |
483 | ], 400); |
484 | } |
485 | |
486 | $customPrompt->user_id = $user->id; |
487 | $customPrompt->fill($validated); |
488 | $customPrompt->save(); |
489 | |
490 | $customPrompt = $customPrompt->fresh(); |
491 | |
492 | $quota = $this->getFlyAIQuota($user); |
493 | $promptsQuota = $this->getPromptQuota($user); |
494 | $savedPrompts = CustomPrompts::where('feature', $validated['feature'])->get(); |
495 | |
496 | return response()->json([ |
497 | 'status' => 'success', |
498 | 'data' => [ |
499 | 'success' => true, |
500 | 'prompt' => $customPrompt, |
501 | 'quota' => $quota, |
502 | 'prompts_quota' => $promptsQuota, |
503 | 'saved_prompts' => $savedPrompts, |
504 | ], |
505 | ]); |
506 | } |
507 | |
508 | public function delete_custom_prompt(Request $request, CustomPrompts $customPrompt): JsonResponse |
509 | { |
510 | $user = $request->user(); |
511 | $customPrompt->user_id = $user->id; |
512 | $feature = $customPrompt->feature; |
513 | |
514 | if ($user->id != $customPrompt->user_id) { |
515 | return response()->json([ |
516 | 'status' => 'error', |
517 | 'message' => 'You are not authorized to delete this custom prompt', |
518 | ], 403); |
519 | } |
520 | |
521 | if (!$customPrompt->delete()) { |
522 | return response()->json([ |
523 | 'status' => 'error', |
524 | 'message' => 'Failed to delete custom prompt', |
525 | ], 400); |
526 | } |
527 | |
528 | $quota = $this->getFlyAIQuota($user); |
529 | $promptsQuota = $this->getPromptQuota($user); |
530 | $savedPrompts = CustomPrompts::where('feature', $feature)->get(); |
531 | |
532 | return response()->json([ |
533 | 'status' => 'success', |
534 | 'data' => [ |
535 | 'message' => 'Custom prompt deleted successfully', |
536 | 'success' => true, |
537 | 'quota' => $quota, |
538 | 'prompts_quota' => $promptsQuota, |
539 | 'saved_prompts' => $savedPrompts, |
540 | ], |
541 | ]); |
542 | } |
543 | |
544 | private function getTypingSpeedConfigByFeature($feature, $user) |
545 | { |
546 | $parameters = Parameter::where('name', 'like', 'flywrite_typing%')->get()->keyBy('name'); |
547 | |
548 | $flypostTypingMode = $parameters['flywrite_typing_post_typing_mode']->value ?? 'all'; |
549 | $flypostTypingSpeed = $parameters['flywrite_typing_post_typing_speed']->value ?? Constants::FLYPOST_FE_TYPING_SPEED_PER_MINUTE; |
550 | $flyengageTypingMode = $parameters['flywrite_typing_engage_typing_mode']->value ?? 'all'; |
551 | $flyengageTypingSpeed = $parameters['flywrite_typing_engage_typing_speed']->value ?? Constants::FLYENGAGE_FE_TYPING_SPEED_PER_MINUTE; |
552 | |
553 | $userSetting = Setting::where('user_id', $user->id)->first(); |
554 | |
555 | $flyPostType = $userSetting?->typing_style ?? $flypostTypingMode; |
556 | $flyEngageType = $userSetting?->typing_style ?? $flyengageTypingMode; |
557 | $flyPostSpeed = (!empty($userSetting?->typing_style) && $userSetting?->typing_style == 'letter') ? ($userSetting->typing_speed ?? $flypostTypingSpeed) : $flypostTypingSpeed; |
558 | $flyEngageSpeed = (!empty($userSetting?->typing_style) && $userSetting?->typing_style == 'letter') ? ($userSetting->typing_speed ?? $flyengageTypingSpeed) : $flyengageTypingSpeed; |
559 | |
560 | if ($user->company_id) { |
561 | $companySetting = Setting::where('company_id', $user->company_id)->first(); |
562 | |
563 | if ($companySetting) { |
564 | if ($companySetting->override_user_typing_style) { |
565 | if (!empty($companySetting->typing_style)) { |
566 | $flyPostType = $companySetting->typing_style; |
567 | $flyEngageType = $companySetting->typing_style; |
568 | |
569 | if ($companySetting->typing_style == 'letter' && !empty($companySetting->typing_speed)) { |
570 | $flyPostSpeed = $companySetting->typing_speed; |
571 | $flyEngageSpeed = $companySetting->typing_speed; |
572 | } |
573 | } |
574 | } |
575 | } |
576 | } |
577 | |
578 | return match ($feature) { |
579 | "flypost" => [ |
580 | "type" => $flyPostType, |
581 | "words_per_minute" => $flyPostSpeed |
582 | ], |
583 | "flyengage" => [ |
584 | "type" => $flyEngageType, |
585 | "words_per_minute" => $flyEngageSpeed |
586 | ], |
587 | default => null |
588 | }; |
589 | } |
590 | |
591 | function convertStringToJson(string $input) |
592 | { |
593 | $cleanedInput = Str::of($input) |
594 | ->replaceMatches('/^```json/m', '') |
595 | ->replaceMatches('/```$/m', '') |
596 | ->replaceMatches('/^```text/m', '') |
597 | ->replaceMatches('/```$/m', '') |
598 | ->trim(); |
599 | |
600 | try { |
601 | return json_decode($cleanedInput, true, 512, JSON_THROW_ON_ERROR); |
602 | } catch (\Exception $e) { |
603 | return $cleanedInput; |
604 | } |
605 | } |
606 | |
607 | public function youtube(Request $request): JsonResponse |
608 | { |
609 | $access_token = GeminiAPI::getAIAccessToken(); |
610 | $youtube_url = $request->input('youtube_url'); |
611 | $prompt = $request->input('prompt'); |
612 | $model = $request->input('model', 'gemini-2.5-flash:streamGenerateContent'); |
613 | $temperature = $request->input('temperature', 1); |
614 | $top_p = $request->input('top_p', 0.95); |
615 | |
616 | $geminiAPI = new GeminiAPI($access_token); |
617 | |
618 | $data = [ |
619 | "contents" => [ |
620 | "role" => "user", |
621 | "parts" => [ |
622 | [ |
623 | "text" => $prompt, |
624 | ], |
625 | [ |
626 | "fileData" => [ |
627 | "mimeType" => "video/*", |
628 | "fileUri" => $youtube_url, |
629 | ] |
630 | ] |
631 | ] |
632 | ], |
633 | "generationConfig" => [ |
634 | "maxOutputTokens" => 8000, |
635 | "temperature" => $temperature, |
636 | "topP" => $top_p, |
637 | ] |
638 | ]; |
639 | |
640 | try { |
641 | |
642 | $response = $geminiAPI->postCompletions($data, $model); |
643 | $responseData = json_decode($response->getBody()->getContents(), true); |
644 | |
645 | $extractedText = ''; |
646 | |
647 | foreach ($responseData as $message) { |
648 | foreach ($message['candidates'] as $candidate) { |
649 | if (isset($candidate['content'])) { |
650 | foreach ($candidate['content']['parts'] as $part) { |
651 | $extractedText .= $part['text']; |
652 | } |
653 | } |
654 | } |
655 | } |
656 | } catch (\Exception $e) { |
657 | // split after `response:` and parse the JSON |
658 | $result = explode('response:', $e->getMessage(), 2); |
659 | if (count($result) > 1) { |
660 | $jsonPart = $result[1]; |
661 | $extractedText = $this->convertStringToJson($jsonPart); |
662 | } else { |
663 | $extractedText = 'Error: ' . $e->getMessage(); |
664 | } |
665 | |
666 | return response()->json([ |
667 | 'status' => 'error', |
668 | 'message' => $extractedText, |
669 | ], 500); |
670 | } |
671 | |
672 | return response()->json($this->convertStringToJson($extractedText)); |
673 | } |
674 | } |