Skip to content

Commit 3fc7673

Browse files
committed
feat(core/cli): add progress callback and wire to --verbose
- Bblslug::translate(): introduce optional `onFeedback(?callable)` and emit progress messages at key steps (start, pre/post validation, filters, length check, HTTP, parsing, restore, done). - Console/Cli: add feedback handler (maps level -> Help::info/warning/error) and pass it when `--verbose` is set. - README: include `onFeedback` in the translate() example and add a brief explanation.
1 parent 01829da commit 3fc7673

File tree

3 files changed

+61
-0
lines changed

3 files changed

+61
-0
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ $result = Bblslug::translate(
274274
// Additional context/prompt pass to model
275275
context: 'Translate as a professional technical translator',
276276
filters: ['url','html_code'], // List of placeholder filters
277+
onFeedback: $verbose ? static function (string $message, string $level = 'info'): void { error_log("[$level] $message"); } : null, // Optional progress callback
277278
promptKey: 'translator', // which prompt template to use
278279
proxy: getenv('BBLSLUG_PROXY'), // Optional proxy URI (http://..., socks5h://...)
279280
sourceLang: 'DE', // Source language code (optional; autodetect if null)
@@ -285,6 +286,9 @@ $result = Bblslug::translate(
285286
echo $result['result'];
286287
```
287288

289+
`onFeedback` is an optional callback for the progress information, accepts (string $message, string $level),
290+
where `$level` includes `'info' | 'warning' | 'error'`. Send null to swhitch off.
291+
288292
Result structure:
289293

290294
```php

src/Bblslug/Bblslug.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ public static function listPrompts(): array
5555
* @param string|null $context Optional context prompt.
5656
* @param bool $dryRun If true: prepare placeholders only.
5757
* @param string[] $filters Placeholder filters to apply.
58+
* @param ?callable(string,string=):void $onFeedback Optional feedback callback that
59+
* receives (message, level).
5860
* @param string $promptKey Prompt template key
5961
* @param string|null $proxy Optional proxy URL (from env or CLI).
6062
* @param string|null $sourceLang Optional source language code.
@@ -88,6 +90,7 @@ public static function translate(
8890
?string $context = null,
8991
bool $dryRun = false,
9092
array $filters = [],
93+
?callable $onFeedback = null,
9194
string $promptKey = 'translator',
9295
?string $proxy = null,
9396
?string $sourceLang = null,
@@ -100,6 +103,25 @@ public static function translate(
100103
$valLogPre = '';
101104
$valLogPost = '';
102105

106+
// Feedback helper.
107+
$say = static function (?callable $cb, string $msg, string $level = 'info'): void {
108+
if (is_callable($cb)) {
109+
try {
110+
$cb($msg, $level);
111+
} catch (\Throwable $e) {
112+
/* swallow on purpose */
113+
}
114+
}
115+
};
116+
117+
$say(
118+
$onFeedback,
119+
"Starting translation (model='{$modelKey}', format='{$format}', dryRun="
120+
. ($dryRun ? 'true' : 'false')
121+
. ")",
122+
'info'
123+
);
124+
103125
// Validate model
104126
$registry = new ModelRegistry();
105127
if (!$registry->has($modelKey)) {
@@ -125,9 +147,11 @@ public static function translate(
125147

126148
// Measure original length
127149
$originalLength = mb_strlen($text);
150+
$say($onFeedback, "Input received (length={$originalLength})", 'info');
128151

129152
// Pre-validation (before filters)
130153
if ($validate && $format !== 'text') {
154+
$say($onFeedback, "Pre-validation started ({$format})", 'info');
131155
switch ($format) {
132156
case 'json':
133157
$jsonValidator = new JsonValidator();
@@ -161,21 +185,26 @@ public static function translate(
161185
// Other formats: no container validation
162186
break;
163187
}
188+
$say($onFeedback, "Pre-validation passed ({$format})", 'info');
164189
}
165190

166191
// Apply placeholder filters
167192
$filterManager = new FilterManager($filters);
193+
$say($onFeedback, "Applying filters: " . (empty($filters) ? '(none)' : implode(', ', $filters)), 'info');
168194
$prepared = $filterManager->apply($text);
169195
$preparedLength = mb_strlen($prepared);
196+
$say($onFeedback, "Filters applied (preparedLength={$preparedLength})", 'info');
170197

171198
// Length guard: make sure prepared text fits model constraints
199+
$say($onFeedback, "Checking model length limits", 'info');
172200
$lengthValidator = TextLengthValidator::fromModelConfig($model);
173201
$lenResult = $lengthValidator->validate($prepared);
174202
if (! $lenResult->isValid()) {
175203
throw new \RuntimeException(
176204
"Input length exceeds model limits: " . implode('; ', $lenResult->getErrors())
177205
);
178206
}
207+
$say($onFeedback, "Length check passed", 'info');
179208

180209
// Prepare options for driver, merging in any CLI-provided variables
181210
$options = array_merge(
@@ -193,6 +222,7 @@ public static function translate(
193222
}
194223

195224
// Build request
225+
$say($onFeedback, "Building request for endpoint: {$endpoint}", 'info');
196226
$req = $driver->buildRequest(
197227
$model,
198228
$prepared,
@@ -205,6 +235,7 @@ public static function translate(
205235
if (!$apiKey) {
206236
throw new \InvalidArgumentException("API key is required for {$modelKey}");
207237
}
238+
$say($onFeedback, "Injecting auth credentials", 'info');
208239
$keyName = $auth['key_name'];
209240
$prefix = isset($auth['prefix']) && $auth['prefix'] !== ''
210241
? $auth['prefix'] . ' '
@@ -226,6 +257,7 @@ public static function translate(
226257
}
227258

228259
// Perform HTTP request
260+
$say($onFeedback, $dryRun ? "Dry-run: skipping HTTP request" : "Sending HTTP request", 'info');
229261
$http = HttpClient::request(
230262
method: 'POST',
231263
url: $req['url'],
@@ -248,13 +280,16 @@ public static function translate(
248280
if ($dryRun) {
249281
$translated = $prepared;
250282
$rawUsage = null;
283+
$say($onFeedback, "Dry-run completed", 'info');
251284
} else {
285+
$say($onFeedback, "HTTP response received (status={$httpStatus})", 'info');
252286
if ($httpStatus >= 400 && empty($model['http_error_handling'])) {
253287
throw new \RuntimeException(
254288
"HTTP {$httpStatus} error from {$req['url']}: {$raw}\n\n" .
255289
$debugRequest . $debugResponse
256290
);
257291
}
292+
$say($onFeedback, "Parsing provider response", 'info');
258293
try {
259294
$parsed = $driver->parseResponse($model, $raw);
260295
$translated = $parsed['text'];
@@ -264,19 +299,23 @@ public static function translate(
264299
$e->getMessage() . "\n\n" . $debugRequest . $debugResponse
265300
);
266301
}
302+
$say($onFeedback, "Provider response parsed", 'info');
267303
}
268304

269305
$consumed = UsageExtractor::extract($model, $rawUsage);
270306

271307
// Restore placeholders
308+
$say($onFeedback, "Restoring placeholders", 'info');
272309
$result = $filterManager->restore($translated);
273310
$finalLength = mb_strlen($result);
311+
$say($onFeedback, "Placeholders restored (translatedLength={$finalLength})", 'info');
274312

275313
// Collect stats
276314
$filterStats = $filterManager->getStats();
277315

278316
// Post-validation (after translation)
279317
if ($validate && $format !== 'text') {
318+
$say($onFeedback, "Post-validation started ({$format})", 'info');
280319
switch ($format) {
281320
case 'json':
282321
$postResult = (new JsonValidator())->validate($result);
@@ -317,13 +356,16 @@ public static function translate(
317356
// Other formats: no container validation
318357
break;
319358
}
359+
$say($onFeedback, "Post-validation passed ({$format})", 'info');
320360
}
321361

322362
// Append post-validation log into response debug
323363
if ($verbose) {
324364
$debugResponse .= $valLogPost;
325365
}
326366

367+
$say($onFeedback, "Done", 'info');
368+
327369
return [
328370
'original' => $text,
329371
'prepared' => $prepared,

src/Bblslug/Console/Cli.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,20 @@ public static function run(): void
202202
// Override by CLI
203203
$variables = array_merge($variables, $cliVars);
204204

205+
// Feedback handler for progress messages.
206+
$feedback = function (string $message, string $level = 'info'): void {
207+
switch ($level) {
208+
case 'warning':
209+
Help::warning($message);
210+
break;
211+
case 'error':
212+
Help::error($message);
213+
break;
214+
default:
215+
Help::info($message);
216+
}
217+
};
218+
205219
// Perform translation
206220
$res = [];
207221
try {
@@ -213,6 +227,7 @@ public static function run(): void
213227
context: $context,
214228
dryRun: $dryRun,
215229
filters: $filters,
230+
onFeedback: $verbose ? $feedback : null,
216231
promptKey: $promptKey,
217232
proxy: $proxy,
218233
sourceLang: $sourceLang,

0 commit comments

Comments
 (0)