GitHub Action commited on
Commit
63e4846
·
1 Parent(s): bb2bd12

Sync ling-space changes from GitHub commit c4aec86

Browse files
smart_writer_kit/agent_for_inspiration_expansion.py CHANGED
@@ -83,6 +83,49 @@ def fetch_inspiration_agent(prompt: str, editor_content: str, style: str, kb_df:
83
  return gr.update(visible=True), error_message, "请检查日志", "请检查日志"
84
 
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  def apply_inspiration_agent(current_text: str, inspiration_text: str):
87
  """
88
  Agent for applying selected inspiration to the editor.
 
83
  return gr.update(visible=True), error_message, "请检查日志", "请检查日志"
84
 
85
 
86
+ def fetch_paragraph_continuation_agent(prompt: str, editor_content: str, style: str, kb_df: pd.DataFrame, short_outline_df: pd.DataFrame, long_outline_df: pd.DataFrame):
87
+ """
88
+ Agent for fetching a single paragraph continuation (Ribbon UI version).
89
+ """
90
+ print("\n[Agent][fetch_paragraph_continuation_agent] === 推理类型:整段续写 (Single) ===")
91
+ try:
92
+ # 1. Format context
93
+ style_context = f"### 整体章程\n{style}\n\n"
94
+ kb_context = _format_df_to_string(kb_df, "知识库")
95
+ short_outline_context = _format_df_to_string(short_outline_df, "当前章节大纲")
96
+ long_outline_context = _format_df_to_string(long_outline_df, "故事总纲")
97
+
98
+ # 2. Build System Prompt
99
+ system_prompt = (
100
+ "你是一个富有创意的长篇小说家。请根据提供的背景设定和当前文本,自然地续写一段高质量的剧情。\n"
101
+ "请直接输出续写内容,不要包含任何解释、前缀或后缀。"
102
+ )
103
+
104
+ # 3. Build User Prompt
105
+ full_context = style_context + kb_context + long_outline_context + short_outline_context
106
+ user_prompt = (
107
+ f"### 背景设定与大纲\n{full_context}\n"
108
+ f"### 当前已写内容 (末尾部分)\n{editor_content[-2000:]}\n\n"
109
+ f"### 用户指令\n{prompt if prompt else '请基于当前内容,自然地延续剧情,写一个完整的段落。'}"
110
+ )
111
+
112
+ # 4. Call LLM
113
+ model_handler = ModelHandler()
114
+ response_generator = model_handler.generate_code(
115
+ system_prompt=system_prompt,
116
+ user_prompt=user_prompt,
117
+ model_choice=LING_1T
118
+ )
119
+
120
+ full_response = "".join(chunk for chunk in response_generator)
121
+ return full_response.strip()
122
+
123
+ except Exception as e:
124
+ print(f"[Agent] Error fetching paragraph continuation: {e}")
125
+ return f"获取续写时出错: {e}"
126
+
127
+
128
+
129
  def apply_inspiration_agent(current_text: str, inspiration_text: str):
130
  """
131
  Agent for applying selected inspiration to the editor.
tab_smart_writer.py CHANGED
@@ -1,39 +1,34 @@
1
  import gradio as gr
2
  import time
3
  from smart_writer_kit.agent_for_streaming_completion import fetch_flow_suggestion_agent, accept_flow_suggestion_agent
4
- from smart_writer_kit.agent_for_inspiration_expansion import fetch_inspiration_agent, apply_inspiration_agent
5
  from smart_writer_kit.agent_for_outline_update import update_outline_status_agent
6
  from smart_writer_kit.agent_for_kb_update import suggest_new_kb_terms_agent
7
 
8
  # --- Mock Data (for UI population only) ---
9
 
10
- MOCK_STYLE = """风格:赛博朋克 / 黑色电影
11
- 视角:第三人称限制视角(主角:凯)
12
- 基调:阴郁、压抑、霓虹闪烁的高科技低生活
13
- 核心规则:
14
- 1. 强调感官描写,特别是光影和声音。
15
- 2. 避免过多的心理独白,通过行动展现心理。
16
  """
17
 
18
  MOCK_KNOWLEDGE_BASE = [
19
- ["凯 (Kai)", "主角,前黑客,现在是义体医生。左臂是老式的军用义体。"],
20
- ["夜之城 (Night City)", "故事发生的舞台,一座永夜的巨型都市,被企业掌控。"],
21
- ["荒坂塔 (Arasaka Tower)", "市中心的最高建筑,象征着绝对的权力。"],
22
- ["赛博精神病 (Cyberpsychosis)", "过度改装义体导致的解离性精神障碍。"],
23
- ["网络监察 (NetWatch)", "负责维护网络安全的组织,被黑客们视为走狗。"]
24
  ]
25
 
26
  MOCK_SHORT_TERM_OUTLINE = [
27
- [True, "凯接到一个神秘电话,对方声称知道他失踪妹妹的下落。"],
28
- [False, "凯前往'来生'酒吧与接头人见面。"],
29
- [False, "在酒吧遇到旧识,引发一场关于过去的争执。"],
30
- [False, "接头人出现,但似乎被跟踪了。"]
31
  ]
32
 
 
33
  MOCK_LONG_TERM_OUTLINE = [
34
- [False, "揭露夜之城背后的惊天阴谋。"],
35
- [False, "凯找回妹妹,或者接受她已经改变的事实。"],
36
- [False, "与荒坂公司的最终决战。"]
37
  ]
38
 
39
  # --- UI Helper Functions ---
@@ -45,51 +40,46 @@ def get_stats(text):
45
  read_time = max(1, words // 200) # Average reading speed
46
  return f"{words} Words | ~{read_time} mins"
47
 
48
- def dismiss_inspiration():
49
- return gr.update(visible=False)
50
-
51
  # --- UI Construction ---
52
 
53
  def create_smart_writer_tab():
54
  debounce_state = gr.State({"last_change": 0, "active": False, "style": "", "kb": [], "short_outline": [], "long_outline": []})
55
- debounce_timer = gr.Timer(0.5, active=False)
 
56
 
57
  with gr.Row(equal_height=False, elem_id="indicator-writing-tab"):
58
  # --- Left Column: Entity Console ---
59
- with gr.Column(scale=0, min_width=384) as left_panel:
60
- gr.Markdown("### 🧠 核心实体控制台")
61
 
62
- with gr.Accordion("整体章程 (Style)", open=True):
63
- style_input = gr.Textbox(
64
- label="整体章程",
65
- lines=8,
66
- value=MOCK_STYLE,
67
- interactive=True
68
- )
69
 
70
- with gr.Accordion("知识库 (Knowledge Base)", open=True):
 
71
  kb_input = gr.Dataframe(
72
- headers=["Term", "Description"],
73
  datatype=["str", "str"],
74
  value=MOCK_KNOWLEDGE_BASE,
75
  interactive=True,
76
- label="知识库",
77
  wrap=True
78
  )
79
  with gr.Row():
80
  btn_suggest_kb = gr.Button("🔍 提取新词条", size="sm")
81
 
82
- md_suggested_terms_header = gr.Markdown("#### 推荐词条", visible=False) # Placeholder for suggested terms
83
  suggested_kb_dataframe = gr.Dataframe(
84
  headers=["Term", "Description"],
85
  datatype=["str", "str"],
86
- visible=False, # Initially hidden
87
  interactive=False,
88
  label="推荐词条"
89
  )
90
 
 
91
 
92
- with gr.Accordion("当前章节大纲 (Short-Term)", open=True):
93
  short_outline_input = gr.Dataframe(
94
  headers=["Done", "Task"],
95
  datatype=["bool", "str"],
@@ -101,7 +91,7 @@ def create_smart_writer_tab():
101
  with gr.Row():
102
  btn_sync_outline = gr.Button("🔄 同步状态", size="sm")
103
 
104
- with gr.Accordion("故事总纲 (Long-Term)", open=False):
105
  long_outline_input = gr.Dataframe(
106
  headers=["Done", "Task"],
107
  datatype=["bool", "str"],
@@ -112,82 +102,69 @@ def create_smart_writer_tab():
112
  )
113
 
114
  # --- Right Column: Writing Canvas ---
115
- with gr.Column(scale=1) as right_panel:
116
- # Toolbar
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  with gr.Row(elem_classes=["toolbar"]):
118
  stats_display = gr.Markdown("0 Words | 0 mins")
119
- inspiration_btn = gr.Button("✨ 继续整段 (Cmd/Ctrl+Enter)", size="sm", variant="primary", elem_id="btn-action-create-paragraph")
120
 
121
- # 主要编辑器区域
122
  editor = gr.Textbox(
123
  label="沉浸写作画布",
124
  placeholder="开始你的创作...",
125
- lines=30,
126
  elem_classes=["writing-editor"],
127
  elem_id="writing-editor",
128
  show_label=False,
129
  )
130
 
131
- # Flow Suggestion
132
- with gr.Row(variant="panel"):
133
- flow_suggestion_display = gr.Textbox(
134
- label="AI 实时续写建议 (按 Tab 采纳)",
135
- value="(等待输入...)",
136
- interactive=False,
137
- scale=4,
138
- elem_classes=["flow-suggestion-box"]
139
- )
140
- accept_flow_btn = gr.Button("采纳(Tab)", scale=1, elem_id='btn-action-accept-flow')
141
- refresh_flow_btn = gr.Button("换一个(Shift+Tab)", scale=1, elem_id='btn-action-change-flow')
142
-
143
- # Debounce Progress
144
- debounce_progress = gr.HTML(value="", visible=False)
145
-
146
- # Inspiration Modal
147
- with gr.Group(visible=False) as inspiration_modal:
148
- gr.Markdown("### 💡 灵感选项 (由 Ling 模型生成)")
149
-
150
- inspiration_prompt_input = gr.Textbox(
151
- label="设定脉络 (可选)",
152
- placeholder="例如:写一段激烈的打斗 / 描写赛博朋克夜景...",
153
- lines=1
154
- )
155
- refresh_inspiration_btn = gr.Button("生成选项(Shift+Enter)")
156
-
157
- with gr.Row():
158
- opt1_btn = gr.Button("...", elem_classes=["inspiration-card"])
159
- opt2_btn = gr.Button("...", elem_classes=["inspiration-card"])
160
- opt3_btn = gr.Button("...", elem_classes=["inspiration-card"])
161
- cancel_insp_btn = gr.Button("取消")
162
 
163
  # --- Interactions ---
164
 
165
  # 1. Stats
166
  editor.change(fn=get_stats, inputs=editor, outputs=stats_display)
167
 
168
- # 2. Inspiration Workflow
169
- # Open Modal (triggered by visible button or hidden trigger button for Cmd+Enter)
170
- open_inspiration_modal_fn = lambda: (gr.update(visible=True), "")
171
- inspiration_btn.click(fn=open_inspiration_modal_fn, outputs=[inspiration_modal, inspiration_prompt_input])
172
-
173
- # Generate Options based on Prompt
174
- refresh_inspiration_btn.click(
175
- fn=fetch_inspiration_agent,
176
- inputs=[inspiration_prompt_input, editor, style_input, kb_input, short_outline_input, long_outline_input],
177
- outputs=[inspiration_modal, opt1_btn, opt2_btn, opt3_btn]
178
- )
179
-
180
- # Apply Option
181
- for btn in [opt1_btn, opt2_btn, opt3_btn]:
182
- btn.click(
183
- fn=apply_inspiration_agent,
184
- inputs=[editor, btn],
185
- outputs=[editor, inspiration_modal, inspiration_prompt_input]
186
- )
187
-
188
- cancel_insp_btn.click(fn=dismiss_inspiration, outputs=inspiration_modal, show_progress="hidden")
189
-
190
- # 3. Flow Suggestion with Debounce
191
  def start_debounce(editor_content, style, kb, short_outline, long_outline):
192
  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")
193
 
@@ -206,12 +183,40 @@ def create_smart_writer_tab():
206
 
207
  editor.change(fn=start_debounce, inputs=[editor, style_input, kb_input, short_outline_input, long_outline_input], outputs=[debounce_state, debounce_timer, debounce_progress])
208
  debounce_timer.tick(fn=update_debounce, inputs=[debounce_state, editor], outputs=[debounce_progress, flow_suggestion_display, debounce_state, debounce_timer])
209
- 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)
 
210
 
211
- # Accept Flow (Triggered by visible Button or hidden Tab Key trigger)
212
  accept_flow_fn_inputs = [editor, flow_suggestion_display]
213
- accept_flow_fn_outputs = [editor]
214
- accept_flow_btn.click(fn=accept_flow_suggestion_agent, inputs=accept_flow_fn_inputs, outputs=accept_flow_fn_outputs, show_progress="hidden")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
  # 4. Agent-based Context Updates
217
  btn_sync_outline.click(
@@ -222,5 +227,5 @@ def create_smart_writer_tab():
222
  btn_suggest_kb.click(
223
  fn=suggest_new_kb_terms_agent,
224
  inputs=[kb_input, editor],
225
- outputs=[suggested_kb_dataframe, md_suggested_terms_header]
226
  )
 
1
  import gradio as gr
2
  import time
3
  from smart_writer_kit.agent_for_streaming_completion import fetch_flow_suggestion_agent, accept_flow_suggestion_agent
4
+ from smart_writer_kit.agent_for_inspiration_expansion import fetch_inspiration_agent, apply_inspiration_agent, fetch_paragraph_continuation_agent
5
  from smart_writer_kit.agent_for_outline_update import update_outline_status_agent
6
  from smart_writer_kit.agent_for_kb_update import suggest_new_kb_terms_agent
7
 
8
  # --- Mock Data (for UI population only) ---
9
 
10
+ MOCK_STYLE = """故事:人类逐渐走向消亡时,人形机器人的休闲生活。
11
+ 风格:自然平淡,文字细腻,描绘未来的荒凉与宁静交织的景象。
12
+ 主题:探索人类与机器的界限,记忆与身份的意义。
 
 
 
13
  """
14
 
15
  MOCK_KNOWLEDGE_BASE = [
16
+ ["Alpha", "故事的主角,女性人形机器人,外表与人类无异。性格有线"],
17
+ ["横滨", "故事发生的主要城市。由于海平面上升,城市部分地区被淹没,形成独特的水上景观。"]
 
 
 
18
  ]
19
 
20
  MOCK_SHORT_TERM_OUTLINE = [
21
+ [False, "故事的场景设定:海平面上升后的城市景观。"],
22
+ [False, "介绍主角 Alpha 的日常生活和她与其他机器人的互动。"],
23
+ [False, "Alpha 发现了一张旧照片,勾起了她对过去人类生活的好奇心。"],
24
+ [False, "奶油蛋糕的制作方法。"]
25
  ]
26
 
27
+ ## 按日常向动画剧情走向写的长纲要。具体。
28
  MOCK_LONG_TERM_OUTLINE = [
29
+ [False, "介绍故事背景。人类逐渐减少,机器人和人的互动。"],
30
+ [False, "Alpha 决定离开居住地,到东京寻找失散的朋友。"],
31
+ [False, "月亮变成了一个巨大的 Disco 灯球。机器人不受控制地开始跳舞,导致全球范围内的混乱。"],
32
  ]
33
 
34
  # --- UI Helper Functions ---
 
40
  read_time = max(1, words // 200) # Average reading speed
41
  return f"{words} Words | ~{read_time} mins"
42
 
 
 
 
43
  # --- UI Construction ---
44
 
45
  def create_smart_writer_tab():
46
  debounce_state = gr.State({"last_change": 0, "active": False, "style": "", "kb": [], "short_outline": [], "long_outline": []})
47
+ debounce_timer = gr.Timer(0.1, active=True)
48
+
49
 
50
  with gr.Row(equal_height=False, elem_id="indicator-writing-tab"):
51
  # --- Left Column: Entity Console ---
52
+ with gr.Column(scale=1) as left_panel:
 
53
 
54
+ style_input = gr.Textbox(
55
+ label="整体故事和风格",
56
+ lines=8,
57
+ value=MOCK_STYLE,
58
+ interactive=True
59
+ )
 
60
 
61
+ with gr.Accordion("写作知识库", open=True):
62
+
63
  kb_input = gr.Dataframe(
64
+ headers=["名称", "说明"],
65
  datatype=["str", "str"],
66
  value=MOCK_KNOWLEDGE_BASE,
67
  interactive=True,
 
68
  wrap=True
69
  )
70
  with gr.Row():
71
  btn_suggest_kb = gr.Button("🔍 提取新词条", size="sm")
72
 
 
73
  suggested_kb_dataframe = gr.Dataframe(
74
  headers=["Term", "Description"],
75
  datatype=["str", "str"],
76
+ visible=False,
77
  interactive=False,
78
  label="推荐词条"
79
  )
80
 
81
+ with gr.Accordion("当前章节大纲", open=True):
82
 
 
83
  short_outline_input = gr.Dataframe(
84
  headers=["Done", "Task"],
85
  datatype=["bool", "str"],
 
91
  with gr.Row():
92
  btn_sync_outline = gr.Button("🔄 同步状态", size="sm")
93
 
94
+ with gr.Accordion("故事整体大纲", open=False):
95
  long_outline_input = gr.Dataframe(
96
  headers=["Done", "Task"],
97
  datatype=["bool", "str"],
 
102
  )
103
 
104
  # --- Right Column: Writing Canvas ---
105
+ with gr.Column(scale=5):
106
+
107
+ # --- RIBBON AREA (Top of Editor) ---
108
+ with gr.Row(variant="panel", elem_classes=["ribbon-container"]):
109
+
110
+ # Area 1: Real-time Continuation (Flow)
111
+ with gr.Column(scale=1, min_width=200):
112
+ gr.Markdown("#### ⚡️ 实时续写")
113
+ with gr.Row():
114
+ btn_accept_flow = gr.Button("采纳续写 (Tab)", size="sm", variant="primary", elem_id='btn-action-accept-flow')
115
+ btn_change_flow = gr.Button("换一个 (Shift+Tab)", size="sm", elem_id='btn-action-change-flow')
116
+
117
+ flow_suggestion_display = gr.Textbox(
118
+ show_label=False,
119
+ placeholder="(等待输入或点击“换一个”...)",
120
+ lines=3,
121
+ interactive=False,
122
+ elem_classes=["flow-suggestion-box"]
123
+ )
124
+ # Debounce Progress Indicator
125
+ debounce_progress = gr.HTML(value="", visible=False)
126
+
127
+ # Area 2: Paragraph Continuation (Inspiration)
128
+ with gr.Column(scale=1, min_width=200):
129
+ gr.Markdown("#### ✨ 整段续写")
130
+ with gr.Row():
131
+ btn_generate_para = gr.Button("整段续写 (Cmd+Enter)", size="sm", variant="primary", elem_id="btn-action-create-paragraph")
132
+ btn_change_para = gr.Button("换一个", size="sm")
133
+ btn_accept_para = gr.Button("采纳", size="sm")
134
+
135
+ para_suggestion_display = gr.Textbox(
136
+ show_label=False,
137
+ placeholder="(点击“整段续写”生成内容...)",
138
+ lines=3,
139
+ interactive=False
140
+ )
141
+
142
+ # Area 3: Adjust/Polish (Placeholder)
143
+ with gr.Column(scale=1, min_width=200):
144
+ gr.Markdown("#### 🛠️ 调整润色")
145
+ gr.Markdown("(Coming Soon)")
146
+
147
+ # --- TOOLBAR ---
148
  with gr.Row(elem_classes=["toolbar"]):
149
  stats_display = gr.Markdown("0 Words | 0 mins")
 
150
 
151
+ # --- EDITOR ---
152
  editor = gr.Textbox(
153
  label="沉浸写作画布",
154
  placeholder="开始你的创作...",
155
+ lines=25, # Reduced lines slightly to accommodate ribbon
156
  elem_classes=["writing-editor"],
157
  elem_id="writing-editor",
158
  show_label=False,
159
  )
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
  # --- Interactions ---
163
 
164
  # 1. Stats
165
  editor.change(fn=get_stats, inputs=editor, outputs=stats_display)
166
 
167
+ # 2. Flow Suggestion Logic
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  def start_debounce(editor_content, style, kb, short_outline, long_outline):
169
  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")
170
 
 
183
 
184
  editor.change(fn=start_debounce, inputs=[editor, style_input, kb_input, short_outline_input, long_outline_input], outputs=[debounce_state, debounce_timer, debounce_progress])
185
  debounce_timer.tick(fn=update_debounce, inputs=[debounce_state, editor], outputs=[debounce_progress, flow_suggestion_display, debounce_state, debounce_timer])
186
+
187
+ btn_change_flow.click(fn=fetch_flow_suggestion_agent, inputs=[editor, style_input, kb_input, short_outline_input, long_outline_input], outputs=flow_suggestion_display)
188
 
 
189
  accept_flow_fn_inputs = [editor, flow_suggestion_display]
190
+ # accept_flow_suggestion_agent returns modified editor text
191
+ btn_accept_flow.click(
192
+ fn=lambda e, s: (accept_flow_suggestion_agent(e, s), ""), # Accept and clear suggestion
193
+ inputs=accept_flow_fn_inputs,
194
+ outputs=[editor, flow_suggestion_display],
195
+ show_progress="hidden"
196
+ )
197
+
198
+ # 3. Paragraph Continuation Logic
199
+ def generate_paragraph_wrapper(editor_val, style, kb, short, long_):
200
+ return fetch_paragraph_continuation_agent(None, editor_val, style, kb, short, long_)
201
+
202
+ for btn in [btn_generate_para, btn_change_para]:
203
+ btn.click(
204
+ fn=generate_paragraph_wrapper,
205
+ inputs=[editor, style_input, kb_input, short_outline_input, long_outline_input],
206
+ outputs=[para_suggestion_display]
207
+ )
208
+
209
+ def accept_para_wrapper(curr, new):
210
+ # Reuse apply_inspiration_agent but extract text.
211
+ # It returns (new_text, modal_update, empty_string)
212
+ res = apply_inspiration_agent(curr, new)
213
+ return res[0], ""
214
+
215
+ btn_accept_para.click(
216
+ fn=accept_para_wrapper,
217
+ inputs=[editor, para_suggestion_display],
218
+ outputs=[editor, para_suggestion_display]
219
+ )
220
 
221
  # 4. Agent-based Context Updates
222
  btn_sync_outline.click(
 
227
  btn_suggest_kb.click(
228
  fn=suggest_new_kb_terms_agent,
229
  inputs=[kb_input, editor],
230
+ outputs=[suggested_kb_dataframe]
231
  )