66use Bblslug \HttpClient ;
77use Bblslug \Models \ModelRegistry ;
88use Bblslug \Models \UsageExtractor ;
9+ use Bblslug \Validation \HtmlValidator ;
910
1011class Bblslug
1112{
@@ -31,19 +32,21 @@ public static function listModels(): array
3132
3233 /**
3334 * Translate text or HTML via any registered model.
34- * @param string $apiKey API key for the model.
35- * @param string $format "text" or "html".
36- * @param string $modelKey Model ID (e.g. "deepl:pro").
37- * @param string $text The source text or HTML.
3835 *
39- * @param string|null $context Optional context prompt.
40- * @param bool $dryRun If true: prepare placeholders only.
41- * @param string[] $filters Placeholder filters to apply.
42- * @param string|null $proxy Optional proxy URL (from env or CLI).
43- * @param string|null $sourceLang Optional source language code.
44- * @param string|null $targetLang Optional target language code.
45- * @param array<string,string> $variables Model-specific vars (e.g. ['some'=>'...'])
46- * @param bool $verbose If true: include request/response logs.
36+ * @param string $apiKey API key for the model.
37+ * @param string $format "text" or "html".
38+ * @param string $modelKey Model ID (e.g. "deepl:pro").
39+ * @param string $text The source text or HTML.
40+ *
41+ * @param string|null $context Optional context prompt.
42+ * @param bool $dryRun If true: prepare placeholders only.
43+ * @param string[] $filters Placeholder filters to apply.
44+ * @param string|null $proxy Optional proxy URL (from env or CLI).
45+ * @param string|null $sourceLang Optional source language code.
46+ * @param string|null $targetLang Optional target language code.
47+ * @param bool $validate If true: perform container syntax validation.
48+ * @param array<string,string> $variables Model-specific vars (e.g. ['some'=>'...'])
49+ * @param bool $verbose If true: include request/response logs.
4750 *
4851 * @return array{
4952 * original: string,
@@ -59,7 +62,7 @@ public static function listModels(): array
5962 * }
6063 *
6164 * @throws \InvalidArgumentException If inputs are invalid.
62- * @throws \RuntimeException On HTTP or parsing errors.
65+ * @throws \RuntimeException On HTTP, parsing or validation errors.
6366 */
6467 public static function translate (
6568 string $ apiKey ,
@@ -73,9 +76,14 @@ public static function translate(
7376 ?string $ proxy = null ,
7477 ?string $ sourceLang = null ,
7578 ?string $ targetLang = null ,
79+ bool $ validate = true ,
7680 array $ variables = [],
7781 bool $ verbose = false
7882 ): array {
83+ // Prepare holders for validation debug
84+ $ valLogPre = '' ;
85+ $ valLogPost = '' ;
86+
7987 // Validate model
8088 $ registry = new ModelRegistry ();
8189 if (!$ registry ->has ($ modelKey )) {
@@ -102,6 +110,24 @@ public static function translate(
102110 // Measure original length
103111 $ originalLength = mb_strlen ($ text );
104112
113+ // Pre-validation (before filters)
114+ if ($ validate && $ format !== 'text ' ) {
115+ $ validator = match ($ format ) {
116+ 'html ' => new HtmlValidator (),
117+ default => null ,
118+ };
119+ if ($ validator ) {
120+ $ result = $ validator ->validate ($ text );
121+ if (! $ result ->isValid ()) {
122+ throw new \RuntimeException (
123+ "Validation failed: " . implode ('; ' , $ result ->getErrors ())
124+ );
125+ } elseif ($ verbose ) {
126+ $ valLogPre = "[Validation pre-pass] \n" ;
127+ }
128+ }
129+ }
130+
105131 // Apply placeholder filters
106132 $ filterManager = new FilterManager ($ filters );
107133 $ prepared = $ filterManager ->apply ($ text );
@@ -165,7 +191,7 @@ public static function translate(
165191 );
166192
167193 $ httpStatus = $ http ['status ' ];
168- $ debugRequest = $ http ['debugRequest ' ];
194+ $ debugRequest = $ valLogPre . $ http ['debugRequest ' ];
169195 $ debugResponse = $ http ['debugResponse ' ];
170196 $ raw = $ http ['body ' ];
171197
@@ -200,6 +226,29 @@ public static function translate(
200226 // Collect stats
201227 $ filterStats = $ filterManager ->getStats ();
202228
229+ // Post-validation (after translation)
230+ if ($ validate && $ format !== 'text ' ) {
231+ $ validator = match ($ format ) {
232+ 'html ' => new HtmlValidator (),
233+ default => null ,
234+ };
235+ if ($ validator ) {
236+ $ res2 = $ validator ->validate ($ result );
237+ if (! $ res2 ->isValid ()) {
238+ throw new \RuntimeException (
239+ "Validation failed: " . implode ('; ' , $ res2 ->getErrors ())
240+ );
241+ } elseif ($ verbose ) {
242+ $ valLogPost = "[Validation post-pass] \n" ;
243+ }
244+ }
245+ }
246+
247+ // Append post-validation log into response debug
248+ if ($ verbose ) {
249+ $ debugResponse .= $ valLogPost ;
250+ }
251+
203252 return [
204253 'original ' => $ text ,
205254 'prepared ' => $ prepared ,
0 commit comments