hyerong commited on
Commit
5eaeb01
Β·
verified Β·
1 Parent(s): a1c1bc3

Upload src/app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. src/app.py +90 -3
src/app.py CHANGED
@@ -1,4 +1,4 @@
1
- import os, json, re, io
2
  import streamlit as st
3
  from openai import OpenAI
4
  from PIL import Image
@@ -89,6 +89,57 @@ def analyze_meeting(text):
89
  raw = re.sub(r"^```json\s*|```\s*$", "", resp.choices[0].message.content.strip())
90
  return json.loads(raw)
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  # ──────────────────────────────────────────────
93
  # 3. UI λ ˆμ΄μ•„μ›ƒ ꡬ성 (헀더 및 μž…λ ₯λΆ€)
94
  # ──────────────────────────────────────────────
@@ -172,7 +223,7 @@ if st.session_state.analysis_result:
172
  if hits:
173
  st.error(f"πŸ”’ 민감정보 감지: {', '.join(hits)} ν•­λͺ© 주의")
174
 
175
- tab1, tab2, tab3, tab4 = st.tabs(["πŸ“‹ 회의 μš”μ•½", "βœ… ν•  일 & μš°μ„ μˆœμœ„", "πŸ“„ 원문 보기", "πŸ€– μ§ˆμ˜μ‘λ‹΅"])
176
 
177
  with tab1:
178
  st.markdown(f"### πŸ“Œ {res.get('meeting_title', '회의 κ²°κ³Ό')}")
@@ -206,9 +257,45 @@ if st.session_state.analysis_result:
206
  st.write(", ".join([f"#{k}" for k in res.get('keywords', [])]))
207
  st.divider()
208
  st.text_area("전사 원문", st.session_state.transcript, height=300)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
  ###################################################### λ©€ν‹°ν„΄ λŒ€ν™” 생성, νžˆμŠ€ν† λ¦¬ μ΅œλŒ€ 10개 μ •λ„λ‘œ
211
- with tab4:
212
  st.markdown("#### πŸ’¬ GPT 4o mini와 λŒ€ν™”ν•˜κΈ° ")
213
  st.info("AIμ—κ²Œ κΆκΈˆν•œ 점을 λ¬Όμ–΄λ³΄μ„Έμš”. 주의: 졜근 10개의 λŒ€ν™” κΈ°μ–΅")
214
  # λŒ€ν™” 기둝 ν‘œμ‹œ
 
1
+ import os, json, re, io, requests
2
  import streamlit as st
3
  from openai import OpenAI
4
  from PIL import Image
 
89
  raw = re.sub(r"^```json\s*|```\s*$", "", resp.choices[0].message.content.strip())
90
  return json.loads(raw)
91
 
92
+
93
+ # ─────────────────────────────────────── Email λ°œμ†‘ ν•¨μˆ˜ μΆ”κ°€ ────────────────────────────────────────────
94
+ # 이메일 μ΄ˆμ•ˆ 생성 ν•¨μˆ˜ μΆ”κ°€!!!!!!!!!!!!
95
+ def generate_email_body(res, recipient_name):
96
+ decisions = "\n".join(f" β€’ {d}" for d in res.get("decisions", []))
97
+ tasks = "\n".join(
98
+ f" - [{t['person']}] {t['task']}" + (f" (κΈ°ν•œ: {t['deadline']})" if t.get("deadline") and t["deadline"] != "null" else "")
99
+ for t in res.get("tasks", [])
100
+ )
101
+ prompt = f"""
102
+ λ‹€μŒ 회의 κ²°κ³Όλ₯Ό λ°”νƒ•μœΌλ‘œ {recipient_name}μ—κ²Œ λ³΄λ‚΄λŠ” 곡식 λ¦¬λ§ˆμΈλ“œ 이메일을 μž‘μ„±ν•˜μ„Έμš”.
103
+ μ •μ€‘ν•˜κ³  전문적인 μ–΄μ‘°λ‘œ μž‘μ„±ν•˜μ„Έμš”.
104
+
105
+ κ·œμΉ™:
106
+ - 이메일 첫 쀄 μΈμ‚¬λŠ” λ°˜λ“œμ‹œ "KT Enterprise ITκΈ°μˆ ν˜μ‹ νŒ€ DX Consulting AI λΉ„μ„œ AIBLEμž…λ‹ˆλ‹€." 둜 μ‹œμž‘ν•˜μ„Έμš”.
107
+ - 이메일 λ§ˆμ§€λ§‰ μ„œλͺ…은 λ°˜λ“œμ‹œ "AIBLE λ“œλ¦Ό" 으둜 λλ‚΄μ„Έμš”.
108
+ - κ·Έ μ™Έ 직원 μ΄λ¦„μ΄λ‚˜ 개인 μ„œλͺ…은 μ ˆλŒ€ ν¬ν•¨ν•˜μ§€ λ§ˆμ„Έμš”.
109
+
110
+ 회의 제λͺ©: {res.get('meeting_title', '회의')}
111
+ κ²°μ • 사항:
112
+ {decisions}
113
+ λ‹΄λ‹Ή 업무:
114
+ {tasks}
115
+
116
+ 이메일 본문만 좜λ ₯ν•˜μ„Έμš” (제λͺ© μ œμ™Έ).
117
+ """
118
+ resp = client.chat.completions.create(
119
+ model="gpt-4o-mini",
120
+ messages=[{"role": "user", "content": prompt}],
121
+ temperature=0.4,
122
+ )
123
+ return resp.choices[0].message.content.strip()
124
+
125
+
126
+ # 이메일 λ°œμ†‘ ν•¨μˆ˜ (SendGrid REST API)
127
+ def send_email(to, subject, body):
128
+ api_key = os.getenv("SENDGRID_API_KEY", "")
129
+ sender = os.getenv("EMAIL_ADDRESS", "")
130
+ resp = requests.post(
131
+ "https://api.sendgrid.com/v3/mail/send",
132
+ headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
133
+ json={
134
+ "personalizations": [{"to": [{"email": to}], "subject": subject}],
135
+ "from": {"email": sender},
136
+ "content": [{"type": "text/plain", "value": body}],
137
+ },
138
+ )
139
+ return resp.status_code == 202
140
+
141
+
142
+
143
  # ──────────────────────────────────────────────
