dipan004 commited on
Commit
5694766
·
verified ·
1 Parent(s): 2623d94

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +19 -44
app.py CHANGED
@@ -12,10 +12,8 @@ from agents.langgraph import run_workflow
12
  from agents.rlhf_workflows import run_rlhf_workflow
13
  from agents.rlhf_routes import rlhf_router
14
 
15
- # Load environment variables from .env file
16
  load_dotenv()
17
 
18
- # Configure logging for the application
19
  logging.basicConfig(level=logging.INFO)
20
  logger = logging.getLogger("financial_notes_api")
21
 
@@ -26,10 +24,8 @@ app = FastAPI(
26
  version="1.0.0"
27
  )
28
 
29
- # Initialize Interactive Feedback Manager
30
  feedback_manager = InteractiveFeedbackManager()
31
 
32
- # Include RLHF routes
33
  app.include_router(rlhf_router)
34
  @app.on_event("startup")
35
  async def startup_event():
@@ -43,26 +39,25 @@ router = APIRouter()
43
 
44
 
45
  @router.post("/notes-llm")
46
- async def notes_llm_route(file: UploadFile = File(...), use_rlhf: bool = Query(False)):
47
- """Generate AI-powered financial notes using Generator-Validator pattern with Interactive Feedback"""
 
 
 
48
  file_path = f"data/input/{file.filename}"
49
  os.makedirs("data/input", exist_ok=True)
50
  with open(file_path, "wb") as buffer:
51
  shutil.copyfileobj(file.file, buffer)
52
 
53
  try:
54
- # Create Generator-Validator pipeline for LLM notes
55
- pipeline = create_notes_pipeline(use_rlhf=use_rlhf)
56
 
57
- # Process through pipeline
58
  generation_result, validation_result = pipeline.process(file_path)
59
 
60
- # Log processing summary
61
  summary = pipeline.get_processing_summary()
62
  logger.info(f"LLM Notes Pipeline Summary: {summary}")
63
 
64
  if generation_result.success and validation_result.is_valid:
65
- # Create interactive feedback session
66
  session_id = feedback_manager.create_session(file_path)
67
 
