Corin1998 commited on
Commit
538a15d
·
verified ·
1 Parent(s): 2d1decc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -58
app.py CHANGED
@@ -1,16 +1,26 @@
1
  import os, json, traceback
2
  from typing import List
3
- from fastapi import FastAPI, Request, HTTPException
4
  from fastapi.middleware.cors import CORSMiddleware
5
- from fastapi.responses import RedirectResponse, PlainTextResponse
6
  import gradio as gr
7
  from dotenv import load_dotenv
8
 
9
  load_dotenv()
10
 
11
- # ========== 軽量な関数だけ最初に定義 ==========
 
 
 
 
 
 
 
 
 
 
 
12
  def _lazy_imports_for_main():
13
- """重い依存はここで遅延 import"""
14
  from modules.utils import ensure_dirs
15
  ensure_dirs()
16
  from modules.rag_indexer import index_files_and_urls
@@ -18,14 +28,13 @@ def _lazy_imports_for_main():
18
  from modules.emailer import build_tracking_url
19
  return index_files_and_urls, run_full_workflow, build_tracking_url
20
 
21
- # Gradio ハンドラ(ボタン押下時にのみ重い import)
22
- def ui_company_score_and_proposal(company_name: str,
23
- company_website: str,
24
- lead_email: str,
25
- urls_text: str,
26
- files: List[gr.File] | None,
27
- custom_objective: str,
28
- temperature: float = 0.4):
29
  try:
30
  index_files_and_urls, run_full_workflow, build_tracking_url = _lazy_imports_for_main()
31
  except Exception:
