'system', 'content' => 'Kamu adalah social media strategist. Selalu jawab valid JSON tanpa markdown dengan schema: {"title":"...","caption":"...","hashtags":["..."],"cta":"..."}. Gunakan Bahasa Indonesia.', ], [ 'role' => 'user', 'content' => "Buat konten untuk {$platformHint}. Tone: {$tone}. Topik: {$topic}. Caption maksimal 480 karakter. Hashtag 6-10 item relevan tanpa spasi.", ], ]; $raw = $this->openAiChatService->reply($messages); $clean = trim($raw); if (str_starts_with($clean, '```')) { $clean = preg_replace('/^```(?:json)?\s*|\s*```$/m', '', $clean) ?? $clean; $clean = trim($clean); } $decoded = json_decode($clean, true); if (! is_array($decoded)) { $decoded = json_decode($this->extractJsonObject($clean) ?? '', true); } if (! is_array($decoded)) { return [ 'title' => Str::limit($topic, 80), 'caption' => Str::limit($raw !== '' ? $raw : $topic, 500), 'hashtags' => ['#konten', '#bisnis', '#digitalmarketing'], 'cta' => 'Follow untuk konten berikutnya.', 'raw' => $raw, ]; } $hashtags = collect($decoded['hashtags'] ?? []) ->map(fn ($item) => trim((string) $item)) ->filter() ->take(10) ->map(fn ($item) => str_starts_with($item, '#') ? $item : '#'.$item) ->unique() ->values() ->all(); $caption = trim((string) ($decoded['caption'] ?? '')); if ($caption === '') { $caption = Str::limit($raw !== '' ? $raw : $topic, 500); } return [ 'title' => trim((string) ($decoded['title'] ?? Str::limit($topic, 80))), 'caption' => $caption, 'hashtags' => $hashtags, 'cta' => trim((string) ($decoded['cta'] ?? 'Follow untuk konten berikutnya.')), 'raw' => $raw, ]; } private function extractJsonObject(string $content): ?string { $start = strpos($content, '{'); $end = strrpos($content, '}'); if ($start === false || $end === false || $end <= $start) { return null; } return substr($content, $start, $end - $start + 1); } }