First Commit
This commit is contained in:
Executable
+271
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\AiModel;
|
||||
use App\Models\AiProvider;
|
||||
use App\Models\PromptGeneration;
|
||||
use App\Models\PromptProject;
|
||||
use App\Services\Ai\AiServiceFactory;
|
||||
use Illuminate\Http\Client\ConnectionException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
class PromptProjectService
|
||||
{
|
||||
/**
|
||||
* Create a new service instance.
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly AiServiceFactory $aiServiceFactory,
|
||||
private readonly PromptGeneration $generationModel
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function index(): array
|
||||
{
|
||||
$userId = auth()->id();
|
||||
$user = auth()->user();
|
||||
|
||||
$query = PromptProject::with(['user', 'provider', 'model'])
|
||||
->latest();
|
||||
|
||||
if (!$user || $user->role !== 'superadmin') {
|
||||
$query->where('user_id', $userId);
|
||||
}
|
||||
|
||||
$paginator = $query->paginate(15);
|
||||
|
||||
return [
|
||||
'data' => $paginator->items(),
|
||||
'total' => $paginator->total(),
|
||||
'per_page' => $paginator->perPage(),
|
||||
'current_page' => $paginator->currentPage(),
|
||||
'last_page' => $paginator->lastPage(),
|
||||
'from' => $paginator->firstItem(),
|
||||
'to' => $paginator->lastItem(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(array $data): PromptProject
|
||||
{
|
||||
$data['examples'] = $this->convertToArray($data['examples'] ?? null);
|
||||
$data['variables'] = $this->convertToArray($data['variables'] ?? null);
|
||||
|
||||
return PromptProject::create($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(PromptProject $project, array $data): PromptProject
|
||||
{
|
||||
$data['examples'] = $this->convertToArray($data['examples'] ?? null);
|
||||
$data['variables'] = $this->convertToArray($data['variables'] ?? null);
|
||||
|
||||
$project->update($data);
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert string to array.
|
||||
*/
|
||||
private function convertToArray(mixed $value): ?array
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = trim($value);
|
||||
|
||||
if ($value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_map('trim', explode(',', $value));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(PromptProject $project): bool
|
||||
{
|
||||
try {
|
||||
$project->delete();
|
||||
|
||||
return true;
|
||||
} catch (RuntimeException $e) {
|
||||
Log::error('Failed to delete prompt project: ' . $e->getMessage());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate prompt for the specified project.
|
||||
*/
|
||||
public function generate(PromptProject $project, mixed $model = null, ?int $userId = null): PromptGeneration
|
||||
{
|
||||
$provider = $project->provider;
|
||||
|
||||
if (!$provider instanceof AiProvider) {
|
||||
throw new InvalidArgumentException('AI provider is required for generation.');
|
||||
}
|
||||
|
||||
if (!$model) {
|
||||
$model = $project->model;
|
||||
}
|
||||
|
||||
if (!$model) {
|
||||
throw new InvalidArgumentException('AI model is required for generation.');
|
||||
}
|
||||
|
||||
if (is_string($model)) {
|
||||
$model = AiModel::firstOrCreate(
|
||||
['provider_id' => $provider->id, 'slug' => $model],
|
||||
['name' => $model, 'slug' => $model, 'is_active' => true]
|
||||
);
|
||||
}
|
||||
|
||||
$modelSlug = $model->slug;
|
||||
$modelId = $model->id;
|
||||
|
||||
$userId ??= auth()->id();
|
||||
|
||||
$promptText = $this->buildPrompt($project);
|
||||
|
||||
$aiData = [
|
||||
'prompt' => $promptText,
|
||||
'temperature' => (float) ($project->temperature ?? 0.7),
|
||||
'max_tokens' => (int) ($project->max_tokens ?? 1024),
|
||||
'model' => $modelSlug,
|
||||
'api_key' => $provider->api_key,
|
||||
'base_url' => $provider->base_url,
|
||||
];
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
$service = $this->aiServiceFactory->make($provider);
|
||||
$result = $service->generatePrompt($aiData);
|
||||
} catch (ConnectionException|RuntimeException $e) {
|
||||
Log::error('Prompt generation failed: ' . $e->getMessage());
|
||||
|
||||
return $this->generationModel::create([
|
||||
'project_id' => $project->id,
|
||||
'user_id' => $userId,
|
||||
'provider_id' => $provider->id,
|
||||
'model_id' => $modelId,
|
||||
'generated_prompt' => $promptText,
|
||||
'input_data' => $aiData,
|
||||
'output_data' => ['error' => $e->getMessage()],
|
||||
'tokens_used' => null,
|
||||
'generation_time' => round(microtime(true) - $startTime, 3),
|
||||
'status' => 'failed',
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->generationModel::create([
|
||||
'project_id' => $project->id,
|
||||
'user_id' => $userId,
|
||||
'provider_id' => $provider->id,
|
||||
'model_id' => $modelId,
|
||||
'generated_prompt' => $promptText,
|
||||
'input_data' => $aiData,
|
||||
'output_data' => $result,
|
||||
'tokens_used' => $result['tokens_used'] ?? null,
|
||||
'generation_time' => round(microtime(true) - $startTime, 3),
|
||||
'status' => 'completed',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the prompt text from project data.
|
||||
*/
|
||||
public function buildPrompt(PromptProject $project): string
|
||||
{
|
||||
$parts = [];
|
||||
|
||||
$parts[] = "You are an expert prompt engineering assistant. Your task is to generate a high-quality, optimized prompt based on the specifications provided below.";
|
||||
|
||||
if ($project->company_name) {
|
||||
$parts[] = "Company Name: " . $project->company_name;
|
||||
}
|
||||
|
||||
if ($project->company_address) {
|
||||
$parts[] = "Company Address: " . $project->company_address;
|
||||
}
|
||||
|
||||
if ($project->company_whatsapp) {
|
||||
$parts[] = "Company WhatsApp: " . $project->company_whatsapp;
|
||||
}
|
||||
|
||||
if ($project->company_website) {
|
||||
$parts[] = "Company Website: " . $project->company_website;
|
||||
}
|
||||
|
||||
if ($project->company_social_media) {
|
||||
$parts[] = "Company Social Media: " . $project->company_social_media;
|
||||
}
|
||||
|
||||
if ($project->company_business_activities) {
|
||||
$parts[] = "Company Business Activities: " . $project->company_business_activities;
|
||||
}
|
||||
|
||||
if ($project->company_services_products) {
|
||||
$parts[] = "Company Services/Products: " . $project->company_services_products;
|
||||
}
|
||||
|
||||
if ($project->objective) {
|
||||
$parts[] = "Objective: " . $project->objective;
|
||||
}
|
||||
|
||||
if ($project->context) {
|
||||
$parts[] = "Context: " . $project->context;
|
||||
}
|
||||
|
||||
if ($project->persona) {
|
||||
$parts[] = "Persona/Role: " . $project->persona;
|
||||
}
|
||||
|
||||
if ($project->tone) {
|
||||
$parts[] = "Tone/Style: " . $project->tone;
|
||||
}
|
||||
|
||||
if ($project->constraints) {
|
||||
$parts[] = "Constraints: " . $project->constraints;
|
||||
}
|
||||
|
||||
if ($project->output_format) {
|
||||
$parts[] = "Output Format: " . $project->output_format;
|
||||
}
|
||||
|
||||
if (!empty($project->examples)) {
|
||||
$parts[] = "Examples:\n" . json_encode($project->examples, JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
if (!empty($project->variables)) {
|
||||
$parts[] = "Variables: " . json_encode($project->variables, JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
$parts[] = "Generate the optimized prompt now. Return ONLY the prompt, without explanations, introductions, or additional text.";
|
||||
|
||||
return implode("\n\n", $parts);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user