Spaces:
Running
Running
File size: 10,713 Bytes
b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 bb2bd12 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c bb2bd12 74ebe5c bb2bd12 74ebe5c bb2bd12 74ebe5c bb2bd12 74ebe5c b931367 74ebe5c b931367 74ebe5c b931367 74ebe5c |
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 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
import gradio as gr
import time
from smart_writer_kit.agent_for_streaming_completion import fetch_flow_suggestion_agent, accept_flow_suggestion_agent
from smart_writer_kit.agent_for_inspiration_expansion import fetch_inspiration_agent, apply_inspiration_agent
from smart_writer_kit.agent_for_outline_update import update_outline_status_agent
from smart_writer_kit.agent_for_kb_update import suggest_new_kb_terms_agent
# --- Mock Data (for UI population only) ---
MOCK_STYLE = """风格:赛博朋克 / 黑色电影
视角:第三人称限制视角(主角:凯)
基调:阴郁、压抑、霓虹闪烁的高科技低生活
核心规则:
1. 强调感官描写,特别是光影和声音。
2. 避免过多的心理独白,通过行动展现心理。
"""
MOCK_KNOWLEDGE_BASE = [
["凯 (Kai)", "主角,前黑客,现在是义体医生。左臂是老式的军用义体。"],
["夜之城 (Night City)", "故事发生的舞台,一座永夜的巨型都市,被企业掌控。"],
["荒坂塔 (Arasaka Tower)", "市中心的最高建筑,象征着绝对的权力。"],
["赛博精神病 (Cyberpsychosis)", "过度改装义体导致的解离性精神障碍。"],
["网络监察 (NetWatch)", "负责维护网络安全的组织,被黑客们视为走狗。"]
]
MOCK_SHORT_TERM_OUTLINE = [
[True, "凯接到一个神秘电话,对方声称知道他失踪妹妹的下落。"],
[False, "凯前往'来生'酒吧与接头人见面。"],
[False, "在酒吧遇到旧识,引发一场关于过去的争执。"],
[False, "接头人出现,但似乎被跟踪了。"]
]
MOCK_LONG_TERM_OUTLINE = [
[False, "揭露夜之城背后的惊天阴谋。"],
[False, "凯找回妹妹,或者接受她已经改变的事实。"],
[False, "与荒坂公司的最终决战。"]
]
# --- UI Helper Functions ---
def get_stats(text):
"""Calculate word count and read time."""
if not text:
return "0 Words | 0 mins"
words = len(text.split())
read_time = max(1, words // 200) # Average reading speed
return f"{words} Words | ~{read_time} mins"
def dismiss_inspiration():
return gr.update(visible=False)
# --- UI Construction ---
def create_smart_writer_tab():
debounce_state = gr.State({"last_change": 0, "active": False, "style": "", "kb": [], "short_outline": [], "long_outline": []})
debounce_timer = gr.Timer(0.5, active=False)
with gr.Row(equal_height=False, elem_id="indicator-writing-tab"):
# --- Left Column: Entity Console ---
with gr.Column(scale=0, min_width=384) as left_panel:
gr.Markdown("### 🧠 核心实体控制台")
with gr.Accordion("整体章程 (Style)", open=True):
style_input = gr.Textbox(
label="整体章程",
lines=8,
value=MOCK_STYLE,
interactive=True
)
with gr.Accordion("知识库 (Knowledge Base)", open=True):
kb_input = gr.Dataframe(
headers=["Term", "Description"],
datatype=["str", "str"],
value=MOCK_KNOWLEDGE_BASE,
interactive=True,
label="知识库",
wrap=True
)
with gr.Row():
btn_suggest_kb = gr.Button("🔍 提取新词条", size="sm")
md_suggested_terms_header = gr.Markdown("#### 推荐词条", visible=False) # Placeholder for suggested terms
suggested_kb_dataframe = gr.Dataframe(
headers=["Term", "Description"],
datatype=["str", "str"],
visible=False, # Initially hidden
interactive=False,
label="推荐词条"
)
with gr.Accordion("当前章节大纲 (Short-Term)", open=True):
short_outline_input = gr.Dataframe(
headers=["Done", "Task"],
datatype=["bool", "str"],
value=MOCK_SHORT_TERM_OUTLINE,
interactive=True,
label="当前章节大纲",
col_count=(2, "fixed"),
)
with gr.Row():
btn_sync_outline = gr.Button("🔄 同步状态", size="sm")
with gr.Accordion("故事总纲 (Long-Term)", open=False):
long_outline_input = gr.Dataframe(
headers=["Done", "Task"],
datatype=["bool", "str"],
value=MOCK_LONG_TERM_OUTLINE,
interactive=True,
label="故事总纲",
col_count=(2, "fixed"),
)
# --- Right Column: Writing Canvas ---
with gr.Column(scale=1) as right_panel:
# Toolbar
with gr.Row(elem_classes=["toolbar"]):
stats_display = gr.Markdown("0 Words | 0 mins")
inspiration_btn = gr.Button("✨ 继续整段 (Cmd/Ctrl+Enter)", size="sm", variant="primary", elem_id="btn-action-create-paragraph")
# 主要编辑器区域
editor = gr.Textbox(
label="沉浸写作画布",
placeholder="开始你的创作...",
lines=30,
elem_classes=["writing-editor"],
elem_id="writing-editor",
show_label=False,
)
# Flow Suggestion
with gr.Row(variant="panel"):
flow_suggestion_display = gr.Textbox(
label="AI 实时续写建议 (按 Tab 采纳)",
value="(等待输入...)",
interactive=False,
scale=4,
elem_classes=["flow-suggestion-box"]
)
accept_flow_btn = gr.Button("采纳(Tab)", scale=1, elem_id='btn-action-accept-flow')
refresh_flow_btn = gr.Button("换一个(Shift+Tab)", scale=1, elem_id='btn-action-change-flow')
# Debounce Progress
debounce_progress = gr.HTML(value="", visible=False)
# Inspiration Modal
with gr.Group(visible=False) as inspiration_modal:
gr.Markdown("### 💡 灵感选项 (由 Ling 模型生成)")
inspiration_prompt_input = gr.Textbox(
label="设定脉络 (可选)",
placeholder="例如:写一段激烈的打斗 / 描写赛博朋克夜景...",
lines=1
)
refresh_inspiration_btn = gr.Button("生成选项(Shift+Enter)")
with gr.Row():
opt1_btn = gr.Button("...", elem_classes=["inspiration-card"])
opt2_btn = gr.Button("...", elem_classes=["inspiration-card"])
opt3_btn = gr.Button("...", elem_classes=["inspiration-card"])
cancel_insp_btn = gr.Button("取消")
# --- Interactions ---
# 1. Stats
editor.change(fn=get_stats, inputs=editor, outputs=stats_display)
# 2. Inspiration Workflow
# Open Modal (triggered by visible button or hidden trigger button for Cmd+Enter)
open_inspiration_modal_fn = lambda: (gr.update(visible=True), "")
inspiration_btn.click(fn=open_inspiration_modal_fn, outputs=[inspiration_modal, inspiration_prompt_input])
# Generate Options based on Prompt
refresh_inspiration_btn.click(
fn=fetch_inspiration_agent,
inputs=[inspiration_prompt_input, editor, style_input, kb_input, short_outline_input, long_outline_input],
outputs=[inspiration_modal, opt1_btn, opt2_btn, opt3_btn]
)
# Apply Option
for btn in [opt1_btn, opt2_btn, opt3_btn]:
btn.click(
fn=apply_inspiration_agent,
inputs=[editor, btn],
outputs=[editor, inspiration_modal, inspiration_prompt_input]
)
cancel_insp_btn.click(fn=dismiss_inspiration, outputs=inspiration_modal, show_progress="hidden")
# 3. Flow Suggestion with Debounce
def start_debounce(editor_content, style, kb, short_outline, long_outline):
return {"last_change": time.time(), "active": True, "style": style, "kb": kb, "short_outline": short_outline, "long_outline": long_outline}, gr.update(active=True), gr.update(visible=True, value="<progress value='0' max='100'></progress> 补全中... 3.0s")
def update_debounce(debounce_state, editor_content):
if not debounce_state["active"]:
return gr.update(), gr.update(), debounce_state, gr.update()
elapsed = time.time() - debounce_state["last_change"]
if elapsed >= 3:
suggestion = fetch_flow_suggestion_agent(editor_content, debounce_state["style"], debounce_state["kb"], debounce_state["short_outline"], debounce_state["long_outline"])
return gr.update(visible=False), suggestion, {"last_change": 0, "active": False, "style": "", "kb": [], "short_outline": [], "long_outline": []}, gr.update(active=False)
else:
progress = int((elapsed / 3) * 100)
remaining = 3 - elapsed
progress_html = f"<progress value='{progress}' max='100'></progress> 补全中... {remaining:.1f}s"
return gr.update(value=progress_html), gr.update(), debounce_state, gr.update()
editor.change(fn=start_debounce, inputs=[editor, style_input, kb_input, short_outline_input, long_outline_input], outputs=[debounce_state, debounce_timer, debounce_progress])
debounce_timer.tick(fn=update_debounce, inputs=[debounce_state, editor], outputs=[debounce_progress, flow_suggestion_display, debounce_state, debounce_timer])
refresh_flow_btn.click(fn=fetch_flow_suggestion_agent, inputs=[editor, style_input, kb_input, short_outline_input, long_outline_input], outputs=flow_suggestion_display)
# Accept Flow (Triggered by visible Button or hidden Tab Key trigger)
accept_flow_fn_inputs = [editor, flow_suggestion_display]
accept_flow_fn_outputs = [editor]
accept_flow_btn.click(fn=accept_flow_suggestion_agent, inputs=accept_flow_fn_inputs, outputs=accept_flow_fn_outputs, show_progress="hidden")
# 4. Agent-based Context Updates
btn_sync_outline.click(
fn=update_outline_status_agent,
inputs=[short_outline_input, editor],
outputs=[short_outline_input]
)
btn_suggest_kb.click(
fn=suggest_new_kb_terms_agent,
inputs=[kb_input, editor],
outputs=[suggested_kb_dataframe, md_suggested_terms_header]
)
|