nakas commited on
Commit
83a7163
·
verified ·
1 Parent(s): ece917e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -187
app.py CHANGED
@@ -7,27 +7,27 @@ import tempfile
7
  import sys
8
  import json
9
  import time
10
- import signal
11
  import atexit
12
 
13
- # Print Python version info for debugging
14
  print(f"Python version: {sys.version}")
 
15
 
16
  # Track running processes for cleanup
17
- running_processes = []
18
 
19
- # Clean up any running processes on exit
20
- def cleanup_processes():
21
- for process in running_processes:
 
22
  try:
23
- if process.poll() is None: # If process is still running
24
- process.terminate()
25
- process.wait(timeout=5)
26
- print(f"Terminated process {process.pid}")
27
  except Exception as e:
28
  print(f"Error terminating process: {e}")
29
 
30
- atexit.register(cleanup_processes)
31
 
32
  def call_openai_api(api_key, prompt):
33
  """Call OpenAI API to generate Gradio app code and requirements"""
@@ -37,48 +37,39 @@ def call_openai_api(api_key, prompt):
37
  }
38
 
39
  system_prompt = """You are an expert at creating Python applications with Gradio.
40
- Your task is to create a complete, standalone Gradio application based on the user's prompt.
41
 
42
  Provide your response in the following JSON format:
43
  {
44
- "app_code": "# Your Python code here...",
45
- "requirements": ["gradio==3.32.0", "numpy", "pandas", ...],
46
- "app_name": "descriptive-name-of-app",
47
  "description": "Brief description of what the app does"
48
  }
49
 
50
  Important guidelines:
51
- 1. The app_code should be a complete Gradio application.
52
- 2. Include demo.launch(server_name="0.0.0.0", server_port=7861) at the end of the code
53
- 3. Don't use any resource that requires internet access (no API calls).
54
- 4. Only use libraries that can be installed via pip.
55
- 5. First requirement should be gradio==3.32.0 (important: use exactly this version).
56
- 6. Use only gr.Interface instead of gr.Blocks to avoid version compatibility issues.
57
- 7. Don't use any buttons' .click() methods or event handlers - use gr.Interface() only.
58
- 8. Make the app functionality self-contained and robust.
59
- 9. Don't create directories or write to any file paths.
60
- 10. Don't use flagging callbacks or features.
61
-
62
- Here's a simple template to follow:
63
 
 
64
  ```python
65
  import gradio as gr
66
  import numpy as np
67
 
68
- # Define your functions here
69
- def process_data(input_data):
70
- result = input_data * 2 # Simple example
71
- return f"Processed: {result}"
72
 
73
- # Create the Gradio interface
74
  demo = gr.Interface(
75
- fn=process_data,
76
- inputs=gr.Number(label="Input Data"),
77
- outputs=gr.Textbox(label="Result"),
78
- title="Data Processor"
79
  )
80
 
81
- # Launch the app
82
  demo.launch(server_name="0.0.0.0", server_port=7861)
83
  ```
84
  """
@@ -108,25 +99,39 @@ demo.launch(server_name="0.0.0.0", server_port=7861)
108
 
109
  # Try to parse the JSON response
110
  try:
111
- # Extract JSON from response
112
- json_pattern = r'```json\s*([\s\S]*?)```|({[\s\S]*})'
113
- json_matches = re.findall(json_pattern, content)
114
-
115
- json_str = ""
116
- for match in json_matches:
117
- if match[0]: # From code block
118
- json_str = match[0]
119
- break
120
- elif match[1]: # Direct JSON
121
- json_str = match[1]
122
- break
123
-
124
- if not json_str:
125
- json_str = content # Try the whole content
126
 
127
- app_info = json.loads(json_str)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
- # Extract Python code if it's wrapped in code blocks
130
  if "```python" in app_info["app_code"]:
131
  code_pattern = r'```python\s*([\s\S]*?)```'
132
  code_match = re.search(code_pattern, app_info["app_code"])
@@ -134,136 +139,90 @@ demo.launch(server_name="0.0.0.0", server_port=7861)
134
  app_info["app_code"] = code_match.group(1)
135
 
136
  return app_info, None
