Skip to content

Commit b0d5a7b

Browse files
committed
improve message display in the user interface
1 parent 4a5ee3c commit b0d5a7b

File tree

8 files changed

+111
-76
lines changed

8 files changed

+111
-76
lines changed

py-src/data_formulator/agents/agent_data_rec.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,22 +156,22 @@ def process_gpt_response(self, input_tables, messages, response):
156156

157157
if len(code_blocks) > 0:
158158
code_str = code_blocks[-1]
159+
159160
try:
160161
result = py_sandbox.run_transform_in_sandbox2020(code_str, [t['rows'] for t in input_tables])
162+
result['code'] = code_str
161163

162164
if result['status'] == 'ok':
163-
new_data = json.loads(result['content'])
164-
result['content'] = new_data
165+
result['content'] = json.loads(result['content'])
165166
else:
166167
logger.info(result['content'])
167-
result['code'] = code_str
168168
except Exception as e:
169169
logger.warning('other error:')
170170
error_message = traceback.format_exc()
171171
logger.warning(error_message)
172-
result = {'status': 'other error', 'content': error_message}
172+
result = {'status': 'other error', 'code': code_str, 'content': f"Unexpected error: {error_message}"}
173173
else:
174-
result = {'status': 'no transformation', 'content': input_tables[0]['rows']}
174+
result = {'status': 'no transformation', 'code': "", 'content': input_tables[0]['rows']}
175175

176176
result['dialog'] = [*messages, {"role": choice.message.role, "content": choice.message.content}]
177177
result['agent'] = 'DataRecAgent'

py-src/data_formulator/agents/agent_data_transform_v2.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -223,23 +223,23 @@ def process_gpt_response(self, input_tables, messages, response):
223223

224224
if len(code_blocks) > 0:
225225
code_str = code_blocks[-1]
226+
226227
try:
227228
result = py_sandbox.run_transform_in_sandbox2020(code_str, [t['rows'] for t in input_tables])
229+
result['code'] = code_str
228230

229231
if result['status'] == 'ok':
230-
new_data = json.loads(result['content'])
231-
result['content'] = new_data
232+
# parse the content
233+
result['content'] = json.loads(result['content'])
232234
else:
233235
logger.info(result['content'])
234-
result['code'] = code_str
235236
except Exception as e:
236237
logger.warning('Error occurred during code execution:')
237238
error_message = str(e)
238239
logger.warning(error_message)
239-
print(error_message)
240-
result = {'status': 'other error', 'content': f"An error occurred while executing the transformation: {error_message}"}
240+
result = {'status': 'other error', 'code': code_str, 'content': f"Unexpected error: {error_message}"}
241241
else:
242-
result = {'status': 'no transformation', 'content': input_tables[0]['rows']}
242+
result = {'status': 'no transformation', 'code': "", 'content': input_tables[0]['rows']}
243243

244244
result['dialog'] = [*messages, {"role": choice.message.role, "content": choice.message.content}]
245245
result['agent'] = 'DataTransformationAgent'
@@ -265,11 +265,6 @@ def run(self, input_tables, description, expected_fields: list[str], n=1):
265265
messages = [{"role":"system", "content": self.system_prompt},
266266
{"role":"user","content": user_query}]
267267

268-
###### the part that calls open_ai
269-
# response = self.client.chat.completions.create(
270-
# model=self.model, messages = messages, temperature=0.7, max_tokens=1200,
271-
# top_p=0.95, n=n, frequency_penalty=0, presence_penalty=0, stop=None)
272-
273268
response = completion_response_wrapper(self.client, self.model, messages, n)
274269

275270
return self.process_gpt_response(input_tables, messages, response)

py-src/data_formulator/app.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ def derive_data():
365365
results = agent.run(input_tables, instruction, [field['name'] for field in new_fields])
366366

367367
repair_attempts = 0
368-
while results[0]['status'] == 'error' and repair_attempts < 2:
368+
while results[0]['status'] == 'error' and repair_attempts == 0: # only try once
369369
error_message = results[0]['content']
370370
new_instruction = f"We run into the following problem executing the code, please fix it:\n\n{error_message}\n\nPlease think step by step, reflect why the error happens and fix the code so that no more errors would occur."
371371

@@ -378,35 +378,13 @@ def derive_data():
378378

379379
repair_attempts += 1
380380

381-
response = flask.jsonify({ "status": "ok", "token": token, "results": results })
381+
response = flask.jsonify({ "token": token, "status": "ok", "results": results })
382382
else:
383383
response = flask.jsonify({ "token": "", "status": "error", "results": [] })
384384

