Skip to content

Commit 2de6b2e

Browse files
author
valentinfrlch
committed
Parse description in structured mode using user-specified field.
1 parent 0792b5f commit 2de6b2e

File tree

4 files changed

+56
-15
lines changed

4 files changed

+56
-15
lines changed

custom_components/llmvision/__init__.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
RESPONSE_FORMAT,
7474
STRUCTURE,
7575
TITLE_FIELD,
76+
DESCPRIPTION_FIELD,
7677
)
7778

7879
CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
@@ -472,6 +473,7 @@ def __init__(self, data_call):
472473
self.response_format = data_call.data.get(RESPONSE_FORMAT, "text")
473474
self.structure = data_call.data.get(STRUCTURE, None)
474475
self.title_field = data_call.data.get(TITLE_FIELD, "")
476+
self.description_field = data_call.data.get(DESCPRIPTION_FIELD, "")
475477
self.memory: Memory | None = None
476478

477479
# ------------ Create Event ------------
@@ -555,11 +557,24 @@ async def _create_event(
555557
title = response.get("title") or "Motion detected"
556558
title = str(title)
557559

560+
description = response.get("response_text", "")
561+
structured_response = response.get("structured_response")
562+
563+
# Handle structured response for description extraction
564+
if (
565+
call.response_format == "json"
566+
and call.description_field
567+
and isinstance(structured_response, dict)
568+
):
569+
description_field_value = structured_response.get(call.description_field)
570+
if description_field_value is not None:
571+
description = str(description_field_value)
572+
558573
await timeline.create_event(
559574
start=start,
560575
end=dt_util.now() + timedelta(minutes=1),
561576
title=title,
562-
description=response["response_text"],
577+
description=description,
563578
key_frame=key_frame,
564579
camera_name=camera_name,
565580
label="",

custom_components/llmvision/const.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
RESPONSE_FORMAT = "response_format"
6868
STRUCTURE = "structure"
6969
TITLE_FIELD = "title_field"
70+
DESCPRIPTION_FIELD = "description_field"
7071
INCLUDE_FILENAME = "include_filename"
7172
EXPOSE_IMAGES = "expose_images"
7273
GENERATE_TITLE = "generate_title"

custom_components/llmvision/providers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -265,8 +265,9 @@ async def call(self, call: Any, _is_fallback_retry: bool = False):
265265
# If parsing fails, return as text
266266
result["response_text"] = response_text
267267
else:
268-
# Also keep text version for backward compatibility
269-
result["response_text"] = response_text
268+
# drop response_text if structured_response is present
269+
if "structured_response" in result:
270+
del result["response_text"]
270271
else:
271272
result["response_text"] = response_text
272273

custom_components/llmvision/services.yaml

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,18 +112,26 @@ image_analyzer:
112112
- "json"
113113
structure:
114114
name: Structure
115-
description: 'JSON schema defining the expected response structure (only used when response_format is json). To enable automatic title generation for calendar events, include a field in your schema for the title (e.g., "title": {"type": "string", "description": "Event title"}) and specify the field name below.'
115+
description: 'JSON schema defining the expected response structure (only used when response_format is json). To enable automatic title generation for timeline events, include a field in your schema for the title (e.g., "title": {"type": "string", "description": "Event title"}) and specify the field names below.'
116116
required: false
117-
example: '{"type": "object", "properties": {"package_detected": {"type": "boolean", "description": "Is a package visible"}, "confidence": {"type": "number", "minimum": 0, "maximum": 100}}, "required": ["package_detected"]}'
117+
example: '{"type": "object", "properties": {"title": {"type": "string", "description": "Event title"}, "description": {"type": "string", "description": "Event description"}, "confidence": {"type": "number", "minimum": 0, "maximum": 100}}, "required": ["title", "description", "confidence"], "additionalProperties": false}'
118118
selector:
119119
text:
120120
multiline: true
121121
title_field:
122122
name: Title Field Name
123-
description: 'Name of the field in your JSON schema that contains the event title (used for calendar entries and notifications). Leave empty to use fallback title "Motion detected".'
123+
description: 'Name of the field in your JSON schema that contains the event title (used for timeline). Leave empty to use fallback title "Motion detected".'
124124
required: false
125125
example: 'title'
126-
default: ''
126+
default: 'title'
127+
selector:
128+
text:
129+
description_field:
130+
name: Description Field Name
131+
description: 'Name of the field in your JSON schema that contains the event description (used for timeline).'
132+
required: false
133+
example: 'description'
134+
default: 'description'
127135
selector:
128136
text:
129137

@@ -251,18 +259,26 @@ video_analyzer:
251259
- "json"
252260
structure:
253261
name: Structure
254-
description: 'JSON schema defining the expected response structure (only used when response_format is json). To enable automatic title generation for calendar events, include a field in your schema for the title (e.g., "title": {"type": "string", "description": "Event title"}) and specify the field name below.'
262+
description: 'JSON schema defining the expected response structure (only used when response_format is json). To enable automatic title generation for timeline events, include a field in your schema for the title (e.g., "title": {"type": "string", "description": "Event title"}) and specify the field names below.'
255263
required: false
256-
example: '{"type": "object", "properties": {"package_detected": {"type": "boolean", "description": "Is a package visible"}, "confidence": {"type": "number", "minimum": 0, "maximum": 100}}, "required": ["package_detected"]}'
264+
example: '{"type": "object", "properties": {"title": {"type": "string", "description": "Event title"}, "description": {"type": "string", "description": "Event description"}, "confidence": {"type": "number", "minimum": 0, "maximum": 100}}, "required": ["title", "description", "confidence"], "additionalProperties": false}'
257265
selector:
258266
text:
259267
multiline: true
260268
title_field:
261269
name: Title Field Name
262-
description: 'Name of the field in your JSON schema that contains the event title (used for calendar entries and notifications). Leave empty to use fallback title "Motion detected".'
270+
description: 'Name of the field in your JSON schema that contains the event title (used for timeline). Leave empty to use fallback title "Motion detected".'
263271
required: false
264272
example: 'title'
265-
default: ''
273+
default: 'title'
274+
selector:
275+
text:
276+
description_field:
277+
name: Description Field Name
278+
description: 'Name of the field in your JSON schema that contains the event description (used for timeline).'
279+
required: false
280+
example: 'description'
281+
default: 'description'
266282
selector:
267283
text:
268284

@@ -393,18 +409,26 @@ stream_analyzer:
393409
- "json"
394410
structure:
395411
name: Structure
396-
description: 'JSON schema defining the expected response structure (only used when response_format is json). To enable automatic title generation for calendar events, include a field in your schema for the title (e.g., "title": {"type": "string", "description": "Event title"}) and specify the field name below.'
412+
description: 'JSON schema defining the expected response structure (only used when response_format is json). To enable automatic title generation for timeline events, include a field in your schema for the title (e.g., "title": {"type": "string", "description": "Event title"}) and specify the field names below.'
397413
required: false
398-
example: '{"type": "object", "properties": {"package_detected": {"type": "boolean", "description": "Is a package visible"}, "confidence": {"type": "number", "minimum": 0, "maximum": 100}}, "required": ["package_detected"]}'
414+
example: '{"type": "object", "properties": {"title": {"type": "string", "description": "Event title"}, "description": {"type": "string", "description": "Event description"}, "confidence": {"type": "number", "minimum": 0, "maximum": 100}}, "required": ["title", "description", "confidence"], "additionalProperties": false}'
399415
selector:
400416
text:
401417
multiline: true
402418
title_field:
403419
name: Title Field Name
404-
description: 'Name of the field in your JSON schema that contains the event title (used for calendar entries and notifications). Leave empty to use fallback title "Motion detected".'
420+
description: 'Name of the field in your JSON schema that contains the event title (used for timeline). Leave empty to use fallback title "Motion detected".'
405421
required: false
406422
example: 'title'
407-
default: ''
423+
default: 'title'
424+
selector:
425+
text:
426+
description_field:
427+
name: Description Field Name
428+
description: 'Name of the field in your JSON schema that contains the event description (used for timeline).'
429+
required: false
430+
example: 'description'
431+
default: 'description'
408432
selector:
409433
text:
410434

0 commit comments

Comments
 (0)