First Commit

This commit is contained in:
marco
2026-09-01 09:22:30 -03:00
commit a43ae23eb4
407 changed files with 36138 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Services\Ai;
use App\Models\AiProvider;
use InvalidArgumentException;
class AiServiceFactory
{
/**
* Create an AI service instance based on the provider.
*
* @param AiProvider|string $provider
* @return AiServiceInterface
*/
public static function make(AiProvider|string $provider): AiServiceInterface
{
if (is_string($provider)) {
$provider = AiProvider::withTrashed()->where('slug', $provider)
->firstOrFail();
}
if (!$provider->is_active) {
throw new InvalidArgumentException('AI provider is not active.');
}
return match ($provider->slug) {
'openrouter', 'openrouter-test' => new OpenRouterService($provider),
'stepfun' => new StepFunService($provider),
'llamacpp' => new LlamaCppService($provider),
default => throw new InvalidArgumentException("Unsupported AI provider: {$provider->slug}"),
};
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Services\Ai;
interface AiServiceInterface
{
/**
* Generate a prompt using the AI provider.
*
* @param array<string, mixed> $data
* @return array<string, mixed>
*/
public function generatePrompt(array $data): array;
/**
* Send a chat conversation to the AI provider.
*
* @param array<int, array{role: string, content: string}> $messages
* @param array<string, mixed> $data
* @return array<string, mixed>
*/
public function chat(array $messages, array $data = []): array;
}
+73
View File
@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace App\Services\Ai;
use App\Models\AiProvider;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
class LlamaCppService implements AiServiceInterface
{
/**
* @param AiProvider $provider
*/
public function __construct(private AiProvider $provider)
{
$this->client = new Client([
'base_uri' => rtrim((string) $provider->base_url, '/') . '/',
'timeout' => 120,
]);
}
public function chat(array $messages, array $data = []): array
{
$baseUrl = rtrim((string) $this->provider->base_url, '/');
if (empty($baseUrl)) {
throw new \InvalidArgumentException('Llama.cpp base URL is not configured.');
}
try {
$response = $this->client->post('v1/chat/completions', [
'headers' => [
'Content-Type' => 'application/json',
],
'json' => [
'model' => (string) ($data['model'] ?? ''),
'messages' => $messages,
'temperature' => isset($data['temperature']) ? (float) $data['temperature'] : null,
'max_tokens' => isset($data['max_tokens']) ? (int) $data['max_tokens'] : null,
],
]);
$body = json_decode($response->getBody()->getContents(), true);
if (!isset($body['choices'][0]['message']['content'])) {
throw new \RuntimeException('Invalid response structure from Llama.cpp server.');
}
return [
'content' => $body['choices'][0]['message']['content'],
'tokens_used' => $body['usage']['total_tokens'] ?? null,
'model' => $body['model'] ?? (string) ($data['model'] ?? ''),
];
} catch (GuzzleException $e) {
Log::error('Llama.cpp API error: ' . $e->getMessage());
throw new \RuntimeException('Failed to generate prompt with Llama.cpp: ' . $e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* {@inheritdoc}
*/
public function generatePrompt(array $data): array
{
return $this->chat(
[['role' => 'user', 'content' => (string) ($data['prompt'] ?? '')]],
$data
);
}
}
+76
View File
@@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace App\Services\Ai;
use App\Models\AiProvider;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
class OpenRouterService implements AiServiceInterface
{
/**
* @param AiProvider $provider
*/
public function __construct(private AiProvider $provider)
{
$this->client = new Client([
'base_uri' => rtrim((string) $provider->base_url, '/') . '/',
'timeout' => 120,
]);
}
public function chat(array $messages, array $data = []): array
{
$apiKey = $data['api_key'] ?? $this->provider->api_key;
if (empty($apiKey)) {
throw new \InvalidArgumentException('OpenRouter API key is not configured.');
}
try {
$response = $this->client->post('chat/completions', [
'headers' => [
'Authorization' => 'Bearer ' . $apiKey,
'Content-Type' => 'application/json',
'HTTP-Referer' => $data['http_referer'] ?? config('app.url'),
'X-Title' => $data['x_title'] ?? config('app.name'),
],
'json' => [
'model' => (string) ($data['model'] ?? ''),
'messages' => $messages,
'temperature' => isset($data['temperature']) ? (float) $data['temperature'] : null,
'max_tokens' => isset($data['max_tokens']) ? (int) $data['max_tokens'] : null,
],
]);
$body = json_decode($response->getBody()->getContents(), true);
if (!isset($body['choices'][0]['message']['content'])) {
throw new \RuntimeException('Invalid response structure from OpenRouter.');
}
return [
'content' => $body['choices'][0]['message']['content'],
'tokens_used' => $body['usage']['total_tokens'] ?? null,
'model' => $body['model'] ?? (string) ($data['model'] ?? ''),
];
} catch (GuzzleException $e) {
Log::error('OpenRouter API error: ' . $e->getMessage());
throw new \RuntimeException('Failed to generate prompt with OpenRouter: ' . $e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* {@inheritdoc}
*/
public function generatePrompt(array $data): array
{
return $this->chat(
[['role' => 'user', 'content' => (string) ($data['prompt'] ?? '')]],
$data
);
}
}
+86
View File
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace App\Services\Ai;
use App\Models\AiProvider;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
class StepFunService implements AiServiceInterface
{
/**
* @param AiProvider $provider
*/
public function __construct(private AiProvider $provider)
{
$this->client = new Client([
'base_uri' => rtrim((string) $provider->base_url, '/') . '/',
'timeout' => 120,
]);
}
public function chat(array $messages, array $data = []): array
{
$apiKey = $data['api_key'] ?? $this->provider->api_key;
if (empty($apiKey)) {
throw new \InvalidArgumentException('StepFun API key is not configured.');
}
try {
$payload = [
'model' => (string) ($data['model'] ?? ''),
'messages' => $messages,
];
if (isset($data['temperature'])) {
$payload['temperature'] = (float) $data['temperature'];
}
if (isset($data['max_tokens'])) {
$payload['max_tokens'] = (int) $data['max_tokens'];
}
if (!empty($data['reasoning_effort'])) {
$payload['reasoning_effort'] = (string) $data['reasoning_effort'];
}
$response = $this->client->post('chat/completions', [
'headers' => [
'Authorization' => 'Bearer ' . $apiKey,
'Content-Type' => 'application/json',
],
'json' => $payload,
]);
$body = json_decode($response->getBody()->getContents(), true);
if (!isset($body['choices'][0]['message']['content'])) {
throw new \RuntimeException('Invalid response structure from StepFun.');
}
return [
'content' => $body['choices'][0]['message']['content'],
'tokens_used' => $body['usage']['total_tokens'] ?? null,
'model' => $body['model'] ?? (string) ($data['model'] ?? ''),
];
} catch (GuzzleException $e) {
Log::error('StepFun API error: ' . $e->getMessage());
throw new \RuntimeException('Failed to generate prompt with StepFun: ' . $e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* {@inheritdoc}
*/
public function generatePrompt(array $data): array
{
return $this->chat(
[['role' => 'user', 'content' => (string) ($data['prompt'] ?? '')]],
$data
);
}
}
+69
View File
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\AiModel;
use App\Models\AiProvider;
use App\Services\Ai\AiServiceFactory;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Log;
use RuntimeException;
class AiAgentService
{
public function __construct(
private readonly AiServiceFactory $aiServiceFactory
) {}
/**
* Execute a prompt using the specified provider and model.
*
* @return array<string, mixed>
*/
public function execute(
string $prompt,
AiProvider $provider,
AiModel $model,
float $temperature = 0.7,
int $maxTokens = 8192
): array {
$startTime = microtime(true);
$aiData = [
'prompt' => $prompt,
'temperature' => $temperature,
'max_tokens' => $maxTokens,
'model' => $model->slug,
'api_key' => $provider->api_key,
'base_url' => $provider->base_url,
];
try {
$service = $this->aiServiceFactory->make($provider);
$result = $service->generatePrompt($aiData);
} catch (ConnectionException|RuntimeException $e) {
Log::error('AI Agent execution failed: ' . $e->getMessage());
return [
'success' => false,
'error' => $e->getMessage(),
'content' => '',
'tokens_used' => null,
'generation_time' => round(microtime(true) - $startTime, 3),
'model' => $model->slug,
'provider' => $provider->name,
];
}
return [
'success' => true,
'content' => $result['content'] ?? '',
'tokens_used' => $result['tokens_used'] ?? null,
'generation_time' => round(microtime(true) - $startTime, 3),
'model' => $model->slug,
'provider' => $provider->name,
];
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\AiProvider;
use Illuminate\Support\Facades\Log;
use RuntimeException;
class AiProviderService
{
/**
* Display a listing of the resource.
*
* @return array<string, mixed>
*/
public function index(): array
{
$paginator = AiProvider::with('models')
->latest()
->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): AiProvider
{
return AiProvider::withTrashed()->firstOrCreate(
['slug' => $data['slug']],
$data
);
}
/**
* Update the specified resource in storage.
*/
public function update(AiProvider $provider, array $data): AiProvider
{
$provider->update($data);
return $provider;
}
/**
* Remove the specified resource from storage.
*/
public function destroy(AiProvider $provider): bool
{
try {
$provider->delete();
return true;
} catch (RuntimeException $e) {
Log::error('Failed to delete AI provider: ' . $e->getMessage());
return false;
}
}
}
+97
View File
@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\AiModel;
use App\Models\AiProvider;
use App\Services\Ai\AiServiceFactory;
use Illuminate\Http\Client\ConnectionException;
use Illuminate\Support\Facades\Log;
use RuntimeException;
class ChatService
{
private const SESSION_KEY = 'chat_conversation';
public function __construct(
private readonly AiServiceFactory $aiServiceFactory
) {}
/**
* Get conversation history from session.
*
* @return array<int, array{role: string, content: string}>
*/
public function getHistory(): array
{
return session()->get(self::SESSION_KEY, []);
}
/**
* Reset conversation history.
*/
public function reset(): void
{
session()->forget(self::SESSION_KEY);
}
/**
* Send a message and get AI response.
*
* @return array<string, mixed>
*/
public function sendMessage(
string $userMessage,
AiProvider $provider,
AiModel $model,
float $temperature = 0.7,
int $maxTokens = 8192
): array {
$history = $this->getHistory();
$history[] = ['role' => 'user', 'content' => $userMessage];
$startTime = microtime(true);
try {
$service = $this->aiServiceFactory->make($provider);
$result = $service->chat($history, [
'temperature' => $temperature,
'max_tokens' => $maxTokens,
'model' => $model->slug,
'api_key' => $provider->api_key,
'base_url' => $provider->base_url,
]);
} catch (ConnectionException|RuntimeException $e) {
Log::error('Chat API error: ' . $e->getMessage());
return [
'success' => false,
'error' => $e->getMessage(),
'content' => '',
'role' => 'assistant',
'tokens_used' => null,
'generation_time' => round(microtime(true) - $startTime, 3),
];
}
$assistantMessage = [
'role' => 'assistant',
'content' => $result['content'] ?? '',
];
$history[] = $assistantMessage;
session()->put(self::SESSION_KEY, $history);
return [
'success' => true,
'content' => $result['content'] ?? '',
'role' => 'assistant',
'tokens_used' => $result['tokens_used'] ?? null,
'generation_time' => round(microtime(true) - $startTime, 3),
];
}
}
+210
View File
@@ -0,0 +1,210 @@
<?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\Models\User;
use App\Services\Ai\AiServiceFactory;
use App\Services\Ai\AiServiceInterface;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Log;
use RuntimeException;
class PromptGenerationService
{
/**
* @param AiServiceFactory $factory
*/
public function __construct(private AiServiceFactory $factory)
{
}
/**
* Generate a prompt based on a prompt project.
*
* @param array<string, mixed> $data
* @return PromptGeneration
*/
public function generate(array $data): PromptGeneration
{
$project = $data['project'] ?? null;
$user = $data['user'] ?? null;
if (!$project instanceof PromptProject) {
throw new InvalidArgumentException('Prompt project is required.');
}
if (!$user instanceof User) {
$user = auth()->user();
}
if (!$user instanceof User) {
throw new InvalidArgumentException('User is required.');
}
$provider = $data['provider_id'] ?? $project->provider_id;
$model = $data['model_id'] ?? $project->model_id;
if (!$provider instanceof AiProvider) {
$provider = AiProvider::findOrFail((int) $provider);
}
if (!$model instanceof AiModel) {
$model = AiModel::findOrFail((int) $model);
}
$promptText = $this->buildPrompt($project);
$aiData = [
'prompt' => $promptText,
'temperature' => (float) ($data['temperature'] ?? $project->temperature ?? 0.7),
'max_tokens' => (int) ($data['max_tokens'] ?? $project->max_tokens ?? 1024),
'model' => $model->slug,
'api_key' => $provider->api_key,
'base_url' => $provider->base_url,
];
$startTime = microtime(true);
try {
$service = $this->factory->make($provider);
$result = $service->generatePrompt($aiData);
} catch (GuzzleException|RuntimeException $e) {
Log::error('Prompt generation failed: ' . $e->getMessage());
return PromptGeneration::create([
'project_id' => $project->id,
'user_id' => $user->id,
'provider_id' => $provider->id,
'model_id' => $model->id,
'generated_prompt' => $promptText,
'input_data' => $aiData,
'output_data' => ['error' => $e->getMessage()],
'tokens_used' => null,
'generation_time' => round(microtime(true) - $startTime, 3),
'status' => 'failed',
]);
}
$generation = PromptGeneration::create([
'project_id' => $project->id,
'user_id' => $user->id,
'provider_id' => $provider->id,
'model_id' => $model->id,
'generated_prompt' => $promptText,
'input_data' => $aiData,
'output_data' => $result,
'tokens_used' => $result['tokens_used'] ?? null,
'generation_time' => round(microtime(true) - $startTime, 3),
'status' => 'completed',
]);
return $generation;
}
/**
* Build the prompt text from project data.
*
* @param PromptProject $project
* @return string
*/
private function buildPrompt(PromptProject $project): string
{
$parts = [];
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);
}
return implode("\n\n", $parts);
}
/**
* Display a listing of prompt generations.
*
* @return \Illuminate\Contracts\Pagination\Paginator
*/
public function index(): \Illuminate\Contracts\Pagination\Paginator
{
$user = auth()->user();
$query = PromptGeneration::query()
->with(['project', 'user', 'provider', 'model'])
->latest();
if (!$user || $user->role !== 'superadmin') {
$query->where('user_id', auth()->id());
}
return $query->paginate(15);
}
/**
* Delete the specified prompt generation.
*/
public function destroy(PromptGeneration $generation): void
{
$generation->delete();
}
}
+271
View File
@@ -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);
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\PromptTemplate;
use Illuminate\Support\Facades\Log;
use RuntimeException;
class PromptTemplateService
{
/**
* Display a listing of the resource.
*
* @return array<string, mixed>
*/
public function index(): array
{
$paginator = PromptTemplate::with('user')
->latest()
->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): PromptTemplate
{
$data['user_id'] = auth()->id();
return PromptTemplate::create($data);
}
/**
* Update the specified resource in storage.
*/
public function update(PromptTemplate $template, array $data): PromptTemplate
{
$template->update($data);
return $template;
}
/**
* Remove the specified resource from storage.
*/
public function destroy(PromptTemplate $template): bool
{
try {
$template->delete();
return true;
} catch (RuntimeException $e) {
Log::error('Failed to delete prompt template: ' . $e->getMessage());
return false;
}
}
}
+141
View File
@@ -0,0 +1,141 @@
<?php
declare(strict_types=1);
namespace App\Services;
class TutorialService
{
/**
* Get tutorial content.
*
* @return array<string, mixed>
*/
public function getContent(): array
{
return [
'title' => 'Prompt Engineering Tutorial',
'description' => 'Learn how to create effective prompts for AI models.',
'sections' => [
[
'id' => 'introduction',
'title' => 'Introduction',
'content' => 'Prompt engineering is the process of designing and optimizing prompts to effectively communicate with AI language models. A well-crafted prompt can significantly improve the quality and relevance of AI-generated responses.',
],
[
'id' => 'objective',
'title' => 'Objective',
'content' => 'Clearly define what you want the AI to achieve. Be specific about the goal and desired outcome. A clear objective helps the AI understand the task and produce more relevant results.',
'tips' => [
'Start with action verbs (e.g., "Write", "Analyze", "Create")',
'Specify the desired outcome explicitly',
'Keep it concise but complete',
],
],
[
'id' => 'context',
'title' => 'Context',
'content' => 'Provide background information that helps the AI understand the situation. Context includes relevant details, constraints, and situational awareness that influence the response.',
'tips' => [
'Include relevant background information',
'Mention any constraints or limitations',
'Provide examples of the desired scenario',
],
],
[
'id' => 'persona',
'title' => 'Persona / Role',
'content' => 'Define who the AI should be. Assigning a specific role or persona helps the AI adopt the appropriate tone, expertise level, and perspective for the task.',
'tips' => [
'Use professional roles (e.g., "Senior Developer", "Marketing Expert")',
'Specify expertise level (e.g., "Expert", "Beginner-friendly")',
'Combine multiple roles if needed',
],
],
[
'id' => 'tone',
'title' => 'Tone / Style',
'content' => 'Specify the communication style and tone. This ensures the AI\'s response matches your expectations for formality, friendliness, and overall style.',
'tips' => [
'Use descriptive adjectives (e.g., "Professional", "Casual", "Technical")',
'Specify formality level',
'Mention any stylistic preferences',
],
],
[
'id' => 'constraints',
'title' => 'Constraints',
'content' => 'Define limitations and rules the AI must follow. Constraints help prevent unwanted content and ensure the response meets specific requirements.',
'tips' => [
'Set word or character limits',
'Exclude certain topics or formats',
'Specify mandatory elements',
],
],
[
'id' => 'output_format',
'title' => 'Output Format',
'content' => 'Specify the expected structure of the response. A clear format helps the AI organize the information in a way that is most useful for you.',
'tips' => [
'Use markdown, JSON, or plain text',
'Specify sections and headings',
'Include examples of the desired format',
],
],
[
'id' => 'examples',
'title' => 'Examples (Few-Shot)',
'content' => 'Provide example inputs and outputs. Few-shot learning helps the AI understand the pattern and quality expected in the response.',
'tips' => [
'Include 2-3 high-quality examples',
'Show both input and desired output',
'Ensure examples cover edge cases',
],
],
[
'id' => 'variables',
'title' => 'Variables',
'content' => 'Use dynamic placeholders for reusable prompts. Variables make your prompts flexible and reusable across different scenarios.',
'tips' => [
'Use clear variable names (e.g., {{topic}}, {{audience}})',
'Document expected variable values',
'Keep variable count manageable',
],
],
[
'id' => 'temperature',
'title' => 'Temperature',
'content' => 'Control the randomness of the AI response. Lower values produce more deterministic and focused outputs, while higher values increase creativity and diversity.',
'tips' => [
'0.0 - 0.3: Factual, deterministic, focused',
'0.4 - 0.7: Balanced, natural, versatile',
'0.8 - 1.0: Creative, diverse, exploratory',
],
],
[
'id' => 'max_tokens',
'title' => 'Max Tokens',
'content' => 'Limit the length of the AI response. Setting an appropriate token limit helps control response length and cost.',
'tips' => [
'Short responses: 100-300 tokens',
'Medium responses: 500-1000 tokens',
'Long responses: 1500+ tokens',
],
],
[
'id' => 'best_practices',
'title' => 'Best Practices',
'content' => 'Follow these guidelines to create effective prompts:',
'tips' => [
'Be specific and clear about your requirements',
'Provide context and examples',
'Iterate and refine your prompts',
'Test with different models and settings',
'Keep prompts concise but complete',
'Use structured formats for complex tasks',
],
],
],
];
}
}