68
  response = FileResponse(
@@ -70,22 +65,19 @@ async def notes_llm_route(file: UploadFile = File(...), use_rlhf: bool = Query(F
70
  filename=os.path.basename(generation_result.output_path)
71
  )
72
 
73
- # Add comprehensive metadata to headers
74
  response.headers["X-Generation-Method"] = "llm"
75
  response.headers["X-Validation-Score"] = str(validation_result.score)
76
  response.headers["X-Attempts-Made"] = str(generation_result.metadata.get("attempt", 1))
77
  response.headers["X-Execution-ID"] = generation_result.metadata.get("execution_id", "")
78
- response.headers["X-Session-ID"] = session_id # New: Interactive session ID
79
- response.headers["X-Interactive-Enabled"] = "true" # New: Indicates interactive mode available
80
 
81
- # Add RLHF metadata if available
82
  if use_rlhf and "rlhf_metadata" in generation_result.metadata:
83
  rlhf_data = generation_result.metadata["rlhf_metadata"]
84
  response.headers["X-RLHF-Statement-ID"] = str(rlhf_data.get("statement_id", ""))
85
  response.headers["X-RLHF-Quality-Score"] = str(rlhf_data.get("predicted_quality", ""))
86
  response.headers["X-RLHF-Confidence"] = str(rlhf_data.get("confidence_score", ""))
87
 
88
- # Add validation feedback
89
  if validation_result.feedback:
90
  response.headers["X-Validation-Feedback"] = json.dumps(validation_result.feedback)
91
 
@@ -100,6 +92,9 @@ async def notes_llm_route(file: UploadFile = File(...), use_rlhf: bool = Query(F
100
  }
101
  raise HTTPException(status_code=500, detail=json.dumps(error_detail))
102
 
 
 
 
103
  except Exception as e:
104
  logger.error(f"LLM Notes pipeline failed: {e}")
105
  raise HTTPException(status_code=500, detail=f"Pipeline processing failed: {str(e)}")
@@ -111,9 +106,7 @@ async def submit_feedback(
111
  feedback_text: str = Form(...),
112
  feedback_type: str = Form(..., regex="^(text|numeric|formula|suggestion)$")
113
  ):
114
- """Submit user feedback for iterative improvement"""
115
  try:
116
- # Add feedback and generate UDF
117
  udf_version = feedback_manager.add_feedback(session_id, feedback_text, feedback_type)
118
 
119
  if udf_version is None:
@@ -134,7 +127,6 @@ async def submit_feedback(
134
 
135
  @router.post("/notes-llm/approve")
136
  async def approve_session(session_id: str = Form(...)):
137
- """Approve the current session and set final UDF"""
138
  try:
139
  success = feedback_manager.approve_session(session_id)
140
 
@@ -158,7 +150,6 @@ async def approve_session(session_id: str = Form(...)):
158
 
159
  @router.get("/notes-llm/session/{session_id}")
160
  async def get_session_info(session_id: str):
161
- """Get session information and feedback history"""
162
  try:
163
  session = feedback_manager.get_session(session_id)
164
 
@@ -194,9 +185,9 @@ async def get_session_info(session_id: str):
194
  @router.post("/notes-llm/generate")
195
  async def generate_with_feedback(
196
  session_id: str = Form(...),
197
- file: UploadFile = File(...)
 
198
  ):
199
- """Generate notes using accumulated feedback and UDFs"""
200
  try:
201
  session = feedback_manager.get_session(session_id)
202
 
@@ -206,16 +197,13 @@ async def generate_with_feedback(
206
  if session.status != 'active':
207
  raise HTTPException(status_code=400, detail=f"Session is {session.status}")
208
 
209
- # Save uploaded file
210
  file_path = f"data/input/{file.filename}"
211
  os.makedirs("data/input", exist_ok=True)
212
  with open(file_path, "wb") as buffer:
213
  shutil.copyfileobj(file.file, buffer)
214
 
215
- # Create pipeline with feedback integration
216
- pipeline = create_notes_pipeline(use_rlhf=False)
217
 
218
- # Prepare feedback context for the generator
219
  udfs_to_apply = []
220
  if session.final_udf:
221
  udfs_to_apply.append(session.final_udf)
@@ -224,7 +212,7 @@ async def generate_with_feedback(
224
 
225
  feedback_context = {
226
  'session_id': session_id,
227
- 'udfs': udfs_to_apply, # Pass final UDF if available, otherwise archived UDFs
228
  'feedback_history': [
229
  {
230
  'text': f.feedback_text,
@@ -235,7 +223,6 @@ async def generate_with_feedback(
235
  'current_iteration': session.current_iteration
236
  }
237
 
238
- # Process through pipeline with feedback context
239
  generation_result, validation_result = pipeline.process(file_path, feedback_context=feedback_context)
240
 
241
  if generation_result.success and validation_result.is_valid:
@@ -244,13 +231,11 @@ async def generate_with_feedback(
244
  filename=os.path.basename(generation_result.output_path)
245
  )
246
 
247
- # Add session and feedback metadata
248
  response.headers["X-Session-ID"] = session_id
249
  response.headers["X-Iteration"] = str(session.current_iteration)
250
  response.headers["X-Feedbacks-Applied"] = str(len(session.feedback_history))
251
  response.headers["X-UDFs-Archived"] = str(len(session.archived_udfs))
252
 
253
- # Add generation metadata
254
  response.headers["X-Generation-Method"] = "llm_with_feedback"
255
  response.headers["X-Validation-Score"] = str(validation_result.score)
256
  response.headers["X-Execution-ID"] = generation_result.metadata.get("execution_id", "")
@@ -268,6 +253,9 @@ async def generate_with_feedback(
268
 
269
  except HTTPException:
270
  raise
 
 
 
271
  except Exception as e:
272
  logger.error(f"Feedback-based generation failed: {e}")
273
  raise HTTPException(status_code=500, detail=f"Generation failed: {str(e)}")
@@ -276,23 +264,18 @@ async def generate_with_feedback(
276
 
277
  @router.post("/notes")
278
  async def notes_route(file: UploadFile = File(...)):
279
- """Generate financial notes directly from uploaded file"""
280
  try:
281
- # Save uploaded file
282
  file_path = f"data/input/{file.filename}"
283
  os.makedirs("data/input", exist_ok=True)
284
  with open(file_path, "wb") as buffer:
285
  shutil.copyfileobj(file.file, buffer)
286
 
287
- # Generate notes directly
288
  result = generate_notes_full_pipeline_from_path(file_path)
289
 
290
  if result["status"] == "success":
291
- # Return the generated Excel file
292
  output_path = result["output_xlsx_path"]
293
  return FileResponse(output_path, filename=os.path.basename(output_path))
294
 
295
- # If generation failed, raise HTTP exception
296
  raise HTTPException(status_code=500, detail=result.get("error", "Notes generation failed"))
297
 
298
  except Exception as e:
@@ -306,7 +289,6 @@ async def pnl_route(file: UploadFile = File(...), use_rlhf: bool = Query(False))
306
  with open(file_path, "wb") as buffer:
307
  shutil.copyfileobj(file.file, buffer)
308
 
309
- # Choose workflow based on RLHF preference
310
  if use_rlhf:
311
  result = run_rlhf_workflow(file_path, "pnl")
312
  else:
@@ -315,7 +297,6 @@ async def pnl_route(file: UploadFile = File(...), use_rlhf: bool = Query(False))
315
  if result["status"] == "success":
316
  response = FileResponse(result["result"].get("output_path", "data/pnl_statement.xlsx"), filename=os.path.basename(result["result"].get("output_path", "data/pnl_statement.xlsx")))
317
 
318
- # Add RLHF metadata to headers if available
319
  if "rlhf_metadata" in result.get("result", {}):
320
  rlhf_data = result["result"]["rlhf_metadata"]
321
  response.headers["X-RLHF-Statement-ID"] = str(rlhf_data.get("statement_id", ""))
@@ -332,17 +313,14 @@ async def bs_route(file: UploadFile = File(...), use_rlhf: bool = Query(False)):
332
  with open(file_path, "wb") as buffer:
333
  shutil.copyfileobj(file.file, buffer)
334
 
335
- # Choose workflow based on RLHF preference
336
  if use_rlhf:
337
  result = run_rlhf_workflow(file_path, "bs")
338
  else:
339
  result = run_workflow(file_path, "bs")
340
 
341
  if result["status"] == "success":
342
- # Use first xlsx file in output dir if present
343
  output_file = result["result"].get("output_path")
344
  if not output_file or not os.path.isfile(output_file):
345
- # Try to find the first .xlsx file in data/output/ (ensure it's a file, not a directory)
346
  output_dir = "data/output/"
347
  xlsx_files = [f for f in os.listdir(output_dir) if f.endswith('.xlsx') and os.path.isfile(os.path.join(output_dir, f))]
348
  if xlsx_files:
@@ -352,7 +330,6 @@ async def bs_route(file: UploadFile = File(...), use_rlhf: bool = Query(False)):
352
 
353
  response = FileResponse(output_file, filename=os.path.basename(output_file))
354
 
355
- # Add RLHF metadata to headers if available
356
  if "rlhf_metadata" in result.get("result", {}):
357
  rlhf_data = result["result"]["rlhf_metadata"]
358
  response.headers["X-RLHF-Statement-ID"] = str(rlhf_data.get("statement_id", ""))
@@ -370,7 +347,6 @@ async def cf_route(file: UploadFile = File(...), use_rlhf: bool = Query(False)):
370
  with open(file_path, "wb") as buffer:
371
  shutil.copyfileobj(file.file, buffer)
372
 
373
- # Choose workflow based on RLHF preference
374
  if use_rlhf:
375
  result = run_rlhf_workflow(file_path, "cf")
376
  else:
@@ -379,7 +355,6 @@ async def cf_route(file: UploadFile = File(...), use_rlhf: bool = Query(False)):
379
  if result["status"] == "success":
380
  response = FileResponse(result["result"].get("output_path", "data/cash_flow_statements.xlsx"), filename=os.path.basename(result["result"].get("output_path", "data/cash_flow_statements.xlsx")))
381
 
382
- # Add RLHF metadata to headers if available
383
  if "rlhf_metadata" in result.get("result", {}):
384
  rlhf_data = result["result"]["rlhf_metadata"]
385
  response.headers["X-RLHF-Statement-ID"] = str(rlhf_data.get("statement_id", ""))
@@ -392,4 +367,4 @@ app.include_router(router)
392
 
393
  if __name__ == "__main__":
394
  import uvicorn
395
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
12
  from agents.rlhf_workflows import run_rlhf_workflow
13
  from agents.rlhf_routes import rlhf_router
14
 
 
15
  load_dotenv()
16
 
 
17
  logging.basicConfig(level=logging.INFO)
18
  logger = logging.getLogger("financial_notes_api")
19
 
 
24
  version="1.0.0"
25
  )
26
 
 
27
  feedback_manager = InteractiveFeedbackManager()
28
 
 
29
  app.include_router(rlhf_router)
30
  @app.on_event("startup")
31
  async def startup_event():
 
39
 
40
 
41
  @router.post("/notes-llm")
42
+ async def notes_llm_route(
43
+ file: UploadFile = File(...),
44
+ use_rlhf: bool = Query(False),
45
+ user_api_key: Optional[str] = Form(None)
46
+ ):
47
  file_path = f"data/input/{file.filename}"
48
  os.makedirs("data/input", exist_ok=True)
49
  with open(file_path, "wb") as buffer:
50
  shutil.copyfileobj(file.file, buffer)
51
 
52
  try:
53
+ pipeline = create_notes_pipeline(use_rlhf=use_rlhf, user_api_key=user_api_key)
 
54
 
 
55
  generation_result, validation_result = pipeline.process(file_path)
56
 
 
57
  summary = pipeline.get_processing_summary()
58
  logger.info(f"LLM Notes Pipeline Summary: {summary}")
59
 
60
  if generation_result.success and validation_result.is_valid:
 
61
  session_id = feedback_manager.create_session(file_path)
62
 
63
  response = FileResponse(
 
65
  filename=os.path.basename(generation_result.output_path)
66
  )
67
 
 
68
  response.headers["X-Generation-Method"] = "llm"
69
  response.headers["X-Validation-Score"] = str(validation_result.score)
70
  response.headers["X-Attempts-Made"] = str(generation_result.metadata.get("attempt", 1))
71
  response.headers["X-Execution-ID"] = generation_result.metadata.get("execution_id", "")
72
+ response.headers["X-Session-ID"] = session_id
73
+ response.headers["X-Interactive-Enabled"] = "true"
74
 
 
75
  if use_rlhf and "rlhf_metadata" in generation_result.metadata:
76
  rlhf_data = generation_result.metadata["rlhf_metadata"]
77
  response.headers["X-RLHF-Statement-ID"] = str(rlhf_data.get("statement_id", ""))
78
  response.headers["X-RLHF-Quality-Score"] = str(rlhf_data.get("predicted_quality", ""))
79
  response.headers["X-RLHF-Confidence"] = str(rlhf_data.get("confidence_score", ""))
80
 
 
81
  if validation_result.feedback:
82
  response.headers["X-Validation-Feedback"] = json.dumps(validation_result.feedback)
83
 
 
92
  }
93
  raise HTTPException(status_code=500, detail=json.dumps(error_detail))
94
 
95
+ except ValueError as ve:
96
+ logger.error(f"API key error: {ve}")
97
+ raise HTTPException(status_code=400, detail=str(ve))
98
  except Exception as e:
99
  logger.error(f"LLM Notes pipeline failed: {e}")
100
  raise HTTPException(status_code=500, detail=f"Pipeline processing failed: {str(e)}")
 
106
  feedback_text: str = Form(...),
107
  feedback_type: str = Form(..., regex="^(text|numeric|formula|suggestion)$")
108
  ):
 
109
  try:
 
110
  udf_version = feedback_manager.add_feedback(session_id, feedback_text, feedback_type)
111
 
112
  if udf_version is None:
 
127
 
128
  @router.post("/notes-llm/approve")
129
  async def approve_session(session_id: str = Form(...)):
 
130
  try:
131
  success = feedback_manager.approve_session(session_id)
132
 
 
150
 
151
  @router.get("/notes-llm/session/{session_id}")
152
  async def get_session_info(session_id: str):
 
153
  try:
154
  session = feedback_manager.get_session(session_id)
155
 
 
185
  @router.post("/notes-llm/generate")
186
  async def generate_with_feedback(
187
  session_id: str = Form(...),
188
+ file: UploadFile = File(...),
189
+ user_api_key: Optional[str] = Form(None)
190
  ):
 
191
  try:
192
  session = feedback_manager.get_session(session_id)
193
 
 
197
  if session.status != 'active':
198
  raise HTTPException(status_code=400, detail=f"Session is {session.status}")
199
 
 
200
  file_path = f"data/input/{file.filename}"
201
  os.makedirs("data/input", exist_ok=True)
202
  with open(file_path, "wb") as buffer:
203
  shutil.copyfileobj(file.file, buffer)
204
 
205
+ pipeline = create_notes_pipeline(use_rlhf=False, user_api_key=user_api_key)
 
206
 
 
207
  udfs_to_apply = []
208
  if session.final_udf:
209
  udfs_to_apply.append(session.final_udf)
 
212
 
213
  feedback_context = {
214
  'session_id': session_id,
215
+ 'udfs': udfs_to_apply,
216
  'feedback_history': [
217
  {
218
  'text': f.feedback_text,
 
223
  'current_iteration': session.current_iteration
224
  }
225
 
 
226
  generation_result, validation_result = pipeline.process(file_path, feedback_context=feedback_context)
227
 
228
  if generation_result.success and validation_result.is_valid:
 
231
  filename=os.path.basename(generation_result.output_path)
232
  )
233
 
 
234
  response.headers["X-Session-ID"] = session_id
235
  response.headers["X-Iteration"] = str(session.current_iteration)
236
  response.headers["X-Feedbacks-Applied"] = str(len(session.feedback_history))
237
  response.headers["X-UDFs-Archived"] = str(len(session.archived_udfs))
238
 
 
239
  response.headers["X-Generation-Method"] = "llm_with_feedback"
240
  response.headers["X-Validation-Score"] = str(validation_result.score)
241
  response.headers["X-Execution-ID"] = generation_result.metadata.get("execution_id", "")
 
253
 
254
  except HTTPException:
255
  raise
256
+ except ValueError as ve:
257
+ logger.error(f"API key error: {ve}")
258
+ raise HTTPException(status_code=400, detail=str(ve))
259
  except Exception as e:
260
  logger.error(f"Feedback-based generation failed: {e}")
261
  raise HTTPException(status_code=500, detail=f"Generation failed: {str(e)}")
 
264
 
265
  @router.post("/notes")
266
  async def notes_route(file: UploadFile = File(...)):
 
267
  try:
 
268
  file_path = f"data/input/{file.filename}"
269
  os.makedirs("data/input", exist_ok=True)
270
  with open(file_path, "wb") as buffer:
271
  shutil.copyfileobj(file.file, buffer)
272
 
 
273
  result = generate_notes_full_pipeline_from_path(file_path)
274
 
275
  if result["status"] == "success":
 
276
  output_path = result["output_xlsx_path"]
277
  return FileResponse(output_path, filename=os.path.basename(output_path))
278
 
 
279
  raise HTTPException(status_code=500, detail=result.get("error", "Notes generation failed"))
280
 
281
  except Exception as e:
 
289
  with open(file_path, "wb") as buffer:
290
  shutil.copyfileobj(file.file, buffer)
291
 
 
292
  if use_rlhf:
293
  result = run_rlhf_workflow(file_path, "pnl")
294
  else:
 
297
  if result["status"] == "success":
298
  response = FileResponse(result["result"].get("output_path", "data/pnl_statement.xlsx"), filename=os.path.basename(result["result"].get("output_path", "data/pnl_statement.xlsx")))
299
 
 
300
  if "rlhf_metadata" in result.get("result", {}):
301
  rlhf_data = result["result"]["rlhf_metadata"]
302
  response.headers["X-RLHF-Statement-ID"] = str(rlhf_data.get("statement_id", ""))
 
313
  with open(file_path, "wb") as buffer:
314
  shutil.copyfileobj(file.file, buffer)
315
 
 
316
  if use_rlhf:
317
  result = run_rlhf_workflow(file_path, "bs")
318
  else:
319
  result = run_workflow(file_path, "bs")
320
 
321
  if result["status"] == "success":
 
322
  output_file = result["result"].get("output_path")
323
  if not output_file or not os.path.isfile(output_file):
 
324
  output_dir = "data/output/"
325
  xlsx_files = [f for f in os.listdir(output_dir) if f.endswith('.xlsx') and os.path.isfile(os.path.join(output_dir, f))]
326
  if xlsx_files:
 
330
 
331
  response = FileResponse(output_file, filename=os.path.basename(output_file))
332
 
 
333
  if "rlhf_metadata" in result.get("result", {}):
334
  rlhf_data = result["result"]["rlhf_metadata"]
335
  response.headers["X-RLHF-Statement-ID"] = str(rlhf_data.get("statement_id", ""))
 
347
  with open(file_path, "wb") as buffer:
348
  shutil.copyfileobj(file.file, buffer)
349
 
 
350
  if use_rlhf:
351
  result = run_rlhf_workflow(file_path, "cf")
352
  else:
 
355
  if result["status"] == "success":
356
  response = FileResponse(result["result"].get("output_path", "data/cash_flow_statements.xlsx"), filename=os.path.basename(result["result"].get("output_path", "data/cash_flow_statements.xlsx")))
357
 
 
358
  if "rlhf_metadata" in result.get("result", {}):
359
  rlhf_data = result["result"]["rlhf_metadata"]
360
  response.headers["X-RLHF-Statement-ID"] = str(rlhf_data.get("statement_id", ""))
 
367
 
368
  if __name__ == "__main__":
369
  import uvicorn
370
+ uvicorn.run(app, host="0.0.0.0", port=7860)