385385
response.headers.add('Access-Control-Allow-Origin', '*')
386386
return response
387387

388-
389-
@app.route('/code-expl', methods=['GET', 'POST'])
390-
def request_code_expl():
391-
if request.is_json:
392-
app.logger.info("# request data: ")
393-
content = request.get_json()
394-
token = content["token"]
395-
396-
client = get_client(content['model']['endpoint'], content['model']['key'])
397-
model = content['model']['model']
398-
app.logger.info(f" model: {content['model']}")
399-
400-
# each table is a dict with {"name": xxx, "rows": [...]}
401-
input_tables = content["input_tables"]
402-
code = content["code"]
403-
404-
code_expl_agent = CodeExplanationAgent(client=client, model=model)
405-
expl = code_expl_agent.run(input_tables, code)
406-
else:
407-
expl = ""
408-
return expl
409-
410388
@app.route('/refine-data', methods=['GET', 'POST'])
411389
def refine_data():
412390

@@ -433,20 +411,41 @@ def refine_data():
433411
results = agent.followup(input_tables, dialog, [field['name'] for field in output_fields], new_instruction)
434412

435413
repair_attempts = 0
436-
while results[0]['status'] == 'error' and repair_attempts < 2:
414+
while results[0]['status'] == 'error' and repair_attempts == 0: # only try once
437415
error_message = results[0]['content']
438416
new_instruction = f"We run into the following problem executing the code, please fix it:\n\n{error_message}\n\nPlease think step by step, reflect why the error happens and fix the code so that no more errors would occur."
417+
prev_dialog = results[0]['dialog']
439418

440-
results = agent.followup(input_tables, dialog, [field['name'] for field in output_fields], new_instruction)
419+
results = agent.followup(input_tables, prev_dialog, [field['name'] for field in output_fields], new_instruction)
441420
repair_attempts += 1
442421

443-
response = flask.jsonify({ "status": "ok", "token": token, "results": results})
422+
response = flask.jsonify({ "token": token, "status": "ok", "results": results})
444423
else:
445424
response = flask.jsonify({ "token": "", "status": "error", "results": []})
446425

447426
response.headers.add('Access-Control-Allow-Origin', '*')
448427
return response
449428

429+
@app.route('/code-expl', methods=['GET', 'POST'])
430+
def request_code_expl():
431+
if request.is_json:
432+
app.logger.info("# request data: ")
433+
content = request.get_json()
434+
token = content["token"]
435+
436+
client = get_client(content['model']['endpoint'], content['model']['key'])
437+
model = content['model']['model']
438+
app.logger.info(f" model: {content['model']}")
439+
440+
# each table is a dict with {"name": xxx, "rows": [...]}
441+
input_tables = content["input_tables"]
442+
code = content["code"]
443+
444+
code_expl_agent = CodeExplanationAgent(client=client, model=model)
445+
expl = code_expl_agent.run(input_tables, code)
446+
else:
447+
expl = ""
448+
return expl
450449
def run_app():
451450
port = 5000 #+ random.randint(0, 999)
452451
url = "http://localhost:{0}".format(port)

src/views/DataFormulator.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
Typography,
1919
Box,
2020
Tooltip,
21+
Button,
2122
} from '@mui/material';
2223

2324

@@ -149,6 +150,10 @@ Totals (7 entries) 5 5 5 15
149150
<Tooltip title={<Box>Example of a table in image format: <Box component="img" sx={{ width: '100%', marginTop: '6px' }} alt="" src={exampleImageTable} /></Box>}><Typography color="secondary" display="inline" sx={{cursor: 'help', "&:hover": {textDecoration: 'underline'}}}>an image</Typography></Tooltip> that contain data into clipboard to get started.
150151
</Typography>
151152
</Box>
153+
<Button size="small" color="inherit"
154+
sx={{position: "absolute", color:'darkgray', bottom: 0, right: 0, textTransform: 'none'}}
155+
target="_blank" rel="noopener noreferrer"
156+
href="https://privacy.microsoft.com/en-US/data-privacy-notice">view data privacy notice</Button>
152157
</Box>;
153158

154159
let modelSelectionDialogBox = <Box sx={{width: '100vw'}}>
@@ -162,6 +167,10 @@ Totals (7 entries) 5 5 5 15
162167
</Typography>
163168
<Typography variant="body1">Specify an OpenAI or Azure OpenAI endpoint to run {toolName}.</Typography>
164169
</Box>
170+
<Button size="small" color="inherit"
171+
sx={{position: "absolute", color:'darkgray', bottom: 0, right: 0, textTransform: 'none'}}
172+
target="_blank" rel="noopener noreferrer"
173+
href="https://privacy.microsoft.com/en-US/data-privacy-notice">view data privacy notice</Button>
165174
</Box>;
166175

