FluxFinance commited on
Commit
222eb80
Β·
verified Β·
1 Parent(s): 83ad611

Upload Advisernote.py

Browse files
Files changed (1) hide show
  1. Advisernote.py +95 -18
Advisernote.py CHANGED
@@ -61,6 +61,9 @@ NOTION_DB_ID = get_config_value("NOTION_DATABASE_ID")
61
  ALL_MAILBOXES = [item.strip() for item in get_config_value("ALL_MAILBOXES").split(",") if item.strip()]
62
  allowed_users = parse_allowed_users(get_config_value("ALLOWED_USERS"))
63
  MAX_EMAILS_PER_FOLDER = 250
 
 
 
64
 
65
  def save_rules_to_db(df):
66
  conn = sqlite3.connect("data.db")
@@ -767,10 +770,10 @@ if check_password():
767
  # --- Helper Functions ---
768
  # (κΈ°μ‘΄ add_line_to_docx, load_docs_from_path, update_status 등은 κ·ΈλŒ€λ‘œ μœ μ§€)
769
 
770
- def ask_ai_note(stage_key, items_data, row_info, manual_note=""):
771
- # 🚨 [파이썬 κ°•λ ₯ ν†΅μ œ 1] λ§€ν•‘λœ 이메일 데이터가 단 ν•œ κ°œλ„ μ—†κ³ , μΆ”κ°€ 수기 λ©”λͺ¨λ„ μ™„μ „νžˆ λΉ„μ–΄μžˆλ‹€λ©΄
772
- # AI 호좜 자체λ₯Ό μƒλž΅ν•˜κ³  μ¦‰μ‹œ μ™„λ²½ν•œ 3쀄 κ³΅λž€μ„ λ¦¬ν„΄ν•˜μ—¬ λΉ„μš©κ³Ό 였λ₯˜λ₯Ό μ›μ²œ μ°¨λ‹¨ν•©λ‹ˆλ‹€.
773
- if not items_data and not manual_note.strip():
774
  return "Date: \nAction: \nPurpose: \n"
775
 
776
  try:
@@ -855,12 +858,69 @@ if check_password():
855
 
856
  return res_text
857
 
858
- except Exception as e:
859
- return f"⚠️ Vertex AI Error: {str(e)}"
860
-
861
-
862
-
863
- # ─────────────────────────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
864
  # 🟒 [μ‹€μ‹œκ°„ 동기화 λ§ˆλ²•μ˜ μ—”μ§„] μ‚¬μ΄λ“œλ°”λ₯Ό 그리기 전에 λ“œλ‘­λ‹€μš΄ 값을 λ¨Όμ € κ°€λ‘œμ±„μ„œ 계산!
865
  # ─────────────────────────────────────────────────────────────────
866
  def sync_and_update_status():
@@ -973,14 +1033,31 @@ if check_password():
973
  is_email = (current_mode == "EMAIL")
974
 
975
  # 🟒 2. 이메일 탭일 경우 μ²΄ν¬λ°•μŠ€λ₯Ό κ°•μ œλ‘œ 끄고(False), 클릭 λͺ» ν•˜κ²Œ μž κΈ‰λ‹ˆλ‹€(disabled).
