Spaces:
Sleeping
Sleeping
File size: 6,733 Bytes
a996e11 cc571ea dc31d2b 9364bd4 dc31d2b a996e11 9364bd4 cc571ea dc31d2b cc571ea dc31d2b e8cc220 cc571ea dc31d2b 9364bd4 dc31d2b a996e11 dc31d2b 9364bd4 dc31d2b a996e11 dc31d2b e8cc220 9364bd4 a996e11 e8cc220 a996e11 9364bd4 cc571ea dc31d2b 9364bd4 dc31d2b 9364bd4 dc31d2b 9364bd4 dc31d2b 5c349c2 9364bd4 cc571ea 727d601 538a15d a996e11 9364bd4 e8cc220 a996e11 dc31d2b a996e11 e8cc220 a996e11 9364bd4 dc31d2b 9364bd4 a996e11 e8cc220 9364bd4 a996e11 dc31d2b 9364bd4 a996e11 9364bd4 a996e11 31203de e8cc220 a996e11 9364bd4 a996e11 e8cc220 a996e11 9364bd4 a996e11 9364bd4 ba67af2 9364bd4 ba67af2 a996e11 ba67af2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
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")))
|