New_Micro_Learning/backend/app/Modules/AI/Infrastructure/LocalStructuringProvider.php

79 خطوط
4.5 KiB
PHP

<?php
namespace App\Modules\AI\Infrastructure;
use App\Modules\AI\Domain\AiProvider;
use Illuminate\Support\Str;
final class LocalStructuringProvider implements AiProvider
{
public function id(): string
{
return 'local-structuring-v1';
}
public function external(): bool
{
return false;
}
public function chat(string $prompt, ?string $systemPrompt = null): string
{
return trim(preg_replace('/\s+/u', ' ', strip_tags($prompt)) ?? '');
}
public function structure(array $fragments, array $options): array
{
$language = $options['language'] ?? 'fa';
$topic = trim((string) ($options['topic'] ?? ''));
$title = $topic !== '' ? $topic : trim((string) ($fragments[0]['heading'] ?? ''));
if ($title === '') {
$title = $language === 'en' ? 'Imported learning draft' : 'پیش‌نویس محتوای واردشده';
}
$lessonCount = max(1, min(12, (int) ($options['lessonCount'] ?? count($fragments) ?: 1)));
$selected = array_slice($fragments, 0, $lessonCount);
if ($selected === []) {
$selected = [['id' => 'generated', 'locator' => null, 'heading' => $title, 'content' => (string) ($options['objective'] ?? $topic)]];
}
$lessons = array_map(function (array $fragment, int $index) use ($language): array {
$heading = trim((string) ($fragment['heading'] ?? ''));
$content = trim($fragment['content']);
return [
'title' => $heading !== '' ? Str::limit($heading, 150, '') : ($language === 'en' ? 'Lesson '.($index + 1) : 'درس '.($index + 1)),
'summary' => Str::limit($content, 500),
'sourceFragmentIds' => [$fragment['id']],
'blocks' => $content === '' ? [] : [['type' => 'text', 'schemaVersion' => 1, 'data' => ['html' => '<p>'.nl2br(e($content)).'</p>']]],
];
}, $selected, array_keys($selected));
return [
'schemaVersion' => 1,
'providerDisclosure' => 'Deterministic local structuring; no external generative model was used.',
'title' => $title,
'description' => (string) ($options['objective'] ?? ($language === 'en' ? 'Draft generated from the supplied source for Designer review.' : 'پیش‌نویس تولیدشده از منبع ورودی برای بازبینی طراح.')),
'settings' => collect($options)->only(['audience', 'objective', 'duration', 'difficulty', 'language', 'tone', 'assessmentLevel', 'interactionDensity'])->all(),
'modules' => [['title' => $language === 'en' ? 'Core content' : 'محتوای اصلی', 'lessons' => $lessons]],
];
}
public function assist(string $operation, string $content, array $context): array
{
$plain = trim(preg_replace('/\s+/u', ' ', strip_tags($content)) ?? '');
$result = match ($operation) {
'generate_lesson' => ['title' => Str::limit($plain, 80), 'outline' => ['مقدمه', 'نکات کلیدی', 'تمرین کاربردی'], 'draft' => $plain],
'generate_quiz' => ['prompt' => 'کدام گزینه با محتوای درس سازگار است؟', 'options' => [Str::limit($plain, 120), 'گزینه نیازمند بازبینی طراح'], 'answerIndex' => 0],
'rewrite' => $plain,
'shorten' => Str::limit($plain, max(120, (int) floor(mb_strlen($plain) * 0.6))),
'simplify' => preg_replace('/[؛:]/u', '.', $plain) ?? $plain,
'split_lesson' => collect(preg_split('/(?<=[.!؟])\s+/u', $plain) ?: [])->chunk(3)->map(fn ($chunk) => $chunk->implode(' '))->values()->all(),
'generate_examples' => ['اصل یا مفهوم: '.Str::limit($plain, 180), 'مثال کاربردی باید توسط طراح با زمینه سازمان تکمیل شود.'],
'add_interaction' => ['type' => 'flashcard', 'front' => Str::limit($plain, 180), 'back' => 'پاسخ باید توسط طراح تأیید شود.'],
'audit_course', 'check_objectives', 'check_assessment_alignment' => ['summary' => 'Local structural audit completed.', 'issues' => $plain === '' ? ['Content is empty.'] : [], 'requiresDesignerReview' => true],
default => $plain,
};
return ['schemaVersion' => 1, 'operation' => $operation, 'proposal' => $result, 'context' => collect($context)->only(['entityType', 'entityId'])->all(), 'providerDisclosure' => 'Local deterministic assistant; review before accepting.'];
}
}