Skip to content

Commit 62611ce

Browse files
UN-3008 [FEAT] Add word-level confidence support (#1672)
* UN-3008 [FEAT] Add word-level confidence support Add word-level confidence feature that extends the existing highlight functionality. This feature allows tracking confidence scores at the word level during extraction. Key changes: - Add enable_word_confidence field to CustomTool model - Add word_confidence_postamble support for custom prompts - Pass word_confidence flag through extraction and indexing pipelines - Update SDK to preserve original text for post-processing - Add dependency check to ensure word confidence requires highlight to be enabled * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * UN-3008 Confidence fixes * UN-3008 Confidence fixes in backend * UN-3008 Fix type annotation for post_process_fn to include original_text parameter - Updated post_process_fn type signature from Callable[[LLMResponseCompat, bool], ...] to Callable[[LLMResponseCompat, bool, str], ...] to match the actual call at line 500-502 - Addresses review comment: #1672 (comment) - The highlight_data plugin's run() method already accepts the third parameter (original_text: str) * UN-3008 Minor fixes * UN-3008 Minor fixes * UN-3008 Remove unused WORD_CONFIDENCE_DATA constant from ResultKeys - Removed WORD_CONFIDENCE_DATA constant as it's not used in main repo's workflow manager - The constant is only needed in unstract-cloud repo which has the rule engine - Prompt studio code uses the string directly, which is appropriate - Addresses review comment: #1672 (comment) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent ba4333f commit 62611ce

19 files changed

Lines changed: 188 additions & 9 deletions

File tree

backend/prompt_studio/prompt_studio_core_v2/constants.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,13 @@ class ToolStudioPromptKeys:
9393
TXT_EXTENTION = ".txt"
9494
TABLE = "table"
9595
PLATFORM_POSTAMBLE = "platform_postamble"
96+
WORD_CONFIDENCE_POSTAMBLE = "word_confidence_postamble"
9697
SUMMARIZE_AS_SOURCE = "summarize_as_source"
9798
VARIABLE_MAP = "variable_map"
9899
RECORD = "record"
99100
FILE_PATH = "file_path"
100101
ENABLE_HIGHLIGHT = "enable_highlight"
102+
ENABLE_WORD_CONFIDENCE = "enable_word_confidence"
101103
REQUIRED = "required"
102104
EXECUTION_SOURCE = "execution_source"
103105
LINE_ITEM = "line-item"
@@ -166,6 +168,7 @@ class IndexingConstants:
166168
FILE_HASH = "file_hash"
167169
OUTPUT_FILE_PATH = "output_file_path"
168170
ENABLE_HIGHLIGHT = "enable_highlight"
171+
ENABLE_WORD_CONFIDENCE = "enable_word_confidence"
169172
USAGE_KWARGS = "usage_kwargs"
170173
PROCESS_TEXT = "process_text"
171174
EXTRACTED_TEXT = "extracted_text"
@@ -190,6 +193,7 @@ class DefaultValues:
190193
DEFAULT_EXCLUDE_FAILED = True
191194
DEFAULT_ENABLE_CHALLENGE = False
192195
DEFAULT_ENABLE_HIGHLIGHT = False
196+
DEFAULT_ENABLE_WORD_CONFIDENCE = False
193197
DEFAULT_SUMMARIZE_AS_SOURCE = False
194198
DEFAULT_SUMMARIZE_CONTEXT = False
195199
DEFAULT_SINGLE_PASS_EXTRACTION_MODE = False
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 4.2.1 on 2025-11-18 05:07
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
("prompt_studio_core_v2", "0004_add_shared_to_org_to_custom_tool"),
9+
]
10+
11+
operations = [
12+
migrations.AddField(
13+
model_name="customtool",
14+
name="enable_word_confidence",
15+
field=models.BooleanField(
16+
db_comment="Flag to enable or disable word-level confidence (depends on enable_highlight)",
17+
default=False,
18+
),
19+
),
20+
]

backend/prompt_studio/prompt_studio_core_v2/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ class CustomTool(DefaultOrganizationMixin, BaseModel):
138138
enable_highlight = models.BooleanField(
139139
db_comment="Flag to enable or disable document highlighting", default=False
140140
)
141+
enable_word_confidence = models.BooleanField(
142+
db_comment="Flag to enable or disable word-level confidence (depends on enable_highlight)",
143+
default=False,
144+
)
141145

