Skip to content

Commit c5f86f0

Browse files
authored
Merge pull request #36 from microsoft/dev
Dev
2 parents ecf7e96 + f67db97 commit c5f86f0

File tree

11 files changed

+166
-91
lines changed

11 files changed

+166
-91
lines changed

py-src/data_formulator/agents/agent_data_rec.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def process_gpt_response(self, input_tables, messages, response):
137137
#log = {'messages': messages, 'response': response.model_dump(mode='json')}
138138

139139
if isinstance(response, Exception):
140-
result = {'status': 'other error', 'content': response.body}
140+
result = {'status': 'other error', 'content': str(response.body)}
141141
return [result]
142142

143143
candidates = []
@@ -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: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def process_gpt_response(self, input_tables, messages, response):
205205
#logger.info(response.prompt_filter_results)
206206

207207
if isinstance(response, Exception):
208-
result = {'status': 'other error', 'content': response.body}
208+
result = {'status': 'other error', 'content': str(response.body)}
209209
return [result]
210210

211211
candidates = []
@@ -223,22 +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:
236-
logger.warning('other error:')
237-
error_message = traceback.format_exc()
237+
logger.warning('Error occurred during code execution:')
238+
error_message = f"An error occurred during code execution. Error type: {type(e).__name__}"
238239
logger.warning(error_message)
239-
result = {'status': 'other error', 'content': error_message}
240+
result = {'status': 'other error', 'code': code_str, 'content': error_message}
240241
else:
241-
result = {'status': 'no transformation', 'content': input_tables[0]['rows']}
242+
result = {'status': 'no transformation', 'code': "", 'content': input_tables[0]['rows']}
242243

243244
result['dialog'] = [*messages, {"role": choice.message.role, "content": choice.message.content}]
244245
result['agent'] = 'DataTransformationAgent'
@@ -264,11 +265,6 @@ def run(self, input_tables, description, expected_fields: list[str], n=1):
264265
messages = [{"role":"system", "content": self.system_prompt},
265266
{"role":"user","content": user_query}]
266267

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

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

py-src/data_formulator/app.py

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -147,15 +147,18 @@ def test_model():
147147
"endpoint": endpoint,
148148
"key": key,
149149
"model": model,
150-
"status": 'ok'
150+
"status": 'ok',
151+
"message": ""
151152
}
152153
except Exception as e:
153-
print(e)
154+
print(f"Error: {e}")
155+
error_message = str(e)
154156
result = {
155157
"endpoint": endpoint,
156158
"key": key,
157159
"model": model,
158-
"status": 'error'
160+
"status": 'error',
161+
"message": error_message,
159162
}
160163
else:
161164
{'status': 'error'}
@@ -362,7 +365,7 @@ def derive_data():
362365
results = agent.run(input_tables, instruction, [field['name'] for field in new_fields])
363366

364367
repair_attempts = 0
365-
while results[0]['status'] == 'error' and repair_attempts < 2:
368+
while results[0]['status'] == 'error' and repair_attempts == 0: # only try once
366369
error_message = results[0]['content']
367370
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."
368371

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

376379
repair_attempts += 1
377380

378-
response = flask.jsonify({ "status": "ok", "token": token, "results": results })
381+
response = flask.jsonify({ "token": token, "status": "ok", "results": results })
379382
else:
380383
response = flask.jsonify({ "token": "", "status": "error", "results": [] })
381384

382385
response.headers.add('Access-Control-Allow-Origin', '*')
383386
return response
384387

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

@@ -423,30 +404,48 @@ def refine_data():
423404
new_instruction = content["new_instruction"]
424405

425406
print("previous dialog")
426-
print(dialog[0]['content'])
407+
print(dialog)
427408

428409
# always resort to the data transform agent
429410
agent = DataTransformationAgentV2(client, model=model)
430411
results = agent.followup(input_tables, dialog, [field['name'] for field in output_fields], new_instruction)
431412

432413
repair_attempts = 0
433-
while results[0]['status'] == 'error' and repair_attempts < 2:
414+
while results[0]['status'] == 'error' and repair_attempts == 0: # only try once
434415
error_message = results[0]['content']
435416
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."
436-
437-
response_message = dialog['response']['choices'][0]['message']
438-
prev_dialog = [*dialog['messages'], {"role": response_message['role'], 'content': response_message['content']}]
417+
prev_dialog = results[0]['dialog']
439418