144
  # 3. UI λ ˆμ΄μ•„μ›ƒ ꡬ성 (헀더 및 μž…λ ₯λΆ€)
145
  # ──────────────────────────────────────────────
 
223
  if hits:
224
  st.error(f"πŸ”’ 민감정보 감지: {', '.join(hits)} ν•­λͺ© 주의")
225
 
226
+ tab1, tab2, tab3, tab4, tab5 = st.tabs(["πŸ“‹ 회의 μš”μ•½", "βœ… ν•  일 & μš°μ„ μˆœμœ„", "πŸ“„ 원문 보기", "βœ‰οΈ 메일 λ°œμ†‘", "πŸ€– μ§ˆμ˜μ‘λ‹΅"])
227
 
228
  with tab1:
229
  st.markdown(f"### πŸ“Œ {res.get('meeting_title', '회의 κ²°κ³Ό')}")
 
257
  st.write(", ".join([f"#{k}" for k in res.get('keywords', [])]))
258
  st.divider()
259
  st.text_area("전사 원문", st.session_state.transcript, height=300)
260
+ ###################################################### 이메일 λ°œμ†‘ μΆ”κ°€
261
+ with tab4:
262
+ st.subheader("βœ‰οΈ 회의 κ²°κ³Ό λ¦¬λ§ˆμΈλ“œ 메일")
263
+
264
+ # μˆ˜μ‹ μž 정보λ₯Ό λ¨Όμ € μž…λ ₯
265
+ recipient_name = st.text_input("μˆ˜μ‹ μž 이름", placeholder="예) κΉ€λŒ€λ¦¬")
266
+ recipient_email = st.text_input("μˆ˜μ‹ μž 이메일 μ£Όμ†Œ", placeholder="예) kim@kt.com")
267
+
268
+ if st.button("πŸ“ 메일 μ΄ˆμ•ˆ 생성"):
269
+ if not recipient_name.strip():
270
+ st.warning("μˆ˜μ‹ μž 이름을 μž…λ ₯ν•΄μ£Όμ„Έμš”.")
271
+ else:
272
+ with st.spinner("메일 μ΄ˆμ•ˆμ„ μƒμ„±ν•˜λŠ” 쀑..."):
273
+ st.session_state.mail_body = generate_email_body(res, recipient_name)
274
+
275
+ if st.session_state.mail_body:
276
+ mail_subject = st.text_input(
277
+ "메일 제λͺ©",
278
+ value=f"[회의 결과 곡유] {res.get('meeting_title', '회의')}",
279
+ )
280
+ edited_body = st.text_area(
281
+ "메일 λ³Έλ¬Έ (μˆ˜μ • κ°€λŠ₯)",
282
+ value=st.session_state.mail_body,
283
+ height=300,
284
+ )
285
+
286
+ if st.button("πŸ“€ 메일 λ°œμ†‘"):
287
+ if not recipient_email.strip():
288
+ st.warning("μˆ˜μ‹ μž 이메일 μ£Όμ†Œλ₯Ό μž…λ ₯ν•΄μ£Όμ„Έμš”.")
289
+ else:
290
+ with st.spinner("메일을 λ°œμ†‘ν•˜λŠ” 쀑..."):
291
+ ok = send_email(recipient_email, mail_subject, edited_body)
292
+ if ok:
293
+ st.success(f"βœ… {recipient_email} 으둜 메일을 λ°œμ†‘ν–ˆμŠ΅λ‹ˆλ‹€.")
294
+ else:
295
+ st.error("메일 λ°œμ†‘μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€. SENDGRID_API_KEY와 EMAIL_ADDRESSλ₯Ό ν™•μΈν•΄μ£Όμ„Έμš”.")
296
 
297
  ###################################################### λ©€ν‹°ν„΄ λŒ€ν™” 생성, νžˆμŠ€ν† λ¦¬ μ΅œλŒ€ 10개 μ •λ„λ‘œ
298
+ with tab5:
299
  st.markdown("#### πŸ’¬ GPT 4o mini와 λŒ€ν™”ν•˜κΈ° ")
300
  st.info("AIμ—κ²Œ κΆκΈˆν•œ 점을 λ¬Όμ–΄λ³΄μ„Έμš”. 주의: 졜근 10개의 λŒ€ν™” κΈ°μ–΅")
301
  # λŒ€ν™” 기둝 ν‘œμ‹œ