Corin1998's picture
Update app.py
9364bd4 verified
import os
import json
import gradio as gr
from fastapi import FastAPI
from starlette.staticfiles import StaticFiles
# ---- 遅延インポート ----
def _lazy_imports_for_main():
from modules.utils import ensure_dirs # noqa: F401
from modules.rag_indexer import index_files_and_urls
from modules.workflow import run_full_workflow
ensure_dirs()
return index_files_and_urls, run_full_workflow
# ---- ユーティリティ ----
EXPORT_DIR = "/tmp/agent_studio/exports"
os.makedirs(EXPORT_DIR, exist_ok=True) # 静的配信用に事前作成
def _link_or_note(path_str):
"""
生成されたファイルパスから /files の相対URLを返す。
存在しなければ理由を文字列で返す。
"""
if not path_str:
return "(まだ生成されていません)"
fname = os.path.basename(path_str)
full = os.path.join(EXPORT_DIR, fname)
if os.path.exists(full):
# Hugging Face Space 上では相対リンクでOK
return f"/files/{fname}"
# modules 側が別ディレクトリで作った場合もあるので存在チェック
if os.path.exists(path_str):
# もし別パスにあるなら公開用にコピーしてリンク化
try:
import shutil
dst = os.path.join(EXPORT_DIR, os.path.basename(path_str))
if path_str != dst:
shutil.copy2(path_str, dst)
return f"/files/{os.path.basename(path_str)}"
except Exception as e:
return f"(生成済みだが公開に失敗: {e})"
return "(パスが無効です)"
# ---- UI コールバック ----
def ui_company_score_and_proposal(
company_name,
company_website,
lead_email,
objective,
urls_text,
files, # gr.File(multiple=True) は使わないが、値は受ける
):
"""
戻り値順:
score_json(str), contexts_text(str), proposal_text(str),
docx_link(str), pptx_link(str), next_actions(str), index_report(str)
"""
index_files_and_urls, run_full_workflow = _lazy_imports_for_main()
# URL & ファイルパス抽出(SpaceのUI都合で来ることがあるため防御的に扱う)
urls = [u.strip() for u in (urls_text or "").splitlines() if u.strip()]
file_paths = []
if files:
# ここには通常来ないが、互換のため残す(path or tempfileオブジェクト)
if isinstance(files, list):
file_paths = [getattr(f, "name", None) or str(f) for f in files if f]
else:
file_paths = [getattr(files, "name", None) or str(files)]
file_paths = [p for p in file_paths if p]
# インデックス更新(失敗しても継続)
try:
index_report = index_files_and_urls(file_paths=file_paths, urls=urls)
except Exception as e:
index_report = f"Index error:\n\n{e}"
# メイン処理(提案ドラフトなど)
result = run_full_workflow(
company_name=company_name or "(未入力)",
company_website=company_website or "",
lead_email=lead_email or "",
objective=objective or "",
temperature=0.3,
)
score_json = json.dumps(result.get("score", {}), ensure_ascii=False, indent=2)
contexts_text = "\n\n---\n\n".join(result.get("top_contexts") or [])
proposal_text = result.get("proposal_markdown", "")
next_actions = result.get("next_actions", "")
# 生成物を /files で配信できる相対リンクに変換(テキストで返す)
docx_path = result.get("exports", {}).get("docx_path", "")
pptx_path = result.get("exports", {}).get("pptx_path", "")
docx_link = _link_or_note(docx_path)
pptx_link = _link_or_note(pptx_path)
return (
score_json,
contexts_text,
proposal_text,
docx_link,
pptx_link,
next_actions,
index_report,
)
# ---- Gradio UI(すべて Textbox / Button のみ)----
with gr.Blocks(title="営業自動化 Agent Studio", analytics_enabled=False, theme=gr.themes.Soft()) as demo:
gr.Markdown("## 営業自動化 Agent Studio(Space内完結・リンクでダウンロード)")
with gr.Row():
with gr.Column():
company_name = gr.Textbox(label="企業名", value="トヨタ自動車")
company_website = gr.Textbox(label="企業Webサイト", value="https://toyota.jp/index.html")
lead_email = gr.Textbox(label="リードのメールアドレス(任意)", value="")
objective = gr.Textbox(label="提案目的(任意)", value="商談化のための初回提案")
urls_text = gr.Textbox(
label="クロールするURL(1行1件、任意)",
lines=4,
placeholder="https://example.com\nhttps://example.com/blog/post1",
)
# NOTICE: ファイル型コンポーネントはスキーマ問題回避のため配置しない
dummy_files = gr.Textbox(visible=False) # 互換のためダミー
run_btn = gr.Button("ワークフロー実行", variant="primary")
with gr.Column():
score_json = gr.Textbox(label="✅ 企業スコア(JSON)", lines=10, interactive=False)
contexts_text = gr.Textbox(label="🧠 抽出コンテキスト(上位)", lines=8, interactive=False)
proposal_text = gr.Textbox(label="✍️ 提案ドラフト(Markdownテキスト)", lines=16)
# ここはテキストでリンクを表示(/files/xxx)
docx_link = gr.Textbox(label="📎 DOCX ダウンロードリンク", interactive=False)
pptx_link = gr.Textbox(label="📎 PPTX ダウンロードリンク", interactive=False)
next_actions = gr.Textbox(label="🤖 次アクション提案", lines=4, interactive=False)
index_report = gr.Textbox(label="🧩 インデックス更新ログ", lines=4, interactive=False)
run_btn.click(
fn=ui_company_score_and_proposal,
inputs=[company_name, company_website, lead_email, objective, urls_text, dummy_files],
outputs=[score_json, contexts_text, proposal_text, docx_link, pptx_link, next_actions, index_report],
)
# ---- FastAPI / 静的ファイル公開(/files -> /tmp/agent_studio/exports)----
app = FastAPI()
# 生成物を静的公開。相対リンク /files/xxx でダウンロード可能。
app.mount("/files", StaticFiles(directory=EXPORT_DIR), name="files")
# Gradio をルートにマウント
app = gr.mount_gradio_app(app, demo.queue(), path="/")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "7860")))