137
- except json.JSONDecodeError:
138
- # Fallback pattern matching if JSON parsing fails
139
- app_code_pattern = r'```python\s*([\s\S]*?)```'
140
- app_code_matches = re.findall(app_code_pattern, content)
141
-
142
- app_code = app_code_matches[0] if app_code_matches else ""
143
-
144
- if not app_code:
145
- return None, "Could not extract app code from response"
146
-
147
- # Try to extract requirements
148
- req_pattern = r'import\s+([a-zA-Z0-9_]+)'
149
- req_matches = re.findall(req_pattern, app_code)
150
-
151
- requirements = ["gradio==3.32.0"]
152
- if req_matches:
153
- for module in req_matches:
154
- if module != "gradio" and module not in requirements and module != "os" and module != "sys":
155
- requirements.append(module)
156
-
157
- # Construct a partial app_info
158
- app_info = {
159
- "app_code": app_code,
160
- "requirements": requirements,
161
- "app_name": "gradio-app",
162
- "description": "Generated Gradio application"
163
- }
164
-
165
- return app_info, None
166
  except Exception as e:
167
- return None, f"Error: {str(e)}"
168
 
169
- def install_and_run_app(app_code, requirements):
170
  """Install requirements and run the app in a subprocess"""
 
 
171
  try:
172
- # Create a temporary directory for the app
173
- temp_dir = tempfile.mkdtemp()
 
 
174
 
175
- # Create app file
176
- app_file = os.path.join(temp_dir, "app.py")
177
- with open(app_file, 'w') as f:
178
- f.write(app_code)
179
 
180
- # Make sure port 7861 is specified
181
- if "server_port" not in app_code:
182
- with open(app_file, 'a') as f:
183
- f.write("\n\n# Ensure the app is running on port 7861\n")
184
- f.write("if 'demo' in locals():\n")
185
- f.write(" demo.launch(server_name='0.0.0.0', server_port=7861)\n")
186
 
187
  # Install requirements
188
- pip_output = ""
189
  for req in requirements:
190
  try:
191
- cmd = [sys.executable, "-m", "pip", "install", req]
192
- result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
193
- pip_output += f"Installing {req}: {'SUCCESS' if result.returncode == 0 else 'FAILED'}\n"
194
  if result.returncode != 0:
195
- pip_output += f"Error: {result.stderr}\n"
196
  except Exception as e:
197
- pip_output += f"Error installing {req}: {str(e)}\n"
198
 
199
  # Run the app
200
- cmd = [sys.executable, app_file]
201
- app_process = subprocess.Popen(
202
- cmd,
203
- stdout=subprocess.PIPE,
204
  stderr=subprocess.PIPE,
205
  text=True
206
  )
207
 
208
- running_processes.append(app_process)
209
-
210
  # Wait a bit for the app to start
211
- time.sleep(3)
212
 
213
  # Check if the process is still running
214
- if app_process.poll() is not None:
215
- stdout, stderr = app_process.communicate()
216
- return None, f"App failed to start:\n{stderr}\n\nInstallation log:\n{pip_output}"
 
217
 
218
  return {
219
- "process": app_process,
220
  "app_file": app_file,
221
- "temp_dir": temp_dir,
222
- "url": "http://localhost:7861",
223
- "pip_output": pip_output
224
  }, None
225
  except Exception as e:
 
 
226
  return None, f"Error setting up and running app: {str(e)}"
227
 
228
- def stop_running_app(app_details):
229
- """Stop a running app and clean up"""
230
- try:
231
- if app_details and "process" in app_details:
232
- process = app_details["process"]
233
- if process.poll() is None: # If process is still running
234
- process.terminate()
235
- try:
236
- process.wait(timeout=5)
237
- except subprocess.TimeoutExpired:
238
- process.kill()
239
-
240
- if process in running_processes:
241
- running_processes.remove(process)
242
-
243
- # Clean up temp directory
244
- if app_details and "temp_dir" in app_details:
245
- import shutil
246
- try:
247
- shutil.rmtree(app_details["temp_dir"])
248
- except:
249
- pass
250
-
251
- return True
252
- except Exception as e:
253
- print(f"Error stopping app: {str(e)}")
254
- return False
255
 