976
- use_ai = st.checkbox(
977
- "πŸ€– AI둜 μžλ™ μž‘μ„±ν•˜κΈ° (API λΉ„μš© λ°œμƒ)",
978
- value=False,
979
- disabled=is_email,
980
- help="🚫 이메일 λͺ¨λ“œμ—μ„œλŠ” AI μš”μ•½μ΄ μ œν•œλ˜λ©° 빈 ν…œν”Œλ¦Ώλ§Œ μƒμ„±λ©λ‹ˆλ‹€." if is_email else "체크λ₯Ό 끄면 λΉ„μš© 없이 λ‚ μ§œλ§Œ μΆ”μΆœλœ 빈 ν…œν”Œλ¦Ώμ΄ μ¦‰μ‹œ μƒμ„±λ©λ‹ˆλ‹€."
981
- )
982
-
983
- col_ai, col_dl = st.columns(2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
984
 
985
  with col_ai:
986
 
 
61
  ALL_MAILBOXES = [item.strip() for item in get_config_value("ALL_MAILBOXES").split(",") if item.strip()]
62
  allowed_users = parse_allowed_users(get_config_value("ALLOWED_USERS"))
63
  MAX_EMAILS_PER_FOLDER = 250
64
+ GEMINI_25_FLASH_INPUT_USD_PER_1M = 0.30
65
+ GEMINI_25_FLASH_OUTPUT_USD_PER_1M = 2.50
66
+ ESTIMATED_OUTPUT_TOKENS_PER_STAGE = 350
67
 
68
  def save_rules_to_db(df):
69
  conn = sqlite3.connect("data.db")
 
770
  # --- Helper Functions ---
771
  # (κΈ°μ‘΄ add_line_to_docx, load_docs_from_path, update_status 등은 κ·ΈλŒ€λ‘œ μœ μ§€)
772
 
773
+ def ask_ai_note(stage_key, items_data, row_info, manual_note=""):
774
+ # 🚨 [파이썬 κ°•λ ₯ ν†΅μ œ 1] λ§€ν•‘λœ 이메일 데이터가 단 ν•œ κ°œλ„ μ—†κ³ , μΆ”κ°€ 수기 λ©”λͺ¨λ„ μ™„μ „νžˆ λΉ„μ–΄μžˆλ‹€λ©΄
775
+ # AI 호좜 자체λ₯Ό μƒλž΅ν•˜κ³  μ¦‰μ‹œ μ™„λ²½ν•œ 3쀄 κ³΅λž€μ„ λ¦¬ν„΄ν•˜μ—¬ λΉ„μš©κ³Ό 였λ₯˜λ₯Ό μ›μ²œ μ°¨λ‹¨ν•©λ‹ˆλ‹€.
776
+ if not items_data and not manual_note.strip():
777
  return "Date: \nAction: \nPurpose: \n"
778
 
779
  try:
 
858
 
859
  return res_text
860
 
861
+ except Exception as e:
862
+ return f"⚠️ Vertex AI Error: {str(e)}"
863
+
864
+ def estimate_text_tokens(text):
865
+ return max(1, int(len(str(text or "")) / 4) + 1)
866
+
867
+ def estimate_ai_summary_cost(full_df, rules_df, manual_note=""):
868
+ if full_df is None or full_df.empty:
869
+ return {"calls": 0, "input_tokens": 0, "output_tokens": 0, "usd": 0.0}
870
+
871
+ current_mode = st.session_state.get('active_mode', 'NOTION')
872
+ if current_mode == "NOTION" and 'Type' in full_df.columns:
873
+ df = full_df[full_df['Type'] == "NOTION"].copy()
874
+ elif 'Type' in full_df.columns:
875
+ df = full_df[full_df['Type'] != "NOTION"].copy()
876
+ else:
877
+ df = full_df.copy()
878
+
879
+ calls = 0
880
+ input_tokens = 0
881
+ output_tokens = 0
882
+ manual_has_text = bool(str(manual_note or "").strip())
883
+
884
+ for _, row in rules_df.iterrows():
885
+ s1 = str(row.get('Sub 1', '')).strip()
886
+ s2 = str(row.get('Sub 2', '')).strip()
887
+ targets = pd.DataFrame()
888
+ if not df.empty and 'Sec' in df.columns:
889
+ stage_values = [s1]
890
+ if s2 and s2 not in ["nan", "x", "None", "-"]:
891
+ stage_values.append(s2)
892
+ targets = df[df['Sec'].isin(stage_values)]
893
+
894
+ if targets.empty and not manual_has_text:
895
+ continue
896
+
897
+ calls += 1
898
+ row_text = " ".join([str(row.get(k, "")) for k in ["Stage Title", "Sub 1", "Sub 2", "Action", "Purpose"]])
899
+ source_text = ""
900
+ for item in targets.to_dict('records') if not targets.empty else []:
901
+ source_text += " ".join([
902
+ str(item.get("Date", "")),
903
+ str(item.get("Subject", "")),
904
+ str(item.get("From", "")),
905
+ str(item.get("To", "")),
906
+ str(item.get("Cc", "")),
907
+ str(item.get("Channel", "")),
908
+ str(item.get("Body", ""))[:1500],
909
+ str(item.get("Comments", "")),
910
+ ]) + "\n"
911
+ prompt_overhead = 1200
912
+ input_tokens += prompt_overhead + estimate_text_tokens(row_text) + estimate_text_tokens(source_text) + estimate_text_tokens(manual_note)
913
+ output_tokens += ESTIMATED_OUTPUT_TOKENS_PER_STAGE
914
+
915
+ usd = (
916
+ input_tokens / 1_000_000 * GEMINI_25_FLASH_INPUT_USD_PER_1M
917
+ + output_tokens / 1_000_000 * GEMINI_25_FLASH_OUTPUT_USD_PER_1M
918
+ )
919
+ return {"calls": calls, "input_tokens": input_tokens, "output_tokens": output_tokens, "usd": usd}
920
+
921
+
922
+
923
+ # ─────────────────────────────────────────────────────────────────
924
  # 🟒 [μ‹€μ‹œκ°„ 동기화 λ§ˆλ²•μ˜ μ—”μ§„] μ‚¬μ΄λ“œλ°”λ₯Ό 그리기 전에 λ“œλ‘­λ‹€μš΄ 값을 λ¨Όμ € κ°€λ‘œμ±„μ„œ 계산!
925
  # ─────────────────────────────────────────────────────────────────
926
  def sync_and_update_status():
 
1033
  is_email = (current_mode == "EMAIL")
1034
 
1035
  # 🟒 2. 이메일 탭일 경우 μ²΄ν¬λ°•μŠ€λ₯Ό κ°•μ œλ‘œ 끄고(False), 클릭 λͺ» ν•˜κ²Œ μž κΈ‰λ‹ˆλ‹€(disabled).
1036
+ use_ai = st.checkbox(
1037
+ "πŸ€– AI둜 μžλ™ μž‘μ„±ν•˜κΈ° (API λΉ„μš© λ°œμƒ)",
1038
+ value=False,
1039
+ disabled=is_email,
1040
+ help="🚫 이메일 λͺ¨λ“œμ—μ„œλŠ” AI μš”μ•½μ΄ μ œν•œλ˜λ©° 빈 ν…œν”Œλ¦Ώλ§Œ μƒμ„±λ©λ‹ˆλ‹€." if is_email else "체크λ₯Ό 끄면 λΉ„μš© 없이 λ‚ μ§œλ§Œ μΆ”μΆœλœ 빈 ν…œν”Œλ¦Ώμ΄ μ¦‰μ‹œ μƒμ„±λ©λ‹ˆλ‹€."
1041
+ )
1042
+
1043
+ if use_ai:
1044
+ cost_estimate = estimate_ai_summary_cost(
1045
+ st.session_state.get('results'),
1046
+ st.session_state['rules_df'],
1047
+ ai_manual_note
1048
+ )
1049
+ approx_nzd = cost_estimate["usd"] * 1.7
1050
+ st.info(
1051
+ f"Estimated Gemini cost before run: "
1052
+ f"{cost_estimate['calls']} call(s), "
1053
+ f"~{cost_estimate['input_tokens']:,} input tokens, "
1054
+ f"~{cost_estimate['output_tokens']:,} output tokens, "
1055
+ f"about USD ${cost_estimate['usd']:.4f} / NZD ${approx_nzd:.4f}."
1056
+ )
1057
+ else:
1058
+ st.caption("AI cost estimate: USD $0.0000 because AI auto-write is off.")
1059
+
1060
+ col_ai, col_dl = st.columns(2)
1061
 
1062
  with col_ai:
1063