Skip to content

Commit a64cb65

Browse files
committed
UI classification and extraction examples
1 parent ab52acb commit a64cb65

File tree

4 files changed

+221
-1
lines changed

4 files changed

+221
-1
lines changed

src/ui/src/components/json-schema-builder/SchemaInspector.jsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ import ArrayConstraints from './constraints/ArrayConstraints';
1919
import ObjectConstraints from './constraints/ObjectConstraints';
2020
import MetadataFields from './constraints/MetadataFields';
2121
import ValueConstraints from './constraints/ValueConstraints';
22+
import ExamplesEditor from './constraints/ExamplesEditor';
2223
import {
2324
TYPE_OPTIONS,
2425
EVALUATION_METHOD_OPTIONS,
2526
X_AWS_IDP_DOCUMENT_TYPE,
2627
X_AWS_IDP_EVALUATION_METHOD,
2728
X_AWS_IDP_CONFIDENCE_THRESHOLD,
29+
X_AWS_IDP_EXAMPLES,
2830
} from '../../constants/schemaConstants';
2931

3032
const SchemaInspector = ({
@@ -111,6 +113,13 @@ const SchemaInspector = ({
111113
/>
112114
</FormField>
113115

116+
{selectedClass[X_AWS_IDP_DOCUMENT_TYPE] && (
117+
<ExamplesEditor
118+
examples={selectedClass[X_AWS_IDP_EXAMPLES] || []}
119+
onChange={(examples) => onUpdateClass({ [X_AWS_IDP_EXAMPLES]: examples })}
120+
/>
121+
)}
122+
114123
{usedIn.length > 0 && (
115124
<FormField
116125
label="Used In"
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import React, { useState } from 'react';
2+
import PropTypes from 'prop-types';
3+
import {
4+
Box,
5+
SpaceBetween,
6+
Header,
7+
FormField,
8+
Input,
9+
Textarea,
10+
Button,
11+
ExpandableSection,
12+
Alert,
13+
Container,
14+
ColumnLayout,
15+
} from '@cloudscape-design/components';
16+
17+
/**
18+
* ExamplesEditor Component
19+
*
20+
* Manages few-shot examples for classification and extraction.
21+
* Examples are stored in the x-aws-idp-examples array with:
22+
* - name: Example identifier
23+
* - classPrompt: Classification prompt (used by classification service)
24+
* - attributesPrompt: Extraction prompt (used by extraction service)
25+
* - imagePath: S3 path or local path to example image(s)
26+
*/
27+
const ExamplesEditor = ({ examples = [], onChange }) => {
28+
const [expandedSections, setExpandedSections] = useState({});
29+
30+
const handleAddExample = () => {
31+
const newExample = {
32+
name: `Example ${examples.length + 1}`,
33+
classPrompt: '',
34+
attributesPrompt: '',
35+
imagePath: '',
36+
};
37+
onChange([...examples, newExample]);
38+
// Auto-expand the new example
39+
setExpandedSections({
40+
...expandedSections,
41+
[examples.length]: true,
42+
});
43+
};
44+
45+
const handleUpdateExample = (index, field, value) => {
46+
const updated = [...examples];
47+
updated[index] = {
48+
...updated[index],
49+
[field]: value,
50+
};
51+
onChange(updated);
52+
};
53+
54+
const handleDeleteExample = (index) => {
55+
const updated = examples.filter((_, i) => i !== index);
56+
onChange(updated);
57+
// Clean up expanded state
58+
const newExpanded = { ...expandedSections };
59+
delete newExpanded[index];
60+
setExpandedSections(newExpanded);
61+
};
62+
63+
const toggleSection = (index) => {
64+
setExpandedSections({
65+
...expandedSections,
66+
[index]: !expandedSections[index],
67+
});
68+
};
69+
70+
return (
71+
<SpaceBetween size="m">
72+
<Box>
73+
<SpaceBetween size="xs">
74+
<Header
75+
variant="h4"
76+
description="Add few-shot examples to improve classification and extraction accuracy"
77+
actions={
78+
<Button
79+
iconName="add-plus"
80+
onClick={handleAddExample}
81+
>
82+
Add Example
83+
</Button>
84+
}
85+
>
86+
Few-Shot Examples ({examples.length})
87+
</Header>
88+
89+
{examples.length === 0 && (
90+
<Alert
91+
type="info"
92+
header="No examples defined"
93+
>
94+
Add examples to provide the model with sample inputs and expected outputs.
95+
Examples help improve accuracy for both classification and extraction tasks.
96+
</Alert>
97+
)}
98+
</SpaceBetween>
99+
</Box>
100+
101+
{examples.map((example, index) => (
102+
<ExpandableSection
103+
key={index}
104+
headerText={example.name || `Example ${index + 1}`}
105+
expanded={expandedSections[index] || false}
106+
onChange={() => toggleSection(index)}
107+
headerActions={
108+
<Button
109+
iconName="remove"
110+
variant="icon"
111+
onClick={(e) => {
112+
e.stopPropagation();
113+
handleDeleteExample(index);
114+
}}
115+
/>
116+
}
117+
>
118+
<Container>
119+
<SpaceBetween size="m">
120+
<FormField
121+
label="Example Name"
122+
description="Unique identifier for this example"
123+
>
124+
<Input
125+
value={example.name || ''}
126+
onChange={({ detail }) =>
127+
handleUpdateExample(index, 'name', detail.value)
128+
}
129+
placeholder="e.g., Invoice Example 1"
130+
/>
131+
</FormField>
132+
133+
<FormField
134+
label="Classification Prompt (classPrompt)"
135+
description="Used by classification service to identify document type. Describe what makes this example match this class."
136+
stretch
137+
>
138+
<Textarea
139+
value={example.classPrompt || ''}
140+
onChange={({ detail }) =>
141+
handleUpdateExample(index, 'classPrompt', detail.value)
142+
}
143+
placeholder="This is an example of the class 'Invoice'. Key characteristics: Has invoice number, date, line items, and total amount."
144+
rows={4}
145+
/>
146+
</FormField>
147+
148+
<FormField
149+
label="Extraction Prompt (attributesPrompt)"
150+
description="Used by extraction service to extract field values. Show expected output format and values."
151+
stretch
152+
>
153+
<Textarea
154+
value={example.attributesPrompt || ''}
155+
onChange={({ detail }) =>
156+
handleUpdateExample(index, 'attributesPrompt', detail.value)
157+
}
158+
placeholder={`Expected attributes are:\n{\n "invoiceNumber": "INV-2024-001",\n "date": "2024-01-15",\n "total": 1250.00\n}`}
159+
rows={8}
160+
/>
161+
</FormField>
162+
163+
<FormField
164+
label="Image Path (imagePath)"
165+
description="S3 URI (s3://bucket/path) or local path to example image. Supports directories for multiple images."
166+
stretch
167+
>
168+
<Input
169+
value={example.imagePath || ''}
170+
onChange={({ detail }) =>
171+
handleUpdateExample(index, 'imagePath', detail.value)
172+
}
173+
placeholder="s3://my-bucket/examples/invoice-1.png or config_library/examples/"
174+
/>
175+
</FormField>
176+
177+
<Alert type="info">
178+
<ColumnLayout columns={2} variant="text-grid">
179+
<div>
180+
<Box variant="strong">Classification Service</Box>
181+
<Box variant="p">Uses <code>classPrompt</code> and <code>imagePath</code></Box>
182+
</div>
183+
<div>
184+
<Box variant="strong">Extraction Service</Box>
185+
<Box variant="p">Uses <code>attributesPrompt</code> and <code>imagePath</code></Box>
186+
</div>
187+
</ColumnLayout>
188+
</Alert>
189+
</SpaceBetween>
190+
</Container>
191+
</ExpandableSection>
192+
))}
193+
</SpaceBetween>
194+
);
195+
};
196+
197+
ExamplesEditor.propTypes = {
198+
examples: PropTypes.arrayOf(
199+
PropTypes.shape({
200+
name: PropTypes.string,
201+
classPrompt: PropTypes.string,
202+
attributesPrompt: PropTypes.string,
203+
imagePath: PropTypes.string,
204+
})
205+
),
206+
onChange: PropTypes.func.isRequired,
207+
};
208+
209+
export default ExamplesEditor;

src/ui/src/constants/schemaConstants.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ export const MAX_PROMPT_OVERRIDE_LENGTH = 10000;
125125
// AWS IDP Example Extensions
126126
// ============================================================================
127127
/** Extensions for few-shot example support */
128+
export const X_AWS_IDP_EXAMPLES = 'x-aws-idp-examples';
128129
export const X_AWS_IDP_CLASS_PROMPT = 'x-aws-idp-class-prompt';
129130
export const X_AWS_IDP_ATTRIBUTES_PROMPT = 'x-aws-idp-attributes-prompt';
130131
export const X_AWS_IDP_IMAGE_PATH = 'x-aws-idp-image-path';

src/ui/src/hooks/useSchemaDesigner.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useState, useCallback, useEffect } from 'react';
22
import { produce } from 'immer';
3-
import { X_AWS_IDP_DOCUMENT_TYPE } from '../constants/schemaConstants';
3+
import { X_AWS_IDP_DOCUMENT_TYPE, X_AWS_IDP_EXAMPLES } from '../constants/schemaConstants';
44

55
const extractInlineObjectsToClasses = (properties, extractedClasses, timestamp) => {
66
const updatedProperties = {};
@@ -631,6 +631,7 @@ export const useSchemaDesigner = (initialSchema = []) => {
631631
properties: sanitizedProps,
632632
...(docTypeClass.attributes.required?.length > 0 ? { required: docTypeClass.attributes.required } : {}),
633633
...(Object.keys(defs).length > 0 ? { $defs: defs } : {}),
634+
...(docTypeClass[X_AWS_IDP_EXAMPLES]?.length > 0 ? { [X_AWS_IDP_EXAMPLES]: docTypeClass[X_AWS_IDP_EXAMPLES] } : {}),
634635
};
635636

636637
console.log('Final schema has $defs?', '$defs' in result);

0 commit comments

Comments
 (0)