Patryk Studzinski commited on
Commit
093fabc
·
1 Parent(s): 5fabfb8

Fix: Handle function-call style

Browse files
Files changed (2) hide show
  1. README.md +159 -3
  2. app/logic/infill_utils.py +10 -0
README.md CHANGED
@@ -10,11 +10,14 @@ pinned: false
10
 
11
  # Bielik App Service
12
 
13
- Multi-model LLM service for description enhancement and A/B testing.
14
 
15
  ## Overview
16
 
17
- This service provides an API for generating enhanced descriptions using multiple open-source LLMs. It supports comparing outputs across different models to evaluate quality, speed, and Polish language support.
 
 
 
18
 
19
  ## Models
20
 
@@ -42,13 +45,20 @@ This service provides an API for generating enhanced descriptions using multiple
42
  | `POST` | `/models/{name}/load` | Load a model into memory |
43
  | `POST` | `/models/{name}/unload` | Unload a model from memory |
44
 
45
- ### Generation
46
 
47
  | Method | Endpoint | Description |
48
  |--------|----------|-------------|
49
  | `POST` | `/enhance-description` | Generate description with single model |
50
  | `POST` | `/compare` | Compare outputs from multiple models |
51
 
 
 
 
 
 
 
 
52
  ---
53
 
54
  ## Lazy Loading
@@ -227,6 +237,137 @@ Compare outputs from multiple models for the same input.
227
 
228
  ---
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  ## Environment Variables
231
 
232
  | Variable | Description | Required |
@@ -255,3 +396,18 @@ uvicorn app.main:app --reload --port 8000
255
  API available at `http://localhost:8000`
256
 
257
  Docs at `http://localhost:8000/docs`
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  # Bielik App Service
12
 
13
+ Multi-model LLM service for description enhancement, batch gap-filling, and A/B testing.
14
 
15
  ## Overview
16
 
17
+ This service provides an API for generating enhanced descriptions using multiple open-source LLMs. It supports:
18
+ - **Description Enhancement**: Generate marketing descriptions from structured data
19
+ - **Batch Infill**: Fill gaps (`[GAP:n]` or `___`) in ad texts with natural words
20
+ - **Multi-Model Comparison**: Compare outputs across different models for A/B testing
21
 
22
  ## Models
23
 
 
45
  | `POST` | `/models/{name}/load` | Load a model into memory |
46
  | `POST` | `/models/{name}/unload` | Unload a model from memory |
47
 
48
+ ### Description Generation
49
 
50
  | Method | Endpoint | Description |
51
  |--------|----------|-------------|
52
  | `POST` | `/enhance-description` | Generate description with single model |
53
  | `POST` | `/compare` | Compare outputs from multiple models |
54
 
55
+ ### Batch Infill (Gap-Filling)
56
+
57
+ | Method | Endpoint | Description |
58
+ |--------|----------|-------------|
59
+ | `POST` | `/infill` | Batch gap-filling with single model |
60
+ | `POST` | `/compare-infill` | Compare gap-filling across multiple models |
61
+
62
  ---
63
 
64
  ## Lazy Loading
 
237
 
238
  ---
239
 
