Sahil Garg commited on
Commit
7e453aa
·
1 Parent(s): 436fd72

/notes-llm added, system.md file added

Browse files
SYSTEM_ARCHITECTURE_BOXES.md ADDED
The diff for this file is too large to render. See raw diff
 
agents/langgraph.py CHANGED
@@ -7,6 +7,7 @@ from agents.simple_tools import (
7
  generate_balance_sheet,
8
  generate_pnl_statement,
9
  generate_cash_flow_statement,
 
10
  )
11
 
12
  class FinancialAgentState(TypedDict):
@@ -44,6 +45,7 @@ workflows = {
44
  "pnl": make_workflow(generate_pnl_statement),
45
  "bs": make_workflow(generate_balance_sheet),
46
  "cf": make_workflow(generate_cash_flow_statement),
 
47
  }
48
 
49
  def run_workflow(file_path: str, kind: str) -> Dict[str, Any]:
 
7
  generate_balance_sheet,
8
  generate_pnl_statement,
9
  generate_cash_flow_statement,
10
+ generate_llm_notes,
11
  )
12
 
13
  class FinancialAgentState(TypedDict):
 
45
  "pnl": make_workflow(generate_pnl_statement),
46
  "bs": make_workflow(generate_balance_sheet),
47
  "cf": make_workflow(generate_cash_flow_statement),
48
+ "notes-llm": make_workflow(generate_llm_notes),
49
  }
50
 
51
  def run_workflow(file_path: str, kind: str) -> Dict[str, Any]:
agents/rlhf_workflows.py CHANGED
@@ -16,6 +16,7 @@ from agents.simple_tools import (
16
  generate_balance_sheet,
17
  generate_pnl_statement,
18
  generate_cash_flow_statement,
 
19
  )
20
  from agents.feedback_manager import FeedbackManager
21
  from agents.reward_model import FinancialRewardModel, RLHFTrainer
@@ -228,6 +229,7 @@ rlhf_workflows = {
228
  "pnl": rlhf_manager.make_rlhf_workflow(generate_pnl_statement, "pnl"),
229
  "bs": rlhf_manager.make_rlhf_workflow(generate_balance_sheet, "balance_sheet"),
230
  "cf": rlhf_manager.make_rlhf_workflow(generate_cash_flow_statement, "cash_flow"),
 
231
  }
232
 
233
  def run_rlhf_workflow(file_path: str, kind: str) -> Dict[str, Any]:
 
16
  generate_balance_sheet,
17
  generate_pnl_statement,
18
  generate_cash_flow_statement,
19
+ generate_llm_notes,
20
  )
21
  from agents.feedback_manager import FeedbackManager
22
  from agents.reward_model import FinancialRewardModel, RLHFTrainer
 
229
  "pnl": rlhf_manager.make_rlhf_workflow(generate_pnl_statement, "pnl"),
230
  "bs": rlhf_manager.make_rlhf_workflow(generate_balance_sheet, "balance_sheet"),
231
  "cf": rlhf_manager.make_rlhf_workflow(generate_cash_flow_statement, "cash_flow"),
232
+ "notes-llm": rlhf_manager.make_rlhf_workflow(generate_llm_notes, "notes"),
233
  }
234
 
235
  def run_rlhf_workflow(file_path: str, kind: str) -> Dict[str, Any]:
agents/simple_agent.py CHANGED
@@ -32,6 +32,7 @@ class FinancialStatementAgent:
32
  try:
