Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,363 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from crewai import Agent, Task, Crew
|
2 |
+
import gradio as gr
|
3 |
+
import asyncio
|
4 |
+
from typing import List, Dict, Any, Generator
|
5 |
+
from langchain_openai import ChatOpenAI
|
6 |
+
import queue
|
7 |
+
import threading
|
8 |
+
import os
|
9 |
+
|
10 |
+
analysis_text = """
|
11 |
+
Stage 1: Ground-Based Spin-Up and Launch Initiation
|
12 |
+
β’ Concept:
|
13 |
+
A large, energyβefficient, electrically powered centrifuge is built on the ground. This βspin launcherβ uses renewable energy (for example, solarβassisted electricity) to accelerate a specially designed payload assembly along a rotating arm or in a long circular tunnel.
|
14 |
+
β’ Benefits:
|
15 |
+
Because most of the kinetic energy is imparted mechanically (rather than via chemical propellant), the system drastically reduces the need for traditional, polluting rocket propellants. This stage is also relatively lowβcost because the βengineβ is essentially an electromagnetic drive rather than a rocket motor.
|
16 |
+
Stage 2: Controlled Payload Release and Orbital Injection
|
17 |
+
β’ Concept:
|
18 |
+
At a preβcalculated high tangential velocity, the payload is released from the spin launcher. A very short, minimalβburn liquid or electric thruster (if needed) βtunesβ the trajectory so that the payload enters a stable, lowβEarth orbit.
|
19 |
+
β’ Benefits:
|
20 |
+
The primary acceleration is mechanical, so only a tiny amount of propellant is required for orbital insertion. This greatly cuts both cost and the environmental impact typically associated with rocket launches.
|
21 |
+
Stage 3: Autonomous On-Orbit Stabilization and Despinning
|
22 |
+
β’ Concept:
|
23 |
+
Once in orbit, the payloadβs onboard guidance and control systems (such as small reaction control thrusters or a yo-yo de-spin mechanism) despin and stabilize the payload. Integrated sensors and attitude-control software adjust the payloadβs orientation and gently circularize the orbit.
|
24 |
+
β’ Benefits:
|
25 |
+
Autonomous stabilization minimizes additional propellant use and prepares the payload for a safe, predictable rendezvous. The controlled despinning ensures that the payloadβs docking adapter remains in the proper orientation for subsequent attachment.
|
26 |
+
Stage 4: Rendezvous and Docking with the Manned Vehicle
|
27 |
+
β’ Concept:
|
28 |
+
A separately launched or preβpositioned manned spacecraft (or space station) maneuvers to intercept the payload. The payload is equipped with a dedicated docking adapter (for instance, a magnetic or mechanical latch system engineered for high-precision contact under lowβg conditions).
|
29 |
+
β’ Benefits:
|
30 |
+
This phase uses conventional lowβdeltaβV maneuvers that are far less expensive than a full chemical orbital insertion burn. The docking system is designed to absorb minor mismatches in velocity or attitude, allowing the crew to safely βhook onβ to the payload. This minimizes extra propellant usage during the rendezvous phase.
|
31 |
+
Stage 5: Integrated Mission Operations and Continued Space Activity
|
32 |
+
β’ Concept:
|
33 |
+
Once docked, the combined systemβthe manned vehicle with the attached spin-delivered payloadβcontinues its mission. The payload might provide additional resources (such as extra fuel, scientific instruments, or habitat modules) that augment the manned vehicleβs capabilities.
|
34 |
+
β’ Benefits:
|
35 |
+
With the payload permanently attached, mission operations (such as orbital adjustments, inter-station transfers, or even on-orbit assembly of larger structures) proceed with enhanced capabilities. The systemβs reliance on mechanical acceleration for the bulk of the launch cut both launch costs and the environmental footprint, leaving only minor orbital maneuvers to be performed with conventional thrusters.
|
36 |
+
Summary
|
37 |
+
This five-stage system marries a ground-based spin acceleration concept with in-space docking and integration to achieve a βpropellant-lightβ method for delivering payloads into orbit. By using a spin launcher to achieve high velocity on the ground (Stage 1) and minimizing onboard chemical propellant (Stage 2), the payload is inserted into orbit economically and with reduced environmental impact. On-orbit stabilization (Stage 3) prepares it for rendezvous with a manned vehicle (Stage 4), after which the combined system carries out the mission (Stage 5).
|
38 |
+
"""
|
39 |
+
|
40 |
+
summary_txt = """
|
41 |
+
Summary
|
42 |
+
This five-stage system marries a ground-based spin acceleration concept with in-space docking and integration to achieve a βpropellant-lightβ method for delivering payloads into orbit. By using a spin launcher to achieve high velocity on the ground (Stage 1) and minimizing onboard chemical propellant (Stage 2), the payload is inserted into orbit economically and with reduced environmental impact. On-orbit stabilization (Stage 3) prepares it for rendezvous with a manned vehicle (Stage 4), after which the combined system carries out the mission (Stage 5).
|
43 |
+
"""
|
44 |
+
|
45 |
+
class AgentMessageQueue:
|
46 |
+
def __init__(self):
|
47 |
+
self.message_queue = queue.Queue()
|
48 |
+
self.last_agent = None
|
49 |
+
|
50 |
+
def add_message(self, message: Dict):
|
51 |
+
print(f"Adding message to queue: {message}") # Debug print
|
52 |
+
self.message_queue.put(message)
|
53 |
+
|
54 |
+
def get_messages(self) -> List[Dict]:
|
55 |
+
messages = []
|
56 |
+
while not self.message_queue.empty():
|
57 |
+
messages.append(self.message_queue.get())
|
58 |
+
return messages
|
59 |
+
|
60 |
+
class ArticleCrew:
|
61 |
+
def __init__(self, api_key: str = None):
|
62 |
+
self.api_key = api_key
|
63 |
+
self.message_queue = AgentMessageQueue()
|
64 |
+
self.planner = None
|
65 |
+
self.writer = None
|
66 |
+
self.editor = None
|
67 |
+
self.current_agent = None
|
68 |
+
self.final_article = None
|
69 |
+
|
70 |
+
def initialize_agents(self, topic: str):
|
71 |
+
if not self.api_key:
|
72 |
+
raise ValueError("OpenAI API key is required")
|
73 |
+
|
74 |
+
os.environ["OPENAI_API_KEY"] = self.api_key
|
75 |
+
llm = ChatOpenAI(temperature=0.7, model="gpt-4")
|
76 |
+
|
77 |
+
self.planner = Agent(
|
78 |
+
role="Content Planner",
|
79 |
+
goal=f"Plan engaging and factually accurate content on {topic}",
|
80 |
+
backstory="Expert content planner with focus on creating engaging outlines",
|
81 |
+
allow_delegation=False,
|
82 |
+
verbose=True,
|
83 |
+
llm=llm
|
84 |
+
)
|
85 |
+
|
86 |
+
self.writer = Agent(
|
87 |
+
role="Content Writer",
|
88 |
+
goal=f"Write insightful and factually accurate piece about {topic}",
|
89 |
+
backstory="Expert content writer with focus on engaging articles",
|
90 |
+
allow_delegation=False,
|
91 |
+
verbose=True,
|
92 |
+
llm=llm
|
93 |
+
)
|
94 |
+
|
95 |
+
self.editor = Agent(
|
96 |
+
role="Editor",
|
97 |
+
goal="Polish and refine the article",
|
98 |
+
backstory="Expert editor with eye for detail and clarity",
|
99 |
+
allow_delegation=False,
|
100 |
+
verbose=True,
|
101 |
+
llm=llm
|
102 |
+
)
|
103 |
+
|
104 |
+
def create_tasks(self, topic: str) -> List[Task]:
|
105 |
+
planner_task = Task(
|
106 |
+
description=f"""Create a detailed content plan for an article about {topic} by:
|
107 |
+
1. Prioritizing the latest trends, key players, and noteworthy news
|
108 |
+
2. Identifying the target audience, considering their interests and pain points
|
109 |
+
3. Developing a detailed content outline including introduction, key points, and call to action
|
110 |
+
4. Including SEO keywords and relevant data or sources""",
|
111 |
+
expected_output="A comprehensive content plan with outline, keywords, and target audience analysis",
|
112 |
+
agent=self.planner
|
113 |
+
)
|
114 |
+
|
115 |
+
writer_task = Task(
|
116 |
+
description="""Based on the provided content plan:
|
117 |
+
1. Use the content plan to craft a compelling blog post
|
118 |
+
2. Incorporate SEO keywords naturally
|
119 |
+
3. Ensure sections/subtitles are properly named in an engaging manner
|
120 |
+
4. Create proper structure with introduction, body, and conclusion
|
121 |
+
5. Proofread for grammatical errors""",
|
122 |
+
expected_output="A well-written article draft following the content plan",
|
123 |
+
agent=self.writer
|
124 |
+
)
|
125 |
+
|
126 |
+
editor_task = Task(
|
127 |
+
description="""Review the written article by:
|
128 |
+
1. Checking for clarity and coherence
|
129 |
+
2. Correcting any grammatical errors and typos
|
130 |
+
3. Ensuring consistent tone and style
|
131 |
+
4. Verifying proper formatting and structure""",
|
132 |
+
expected_output="A polished, final version of the article ready for publication",
|
133 |
+
agent=self.editor
|
134 |
+
)
|
135 |
+
|
136 |
+
return [planner_task, writer_task, editor_task]
|
137 |
+
|
138 |
+
async def process_article(self, topic: str) -> Generator[List[Dict], None, None]:
|
139 |
+
def add_agent_messages(agent_name: str, tasks: str, emoji: str = "π€"):
|
140 |
+
# Add agent header
|
141 |
+
self.message_queue.add_message({
|
142 |
+
"role": "assistant",
|
143 |
+
"content": agent_name,
|
144 |
+
"metadata": {"title": f"{emoji} {agent_name}"}
|
145 |
+
})
|
146 |
+
|
147 |
+
# Add task description
|
148 |
+
self.message_queue.add_message({
|
149 |
+
"role": "assistant",
|
150 |
+
"content": tasks,
|
151 |
+
"metadata": {"title": f"π Task for {agent_name}"}
|
152 |
+
})
|
153 |
+
|
154 |
+
def setup_next_agent(current_agent: str) -> None:
|
155 |
+
agent_sequence = {
|
156 |
+
"Content Planner": ("Content Writer", """1. Use the content plan to craft a compelling blog post
|
157 |
+
2. Incorporate SEO keywords naturally
|
158 |
+
3. Ensure sections/subtitles are properly named in an engaging manner
|
159 |
+
4. Create proper structure with introduction, body, and conclusion
|
160 |
+
5. Proofread for grammatical errors"""),
|
161 |
+
|
162 |
+
"Content Writer": ("Editor", """1. Review the article for clarity and coherence
|
163 |
+
2. Check for grammatical errors and typos
|
164 |
+
3. Ensure consistent tone and style
|
165 |
+
4. Verify proper formatting and structure""")
|
166 |
+
}
|
167 |
+
|
168 |
+
if current_agent in agent_sequence:
|
169 |
+
next_agent, tasks = agent_sequence[current_agent]
|
170 |
+
self.current_agent = next_agent
|
171 |
+
add_agent_messages(next_agent, tasks)
|
172 |
+
|
173 |
+
|
174 |
+
def task_callback(task_output) -> None:
|
175 |
+
print(f"Task callback received: {task_output}") # Debug print
|
176 |
+
|
177 |
+
# Extract content from raw output
|
178 |
+
raw_output = task_output.raw
|
179 |
+
if "## Final Answer:" in raw_output:
|
180 |
+
content = raw_output.split("## Final Answer:")[1].strip()
|
181 |
+
else:
|
182 |
+
content = raw_output.strip()
|
183 |
+
|
184 |
+
# Handle the output based on current agent
|
185 |
+
if self.current_agent == "Editor":
|
186 |
+
# Don't show editor's output with metadata
|
187 |
+
# Instead, show completion message and final article
|
188 |
+
self.message_queue.add_message({
|
189 |
+
"role": "assistant",
|
190 |
+
"content": "Final article is ready!",
|
191 |
+
"metadata": {"title": "π Final Article"}
|
192 |
+
})
|
193 |
+
|
194 |
+
# Convert common markdown patterns to Gradio-compatible markdown
|
195 |
+
formatted_content = content
|
196 |
+
# Ensure proper spacing for headers
|
197 |
+
formatted_content = formatted_content.replace("\n#", "\n\n#")
|
198 |
+
# Ensure proper spacing for lists
|
199 |
+
formatted_content = formatted_content.replace("\n-", "\n\n-")
|
200 |
+
formatted_content = formatted_content.replace("\n*", "\n\n*")
|
201 |
+
formatted_content = formatted_content.replace("\n1.", "\n\n1.")
|
202 |
+
# Ensure proper spacing for paragraphs
|
203 |
+
formatted_content = formatted_content.replace("\n\n\n", "\n\n")
|
204 |
+
|
205 |
+
# Add the final article content without metadata
|
206 |
+
self.message_queue.add_message({
|
207 |
+
"role": "assistant",
|
208 |
+
"content": formatted_content
|
209 |
+
})
|
210 |
+
else:
|
211 |
+
# For other agents, show their output with metadata
|
212 |
+
self.message_queue.add_message({
|
213 |
+
"role": "assistant",
|
214 |
+
"content": content,
|
215 |
+
"metadata": {"title": f"β¨ Output from {self.current_agent}"}
|
216 |
+
})
|
217 |
+
# Setup next agent
|
218 |
+
setup_next_agent(self.current_agent)
|
219 |
+
|
220 |
+
def step_callback(output: Any) -> None:
|
221 |
+
print(f"Step callback received: {output}") # Debug print
|
222 |
+
# We'll only use step_callback for logging purposes now
|
223 |
+
pass
|
224 |
+
|
225 |
+
try:
|
226 |
+
self.initialize_agents(topic)
|
227 |
+
self.current_agent = "Content Planner"
|
228 |
+
|
229 |
+
# Start process
|
230 |
+
yield [{
|
231 |
+
"role": "assistant",
|
232 |
+
"content": "Starting work on your article...",
|
233 |
+
"metadata": {"title": "π Process Started"}
|
234 |
+
}]
|
235 |
+
|
236 |
+
# Initialize first agent
|
237 |
+
add_agent_messages("Content Planner",
|
238 |
+
"""1. Prioritize the latest trends, key players, and noteworthy news
|
239 |
+
2. Identify the target audience, considering their interests and pain points
|
240 |
+
3. Develop a detailed content outline including introduction, key points, and call to action
|
241 |
+
4. Include SEO keywords and relevant data or sources""")
|
242 |
+
|
243 |
+
crew = Crew(
|
244 |
+
agents=[self.planner, self.writer, self.editor],
|
245 |
+
tasks=self.create_tasks(topic),
|
246 |
+
verbose=True,
|
247 |
+
step_callback=step_callback,
|
248 |
+
task_callback=task_callback
|
249 |
+
)
|
250 |
+
|
251 |
+
def run_crew():
|
252 |
+
try:
|
253 |
+
crew.kickoff()
|
254 |
+
except Exception as e:
|
255 |
+
print(f"Error in crew execution: {str(e)}") # Debug print
|
256 |
+
self.message_queue.add_message({
|
257 |
+
"role": "assistant",
|
258 |
+
"content": f"An error occurred: {str(e)}",
|
259 |
+
"metadata": {"title": "β Error"}
|
260 |
+
})
|
261 |
+
|
262 |
+
thread = threading.Thread(target=run_crew)
|
263 |
+
thread.start()
|
264 |
+
|
265 |
+
while thread.is_alive() or not self.message_queue.message_queue.empty():
|
266 |
+
messages = self.message_queue.get_messages()
|
267 |
+
if messages:
|
268 |
+
print(f"Yielding messages: {messages}") # Debug print
|
269 |
+
yield messages
|
270 |
+
await asyncio.sleep(0.1)
|
271 |
+
|
272 |
+
except Exception as e:
|
273 |
+
print(f"Error in process_article: {str(e)}") # Debug print
|
274 |
+
yield [{
|
275 |
+
"role": "assistant",
|
276 |
+
"content": f"An error occurred: {str(e)}",
|
277 |
+
"metadata": {"title": "β Error"}
|
278 |
+
}]
|
279 |
+
|
280 |
+
# [Rest of the code remains the same]
|
281 |
+
|
282 |
+
|
283 |
+
def create_demo():
|
284 |
+
article_crew = None
|
285 |
+
|
286 |
+
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
287 |
+
gr.Markdown("# π AI Article Writing Crew")
|
288 |
+
|
289 |
+
openai_api_key = gr.Textbox(
|
290 |
+
label='OpenAI API Key',
|
291 |
+
type='password',
|
292 |
+
placeholder='Enter your OpenAI API key...',
|
293 |
+
interactive=True
|
294 |
+
)
|
295 |
+
|
296 |
+
chatbot = gr.Chatbot(
|
297 |
+
label="Writing Process",
|
298 |
+
height=700,
|
299 |
+
type="messages",
|
300 |
+
show_label=True,
|
301 |
+
visible=False,
|
302 |
+
avatar_images=(None, "https://avatars.githubusercontent.com/u/170677839?v=4"),
|
303 |
+
render_markdown=True # Enable markdown rendering
|
304 |
+
)
|
305 |
+
|
306 |
+
with gr.Row(equal_height=True):
|
307 |
+
topic = gr.Textbox(
|
308 |
+
label="Article Topic",
|
309 |
+
placeholder="Enter topic...",
|
310 |
+
scale=4,
|
311 |
+
visible=False
|
312 |
+
)
|
313 |
+
btn = gr.Button("Write Article", variant="primary", scale=1, visible=False)
|
314 |
+
|
315 |
+
async def process_input(topic, history, api_key):
|
316 |
+
nonlocal article_crew
|
317 |
+
if not api_key:
|
318 |
+
history = history or []
|
319 |
+
history.append({
|
320 |
+
"role": "assistant",
|
321 |
+
"content": "Please provide an OpenAI API key.",
|
322 |
+
"metadata": {"title": "β Error"}
|
323 |
+
})
|
324 |
+
yield history
|
325 |
+
return
|
326 |
+
|
327 |
+
if article_crew is None:
|
328 |
+
article_crew = ArticleCrew(api_key=api_key)
|
329 |
+
|
330 |
+
history = history or []
|
331 |
+
history.append({"role": "user", "content": f"Write an article about: {topic}"})
|
332 |
+
yield history
|
333 |
+
|
334 |
+
try:
|
335 |
+
async for messages in article_crew.process_article(topic):
|
336 |
+
history.extend(messages)
|
337 |
+
yield history
|
338 |
+
except Exception as e:
|
339 |
+
history.append({
|
340 |
+
"role": "assistant",
|
341 |
+
"content": f"An error occurred: {str(e)}",
|
342 |
+
"metadata": {"title": "β Error"}
|
343 |
+
})
|
344 |
+
yield history
|
345 |
+
|
346 |
+
def show_interface():
|
347 |
+
return {
|
348 |
+
openai_api_key: gr.Textbox(visible=False),
|
349 |
+
chatbot: gr.Chatbot(visible=True),
|
350 |
+
topic: gr.Textbox(visible=True),
|
351 |
+
btn: gr.Button(visible=True)
|
352 |
+
}
|
353 |
+
|
354 |
+
openai_api_key.submit(show_interface, None, [openai_api_key, chatbot, topic, btn])
|
355 |
+
btn.click(process_input, [topic, chatbot, openai_api_key], [chatbot])
|
356 |
+
topic.submit(process_input, [topic, chatbot, openai_api_key], [chatbot])
|
357 |
+
|
358 |
+
return demo
|
359 |
+
|
360 |
+
if __name__ == "__main__":
|
361 |
+
demo = create_demo()
|
362 |
+
demo.queue()
|
363 |
+
demo.launch(debug=True)
|