167176
console.log("selected model?")
@@ -172,5 +181,7 @@ Totals (7 entries) 5 5 5 15
172181
<DndProvider backend={HTML5Backend}>
173182
{selectedModel == undefined ? modelSelectionDialogBox : (tables.length > 0 ? fixedSplitPane : dataUploadRequestBox)}
174183
</DndProvider>
184+
185+
175186
</Box>);
176187
}

src/views/EncodingShelfCard.tsx

Lines changed: 16 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -311,28 +311,23 @@ export const EncodingShelfCard: FC<EncodingShelfCardProps> = function ({ chartId
311311
dispatch(dfActions.changeChartRunningStatus({chartId, status: false}))
312312
console.log(data);
313313
console.log(token);
314-
if (data["status"] == "ok") {
314+
if (data.results.length > 0) {
315315
if (data["token"] == token) {
316316
let candidates = data["results"].filter((item: any) => {
317317
return item["status"] == "ok" && item["content"].length > 0
318318
});
319-
console.log(data)
320-
console.log(data.results[0])
319+
321320
if (candidates.length == 0) {
322-
try {
323-
let errorMessage = data.results[0].content;
324-
dispatch(dfActions.addMessages({
325-
"timestamp": Date.now(),
326-
"type": "error",
327-
"value": `Unable to find a data transformation for the chart, please check concepts, encodings and clarification questions. Details: "${errorMessage}"`
328-
}));
329-
} catch (e) {
330-
dispatch(dfActions.addMessages({
331-
"timestamp": Date.now(),
332-
"type": "error",
333-
"value": `Unable to find a data transformation for the chart, please check concepts, encodings and clarification questions.`
334-
}));
335-
}
321+
let errorMessage = data.results[0].content;
322+
let code = data.results[0].code;
323+
324+
dispatch(dfActions.addMessages({
325+
"timestamp": Date.now(),
326+
"type": "error",
327+
"value": `Data formulation failed, please retry.`,
328+
"code": code,
329+
"detail": errorMessage
330+
}));
336331
} else {
337332

338333
// PART 1: handle triggers
@@ -455,7 +450,7 @@ export const EncodingShelfCard: FC<EncodingShelfCardProps> = function ({ chartId
455450
dispatch(dfActions.addMessages({
456451
"timestamp": Date.now(),
457452
"type": "success",
458-
"value": `Data formulation for ${fieldNamesStr} complete, found ${candidates.length} candidates.`
453+
"value": `Data formulation for ${fieldNamesStr} succeeded.`
459454
}));
460455
}
461456
}
@@ -464,7 +459,7 @@ export const EncodingShelfCard: FC<EncodingShelfCardProps> = function ({ chartId
464459
dispatch(dfActions.addMessages({
465460
"timestamp": Date.now(),
466461
"type": "error",
467-
"value": "unable to perform data formulation."
462+
"value": "No result is returned from the data formulation agent. Please try again."
468463
}));
469464
}
470465
}).catch((error) => {
@@ -473,7 +468,8 @@ export const EncodingShelfCard: FC<EncodingShelfCardProps> = function ({ chartId
473468
dispatch(dfActions.addMessages({
474469
"timestamp": Date.now(),
475470
"type": "error",
476-
"value": `Data formulation for ${fieldNamesStr} fails, maybe try something differently? Error: ${error.message}`
471+
"value": `Data formulation failed, please try again.`,
472+
"detail": error.message
477473
}));
478474
});
479475
}

src/views/EncodingShelfThread.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -284,16 +284,21 @@ export const EncodingShelfThread: FC<EncodingShelfThreadProps> = function ({ cha
284284
dispatch(dfActions.changeChartRunningStatus({chartId, status: false}))
285285
console.log(data);
286286
console.log(token);
287-
if (data["status"] == "ok") {
287+
if (data.results.length > 0) {
288288
if (data["token"] == token) {
289-
let candidates = data["results"].filter((item: any) => {
290-
return item["content"].length > 0
289+
let candidates = data.results.filter((item: any) => {
290+
return item["status"] == "ok" && item["content"].length > 0
291291
});
292+
292293
if (candidates.length == 0) {
294+
let errorMessage = data.results[0].content;
295+
let code = data.results[0].code;
293296
dispatch(dfActions.addMessages({
294297
"timestamp": Date.now(),
295298
"type": "error",
296-
"value": "Unable to find a data transformation for the chart, please check concepts, encodings and clarification questions."
299+
"value": `Data formulation failed, please retry.`,
300+
"code": code,
301+
"detail": errorMessage
297302
}));
298303
} else {
299304

@@ -374,7 +379,7 @@ export const EncodingShelfThread: FC<EncodingShelfThreadProps> = function ({ cha
374379
dispatch(dfActions.addMessages({
375380
"timestamp": Date.now(),
376381
"type": "success",
377-
"value": `Data formulation for ${fieldNamesStr} complete, found ${candidates.length} candidates.`
382+
"value": `Data formulation for ${fieldNamesStr} succeeded.`
378383
}));
379384
}
380385
}
@@ -383,7 +388,7 @@ export const EncodingShelfThread: FC<EncodingShelfThreadProps> = function ({ cha
383388
dispatch(dfActions.addMessages({
384389
"timestamp": Date.now(),
385390
"type": "error",
386-
"value": "unable to perform data formulation."
391+
"value": "No result is returned from the data formulation agent. Please try again."
387392
}));
388393
}
389394
}).catch((error) => {
@@ -393,7 +398,8 @@ export const EncodingShelfThread: FC<EncodingShelfThreadProps> = function ({ cha
393398
dispatch(dfActions.addMessages({
394399
"timestamp": Date.now(),
395400
"type": "error",
396-
"value": `Data formulation for ${fieldNamesStr} failed due to network issue.`
401+
"value": `Data formulation failed, please try again.`,
402+
"detail": error.message
397403
}));
398404
});
399405
}

src/views/MessageSnackbar.tsx

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export interface Message {
1616
type: "success" | "info" | "error",
1717
timestamp: number,
1818
value: string,
19+
detail?: string, // error details
20+
code?: string // if this message is related to a code error, include code as well
1921
}
2022

2123
export function MessageSnackbar() {
@@ -65,16 +67,41 @@ export function MessageSnackbar() {
6567
setOpen(true);
6668
setMessage(messages[messages.length - 1]);
6769
}}><InfoIcon /></IconButton></Tooltip>
68-
<Button size="small" color="inherit" sx={{position: "absolute", color:'darkgray', bottom: 0, right: 0, textTransform: 'none'}} target="_blank" rel="noopener noreferrer" href="https://privacy.microsoft.com/en-US/data-privacy-notice">view data privacy notice</Button>
6970
{message != undefined ? <Snackbar
7071
open={open && message != undefined}
71-
autoHideDuration={10000}
72+
autoHideDuration={message?.type == "error" ? 15000 : 5000}
7273
anchorOrigin={{vertical: 'bottom', horizontal: 'right'}}
7374
onClose={handleClose}
7475
action={action}
7576
>
76-
<Alert onClose={handleClose} severity={message?.type} sx={{ width: '100%' }}>
77-
<Typography fontSize={10} component="span" sx={{margin: "auto", opacity: 0.7}}>[{timestamp}]</Typography> {message?.value}
77+
<Alert onClose={handleClose} severity={message?.type} sx={{ maxWidth: '400px' }}>
78+
<Typography fontSize={10} component="span" sx={{margin: "auto", opacity: 0.7}}>[{timestamp}]</Typography> &nbsp;
79+
{message?.value}
80+
{message?.detail ?
81+
<>
82+
<br />
83+
<Typography fontSize={12} component="span" sx={{marginBottom: 0, fontWeight: 'bold'}}>
84+
[error details]
85+
</Typography>
86+
<br />
87+
<Typography fontSize={12} component="span" sx={{margin: "auto", opacity: 0.7}}>
88+
{message?.detail}
89+
</Typography>
90+
</>
91+
: ""}
92+
{message?.code ?
93+
<>
94+
<br />
95+
<Typography fontSize={12} component="span" sx={{marginBottom: 0, fontWeight: 'bold'}}>
96+
[generated code]
97+
</Typography>
98+
<Typography fontSize={10} component="span" sx={{margin: "auto", opacity: 0.7}}>
99+
<pre style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', marginTop: 1 }}>
100+
{message?.code?.split('\n').filter(line => line.trim() !== '').join('\n')}
101+
</pre>
102+
</Typography>
103+
</>
104+
: ""}
78105
</Alert>
79106
</Snackbar> : ""}
80107
</Box>

0 commit comments

Comments
 (0)