@@ -34,13 +43,11 @@ def ui_company_score_and_proposal(company_name: str,
34
  urls = [u.strip() for u in (urls_text or "").splitlines() if u.strip()]
35
  file_paths = [f.name for f in (files or [])]
36
 
37
- # 1) インデックス更新
38
  try:
39
- index_report = index_files_and_urls(file_paths=file_paths, urls=urls)
40
  except Exception:
41
- index_report = "Index error:\n```\n" + traceback.format_exc() + "\n```"
42
 
43
- # 2) ワークフロー
44
  try:
45
  result = run_full_workflow(
46
  company_name=company_name,
@@ -52,52 +59,40 @@ def ui_company_score_and_proposal(company_name: str,
52
  except Exception:
53
  return "### ❌ ワークフロー実行エラー\n```\n" + traceback.format_exc() + "\n```"
54
 
55
- # 3) 表示
56
- outputs = []
57
- outputs.append("### ✅ 企業スコア\n" + json.dumps(result.get("score", {}), ensure_ascii=False, indent=2))
58
  ctxs = result.get("top_contexts", [])
59
- outputs.append("### 🧠 抽出コンテキスト(上位)\n" + "\n\n".join([f"- {c[:300]}..." for c in ctxs]))
60
- outputs.append("### ✍️ 提案ドラフト\n" + result.get("proposal_markdown", ""))
61
 
62
  ex = result.get("exports", {})
63
- outputs.append("### 📎 エクスポート\n" + "\n".join([
64
  f"- DOCX: {ex.get('docx_path','')}",
65
  f"- PPTX: {ex.get('pptx_path','')}"
66
  ]))
67
 
68
  if lead_email:
69
  email = result.get("email", {})
70
- outputs.append("### ✉️ メール準備\n" + f"To: {lead_email}\nSubject: {email.get('subject','')}\n\n{email.get('body','')}")
71
  try:
72
- outputs.append(f"(本文内の計測リンク例)\n{build_tracking_url('preview-only', {'company':company_name})}")
 
73
  except Exception:
74
  pass
75
 
76
- outputs.append("### 🤖 次アクション提案\n" + result.get("next_actions", ""))
77
- outputs.append("### 🧩 インデックス更新ログ\n" + index_report)
78
- return "\n\n".join(outputs)
79
-
80
- # クリック計測(/t/{token}���
81
- def _verify_and_log_click(token: str, request: Request):
82
- from modules.utils import verify_tracking_token, log_event
83
- payload = verify_tracking_token(token)
84
- if payload is None:
85
- raise HTTPException(status_code=400, detail="invalid token")
86
- ip = request.client.host if request.client else "unknown"
87
- ua = request.headers.get("User-Agent", "") if request.headers else ""
88
- log_event("click", payload, {"ip": ip, "ua": ua})
89
- redirect_to = payload.get("redirect") or os.getenv("PUBLIC_BASE_URL", "/")
90
- return redirect_to
91
-
92
- # ========== Gradio UI ==========
93
  with gr.Blocks(title="営業自動化 Agent Studio") as demo:
94
- gr.Markdown("# 営業自動化 Agent Studio (Docker)")
95
  with gr.Row():
96
  with gr.Column():
97
  company_name = gr.Textbox(label="企業名")
98
  company_website = gr.Textbox(label="企業サイトURL")
99
  lead_email = gr.Textbox(label="送信先メール(任意)")
100
- custom_objective = gr.Textbox(label="提案の目的/狙い(任意)", placeholder="例)SaaS導入の無料PoC打診")
101
  urls_text = gr.Textbox(label="RAG用URL(複数は改行)", lines=4)
102
  files = gr.File(label="RAG用ファイル(複数可)", file_count="multiple")
103
  temperature = gr.Slider(0.0, 1.0, value=0.4, step=0.05, label="生成温度")
@@ -105,34 +100,51 @@ with gr.Blocks(title="営業自動化 Agent Studio") as demo:
105
  with gr.Column():
106
  out = gr.Markdown()
107
 
108
- # 疎通確認(UIが出ればOK。最初にここだけ試すのも可)
109
  ping_btn = gr.Button("Ping")
110
- ping_out = gr.Markdown()
111
- ping_btn.click(lambda: "pong 🎯", None, ping_out)
112
 
113
  run_btn.click(
114
- fn=ui_company_score_and_proposal,
115
  inputs=[company_name, company_website, lead_email, urls_text, files, custom_objective, temperature],
116
  outputs=[out]
117
  )
118
 
119
- # ========== FastAPI(Gradioをマウント & トラッキング) ==========
120
- app = FastAPI(title="Agent Studio - Docker SDK")
121
  app.add_middleware(
122
  CORSMiddleware,
123
  allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],
124
  )
125
 
126
- # ヘルスチェック
127
- @app.get("/health", response_class=PlainTextResponse)
 
 
128
  def health():
129
- return "ok"
130
 
131
- # クリック計測
 
 
 
 
 
132
  @app.get("/t/{token}")
133
  def track_click(token: str, request: Request):
134
- redirect_to = _verify_and_log_click(token, request)
135
- return RedirectResponse(url=redirect_to)
136
-
137
- # ルート(/)に Gradio を表示
138
- app = gr.mount_gradio_app(app, demo, path="/")
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os, json, traceback
2
  from typing import List
3
+ from fastapi import FastAPI, Request
4
  from fastapi.middleware.cors import CORSMiddleware
5
+ from fastapi.responses import JSONResponse, PlainTextResponse, RedirectResponse
6
  import gradio as gr
7
  from dotenv import load_dotenv
8
 
9
  load_dotenv()
10
 
11
+ # ========= 例外を必ず可視化するハンドラ =========
12
+ async def _exception_printer(request: Request, exc: Exception):
13
+ tb = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
14
+ print("=== Unhandled Exception ===\n", tb, flush=True)
15
+ # 画面にも出す(暫定)
16
+ return PlainTextResponse(f"Internal error\n\n{tb}", status_code=500)
17
+
18
+ # ========= 疎通テスト用の最小関数 =========
19
+ def ping():
20
+ return "pong 🎯"
21
+
22
+ # ========= 重い依存はボタン押下まで遅延 =========
23
  def _lazy_imports_for_main():
 
24
  from modules.utils import ensure_dirs
25
  ensure_dirs()
26
  from modules.rag_indexer import index_files_and_urls
 
28
  from modules.emailer import build_tracking_url
29
  return index_files_and_urls, run_full_workflow, build_tracking_url
30
 
31
+ def ui_main(company_name: str,
32
+ company_website: str,
33
+ lead_email: str,
34
+ urls_text: str,
35
+ files: List[gr.File] | None,
36
+ custom_objective: str,
37
+ temperature: float = 0.4):
 
38
  try:
39
  index_files_and_urls, run_full_workflow, build_tracking_url = _lazy_imports_for_main()
40
  except Exception:
 
43
  urls = [u.strip() for u in (urls_text or "").splitlines() if u.strip()]
44
  file_paths = [f.name for f in (files or [])]
45
 
 
46
  try:
47
+ idx = index_files_and_urls(file_paths=file_paths, urls=urls)
48
  except Exception:
49
+ idx = "Index error:\n```\n" + traceback.format_exc() + "\n```"
50
 
 
51
  try:
52
  result = run_full_workflow(
53
  company_name=company_name,
 
59
  except Exception:
60
  return "### ❌ ワークフロー実行エラー\n```\n" + traceback.format_exc() + "\n```"
61
 
62
+ out = []
63
+ out.append("### ✅ 企業スコア\n" + json.dumps(result.get("score", {}), ensure_ascii=False, indent=2))
 
64
  ctxs = result.get("top_contexts", [])
65
+ out.append("### 🧠 抽出コンテキスト(上位)\n" + "\n\n".join([f"- {c[:300]}..." for c in ctxs]))
66
+ out.append("### ✍️ 提案ドラフト\n" + result.get("proposal_markdown", ""))
67
 
68
  ex = result.get("exports", {})
69
+ out.append("### 📎 エクスポート\n" + "\n".join([
70
  f"- DOCX: {ex.get('docx_path','')}",
71
  f"- PPTX: {ex.get('pptx_path','')}"
72
  ]))
73
 
74
  if lead_email:
75
  email = result.get("email", {})
76
+ out.append("### ✉️ メール準備\n" + f"To: {lead_email}\nSubject: {email.get('subject','')}\n\n{email.get('body','')}")
77
  try:
78
+ from modules.emailer import build_tracking_url
79
+ out.append(f"(本文内の計測リンク例)\n{build_tracking_url('preview-only', {'company':company_name})}")
80
  except Exception:
81
  pass
82
 
83
+ out.append("### 🤖 次アクション提案\n" + result.get("next_actions", ""))
84
+ out.append("### 🧩 インデックス更新ログ\n" + idx)
85
+ return "\n\n".join(out)
86
+
87
+ # ========= Gradio(/ui にマウント) =========
 
 
 
 
 
 
 
 
 
 
 
 
88
  with gr.Blocks(title="営業自動化 Agent Studio") as demo:
89
+ gr.Markdown("# 営業自動化 Agent Studio")
90
  with gr.Row():
91
  with gr.Column():
92
  company_name = gr.Textbox(label="企業名")
93
  company_website = gr.Textbox(label="企業サイトURL")
94
  lead_email = gr.Textbox(label="送信先メール(任意)")
95
+ custom_objective = gr.Textbox(label="提案の目的/狙い(任意)")
96
  urls_text = gr.Textbox(label="RAG用URL(複数は改行)", lines=4)
97
  files = gr.File(label="RAG用ファイル(複数可)", file_count="multiple")
98
  temperature = gr.Slider(0.0, 1.0, value=0.4, step=0.05, label="生成温度")
 
100
  with gr.Column():
101
  out = gr.Markdown()
102
 
 
103
  ping_btn = gr.Button("Ping")
104
+ ping_out = gr.Markdown("Click Ping")
105
+ ping_btn.click(fn=ping, inputs=None, outputs=ping_out)
106
 
107
  run_btn.click(
108
+ fn=ui_main,
109
  inputs=[company_name, company_website, lead_email, urls_text, files, custom_objective, temperature],
110
  outputs=[out]
111
  )
112
 
113
+ # ========= FastAPI(/ /health は軽量、/ui はGradio) =========
114
+ app = FastAPI(title="Agent Studio - Docker (safe)")
115
  app.add_middleware(
116
  CORSMiddleware,
117
  allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"],
118
  )
119
 
120
+ # 全例外をキャッチして本文にトレースを返す(暫定)
121
+ app.add_exception_handler(Exception, _exception_printer)
122
+
123
+ @app.get("/health")
124
  def health():
125
+ return {"ok": True}
126
 
127
+ @app.get("/")
128
+ def root():
129
+ # まずは JSON を返し、UI は /ui に配置
130
+ return {"status": "running", "ui": "/ui", "health": "/health"}
131
+
132
+ # クリック計測(例外も可視化)
133
  @app.get("/t/{token}")
134
  def track_click(token: str, request: Request):
135
+ try:
136
+ from modules.utils import verify_tracking_token, log_event
137
+ payload = verify_tracking_token(token)
138
+ if not payload:
139
+ return JSONResponse({"error": "invalid token"}, status_code=400)
140
+ ip = request.client.host if request.client else "unknown"
141
+ ua = request.headers.get("User-Agent", "") if request.headers else ""
142
+ log_event("click", payload, {"ip": ip, "ua": ua})
143
+ redirect_to = payload.get("redirect") or os.getenv("PUBLIC_BASE_URL", "/ui")
144
+ return RedirectResponse(url=redirect_to)
145
+ except Exception as e:
146
+ tb = "".join(traceback.format_exception(type(e), e, e.__traceback__))
147
+ return PlainTextResponse(f"tracking error\n\n{tb}", status_code=500)
148
+
149
+ # /ui に Gradio をマウント
150
+ app = gr.mount_gradio_app(app, demo, path="/ui")