Spaces:
Sleeping
Sleeping
Upload Advisernote.py
Browse files- 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
|