256
  # Create the Gradio interface
257
  with gr.Blocks(title="Gradio App Generator") as demo:
258
  gr.Markdown("# 🤖 Gradio App Generator")
259
  gr.Markdown("""
260
  This app generates a Gradio application based on your description, installs required packages,
261
- and runs it directly within this container. The generated app will be displayed below.
262
  """)
263
 
264
- # State variable to track running app details
265
- app_details_state = gr.State(None)
266
-
267
  with gr.Row():
268
  with gr.Column(scale=1):
269
  api_key = gr.Textbox(
@@ -286,8 +245,8 @@ with gr.Blocks(title="Gradio App Generator") as demo:
286
  with gr.Accordion("Generated Code", open=False):
287
  code_output = gr.Code(language="python", label="App Code")
288
 
289
- with gr.Accordion("Package Installation Log", open=False):
290
- install_output = gr.Textbox(label="Installation Log", lines=5)
291
 
292
  status_output = gr.Markdown("")
293
 
@@ -295,11 +254,10 @@ with gr.Blocks(title="Gradio App Generator") as demo:
295
  # Frame to display the running app
296
  app_frame = gr.HTML("<div style='text-align:center; padding:50px;'><h3>Your generated app will appear here</h3></div>")
297
 
298
- def on_generate(api_key_val, prompt_val, current_app_details):
299
- # Stop any previously running app
300
- if current_app_details:
301
- stop_running_app(current_app_details)
302
-
303
  # Validate API key
304
  if not api_key_val or len(api_key_val) < 20 or not api_key_val.startswith("sk-"):
305
  return (
@@ -309,7 +267,7 @@ with gr.Blocks(title="Gradio App Generator") as demo:
309
  )
310
 
311
  try:
312
- # Call the OpenAI API to generate app code and requirements
313
  status_message = "⏳ Generating app code..."
314
  yield (
315
  None, None, status_message,
@@ -317,18 +275,24 @@ with gr.Blocks(title="Gradio App Generator") as demo:
317
  gr.update(visible=False), None
318
  )
319
 
320
- app_info, api_error = call_openai_api(api_key_val, prompt_val)
321
- if api_error or not app_info:
322
  return (
323
- None, None, f"⚠️ {api_error or 'Failed to generate app'}",
324
  "<div style='text-align:center; padding:50px;'><h3>Error generating app</h3></div>",
325
  gr.update(visible=False), None
326
  )
327
 
328
- # At this point we have app code and requirements
329
  code = app_info["app_code"]
330
  requirements = app_info["requirements"]
331
 
 
 
 
 
 
 
 
332
  status_message = "⏳ Installing packages and starting app..."
333
  yield (
334
  code, None, status_message,
@@ -337,15 +301,15 @@ with gr.Blocks(title="Gradio App Generator") as demo:
337
  )
338
 
339
  # Install packages and run the app
340
- app_details, run_error = install_and_run_app(code, requirements)
341
- if run_error or not app_details:
342
  return (
343
- code, None, f"⚠️ {run_error or 'Failed to run app'}",
344
  "<div style='text-align:center; padding:50px;'><h3>Error running app</h3></div>",
345
  gr.update(visible=False), None
346
  )
347
 
348
- # Create iframe to display the app
349
  iframe_html = f"""
350
  <div style="height:600px; border:1px solid #ddd; border-radius:5px; overflow:hidden;">
351
  <iframe src="http://localhost:7861" width="100%" height="100%" frameborder="0"></iframe>
@@ -353,10 +317,8 @@ with gr.Blocks(title="Gradio App Generator") as demo:
353
  """
354
 
355
  return (
356
- code, app_details["pip_output"],
357
- f"✅ App is running! View it below.",
358
- iframe_html,
359
- gr.update(visible=True), app_details
360
  )
361
  except Exception as e:
362
  import traceback
@@ -367,36 +329,44 @@ with gr.Blocks(title="Gradio App Generator") as demo:
367
  gr.update(visible=False), None
368
  )
369
 
370
- def on_stop(current_app_details):
371
- if current_app_details:
372
- stopped = stop_running_app(current_app_details)
373
- if stopped:
374
- return (
375
- "✅ App stopped successfully",
376
- "<div style='text-align:center; padding:50px;'><h3>App stopped</h3></div>",
377
- gr.update(visible=False), None
378
- )
379
 
