PluginProbe
BetterLinks – Link Shortener, Link Cloaking, Redirects, Affiliate Link Manager & MCP / trunk
BetterLinks – Link Shortener, Link Cloaking, Redirects, Affiliate Link Manager & MCP vtrunk
3.1.3 3.1.2 3.1.1 3.1.0 3.0.1 3.0.0 2.4.13 2.4.12 2.4.11 2.4.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 All 110 releases
betterlinks / includes / Tools / PromptAnalyzer.php

PromptAnalyzer.php in BetterLinks – Link Shortener, Link Cloaking, Redirects, Affiliate Link Manager & MCP trunk, at includes/Tools/PromptAnalyzer.php

267 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace BetterLinks\Tools;
4 if ( ! defined( 'ABSPATH' ) ) { exit; }
5
6 /**
7 * Prompt Analyzer - Parses user prompts to extract constraints for AI link generation
8 *
9 * Analyzes user prompts to extract:
10 * - Title length limits (characters or words)
11 * - Description length limits (characters or words)
12 * - Category assignment
13 * - Tags assignment
14 * - Meta title limits
15 * - Meta description limits
16 */
17 class PromptAnalyzer {
18
19 /**
20 * Analyze the user prompt and extract constraints
21 *
22 * @param string $prompt The user's AI prompt
23 * @return array Extracted constraints
24 */
25 public static function analyze( $prompt ) {
26 $constraints = array(
27 'title_max_chars' => null,
28 'title_max_words' => null,
29 'title_min_words' => null,
30 'description_max_chars' => null,
31 'description_max_words' => null,
32 'description_min_words' => null,
33 'category' => null,
34 'tags' => array(),
35 'meta_title_max_chars' => null,
36 'meta_title_max_words' => null,
37 'meta_description_max_chars' => null,
38 'meta_description_max_words' => null,
39 );
40
41 if ( empty( $prompt ) ) {
42 return $constraints;
43 }
44
45 // Extract title constraints
46 $constraints['title_max_chars'] = self::extract_max_chars( $prompt, 'title' );
47 $constraints['title_max_words'] = self::extract_max_words( $prompt, 'title' );
48 $constraints['title_min_words'] = self::extract_min_words( $prompt, 'title' );
49
50 // Extract description constraints
51 $constraints['description_max_chars'] = self::extract_max_chars( $prompt, 'description' );
52 $constraints['description_max_words'] = self::extract_max_words( $prompt, 'description' );
53 $constraints['description_min_words'] = self::extract_min_words( $prompt, 'description' );
54
55 // Extract meta title constraints
56 $constraints['meta_title_max_chars'] = self::extract_max_chars( $prompt, 'meta title' );
57 $constraints['meta_title_max_words'] = self::extract_max_words( $prompt, 'meta title' );
58
59 // Extract meta description constraints
60 $constraints['meta_description_max_chars'] = self::extract_max_chars( $prompt, 'meta description' );
61 $constraints['meta_description_max_words'] = self::extract_max_words( $prompt, 'meta description' );
62
63 // Extract category and tags
64 // Priority 1: Try AI-based extraction first (if API keys are configured)
65 // $ai_extraction = AIPromptExtractor::extract_from_prompt( $prompt );
66 // $category = $ai_extraction['category'];
67 // $tags = $ai_extraction['tags'];
68 // echo 'c<pre>'; print_r($category); echo '</pre>';
69 // echo 't<pre>'; print_r($tags); echo '</pre>';
70 // Priority 2: If AI extraction didn't return results, fall back to regex-based extraction
71 if ( ( empty( $category ) && empty( $tags ) ) || ( empty( $category ) && ! empty( $tags ) ) || ( ! empty( $category ) && empty( $tags ) ) ) {
72 $regex_category = self::extract_category( $prompt );
73 $regex_tags = self::extract_tags( $prompt );
74
75 // Use regex results if AI didn't find them
76 if ( empty( $category ) && ! empty( $regex_category ) ) {
77 $category = $regex_category;
78 }
79 if ( empty( $tags ) && ! empty( $regex_tags ) ) {
80 $tags = $regex_tags;
81 }
82 }
83
84 $constraints['category'] = $category;
85 $constraints['tags'] = $tags;
86
87 return $constraints;
88 }
89
90 /**
91 * Extract maximum character limit for a field
92 *
93 * @param string $prompt The prompt text
94 * @param string $field The field name (title, description, etc.)
95 * @return int|null Maximum characters or null if not specified
96 */
97 private static function extract_max_chars( $prompt, $field ) {
98 // Match patterns like "title max 50 characters" or "title maximum 50 chars"
99 $pattern = '/(?:' . preg_quote( $field ) . ').*?(?:max|maximum)\s+(\d+)\s+(?:char|character)/i';
100 if ( preg_match( $pattern, $prompt, $matches ) ) {
101 return intval( $matches[1] );
102 }
103 return null;
104 }
105
106 /**
107 * Extract maximum word limit for a field
108 *
109 * @param string $prompt The prompt text
110 * @param string $field The field name
111 * @return int|null Maximum words or null if not specified
112 */
113 private static function extract_max_words( $prompt, $field ) {
114 // Match patterns like "title max 5 words" or "title maximum 5 word"
115 $pattern = '/(?:' . preg_quote( $field ) . ').*?(?:max|maximum)\s+(\d+)\s+(?:word)/i';
116 if ( preg_match( $pattern, $prompt, $matches ) ) {
117 return intval( $matches[1] );
118 }
119 return null;
120 }
121
122 /**
123 * Extract minimum word limit for a field
124 *
125 * @param string $prompt The prompt text
126 * @param string $field The field name
127 * @return int|null Minimum words or null if not specified
128 */
129 private static function extract_min_words( $prompt, $field ) {
130 // Match patterns like "title min 2 words" or "title minimum 2 word"
131 $pattern = '/(?:' . preg_quote( $field ) . ').*?(?:min|minimum)\s+(\d+)\s+(?:word)/i';
132 if ( preg_match( $pattern, $prompt, $matches ) ) {
133 return intval( $matches[1] );
134 }
135 return null;
136 }
137
138 /**
139 * Extract category from prompt
140 *
141 * @param string $prompt The prompt text
142 * @return string|null Category name or null if not specified
143 */
144 private static function extract_category( $prompt ) {
145 // Try multiple patterns to handle various category specification formats
146
147 // Pattern 1: "category: Technology" or "assign to category Technology"
148 $pattern1 = '/(?:category|assign\s+to\s+category)[\s:]+([A-Za-z0-9\s\-]+?)(?:\s+(?:and|with|tags?|tag)|,|\.|\s*$)/i';
149
150 // Pattern 2: "- category: Technology" (list format with dash)
151 $pattern2 = '/[\-\*]\s*(?:category|assign\s+to\s+category)[\s:]+([A-Za-z0-9\s\-]+?)(?:\s+(?:and|with|tags?|tag)|[\-\*]|,|\.|\s*$)/i';
152
153 // Pattern 3: "category Technology" (without colon)
154 $pattern3 = '/(?:category|assign\s+to\s+category)\s+([A-Za-z0-9\s\-]+?)(?:\s+(?:and|with|tags?|tag)|[\-\*]|,|\.|\s*$)/i';
155
156 $patterns = array( $pattern1, $pattern2, $pattern3 );
157
158 foreach ( $patterns as $pattern ) {
159 if ( preg_match( $pattern, $prompt, $matches ) ) {
160 $category = trim( $matches[1] );
161
162 // Remove any trailing "and", "with", dashes, or commas that might have been captured
163 $category = preg_replace( '/\s+(?:and|with)$/i', '', $category );
164 $category = preg_replace( '/[\-\*,]\s*$/', '', $category );
165
166 // Remove any newlines or extra whitespace
167 $category = preg_replace( '/[\r\n]+/', '', $category );
168 $category = preg_replace( '/\s+/', ' ', $category );
169 $category = trim( $category );
170
171 // Validate category (must contain at least one letter or number)
172 if ( ! empty( $category ) && preg_match( '/[A-Za-z0-9]/', $category ) ) {
173 return $category;
174 }
175 }
176 }
177
178 return null;
179 }
180
181 /**
182 * Extract tags from prompt
183 *
184 * @param string $prompt The prompt text
185 * @return array Array of tags
186 */
187 private static function extract_tags( $prompt ) {
188 $tags = array();
189
190 // Try multiple patterns to handle various tag specification formats
191 // Pattern 1: "tags: tag1, tag2, tag3" or "tag: tag1" (most common)
192 $pattern1 = '/(?:tags?|tag)\s*:\s*([A-Za-z0-9\s,\-]+?)(?:\s+(?:and|with|category|assign)|,\s*category|,\s*assign|\.|\s*$)/i';
193
194 // Pattern 2: "- tags: tag1, tag2" (list format with dash)
195 $pattern2 = '/[\-\*]\s*(?:tags?|tag)\s*:\s*([A-Za-z0-9\s,\-]+?)(?:\s+(?:and|with|category|assign)|[\-\*]|\.|\s*$)/i';
196
197 // Pattern 3: "tags tag1, tag2, tag3" (without colon, but with comma-separated values)
198 $pattern3 = '/(?:tags?|tag)\s+([A-Za-z0-9\s,\-]+?)(?:\s+(?:and|with|category|assign)|[\-\*]|\.|\s*$)/i';
199
200 // Try each pattern in order
201 $patterns = array( $pattern1, $pattern2, $pattern3 );
202
203 foreach ( $patterns as $pattern ) {
204 if ( preg_match( $pattern, $prompt, $matches ) ) {
205 $tag_string = trim( $matches[1] );
206
207 // Remove any trailing "and", "with", or dashes that might have been captured
208 $tag_string = preg_replace( '/\s+(?:and|with)$/i', '', $tag_string );
209 $tag_string = preg_replace( '/[\-\*]\s*$/', '', $tag_string );
210
211 // Remove any newlines or extra whitespace
212 $tag_string = preg_replace( '/[\r\n]+/', '', $tag_string );
213 $tag_string = preg_replace( '/\s+/', ' ', $tag_string );
214
215 // Split by comma and clean up
216 $tag_array = array_map( 'trim', explode( ',', $tag_string ) );
217
218 // Filter out empty values and validate tags
219 $tags = array_filter( $tag_array, function( $tag ) {
220 // Only keep tags that are not empty and don't contain only special characters
221 return ! empty( $tag ) && preg_match( '/[A-Za-z0-9]/', $tag );
222 });
223
224 // If we found valid tags, return them
225 if ( ! empty( $tags ) ) {
226 return array_values( $tags ); // Re-index array
227 }
228 }
229 }
230
231 return $tags;
232 }
233
234 /**
235 * Apply constraints to a string
236 *
237 * @param string $text The text to constrain
238 * @param int|null $max_chars Maximum characters
239 * @param int|null $max_words Maximum words
240 * @param int|null $min_words Minimum words
241 * @return string Constrained text
242 */
243 public static function apply_constraints( $text, $max_chars = null, $max_words = null, $min_words = null ) {
244 if ( empty( $text ) ) {
245 return $text;
246 }
247
248 // Apply character limit first
249 if ( ! is_null( $max_chars ) && strlen( $text ) > $max_chars ) {
250 $text = substr( $text, 0, $max_chars );
251 }
252
253 // Apply word limits
254 if ( ! is_null( $max_words ) || ! is_null( $min_words ) ) {
255 $words = explode( ' ', $text );
256
257 if ( ! is_null( $max_words ) && count( $words ) > $max_words ) {
258 $words = array_slice( $words, 0, $max_words );
259 $text = implode( ' ', $words );
260 }
261 }
262
263 return trim( $text );
264 }
265 }
266
267