33
  prompts = {
34
  "notes": f"Generate financial notes for note numbers {note_numbers} from {file_path}",
 
35
  "balance_sheet": f"Generate balance sheet from {file_path} using note numbers {note_numbers}",
36
  "pnl": f"Generate P&L statement from {file_path} using note numbers {note_numbers}",
37
  "cash_flow": f"Generate cash flow statement from {file_path} using note numbers {note_numbers}"
 
32
  try:
33
  prompts = {
34
  "notes": f"Generate financial notes for note numbers {note_numbers} from {file_path}",
35
+ "notes-llm": f"Generate financial notes using LLM for note numbers {note_numbers} from {file_path}",
36
  "balance_sheet": f"Generate balance sheet from {file_path} using note numbers {note_numbers}",
37
  "pnl": f"Generate P&L statement from {file_path} using note numbers {note_numbers}",
38
  "cash_flow": f"Generate cash flow statement from {file_path} using note numbers {note_numbers}"
agents/simple_tools.py CHANGED
@@ -278,6 +278,10 @@ def generate_cash_flow_statement(file_path: str) -> Dict[str, Any]:
278
  if os.getenv("OPENROUTER_API_KEY"):
279
  env["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
280
  env["INPUT_FILE"] = "data/clean_financial_data_cfs.json"
 
 
 
 
281
  cwd = os.getcwd()
282
 
283
  # Step 1: Run cash_flow_data_extractor.py
@@ -366,10 +370,109 @@ def generate_cash_flow_statement(file_path: str) -> Dict[str, Any]:
366
  "execution_time": execution_time
367
  }
368
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  # Simplified tool list - only financial statement generation
370
  FINANCIAL_TOOLS = [
371
  generate_notes_full_pipeline_from_path,
372
  generate_balance_sheet,
373
  generate_pnl_statement,
374
- generate_cash_flow_statement
 
375
  ]
 
278
  if os.getenv("OPENROUTER_API_KEY"):
279
  env["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
280
  env["INPUT_FILE"] = "data/clean_financial_data_cfs.json"
281
+ env["CFS_JSON_INPUT"] = "data/clean_financial_data_cfs.json"
282
+ env["CFS_JSON_OUTPUT"] = "data/extracted_cfs_data.json"
283
+ env["CFS_EXTRACTED_FILE"] = "data/extracted_cfs_data.json"
284
+ env["CFS_OUTPUT_FILE"] = "data/cash_flow_statements.xlsx"
285
  cwd = os.getcwd()
286
 
287
  # Step 1: Run cash_flow_data_extractor.py
 
370
  "execution_time": execution_time
371
  }
372
 
373
+ @tool
374
+ def generate_llm_notes(file_path: str, note_numbers: str = "") -> Dict[str, Any]:
375
+ """
376
+ Generate notes using LLM-based approach (FlexibleFinancialNoteGenerator)
377
+ Args:
378
+ file_path: Path to trial balance Excel file
379
+ note_numbers: Optional comma-separated note numbers to generate
380
+ Returns:
381
+ dict with status, output_xlsx_path, and error if any
382
+ """
383
+ execution_id = str(uuid.uuid4())[:8]
384
+ start_time = time.time()
385
+
386
+ try:
387
+ # Use the complete LLM notes pipeline from existing scripts
388
+ env = os.environ.copy()
389
+ if os.getenv("OPENROUTER_API_KEY"):
390
+ env["OPENROUTER_API_KEY"] = os.getenv("OPENROUTER_API_KEY")
391
+ cwd = os.getcwd()
392
+
393
+ # Step 1: Run LLM notes data processor
394
+ logger.info("Step 1: Processing trial balance data")
395
+ result1 = subprocess.run(
396
+ ["python", "notes/llm_notes_data_processor.py", file_path],
397
+ env=env,
398
+ cwd=cwd,
399
+ capture_output=True,
400
+ text=True
401
+ )
402
+
403
+ if result1.returncode != 0:
404
+ return {"status": "error", "error": f"LLM notes data processing failed: {result1.stderr}"}
405
+
406
+ # Step 2: Run LLM notes generator
407
+ logger.info("Step 2: Generating notes using LLM")
408
+ if note_numbers:
409
+ # Generate specific notes
410
+ result2 = subprocess.run(
411
+ ["python", "notes/llm_notes_generator.py", "specific", note_numbers],
412
+ env=env,
413
+ cwd=cwd,
414
+ capture_output=True,
415
+ text=True
416
+ )
417
+ else:
418
+ # Generate all notes
419
+ result2 = subprocess.run(
420
+ ["python", "notes/llm_notes_generator.py", "all", ""],
421
+ env=env,
422
+ cwd=cwd,
423
+ capture_output=True,
424
+ text=True
425
+ )
426
+
427
+ if result2.returncode != 0:
428
+ return {"status": "error", "error": f"LLM notes generation failed: {result2.stderr}"}
429
+
430
+ # Step 3: Convert to Excel
431
+ logger.info("Step 3: Converting to Excel format")
432
+ input_json = "data/generated_notes/notes.json"
433
+ output_excel = "data/generated_notes_excel/notes.xlsx"
434
+
435
+ result3 = subprocess.run(
436
+ ["python", "notes/llm_notes_excel_converter.py", input_json, output_excel],
437
+ env=env,
438
+ cwd=cwd,
439
+ capture_output=True,
440
+ text=True
441
+ )
442
+
443
+ if result3.returncode == 0:
444
+ execution_time = round(time.time() - start_time, 2)
445
+ return {
446
+ "status": "success",
447
+ "message": "LLM-based notes generated successfully",
448
+ "output_xlsx_path": output_excel,
449
+ "execution_id": execution_id,
450
+ "execution_time": execution_time
451
+ }
452
+ else:
453
+ execution_time = round(time.time() - start_time, 2)
454
+ return {
455
+ "status": "error",
456
+ "error": f"Excel conversion failed: {result3.stderr}",
457
+ "execution_id": execution_id,
458
+ "execution_time": execution_time
459
+ }
460
+
461
+ except Exception as e:
462
+ execution_time = round(time.time() - start_time, 2)
463
+ logger.error(f"LLM notes generation failed: {e}")
464
+ return {
465
+ "status": "error",
466
+ "error": f"LLM notes generation failed: {e}",
467
+ "execution_id": execution_id,
468
+ "execution_time": execution_time
469
+ }
470
+
471
  # Simplified tool list - only financial statement generation
472
  FINANCIAL_TOOLS = [
473
  generate_notes_full_pipeline_from_path,
474
  generate_balance_sheet,
475
  generate_pnl_statement,
476
+ generate_cash_flow_statement,
477
+ generate_llm_notes
478
  ]
app.py CHANGED
@@ -1,12 +1,15 @@
1
- from fastapi import FastAPI, APIRouter, UploadFile, File, HTTPException, Query
2
  from fastapi.responses import FileResponse
 
3
  import os
4
  import shutil
5
  import logging
 
6
  from agents.langgraph import run_workflow
7
  from agents.rlhf_workflows import run_rlhf_workflow
8
  from agents.rlhf_routes import rlhf_router
9
 
 
10
  # Configure logging for the application
11
  logging.basicConfig(level=logging.INFO)
12
  logger = logging.getLogger("financial_notes_api")
@@ -31,58 +34,32 @@ async def shutdown_event():
31
 
32
  router = APIRouter()
33
 
34
- """@router.post("/new")
35
- async def llm_generate_and_excel(
36
- file: UploadFile = File(...),
37
- note_number: Optional[str] = Form(None)
38
- ):
39
  os.makedirs("data/input", exist_ok=True)
40
- file_location = f"data/input/{file.filename}"
41
- with open(file_location, "wb") as buffer:
42
  shutil.copyfileobj(file.file, buffer)
43
- structured_data = extract_trial_balance_data(file_location)
44
- output_json = "data/output1/parsed_trial_balance.json"
45
- analyze_and_save_results(structured_data, output_json)
46
- try:
47
- generator = FlexibleFinancialNoteGenerator()
48
- except Exception as e:
49
- logger.error(f"Generator init failed: {e}")
50
- raise HTTPException(status_code=500, detail=f"Generator init failed: {e}")
51
- os.makedirs("data/generated_notes_excel", exist_ok=True)
52
- wrapped_json_path = "data/generated_notes/notes_wrapped.json"
53
- if note_number:
54
- note_numbers = [n.strip() for n in note_number.split(",")]
55
- all_notes = []
56
- for n in note_numbers:
57
- success = generator.generate_note(n, trial_balance_path=output_json)
58
- if success:
59
- with open("data/generated_notes/notes.json", "r", encoding="utf-8") as f:
60
- note_json = json.load(f)
61
- all_notes.append(note_json)
62
- with open("data/generated_notes/notes.json", "w", encoding="utf-8") as f:
63
- json.dump({"notes": all_notes}, f, indent=2, ensure_ascii=False)
64
- wrapped = normalize_llm_notes_json({"notes": all_notes})
65
- with open(wrapped_json_path, "w", encoding="utf-8") as f2:
66
- json.dump(wrapped, f2, ensure_ascii=False, indent=2)
67
- excel_path = "data/generated_notes_excel/notes.xlsx"
68
- json_to_xlsx(wrapped_json_path, excel_path)
69
  else:
70
- results = generator.generate_all_notes(trial_balance_path=output_json)
71
- if not any(results.values()):
72
- logger.error("Failed to generate any notes. LLM API may be down or unreachable.")
73
- raise HTTPException(status_code=500, detail="Failed to generate any notes. LLM API may be down or unreachable.")
74
- with open("data/generated_notes/notes.json", "r", encoding="utf-8") as f:
75
- notes_json = json.load(f)
76
- wrapped = normalize_llm_notes_json(notes_json)
77
- with open(wrapped_json_path, "w", encoding="utf-8") as f2:
78
- json.dump(wrapped, f2, ensure_ascii=False, indent=2)
79
- excel_path = "data/generated_notes_excel/notes.xlsx"
80
- json_to_xlsx(wrapped_json_path, excel_path)
81
- return FileResponse(
82
- excel_path,
83
- filename=os.path.basename(excel_path),
84
- media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
85
- )"""
86
 
87
 
88
  @router.post("/notes")
 
1
+ from fastapi import FastAPI, APIRouter, UploadFile, File, HTTPException, Query, Form
2
  from fastapi.responses import FileResponse
3
+ from typing import Optional
4
  import os
5
  import shutil
6
  import logging
7
+ import json
8
  from agents.langgraph import run_workflow
9
  from agents.rlhf_workflows import run_rlhf_workflow
10
  from agents.rlhf_routes import rlhf_router
11
 
12
+
13
  # Configure logging for the application
14
  logging.basicConfig(level=logging.INFO)
15
  logger = logging.getLogger("financial_notes_api")
 
34
 
35
  router = APIRouter()
36
 
37
+
38
+ @router.post("/notes-llm")
39
+ async def notes_llm_route(file: UploadFile = File(...), use_rlhf: bool = Query(False)):
40
+ file_path = f"data/input/{file.filename}"
 
41
  os.makedirs("data/input", exist_ok=True)
42
+ with open(file_path, "wb") as buffer:
 
43
  shutil.copyfileobj(file.file, buffer)
44
+
45
+ # Choose workflow based on RLHF preference - using "notes-llm" as statement type
46
+ if use_rlhf:
47
+ result = run_rlhf_workflow(file_path, "notes-llm")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  else:
49
+ result = run_workflow(file_path, "notes-llm")
50
+
51
+ if result["status"] == "success":
52
+ response = FileResponse(result["result"]["output_xlsx_path"], filename=os.path.basename(result["result"]["output_xlsx_path"]))
53
+
54
+ # Add RLHF metadata to headers if available
55
+ if "rlhf_metadata" in result.get("result", {}):
56
+ rlhf_data = result["result"]["rlhf_metadata"]
57
+ response.headers["X-RLHF-Statement-ID"] = str(rlhf_data.get("statement_id", ""))
58
+ response.headers["X-RLHF-Quality-Score"] = str(rlhf_data.get("predicted_quality", ""))
59
+ response.headers["X-RLHF-Confidence"] = str(rlhf_data.get("confidence_score", ""))
60
+
61
+ return response
62
+ raise HTTPException(status_code=500, detail=result["error"])
 
 
63
 
64
 
65
  @router.post("/notes")
notes/llm_notes_data_processor.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ LLM Notes Data Processor - Extract and analyze trial balance data for LLM notes generation
4
+ """
5
+ import sys
6
+ import os
7
+ import json
8
+ import logging
9
+ from pathlib import Path
10
+
11
+ # Add parent directory to path for imports
12
+ sys.path.append(str(Path(__file__).parent.parent))
13
+
14
+ from notes.data_extraction import extract_trial_balance_data
15
+
16
+ # Configure logging
17
+ logging.basicConfig(level=logging.INFO)
18
+ logger = logging.getLogger(__name__)
19
+
20
+ def save_trial_balance_data(data, output_path):
21
+ """Save trial balance data to JSON file"""
22
+ # Convert TrialBalanceRecord objects to dictionaries
23
+ records = []
24
+ for record in data:
25
+ if hasattr(record, 'model_dump'):
26
+ records.append(record.model_dump())
27
+ elif hasattr(record, 'dict'):
28
+ records.append(record.dict())
29
+ else:
30
+ records.append(record)
31
+
32
+ output_data = {
33
+ "trial_balance": records,
34
+ "metadata": {
35
+ "total_records": len(records),
36
+ "source": "llm_notes_data_processor"
37
+ }
38
+ }
39
+
40
+ with open(output_path, 'w', encoding='utf-8') as f:
41
+ json.dump(output_data, f, indent=2, ensure_ascii=False)
42
+
43
+ logger.info(f"Saved {len(records)} records to {output_path}")
44
+
45
+ def main():
46
+ """Main function to process trial balance data for LLM notes generation"""
47
+ if len(sys.argv) != 2:
48
+ logger.error("Usage: python llm_notes_data_processor.py <input_file>")
49
+ sys.exit(1)
50
+
51
+ input_file = sys.argv[1]
52
+
53
+ if not os.path.exists(input_file):
54
+ logger.error(f"Input file not found: {input_file}")
55
+ sys.exit(1)
56
+
57
+ try:
58
+ logger.info(f"Processing trial balance data from: {input_file}")
59
+
60
+ # Extract trial balance data
61
+ structured_data = extract_trial_balance_data(input_file)
62
+ logger.info("Data extraction completed")
63
+
64
+ # Save the data
65
+ output_json = "data/output1/parsed_trial_balance.json"
66
+ os.makedirs("data/output1", exist_ok=True)
67
+ save_trial_balance_data(structured_data, output_json)
68
+ logger.info(f"Data analysis completed. Results saved to: {output_json}")
69
+
70
+ logger.info("LLM notes data processing completed successfully")
71
+ return 0
72
+
73
+ except Exception as e:
74
+ logger.error(f"Error processing data: {e}")
75
+ return 1
76
+
77
+ if __name__ == "__main__":
78
+ sys.exit(main())
notes/llm_notes_excel_converter.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ LLM Notes Excel Converter - Convert JSON notes to Excel format
4
+ """
5
+ import sys
6
+ import os
7
+ import json
8
+ import logging
9
+ from pathlib import Path
10
+
11
+ # Add parent directory to path for imports
12
+ sys.path.insert(0, str(Path(__file__).parent.parent)) # Insert at beginning to prioritize main utils
13
+
14
+ from utils.utils_normalize import normalize_llm_notes_json
15
+ from notes.json_to_excel import json_to_xlsx
16
+
17
+ # Configure logging
18
+ logging.basicConfig(level=logging.INFO)
19
+ logger = logging.getLogger(__name__)
20
+
21
+ def main():
22
+ """Main function to convert LLM-generated notes JSON to Excel"""
23
+ if len(sys.argv) != 3:
24
+ logger.error("Usage: python llm_notes_excel_converter.py <input_json> <output_excel>")
25
+ sys.exit(1)
26
+
27
+ input_json = sys.argv[1]
28
+ output_excel = sys.argv[2]
29
+
30
+ if not os.path.exists(input_json):
31
+ logger.error(f"Input JSON file not found: {input_json}")
32
+ sys.exit(1)
33
+
34
+ try:
35
+ logger.info(f"Converting notes from {input_json} to {output_excel}")
36
+
37
+ # Load the generated notes
38
+ with open(input_json, "r", encoding="utf-8") as f:
39
+ notes_data = json.load(f)
40
+
41
+ # Normalize the JSON structure
42
+ wrapped_json_path = "data/generated_notes/notes_wrapped.json"
43
+ os.makedirs("data/generated_notes", exist_ok=True)
44
+
45
+ if isinstance(notes_data, dict) and "notes" in notes_data:
46
+ # Already in correct format
47
+ normalized_data = notes_data
48
+ else:
49
+ # Wrap in notes structure
50
+ normalized_data = {"notes": notes_data} if isinstance(notes_data, list) else notes_data
51
+
52
+ # Normalize using the utility function
53
+ wrapped = normalize_llm_notes_json(normalized_data)
54
+
55
+ # Save wrapped JSON
56
+ with open(wrapped_json_path, "w", encoding="utf-8") as f:
57
+ json.dump(wrapped, f, ensure_ascii=False, indent=2)
58
+
59
+ # Convert to Excel
60
+ os.makedirs(os.path.dirname(output_excel), exist_ok=True)
61
+ json_to_xlsx(wrapped_json_path, output_excel)
62
+
63
+ logger.info(f"Excel conversion completed. Output saved to: {output_excel}")
64
+ return 0
65
+
66
+ except Exception as e:
67
+ logger.error(f"Error converting to Excel: {e}")
68
+ return 1
69
+
70
+ if __name__ == "__main__":
71
+ sys.exit(main())
notes/llm_notes_generator.py CHANGED
@@ -115,12 +115,16 @@ class FlexibleFinancialNoteGenerator:
115
  }
116
 
117
  def load_note_templates(self) -> Dict[str, Any]:
118
- """Load note templates from app.notes_template.py file."""
119
  try:
120
- from .notes_template import note_templates
 
 
 
 
121
  return note_templates
122
  except ImportError as e:
123
- logger.error(f"Error importing note_templates from app.notes_template: {e}")
124
  return {}
125
  except Exception as e:
126
  logger.error(f"Unexpected error loading note_templates: {e}")
@@ -497,32 +501,91 @@ class FlexibleFinancialNoteGenerator:
497
  def main() -> None:
498
  """Main function to run the flexible note generator"""
499
  try:
500
- generator = FlexibleFinancialNoteGenerator()
501
- if not generator.note_templates:
502
- logger.error("No note templates loaded. Check app/new.py")
503
- return
504
-
505
- logger.info(f"Loaded {len(generator.note_templates)} note templates")
506
- choice = input("\nGenerate (1) specific note or (2) all notes? Enter 1 or 2: ").strip()
507
-
508
- if choice == "1":
509
- available_notes = list(generator.note_templates.keys())
510
- print(f"Available notes: {', '.join(available_notes)}")
511
- note_number = input("Enter note number: ").strip()
512
- if note_number in available_notes:
513
- success = generator.generate_note(note_number)
514
- logger.info(f"Note {note_number} {'generated successfully' if success else 'generated with issues'}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  else:
516
- logger.error(f"Note {note_number} not found")
517
- elif choice == "2":
518
- results = generator.generate_all_notes()
519
- successful = sum(1 for success in results.values() if success)
520
- total = len(results)
521
- logger.info(f"{successful}/{total} notes generated successfully")
522
  else:
523
- logger.error("Invalid choice. Enter 1 or 2.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524
  except Exception as e:
525
  logger.error(f"Error: {e}", exc_info=True)
 
526
 
527
  if __name__ == "__main__":
528
  main()
 
115
  }
116
 
117
  def load_note_templates(self) -> Dict[str, Any]:
118
+ """Load note templates from notes_template.py file."""
119
  try:
120
+ # Add parent directory to path for imports when run as script
121
+ if __name__ == "__main__":
122
+ sys.path.append(str(Path(__file__).parent.parent))
123
+
124
+ from notes_template import note_templates
125
  return note_templates
126
  except ImportError as e:
127
+ logger.error(f"Error importing note_templates from notes_template: {e}")
128
  return {}
129
  except Exception as e:
130
  logger.error(f"Unexpected error loading note_templates: {e}")
 
501
  def main() -> None:
502
  """Main function to run the flexible note generator"""
503
  try:
504
+ # Check for command line arguments
505
+ if len(sys.argv) > 1:
506
+ # Command line mode
507
+ if len(sys.argv) < 3:
508
+ logger.error("Usage: python llm_notes_generator.py <mode> <note_numbers>")
509
+ logger.error(" mode: 'specific' or 'all'")
510
+ logger.error(" note_numbers: comma-separated note numbers (for specific mode)")
511
+ sys.exit(1)
512
+
513
+ mode = sys.argv[1].lower()
514
+ note_numbers = sys.argv[2] if len(sys.argv) > 2 else ""
515
+
516
+ generator = FlexibleFinancialNoteGenerator()
517
+ if not generator.note_templates:
518
+ logger.error("No note templates loaded. Check app/new.py")
519
+ sys.exit(1)
520
+
521
+ logger.info(f"Loaded {len(generator.note_templates)} note templates")
522
+
523
+ if mode == "specific":
524
+ if not note_numbers:
525
+ logger.error("Note numbers required for specific mode")
526
+ sys.exit(1)
527
+
528
+ note_list = [n.strip() for n in note_numbers.split(",")]
529
+ all_notes = []
530
+
531
+ for note_number in note_list:
532
+ if note_number in generator.note_templates:
533
+ success = generator.generate_note(note_number)
534
+ if success:
535
+ # Load the generated note
536
+ with open("data/generated_notes/notes.json", "r", encoding="utf-8") as f:
537
+ note_data = json.load(f)
538
+ all_notes.append(note_data)
539
+ logger.info(f"Note {note_number} generated successfully")
540
+ else:
541
+ logger.error(f"Failed to generate note {note_number}")
542
+ else:
543
+ logger.error(f"Note {note_number} not found in templates")
544
+
545
+ # Save all notes
546
+ output_dir = settings.output_dir
547
+ Path(output_dir).mkdir(parents=True, exist_ok=True)
548
+ with open(f"{output_dir}/notes.json", "w", encoding="utf-8") as f:
549
+ json.dump({"notes": all_notes}, f, indent=2, ensure_ascii=False)
550
+ logger.info(f"All notes saved to {output_dir}/notes.json")
551
+
552
+ elif mode == "all":
553
+ results = generator.generate_all_notes()
554
+ successful = sum(1 for success in results.values() if success)
555
+ total = len(results)
556
+ logger.info(f"{successful}/{total} notes generated successfully")
557
  else:
558
+ logger.error("Invalid mode. Use 'specific' or 'all'")
559
+ sys.exit(1)
 
 
 
 
560
  else:
561
+ # Interactive mode (original behavior)
562
+ generator = FlexibleFinancialNoteGenerator()
563
+ if not generator.note_templates:
564
+ logger.error("No note templates loaded. Check app/new.py")
565
+ return
566
+
567
+ logger.info(f"Loaded {len(generator.note_templates)} note templates")
568
+ choice = input("\nGenerate (1) specific note or (2) all notes? Enter 1 or 2: ").strip()
569
+
570
+ if choice == "1":
571
+ available_notes = list(generator.note_templates.keys())
572
+ print(f"Available notes: {', '.join(available_notes)}")
573
+ note_number = input("Enter note number: ").strip()
574
+ if note_number in available_notes:
575
+ success = generator.generate_note(note_number)
576
+ logger.info(f"Note {note_number} {'generated successfully' if success else 'generated with issues'}")
577
+ else:
578
+ logger.error(f"Note {note_number} not found")
579
+ elif choice == "2":
580
+ results = generator.generate_all_notes()
581
+ successful = sum(1 for success in results.values() if success)
582
+ total = len(results)
583
+ logger.info(f"{successful}/{total} notes generated successfully")
584
+ else:
585
+ logger.error("Invalid choice. Enter 1 or 2.")
586
  except Exception as e:
587
  logger.error(f"Error: {e}", exc_info=True)
588
+ sys.exit(1)
589
 
590
  if __name__ == "__main__":
591
  main()