380
- return (
381
- "⚠️ No app was running",
382
- "<div style='text-align:center; padding:50px;'><h3>No app was running</h3></div>",
383
- gr.update(visible=False), None
384
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
385
 
386
  generate_btn.click(
387
  on_generate,
388
- inputs=[api_key, prompt, app_details_state],
389
  outputs=[
390
  code_output, install_output, status_output, app_frame,
391
- stop_btn, app_details_state
392
  ]
393
  )
394
 
395
  stop_btn.click(
396
  on_stop,
397
- inputs=[app_details_state],
398
- outputs=[status_output, app_frame, stop_btn, app_details_state]
399
  )
400
 
401
  if __name__ == "__main__":
402
- demo.queue().launch(server_name="0.0.0.0", server_port=7860)
 
 
7
  import sys
8
  import json
9
  import time
 
10
  import atexit
11
 
12
+ # Print Python version and gradio version for debugging
13
  print(f"Python version: {sys.version}")
14
+ print(f"Gradio version: {gr.__version__}")
15
 
16
  # Track running processes for cleanup
17
+ running_process = None
18
 
19
+ # Clean up running process on exit
20
+ def cleanup_process():
21
+ global running_process
22
+ if running_process and running_process.poll() is None:
23
  try:
24
+ running_process.terminate()
25
+ running_process.wait(timeout=5)
26
+ print(f"Terminated process {running_process.pid}")
 
27
  except Exception as e:
28
  print(f"Error terminating process: {e}")
29
 
30
+ atexit.register(cleanup_process)
31
 
32
  def call_openai_api(api_key, prompt):
33
  """Call OpenAI API to generate Gradio app code and requirements"""
 
37
  }
38
 
39
  system_prompt = """You are an expert at creating Python applications with Gradio.
40
+ Create a complete, standalone Gradio application based on the user's prompt.
41
 
42
  Provide your response in the following JSON format:
43
  {
44
+ "app_code": "# Python code here...",
45
+ "requirements": ["gradio==3.32.0", "other_packages"],
 
46
  "description": "Brief description of what the app does"
47
  }
48
 
49
  Important guidelines:
50
+ 1. The app_code should be a complete Gradio application
51
+ 2. Use ONLY gr.Interface (NOT gr.Blocks)
52
+ 3. Always include server_name="0.0.0.0" and server_port=7861 in the launch parameters
53
+ 4. First requirement must be exactly "gradio==3.32.0"
54
+ 5. Keep the app simple and focused on the user's request
55
+ 6. DO NOT use any flagging callbacks
56
+ 7. DO NOT create or use any directories or file paths
 
 
 
 
 
57
 
58
+ Example:
59
  ```python
60
  import gradio as gr
61
  import numpy as np
62
 
63
+ def process(input_value):
64
+ return input_value * 2
 
 
65
 
 
66
  demo = gr.Interface(
67
+ fn=process,
68
+ inputs=gr.Number(label="Input"),
69
+ outputs=gr.Number(label="Output"),
70
+ title="Number Doubler"
71
  )
72
 
 
73
  demo.launch(server_name="0.0.0.0", server_port=7861)
74
  ```
75
  """
 
99
 
100
  # Try to parse the JSON response
101
  try:
102
+ # Look for JSON object
103
+ json_pattern = r'({[\s\S]*})'
104
+ json_match = re.search(json_pattern, content)
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
+ if json_match:
107
+ json_str = json_match.group(1)
108
+ app_info = json.loads(json_str)
109
+ else:
110
+ # Try to extract code and requirements manually
111
+ code_pattern = r'```python\s*([\s\S]*?)```'
112
+ code_match = re.search(code_pattern, content)
113
+
114
+ if not code_match:
115
+ return None, "No code found in the response"
116
+
117
+ code = code_match.group(1)
118
+
119
+ # Extract requirements from imports
120
+ req_pattern = r'import\s+([a-zA-Z0-9_]+)'
121
+ req_matches = re.findall(req_pattern, code)
122
+
123
+ requirements = ["gradio==3.32.0"]
124
+ for module in req_matches:
125
+ if module != "gradio" and module not in requirements and module not in ["os", "sys"]:
126
+ requirements.append(module)
127
+
128
+ app_info = {
129
+ "app_code": code,
130
+ "requirements": requirements,
131
+ "description": "Generated Gradio application"
132
+ }
133
 
