Spaces:
Runtime error
Runtime error
| import os | |
| import json | |
| from pathlib import Path | |
| from typing import Optional, List | |
| from fastapi import FastAPI, Request, HTTPException | |
| from fastapi.responses import PlainTextResponse, RedirectResponse | |
| import gradio as gr | |
| from dotenv import load_dotenv | |
| from modules.workflow import run_full_workflow | |
| from modules.rag_indexer import index_files_and_urls | |
| from modules.rag_retriever import retrieve_contexts | |
| from modules.emailer import build_tracking_url | |
| from modules.utils import ensure_dirs, verify_tracking_token, log_event | |
| load_dotenv() | |
| ensure_dirs() | |
| app = FastAPI(title="Agent Studio") | |
| ### ===== Gradio UI ===== | |
| def ui_company_score_and_proposal(company_name: str, | |
| company_website: str, | |
| lead_email: str, | |
| urls_text: str, | |
| files: List[gr.File] | None, | |
| custom_objective: str, | |
| temperature: float = 0.4): | |
| urls = [u.strip() for u in urls_text.splitlines() if u.strip()] if urls_text else [] | |
| file_paths = [f.name for f in (files or [])] | |
| # 1) インデックス更新(アップロードとURL) | |
| index_report = index_files_and_urls(file_paths=file_paths, urls=urls) | |
| # 2) ワークフロー実行(スコア→RAG→提案→次アクション) | |
| result = run_full_workflow( | |
| company_name=company_name, | |
| company_website=company_website, | |
| lead_email=lead_email, | |
| objective=custom_objective, | |
| temperature=temperature | |
| ) | |
| # 3) 出力まとめ | |
| outputs = [] | |
| outputs.append("### ✅ 企業スコア\n" + json.dumps(result["score"], ensure_ascii=False, indent=2)) | |
| outputs.append("### 🧠 抽出コンテキスト(上位)\n" + "\n\n".join([f"- {c[:300]}..." for c in result["top_contexts"]])) | |
| outputs.append("### ✍️ 提案ドラフト\n" + result["proposal_markdown"]) | |
| outputs.append("### 📎 エクスポート\n" + "\n".join([ | |
| f"- DOCX: {result['exports']['docx_path']}", | |
| f"- PPTX: {result['exports']['pptx_path']}" | |
| ])) | |
| if lead_email: | |
| outputs.append("### ✉️ メール準備\n" + f"To: {lead_email}\nSubject: {result['email']['subject']}\n\n{result['email']['body']}") | |
| outputs.append(f"(本文内のトラッキングリンク例)\n{build_tracking_url('preview-only', {'company':company_name})}") | |
| outputs.append("### 🤖 次アクション提案\n" + result["next_actions"]) | |
| outputs.append("### 🧩 インデックス更新ログ\n" + index_report) | |
| return "\n\n".join(outputs) | |
| with gr.Blocks(title="Agent Studio") as demo: | |
| gr.Markdown("# 営業自動化 Agent Studio") | |
| with gr.Row(): | |
| with gr.Column(): | |
| company_name = gr.Textbox(label="企業名", placeholder="例)Acme Corp") | |
| company_website = gr.Textbox(label="企業サイトURL", placeholder="例)https://www.acme.com") | |
| lead_email = gr.Textbox(label="送信先メール(任意)", placeholder="例)lead@acme.com") | |
| custom_objective = gr.Textbox(label="提案の目的/狙い(任意)", placeholder="例)SaaS導入の無料PoC打診") | |
| urls_text = gr.Textbox(label="RAG用URL(複数は改行)", lines=4) | |
| files = gr.File(label="RAG用ファイル(複数可)", file_count="multiple") | |
| temperature = gr.Slider(0.0, 1.0, value=0.4, step=0.05, label="生成温度") | |
| run_btn = gr.Button("ワークフロー実行", variant="primary") | |
| with gr.Column(): | |
| out = gr.Markdown(label="結果") | |
| run_btn.click( | |
| fn=ui_company_score_and_proposal, | |
| inputs=[company_name, company_website, lead_email, urls_text, files, custom_objective, temperature], | |
| outputs=[out] | |
| ) | |
| # HF Spacesは "app" 変数をエクスポートしている必要あり | |
| # FastAPIにGradioをマウント(ルートに表示) | |
| from fastapi.middleware.cors import CORSMiddleware | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"] | |
| ) | |
| app = gr.mount_gradio_app(app, demo, path="/") | |
| ### ===== クリックトラッキング・エンドポイント ===== | |
| def track_click(token: str, request: Request): | |
| # token署名検証 & ペイロード取得 | |
| payload = verify_tracking_token(token) | |
| if payload is None: | |
| raise HTTPException(status_code=400, detail="invalid token") | |
| # ログ | |
| ip = request.client.host if request.client else "unknown" | |
| ua = request.headers.get("User-Agent", "") | |
| log_event(event_type="click", payload=payload, meta={"ip": ip, "ua": ua}) | |
| # 遷移先URL(ペイロードにredirectがあればそこへ、なければUIトップへ) | |
| redirect_to = payload.get("redirect") or os.getenv("PUBLIC_BASE_URL", "/") | |
| return RedirectResponse(url=redirect_to) | |