240
+ ### `POST /infill`
241
+
242
+ Batch gap-filling for ads using a single model. Accepts texts with `[GAP:n]` markers or `___` and returns filled text with per-gap choices and alternatives.
243
+
244
+ **Gap Notation:**
245
+ - `[GAP:1]`, `[GAP:2]`, ... → Explicit numbered gaps (preferred)
246
+ - `___` → Auto-numbered in scan order
247
+
248
+ **Request:**
249
+ ```json
250
+ {
251
+ "domain": "cars",
252
+ "items": [
253
+ {
254
+ "id": "ad1",
255
+ "text_with_gaps": "Sprzedam [GAP:1] BMW w [GAP:2] stanie technicznym"
256
+ },
257
+ {
258
+ "id": "ad2",
259
+ "text_with_gaps": "Auto ma ___ km przebiegu i ___ lakier"
260
+ }
261
+ ],
262
+ "model": "bielik-1.5b",
263
+ "options": {
264
+ "top_n_per_gap": 3,
265
+ "language": "pl",
266
+ "temperature": 0.6
267
+ }
268
+ }
269
+ ```
270
+
271
+ **Response:**
272
+ ```json
273
+ {
274
+ "model": "bielik-1.5b",
275
+ "results": [
276
+ {
277
+ "id": "ad1",
278
+ "status": "ok",
279
+ "filled_text": "Sprzedam eleganckie BMW w doskonałym stanie technicznym",
280
+ "gaps": [
281
+ {
282
+ "index": 1,
283
+ "marker": "[GAP:1]",
284
+ "choice": "eleganckie",
285
+ "alternatives": ["piękne", "zadbane"]
286
+ },
287
+ {
288
+ "index": 2,
289
+ "marker": "[GAP:2]",
290
+ "choice": "doskonałym",
291
+ "alternatives": ["bardzo dobrym", "idealnym"]
292
+ }
293
+ ],
294
+ "error": null
295
+ }
296
+ ],
297
+ "total_time": 3.45,
298
+ "processed_count": 2,
299
+ "error_count": 0
300
+ }
301
+ ```
302
+
303
+ **Options:**
304
+ | Field | Type | Default | Description |
305
+ |-------|------|---------|-------------|
306
+ | `gap_notation` | string | `"auto"` | `"auto"`, `"[GAP:n]"`, or `"___"` |
307
+ | `top_n_per_gap` | int | `3` | Alternatives per gap (1-5) |
308
+ | `language` | string | `"pl"` | Output language |
309
+ | `temperature` | float | `0.6` | Generation temperature (0-1) |
310
+ | `max_new_tokens` | int | `256` | Max tokens to generate |
311
+
312
+ ---
313
+
314
+ ### `POST /compare-infill`
315
+
316
+ Multi-model batch gap-filling comparison for A/B testing.
317
+
318
+ **Request:**
319
+ ```json
320
+ {
321
+ "domain": "cars",
322
+ "items": [
323
+ {
324
+ "id": "ad1",
325
+ "text_with_gaps": "Sprzedam [GAP:1] BMW w [GAP:2] stanie"
326
+ }
327
+ ],
328
+ "models": ["bielik-1.5b", "qwen2.5-3b", "pllum-12b"],
329
+ "options": {
330
+ "top_n_per_gap": 3
331
+ }
332
+ }
333
+ ```
334
+
335
+ **Response:**
336
+ ```json
337
+ {
338
+ "domain": "cars",
339
+ "models": [
340
+ {
341
+ "model": "bielik-1.5b",
342
+ "type": "local",
343
+ "results": [...],
344
+ "time": 2.1,
345
+ "error_count": 0
346
+ },
347
+ {
348
+ "model": "qwen2.5-3b",
349
+ "type": "local",
350
+ "results": [...],
351
+ "time": 1.8,
352
+ "error_count": 0
353
+ }
354
+ ],
355
+ "total_time": 5.2
356
+ }
357
+ ```
358
+
359
+ ---
360
+
361
+ ## Domains
362
+
363
+ Currently supported domains:
364
+
365
+ | Domain | Schema Fields |
366
+ |--------|---------------|
367
+ | `cars` | `make`, `model`, `year`, `mileage`, `features[]`, `condition` |
368
+
369
+ ---
370
+
371
  ## Environment Variables
372
 
373
  | Variable | Description | Required |
 
396
  API available at `http://localhost:8000`
397
 
398
  Docs at `http://localhost:8000/docs`
399
+
400
+ ## Live Demo
401
+
402
+ Deployed on HuggingFace Spaces:
403
+
404
+ **URL:** `https://studzinsky-bielik-app-service.hf.space`
405
+
406
+ **Quick Test:**
407
+ ```bash
408
+ # Health check
409
+ curl https://studzinsky-bielik-app-service.hf.space/health
410
+
411
+ # List models
412
+ curl https://studzinsky-bielik-app-service.hf.space/models
413
+ ```
app/logic/infill_utils.py CHANGED
@@ -96,6 +96,7 @@ def parse_infill_json(raw_output: str) -> Optional[dict]:
96
  Handles common LLM quirks:
97
  - JSON wrapped in markdown code blocks
98
  - Leading/trailing text before/after JSON
 
99
  - Minor formatting issues
100
 
101
  Args:
@@ -147,6 +148,15 @@ def parse_infill_json(raw_output: str) -> Optional[dict]:
147
  try:
148
  parsed = json.loads(json_str)
149
 
 
 
 
 
 
 
 
 
 
150
  # Validate required fields
151
  if 'filled_text' not in parsed and 'gaps' not in parsed:
152
  return None
 
96
  Handles common LLM quirks:
97
  - JSON wrapped in markdown code blocks
98
  - Leading/trailing text before/after JSON
99
+ - Function-call style wrapper ({"name": "...", "arguments": {...}})
100
  - Minor formatting issues
101
 
102
  Args:
 
148
  try:
149
  parsed = json.loads(json_str)
150
 
151
+ # Handle function-call style wrapper:
152
+ # {"name": "filled_text", "arguments": {"filled_text": "...", "gaps": [...]}}
153
+ if 'arguments' in parsed and isinstance(parsed['arguments'], dict):
154
+ parsed = parsed['arguments']
155
+
156
+ # Also handle: {"name": "...", "parameters": {...}}
157
+ if 'parameters' in parsed and isinstance(parsed['parameters'], dict):
158
+ parsed = parsed['parameters']
159
+
160
  # Validate required fields
161
  if 'filled_text' not in parsed and 'gaps' not in parsed:
162
  return None