134
+ # Clean up the code if needed
135
  if "```python" in app_info["app_code"]:
136
  code_pattern = r'```python\s*([\s\S]*?)```'
137
  code_match = re.search(code_pattern, app_info["app_code"])
 
139
  app_info["app_code"] = code_match.group(1)
140
 
141
  return app_info, None
142
+ except Exception as e:
143
+ return None, f"Failed to parse API response: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  except Exception as e:
145
+ return None, f"API call failed: {str(e)}"
146
 
147
+ def install_and_run_app(code, requirements):
148
  """Install requirements and run the app in a subprocess"""
149
+ global running_process
150
+
151
  try:
152
+ # Clean up any previous process
153
+ if running_process and running_process.poll() is None:
154
+ running_process.terminate()
155
+ running_process.wait(timeout=5)
156
 
157
+ # Create a temporary file for the app
158
+ fd, app_file = tempfile.mkstemp(suffix='.py')
159
+ os.close(fd)
 
160
 
161
+ with open(app_file, 'w') as f:
162
+ f.write(code)
 
 
 
 
163
 
164
  # Install requirements
165
+ install_output = ""
166
  for req in requirements:
167
  try:
168
+ cmd = [sys.executable, "-m", "pip", "install", "--upgrade", "--no-cache-dir", req]
169
+ result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=120)
170
+ install_output += f"Installing {req}: {'SUCCESS' if result.returncode == 0 else 'FAILED'}\n"
171
  if result.returncode != 0:
172
+ install_output += f"Error: {result.stderr}\n"
173
  except Exception as e:
174
+ install_output += f"Error installing {req}: {str(e)}\n"
175
 
176
  # Run the app
177
+ running_process = subprocess.Popen(
178
+ [sys.executable, app_file],
179
+ stdout=subprocess.PIPE,
 
180
  stderr=subprocess.PIPE,
181
  text=True
182
  )
183
 
 
 
184
  # Wait a bit for the app to start
185
+ time.sleep(5)
186
 
187
  # Check if the process is still running
188
+ if running_process.poll() is not None:
189
+ stdout, stderr = running_process.communicate()
190
+ os.unlink(app_file)
191
+ return None, f"App failed to start:\n{stderr}\n\nInstallation log:\n{install_output}"
192
 
193
  return {
 
194
  "app_file": app_file,
195
+ "install_output": install_output
 
 
196
  }, None
197
  except Exception as e:
198
+ if 'app_file' in locals() and os.path.exists(app_file):
199
+ os.unlink(app_file)
200
  return None, f"Error setting up and running app: {str(e)}"
201
 
202
+ def stop_running_app():
203
+ """Stop the running app"""
204
+ global running_process
205
+
206
+ if running_process and running_process.poll() is None:
207
+ try:
208
+ running_process.terminate()
209
+ running_process.wait(timeout=5)
210
+ running_process = None
211
+ return True
212
+ except Exception as e:
213
+ print(f"Error stopping app: {str(e)}")
214
+
215
+ running_process = None
216
+ return False
 
 
 
 
 
 
 
 
 
 
 
 
217
 
218
  # Create the Gradio interface
219
  with gr.Blocks(title="Gradio App Generator") as demo:
220
  gr.Markdown("# 🤖 Gradio App Generator")
221
  gr.Markdown("""
222
  This app generates a Gradio application based on your description, installs required packages,
223
+ and runs it directly. The generated app will be displayed below.
224
  """)
225
 
 
 
 
226
  with gr.Row():
227
  with gr.Column(scale=1):