440419
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)

py-src/data_formulator/py_sandbox.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def block_mischief(event,arg):
3838
try:
3939
exec(code, allowed_objects)
4040
except Exception as err:
41-
error_message = traceback.format_exc()
41+
error_message = f"Error: {type(err).__name__} - {str(err)}"
4242
conn.send({'status': 'error', 'content': error_message})
4343
conn.close()
4444
return allowed_objects

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "data_formulator"
7-
version = "0.1.2"
7+
version = "0.1.3"
88

99
requires-python = ">=3.9"
1010
authors = [

src/app/dfSlice.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface DataFormulatorState {
3030

3131
oaiModels: {endpoint: string, key: string, model: string }[];
3232
selectedModel: {endpoint: string, model: string} | undefined;
33-
testedModels: {endpoint: string, model: string, status: 'ok' | 'error' | 'testing' | 'unknown'}[];
33+
testedModels: {endpoint: string, model: string, status: 'ok' | 'error' | 'testing' | 'unknown', message: string}[];
3434

3535
tables : DictTable[];
3636
charts: Chart[];
@@ -278,12 +278,13 @@ export const dataFormulatorSlice = createSlice({
278278
state.oaiModels = state.oaiModels.filter(oaiModel => oaiModel.model != model || oaiModel.endpoint != endpoint );
279279
state.testedModels = state.testedModels.filter(m => !(m.model == model && m.endpoint == endpoint));
280280
},
281-
updateModelStatus: (state, action: PayloadAction<{model: string, endpoint: string, status: 'ok' | 'error' | 'testing' | 'unknown'}>) => {
281+
updateModelStatus: (state, action: PayloadAction<{model: string, endpoint: string, status: 'ok' | 'error' | 'testing' | 'unknown', message: string}>) => {
282282
let model = action.payload.model;
283283
let endpoint = action.payload.endpoint;
284284
let status = action.payload.status;
285-
286-
state.testedModels = [...state.testedModels.filter(t => !(t.model == model && t.endpoint == endpoint)), {model, endpoint, status} ]
285+
let message = action.payload.message;
286+
287+
state.testedModels = [...state.testedModels.filter(t => !(t.model == model && t.endpoint == endpoint)), {model, endpoint, status, message} ]
287288
},
288289
addTable: (state, action: PayloadAction<DictTable>) => {
289290
let table = action.payload;

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: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -311,16 +311,22 @@ 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) => {
317-
return item["content"].length > 0
317+
return item["status"] == "ok" && item["content"].length > 0
318318
});
319+
319320
if (candidates.length == 0) {
321+
let errorMessage = data.results[0].content;
322+
let code = data.results[0].code;
323+
320324
dispatch(dfActions.addMessages({
321325
"timestamp": Date.now(),
322326
"type": "error",
323-
"value": "Unable to find a data transformation for the chart, please check concepts, encodings and clarification questions."
327+
"value": `Data formulation failed, please retry.`,
328+
"code": code,
329+
"detail": errorMessage
324330
}));
325331
} else {
326332

@@ -444,7 +450,7 @@ export const EncodingShelfCard: FC<EncodingShelfCardProps> = function ({ chartId
444450
dispatch(dfActions.addMessages({
445451
"timestamp": Date.now(),
446452
"type": "success",
447-
"value": `Data formulation for ${fieldNamesStr} complete, found ${candidates.length} candidates.`
453+
"value": `Data formulation for ${fieldNamesStr} succeeded.`
448454
}));
449455
}
450456
}
@@ -453,7 +459,7 @@ export const EncodingShelfCard: FC<EncodingShelfCardProps> = function ({ chartId
453459
dispatch(dfActions.addMessages({
454460
"timestamp": Date.now(),
455461
"type": "error",
456-
"value": "unable to perform data formulation."
462+
"value": "No result is returned from the data formulation agent. Please try again."
457463
}));
458464
}
459465
}).catch((error) => {
@@ -462,7 +468,8 @@ export const EncodingShelfCard: FC<EncodingShelfCardProps> = function ({ chartId
462468
dispatch(dfActions.addMessages({
463469
"timestamp": Date.now(),
464470
"type": "error",
465-
"value": `Data formulation for ${fieldNamesStr} fails, maybe try something differently?`
471+
"value": `Data formulation failed, please try again.`,
472+
"detail": error.message
466473
}));
467474
});
468475
}

0 commit comments

Comments
 (0)