142146
# Introduced field to establish M2M relation between users and custom_tool.
143147
# This will introduce intermediary table which relates both the models.

backend/prompt_studio/prompt_studio_core_v2/prompt_studio_helper.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,8 @@ def index_document(
412412
document_id=document_id,
413413
run_id=run_id,
414414
enable_highlight=tool.enable_highlight,
415+
enable_word_confidence=tool.enable_word_confidence,
416+
doc_id=doc_id,
415417
)
416418
if tool.summarize_context:
417419
summarize_file_path = PromptStudioHelper.summarize(
@@ -972,9 +974,13 @@ def _fetch_response(
972974
tool_settings[TSPKeys.POSTAMBLE] = tool.postamble
973975
tool_settings[TSPKeys.GRAMMAR] = grammar_list
974976
tool_settings[TSPKeys.ENABLE_HIGHLIGHT] = tool.enable_highlight
977+
tool_settings[TSPKeys.ENABLE_WORD_CONFIDENCE] = tool.enable_word_confidence
975978
tool_settings[TSPKeys.PLATFORM_POSTAMBLE] = getattr(
976979
settings, TSPKeys.PLATFORM_POSTAMBLE.upper(), ""
977980
)
981+
tool_settings[TSPKeys.WORD_CONFIDENCE_POSTAMBLE] = getattr(
982+
settings, TSPKeys.WORD_CONFIDENCE_POSTAMBLE.upper(), ""
983+
)
978984
file_hash = fs_instance.get_hash_from_file(path=doc_path)
979985

980986
payload = {
@@ -1246,10 +1252,14 @@ def _fetch_single_pass_response(
12461252
tool_settings[TSPKeys.CHUNK_OVERLAP] = default_profile.chunk_overlap
12471253
tool_settings[TSPKeys.ENABLE_CHALLENGE] = tool.enable_challenge
12481254
tool_settings[TSPKeys.ENABLE_HIGHLIGHT] = tool.enable_highlight
1255+
tool_settings[TSPKeys.ENABLE_WORD_CONFIDENCE] = tool.enable_word_confidence
12491256
tool_settings[TSPKeys.CHALLENGE_LLM] = challenge_llm
12501257
tool_settings[TSPKeys.PLATFORM_POSTAMBLE] = getattr(
12511258
settings, TSPKeys.PLATFORM_POSTAMBLE.upper(), ""
12521259
)
1260+
tool_settings[TSPKeys.WORD_CONFIDENCE_POSTAMBLE] = getattr(
1261+
settings, TSPKeys.WORD_CONFIDENCE_POSTAMBLE.upper(), ""
1262+
)
12531263
tool_settings[TSPKeys.SUMMARIZE_AS_SOURCE] = tool.summarize_as_source
12541264
for prompt in prompts:
12551265
if not prompt.prompt:
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Generated migration for word_confidence_data field
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
dependencies = [
8+
(
9+
"prompt_studio_output_manager_v2",
10+
"0003_promptstudiooutputmanager_confidence_data",
11+
),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name="promptstudiooutputmanager",
17+
name="word_confidence_data",
18+
field=models.JSONField(
19+
blank=True,
20+
db_comment="Field to store word-level confidence data",
21+
null=True,
22+
),
23+
),
24+
]

backend/prompt_studio/prompt_studio_output_manager_v2/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ class PromptStudioOutputManager(BaseModel):
3737
null=True,
3838
blank=True,
3939
)
40+
word_confidence_data = models.JSONField(
41+
db_comment="Field to store word-level confidence data",
42+
editable=True,
43+
null=True,
44+
blank=True,
45+
)
4046
eval_metrics = models.JSONField(
4147
db_column="eval_metrics",
4248
null=False,

backend/prompt_studio/prompt_studio_output_manager_v2/output_manager_helper.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def update_or_create_prompt_output(
6363
challenge_data: dict[str, Any] | None,
6464
highlight_data: dict[str, Any] | None,
6565
confidence_data: dict[str, Any] | None,
66+
word_confidence_data: dict[str, Any] | None,
6667
) -> PromptStudioOutputManager:
6768
"""Handles creating or updating a single prompt output and returns
6869
the instance.
@@ -81,6 +82,7 @@ def update_or_create_prompt_output(
8182
"challenge_data": challenge_data,
8283
"highlight_data": highlight_data,
8384
"confidence_data": confidence_data,
85+
"word_confidence_data": word_confidence_data,
8486
},
8587
)
8688

@@ -103,6 +105,7 @@ def update_or_create_prompt_output(
103105
"challenge_data": challenge_data,
104106
"highlight_data": highlight_data,
105107
"confidence_data": confidence_data,
108+
"word_confidence_data": word_confidence_data,
106109
}
107110
PromptStudioOutputManager.objects.filter(
108111
document_manager=document_manager,
@@ -126,6 +129,7 @@ def update_or_create_prompt_output(
126129
challenge_data = metadata.get("challenge_data")
127130
highlight_data = metadata.get("highlight_data")
128131
confidence_data = metadata.get("confidence_data")
132+
word_confidence_data = metadata.get("word_confidence_data")
129133

130134
if not prompts:
131135
return serialized_data
@@ -147,6 +151,8 @@ def update_or_create_prompt_output(
147151
highlight_data = highlight_data.get(prompt.prompt_key)
148152
if confidence_data:
149153
confidence_data = confidence_data.get(prompt.prompt_key)
154+
if word_confidence_data:
155+
word_confidence_data = word_confidence_data.get(prompt.prompt_key)
150156
if challenge_data:
151157
challenge_data = challenge_data.get(prompt.prompt_key)
152158

@@ -171,6 +177,7 @@ def update_or_create_prompt_output(
171177
challenge_data=challenge_data,
172178
highlight_data=highlight_data,
173179
confidence_data=confidence_data,
180+
word_confidence_data=word_confidence_data,
174181
)
175182

176183
# Serialize the instance

backend/prompt_studio/prompt_studio_registry_v2/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class JsonSchemaKey:
9898
SUMMARIZE_PROMPT = "summarize_prompt"
9999
SUMMARIZE_AS_SOURCE = "summarize_as_source"
100100
ENABLE_HIGHLIGHT = "enable_highlight"
101+
ENABLE_WORD_CONFIDENCE = "enable_word_confidence"
101102
PLATFORM_POSTAMBLE = "platform_postamble"
102103
REQUIRED = "required"
103104
# Webhook postprocessing settings

backend/prompt_studio/prompt_studio_registry_v2/prompt_studio_registry_helper.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ def frame_spec(tool: CustomTool) -> Spec:
7878
"default": False,
7979
"description": "Enables highlight",
8080
},
81+
"enable_word_confidence": {
82+
"type": "boolean",
83+
"title": "Enable word confidence",
84+
"default": False,
85+
"description": "Enables word-level confidence (depends on enable_highlight)",
86+
},
8187
}
8288

8389
spec = Spec(
@@ -283,9 +289,13 @@ def frame_export_json(
283289
tool.single_pass_extraction_mode
284290
)
285291
tool_settings[JsonSchemaKey.ENABLE_HIGHLIGHT] = tool.enable_highlight
292+
tool_settings[JsonSchemaKey.ENABLE_WORD_CONFIDENCE] = tool.enable_word_confidence
286293
tool_settings[JsonSchemaKey.PLATFORM_POSTAMBLE] = getattr(
287294
settings, JsonSchemaKey.PLATFORM_POSTAMBLE.upper(), ""
288295
)
296+
tool_settings[JsonSchemaKey.WORD_CONFIDENCE_POSTAMBLE] = getattr(
297+
settings, JsonSchemaKey.WORD_CONFIDENCE_POSTAMBLE.upper(), ""
298+
)
289299

290300
for prompt in prompts:
291301
if prompt.prompt_type == JsonSchemaKey.NOTES or not prompt.active:

frontend/src/components/custom-tools/document-manager/DocumentManager.jsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,23 @@ function DocumentManager({ generateIndex, handleUpdateTool, handleDocChange }) {
380380
if (typeof confidence === "number") {
381381
return confidence.toFixed(2);
382382
}
383+
// Handle word confidence format: object with line numbers as keys
384+
if (
385+
confidence &&
386+
typeof confidence === "object" &&
387+
!Array.isArray(confidence)
388+
) {
389+
const values = Object.values(confidence);
390+
if (
391+
values.length > 0 &&
392+
values.every((v) => typeof v === "number")
393+
) {
394+
const avg =
395+
values.reduce((sum, val) => sum + val, 0) /
396+
values.length;
397+
return avg.toFixed(2);
398+
}
399+
}
383400
// Handle old format: nested array structure
384401
if (confidence?.[0]?.[0]?.confidence) {
385402
return confidence[0][0].confidence;

0 commit comments

Comments
 (0)