228
  api_key = gr.Textbox(
 
245
  with gr.Accordion("Generated Code", open=False):
246
  code_output = gr.Code(language="python", label="App Code")
247
 
248
+ with gr.Accordion("Installation Log", open=False):
249
+ install_output = gr.Textbox(label="Package Installation Log", lines=5)
250
 
251
  status_output = gr.Markdown("")
252
 
 
254
  # Frame to display the running app
255
  app_frame = gr.HTML("<div style='text-align:center; padding:50px;'><h3>Your generated app will appear here</h3></div>")
256
 
257
+ # App file path storage
258
+ app_file_state = gr.State(None)
259
+
260
+ def on_generate(api_key_val, prompt_val):
 
261
  # Validate API key
262
  if not api_key_val or len(api_key_val) < 20 or not api_key_val.startswith("sk-"):
263
  return (
 
267
  )
268
 
269
  try:
270
+ # Generate app code and requirements
271
  status_message = "⏳ Generating app code..."
272
  yield (
273
  None, None, status_message,
 
275
  gr.update(visible=False), None
276
  )
277
 
278
+ app_info, error = call_openai_api(api_key_val, prompt_val)
279
+ if error or not app_info:
280
  return (
281
+ None, None, f"⚠️ {error or 'Failed to generate app'}",
282
  "<div style='text-align:center; padding:50px;'><h3>Error generating app</h3></div>",
283
  gr.update(visible=False), None
284
  )
285
 
 
286
  code = app_info["app_code"]
287
  requirements = app_info["requirements"]
288
 
289
+ # Make sure server_name and server_port are specified
290
+ if "server_name" not in code or "server_port" not in code:
291
+ if "demo.launch(" in code:
292
+ code = code.replace("demo.launch(", "demo.launch(server_name=\"0.0.0.0\", server_port=7861, ")
293
+ else:
294
+ code += "\n\ndemo.launch(server_name=\"0.0.0.0\", server_port=7861)"
295
+
296
  status_message = "⏳ Installing packages and starting app..."
297
  yield (
298
  code, None, status_message,
 
301
  )
302
 
303
  # Install packages and run the app
304
+ run_result, run_error = install_and_run_app(code, requirements)
305
+ if run_error or not run_result:
306
  return (
307
+ code, None, f"⚠️ {run_error}",
308
  "<div style='text-align:center; padding:50px;'><h3>Error running app</h3></div>",
309
  gr.update(visible=False), None
310
  )
311
 
312
+ # Show the app in an iframe
313
  iframe_html = f"""
314
  <div style="height:600px; border:1px solid #ddd; border-radius:5px; overflow:hidden;">
315
  <iframe src="http://localhost:7861" width="100%" height="100%" frameborder="0"></iframe>
 
317
  """
318
 
319
  return (
320
+ code, run_result["install_output"], f"✅ App is running! View it below.",
321
+ iframe_html, gr.update(visible=True), run_result["app_file"]
 
 
322
  )
323
  except Exception as e:
324
  import traceback
 
329
  gr.update(visible=False), None
330
  )
331
 
332
+ def on_stop(app_file):
333
+ stopped = stop_running_app()
 
 
 
 
 
 
 
334
 
335
+ # Clean up the app file
336
+ if app_file and os.path.exists(app_file):
337
+ try:
338
+ os.unlink(app_file)
339
+ except:
340
+ pass
341
+
342
+ if stopped:
343
+ return (
344
+ "✅ App stopped successfully",
345
+ "<div style='text-align:center; padding:50px;'><h3>App stopped</h3></div>",
346
+ gr.update(visible=False), None
347
+ )
348
+ else:
349
+ return (
350
+ "⚠️ No app was running",
351
+ "<div style='text-align:center; padding:50px;'><h3>No app was running</h3></div>",
352
+ gr.update(visible=False), None
353
+ )
354
 
355
  generate_btn.click(
356
  on_generate,
357
+ inputs=[api_key, prompt],
358
  outputs=[
359
  code_output, install_output, status_output, app_frame,
360
+ stop_btn, app_file_state
361
  ]
362
  )
363
 
364
  stop_btn.click(
365
  on_stop,
366
+ inputs=[app_file_state],
367
+ outputs=[status_output, app_frame, stop_btn, app_file_state]
368
  )
369
 
370
  if __name__ == "__main__":
371
+ # Make sure we're not trying to use flagging
372
+ demo.launch(server_name="0.0.0.0", server_port=7860, flagging_callback=None)