cryogenic22 commited on
Commit
388a202
·
verified ·
1 Parent(s): 87958cc

Update multi_agent_book_workflow.py

Browse files
Files changed (1) hide show
  1. multi_agent_book_workflow.py +170 -137
multi_agent_book_workflow.py CHANGED
@@ -1,12 +1,12 @@
1
  # File: multi_agent_book_workflow.py
2
- # Location: /multi_agent_book_workflow.py (in root directory)
3
  # Description: Core multi-agent book writing orchestration system
4
 
5
  import os
6
  import uuid
7
  import numpy as np
8
  import streamlit as st
9
- from typing import Dict, List, Any
10
 
11
  # Vector Store and Embedding
12
  from langchain.embeddings import OpenAIEmbeddings
@@ -16,11 +16,46 @@ from langchain.docstore.document import Document
16
  # Agent and LLM Frameworks
17
  from crewai import Agent, Task, Crew
18
  from langchain_openai import ChatOpenAI
19
- from langchain.chat_models import ChatAnthropic
 
20
  from langchain.memory import ConversationBufferMemory
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  class BookWritingOrchestrator:
23
- def __init__(self, api_key=None):
24
  """
25
  Initialize the book writing orchestrator with multi-agent setup
26
 
@@ -46,7 +81,7 @@ class BookWritingOrchestrator:
46
  st.error(f"Agent setup failed: {e}")
47
  raise RuntimeError("Could not initialize book writing agents") from e
48
 
49
- def _setup_api_keys(self, api_key=None):
50
  """
51
  Validate and set up API keys with comprehensive checks
52
  """
@@ -94,11 +129,11 @@ class BookWritingOrchestrator:
94
  api_key=self.openai_api_key
95
  )
96
 
97
- # Anthropic Configuration
98
- self.anthropic_llm = ChatAnthropic(
99
- model="claude-3-sonnet-20240229",
100
- anthropic_api_key=self.anthropic_api_key,
101
- temperature=0.7
102
  )
103
 
104
  # Global memory for cross-chapter context
@@ -156,46 +191,6 @@ class BookWritingOrchestrator:
156
  memory=True
157
  )
158
 
159
- def _fallback_book_concept(self, initial_prompt: str) -> Dict[str, Any]:
160
- """
161
- Provide a fallback response when book concept generation fails
162
-
163
- Args:
164
- initial_prompt (str): Original user prompt
165
-
166
- Returns:
167
- Dict: Basic book concept structure
168
- """
169
- return {
170
- "title": "Untitled Project",
171
- "genre": "General Fiction",
172
- "target_audience": "General Adult",
173
- "core_premise": initial_prompt,
174
- "chapter_outline": ["Chapter 1: Introduction"],
175
- "narrative_approach": "Standard Third-Person Narrative",
176
- "status": "fallback_generated"
177
- }
178
-
179
- def _fallback_chapter_content(self, book_concept: Dict[str, Any], chapter_number: int) -> str:
180
- """
181
- Provide fallback chapter content when generation fails
182
-
183
- Args:
184
- book_concept (Dict): Book concept data
185
- chapter_number (int): Chapter number
186
-
187
- Returns:
188
- str: Basic chapter content
189
- """
190
- return f"""
191
- Chapter {chapter_number}
192
-
193
- [Placeholder content for {book_concept.get('title', 'Untitled')}]
194
-
195
- This is auto-generated fallback content due to an error in chapter generation.
196
- Please try regenerating this chapter or contact support if the issue persists.
197
- """
198
-
199
  def generate_book_concept(self, initial_prompt: str) -> Dict[str, Any]:
200
  """
201
  Generate a comprehensive book concept using multi-agent collaboration
@@ -213,22 +208,31 @@ class BookWritingOrchestrator:
213
  {initial_prompt}
214
 
215
  Provide detailed outputs including:
216
- 1. Unique Book Title
217
- 2. Genre and Subgenre
218
- 3. Target Audience
219
- 4. Core Narrative Premise
220
- 5. Potential Chapter Outline
221
- 6. Distinctive Narrative Approach
 
 
 
222
  """,
223
- agent=self.concept_agent,
224
- expected_output="A detailed JSON-like structure of the book concept"
225
  )
226
 
227
  # Research Validation Task
228
  research_task = Task(
229
- description="Validate and enrich the book concept with additional research insights",
230
- agent=self.research_agent,
231
- expected_output="Research-backed annotations and potential depth areas"
 
 
 
 
 
 
 
232
  )
233
 
234
  # Create Crew for Collaborative Processing
@@ -238,106 +242,68 @@ class BookWritingOrchestrator:
238
  verbose=True
239
  )
240
 
241
- # Execute Collaborative Workflow
242
  try:
 
243
  result = book_concept_crew.kickoff()
244
 
 
 
 
245
  # Store Context in FAISS Vector Store
246
- self._store_context('book_concept', result)
 
 
247
 
248
- return self._parse_concept(result)
249
-
250
  except Exception as e:
251
  st.error(f"Book concept generation failed: {e}")
252
  return self._fallback_book_concept(initial_prompt)
253
 
254
- def _store_context(self, context_key: str, content: str):
255
- """
256
- Store context in FAISS vector store
257
  """
258
- try:
259
- # Create a document with metadata
260
- document = Document(
261
- page_content=content,
262
- metadata={
263
- "project_id": self.project_id,
264
- "context_key": context_key
265
- }
266
- )
267
-
268
- # Add document to FAISS vector store
269
- new_store = FAISS.from_documents([document], self.embeddings)
270
- self.context_store.merge_from(new_store)
271
-
272
- except Exception as e:
273
- st.error(f"Context storage failed: {e}")
274
-
275
- def _parse_concept(self, raw_concept: str) -> Dict[str, Any]:
276
- """
277
- Parse the raw concept output into a structured format
278
 
279
  Args:
280
- raw_concept (str): Raw concept from agents
281
-
282
- Returns:
283
- Dict: Structured book concept
284
- """
285
- try:
286
- # Basic parsing - in practice you might want more sophisticated parsing
287
- lines = raw_concept.strip().split('\n')
288
- concept = {}
289
-
290
- for line in lines:
291
- if ':' in line:
292
- key, value = line.split(':', 1)
293
- concept[key.strip()] = value.strip()
294
 
295
- return concept
296
- except Exception as e:
297
- st.error(f"Concept parsing failed: {e}")
298
- return self._fallback_book_concept("Failed to parse concept")
299
-
300
- def generate_chapter_content(
301
- self,
302
- book_concept: Dict[str, Any],
303
- chapter_number: int
304
- ) -> str:
305
- """
306
- Generate content for a specific chapter using multi-agent workflow
307
-
308
- Args:
309
- book_concept (Dict): Comprehensive book concept
310
- chapter_number (int): Chapter to generate
311
-
312
  Returns:
313
  str: Generated chapter content
314
  """
315
  # Get previous context if available
316
  previous_context = self._retrieve_context(chapter_number - 1) if chapter_number > 1 else ""
317
 
318
- # Content Generation Task
319
  writing_task = Task(
320
  description=f"""
321
- Write Chapter {chapter_number} for the book
 
 
322
 
323
- Book Concept: {book_concept}
324
  Previous Context: {previous_context}
325
 
326
- Generate a draft that:
327
- 1. Maintains narrative continuity
328
- 2. Advances the story
329
- 3. Matches the established tone and style
330
- 4. Incorporates research insights
 
 
331
  """,
332
- agent=self.writing_agent,
333
- expected_output="A complete chapter draft with narrative coherence"
334
  )
335
 
336
  # Editing Task
337
  editing_task = Task(
338
- description="Review and refine the generated chapter draft",
339
- agent=self.editing_agent,
340
- expected_output="Polished chapter content with suggested improvements"
 
 
 
 
 
 
 
341
  )
342
 
343
  # Create Crew for Chapter Generation
@@ -354,29 +320,74 @@ class BookWritingOrchestrator:
354
  # Store Chapter Context
355
  self._store_context(f'chapter_{chapter_number}', chapter_content)
356
 
357
- # Update global memory
358
  self.global_memory.chat_memory.add_user_message(
359
  f"Chapter {chapter_number} Summary: {chapter_content[:500]}..."
360
  )
361
 
362
  return chapter_content
363
-
364
  except Exception as e:
365
  st.error(f"Chapter generation failed: {e}")
366
  return self._fallback_chapter_content(book_concept, chapter_number)
367
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  def _retrieve_context(self, chapter_number: int) -> str:
369
  """
370
  Retrieve context for a specific chapter
371
 
372
  Args:
373
- chapter_number (int): Chapter number to retrieve context for
374
 
375
  Returns:
376
  str: Retrieved context or empty string
377
  """
378
  try:
379
- # Search for relevant context in vector store
380
  search_results = self.context_store.similarity_search(
381
  f"chapter_{chapter_number}",
382
  k=1
@@ -391,12 +402,34 @@ class BookWritingOrchestrator:
391
  st.error(f"Context retrieval failed: {e}")
392
  return ""
393
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  def main():
395
  """
396
  Demonstration of BookWritingOrchestrator
397
  """
398
  try:
399
- # Create orchestrator
400
  orchestrator = BookWritingOrchestrator()
401
 
402
  # Generate book concept
 
1
  # File: multi_agent_book_workflow.py
2
+ # Location: /multi_agent_book_workflow.py
3
  # Description: Core multi-agent book writing orchestration system
4
 
5
  import os
6
  import uuid
7
  import numpy as np
8
  import streamlit as st
9
+ from typing import Dict, List, Any, Optional
10
 
11
  # Vector Store and Embedding
12
  from langchain.embeddings import OpenAIEmbeddings
 
16
  # Agent and LLM Frameworks
17
  from crewai import Agent, Task, Crew
18
  from langchain_openai import ChatOpenAI
19
+ from langchain_core.language_models.chat_models import BaseChatModel
20
+ from langchain.schema import AIMessage, HumanMessage
21
  from langchain.memory import ConversationBufferMemory
22
+ import anthropic
23
+
24
+ class CustomChatAnthropic(BaseChatModel):
25
+ """Custom Chat Model for Anthropic"""
26
+
27
+ def __init__(self, model_name: str, temperature: float, anthropic_api_key: str):
28
+ """Initialize the custom Anthropic chat model"""
29
+ super().__init__()
30
+ self.client = anthropic.Anthropic(api_key=anthropic_api_key)
31
+ self.model_name = model_name
32
+ self.temperature = temperature
33
+
34
+ def _generate(self, messages, stop=None, run_manager=None, **kwargs):
35
+ """Generate a response"""
36
+ message_content = []
37
+ for message in messages:
38
+ if isinstance(message, HumanMessage):
39
+ message_content.append({"role": "user", "content": message.content})
40
+ elif isinstance(message, AIMessage):
41
+ message_content.append({"role": "assistant", "content": message.content})
42
+
43
+ response = self.client.messages.create(
44
+ model=self.model_name,
45
+ messages=message_content,
46
+ temperature=self.temperature,
47
+ max_tokens=1000
48
+ )
49
+
50
+ return AIMessage(content=response.content[0].text)
51
+
52
+ @property
53
+ def _llm_type(self) -> str:
54
+ """Return type of llm."""
55
+ return "anthropic"
56
 
57
  class BookWritingOrchestrator:
58
+ def __init__(self, api_key: Optional[str] = None):
59
  """
60
  Initialize the book writing orchestrator with multi-agent setup
61
 
 
81
  st.error(f"Agent setup failed: {e}")
82
  raise RuntimeError("Could not initialize book writing agents") from e
83
 
84
+ def _setup_api_keys(self, api_key: Optional[str] = None):
85
  """
86
  Validate and set up API keys with comprehensive checks
87
  """
 
129
  api_key=self.openai_api_key
130
  )
131
 
132
+ # Anthropic Configuration with custom wrapper
133
+ self.anthropic_llm = CustomChatAnthropic(
134
+ model_name="claude-3-sonnet-20240229",
135
+ temperature=0.7,
136
+ anthropic_api_key=self.anthropic_api_key
137
  )
138
 
139
  # Global memory for cross-chapter context
 
191
  memory=True
192
  )
193
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  def generate_book_concept(self, initial_prompt: str) -> Dict[str, Any]:
195
  """
196
  Generate a comprehensive book concept using multi-agent collaboration
 
208
  {initial_prompt}
209
 
210
  Provide detailed outputs including:
211
+ 1. Title: A unique and compelling book title
212
+ 2. Genre: Primary genre and any relevant subgenres
213
+ 3. Target Audience: Specific demographic and reader profile
214
+ 4. Core Premise: The central concept or hook
215
+ 5. Chapter Outline: Brief outline of proposed chapters
216
+ 6. Narrative Approach: Point of view and stylistic elements
217
+ 7. Key Themes: Major themes to be explored
218
+
219
+ Format the output as a structured JSON object.
220
  """,
221
+ agent=self.concept_agent
 
222
  )
223
 
224
  # Research Validation Task
225
  research_task = Task(
226
+ description="""
227
+ Review and enhance the generated book concept with:
228
+ 1. Market analysis and genre conventions
229
+ 2. Comparable successful titles
230
+ 3. Unique selling points
231
+ 4. Potential areas for deeper exploration
232
+
233
+ Add these insights to the concept structure.
234
+ """,
235
+ agent=self.research_agent
236
  )
237
 
238
  # Create Crew for Collaborative Processing
 
242
  verbose=True
243
  )
244
 
 
245
  try:
246
+ # Execute Collaborative Workflow
247
  result = book_concept_crew.kickoff()
248
 
249
+ # Parse and structure the result
250
+ parsed_concept = self._parse_concept(result)
251
+
252
  # Store Context in FAISS Vector Store
253
+ self._store_context('book_concept', str(parsed_concept))
254
+
255
+ return parsed_concept
256
 
 
 
257
  except Exception as e:
258
  st.error(f"Book concept generation failed: {e}")
259
  return self._fallback_book_concept(initial_prompt)
260
 
261
+ def generate_chapter_content(self, book_concept: Dict[str, Any], chapter_number: int) -> str:
 
 
262
  """
263
+ Generate content for a specific chapter
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
  Args:
266
+ book_concept (Dict): Book concept data
267
+ chapter_number (int): Chapter number to generate
 
 
 
 
 
 
 
 
 
 
 
 
268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
  Returns:
270
  str: Generated chapter content
271
  """
272
  # Get previous context if available
273
  previous_context = self._retrieve_context(chapter_number - 1) if chapter_number > 1 else ""
274
 
275
+ # Writing Task
276
  writing_task = Task(
277
  description=f"""
278
+ Write Chapter {chapter_number} for:
279
+ Title: {book_concept.get('title', 'Untitled')}
280
+ Genre: {book_concept.get('genre', 'Unspecified')}
281
 
 
282
  Previous Context: {previous_context}
283
 
284
+ Requirements:
285
+ 1. Follow the established narrative style
286
+ 2. Maintain consistency with previous chapters
287
+ 3. Advance the plot or themes meaningfully
288
+ 4. Include appropriate scene-setting and character development
289
+
290
+ Generate a complete, polished chapter.
291
  """,
292
+ agent=self.writing_agent
 
293
  )
294
 
295
  # Editing Task
296
  editing_task = Task(
297
+ description="""
298
+ Review and refine the chapter focusing on:
299
+ 1. Narrative flow and pacing
300
+ 2. Character consistency
301
+ 3. Thematic development
302
+ 4. Language and style polish
303
+
304
+ Provide a final, edited version.
305
+ """,
306
+ agent=self.editing_agent
307
  )
308
 
309
  # Create Crew for Chapter Generation
 
320
  # Store Chapter Context
321
  self._store_context(f'chapter_{chapter_number}', chapter_content)
322
 
323
+ # Update global memory with chapter summary
324
  self.global_memory.chat_memory.add_user_message(
325
  f"Chapter {chapter_number} Summary: {chapter_content[:500]}..."
326
  )
327
 
328
  return chapter_content
329
+
330
  except Exception as e:
331
  st.error(f"Chapter generation failed: {e}")
332
  return self._fallback_chapter_content(book_concept, chapter_number)
333
 
334
+ def _parse_concept(self, raw_concept: str) -> Dict[str, Any]:
335
+ """
336
+ Parse the raw concept output into a structured format
337
+ """
338
+ try:
339
+ # Split the content into sections
340
+ sections = raw_concept.strip().split('\n\n')
341
+ concept = {}
342
+
343
+ # Extract key-value pairs
344
+ for section in sections:
345
+ lines = section.strip().split('\n')
346
+ for line in lines:
347
+ if ':' in line:
348
+ key, value = line.split(':', 1)
349
+ concept[key.strip()] = value.strip()
350
+
351
+ # Ensure required fields
352
+ required_fields = ['title', 'genre', 'target_audience', 'core_premise']
353
+ for field in required_fields:
354
+ if field not in concept:
355
+ concept[field] = "Unspecified"
356
+
357
+ return concept
358
+
359
+ except Exception as e:
360
+ st.error(f"Concept parsing failed: {e}")
361
+ return self._fallback_book_concept("Failed to parse concept")
362
+
363
+ def _store_context(self, context_key: str, content: str):
364
+ """Store context in FAISS vector store"""
365
+ try:
366
+ document = Document(
367
+ page_content=content,
368
+ metadata={
369
+ "project_id": self.project_id,
370
+ "context_key": context_key
371
+ }
372
+ )
373
+
374
+ new_store = FAISS.from_documents([document], self.embeddings)
375
+ self.context_store.merge_from(new_store)
376
+
377
+ except Exception as e:
378
+ st.error(f"Context storage failed: {e}")
379
+
380
  def _retrieve_context(self, chapter_number: int) -> str:
381
  """
382
  Retrieve context for a specific chapter
383
 
384
  Args:
385
+ chapter_number (int): Chapter number
386
 
387
  Returns:
388
  str: Retrieved context or empty string
389
  """
390
  try:
 
391
  search_results = self.context_store.similarity_search(
392
  f"chapter_{chapter_number}",
393
  k=1
 
402
  st.error(f"Context retrieval failed: {e}")
403
  return ""
404
 
405
+ def _fallback_book_concept(self, initial_prompt: str) -> Dict[str, Any]:
406
+ """Provide fallback book concept"""
407
+ return {
408
+ "title": "Untitled Project",
409
+ "genre": "General Fiction",
410
+ "target_audience": "General Adult",
411
+ "core_premise": initial_prompt,
412
+ "chapter_outline": ["Chapter 1: Introduction"],
413
+ "narrative_approach": "Standard Third-Person Narrative",
414
+ "status": "fallback_generated"
415
+ }
416
+
417
+ def _fallback_chapter_content(self, book_concept: Dict[str, Any], chapter_number: int) -> str:
418
+ """Provide fallback chapter content"""
419
+ return f"""
420
+ Chapter {chapter_number}
421
+
422
+ [Placeholder content for {book_concept.get('title', 'Untitled')}]
423
+
424
+ This is auto-generated fallback content due to an error in chapter generation.
425
+ Please try regenerating this chapter or contact support if the issue persists.
426
+ """
427
+
428
  def main():
429
  """
430
  Demonstration of BookWritingOrchestrator
431
  """
432
  try:
 
433
  orchestrator = BookWritingOrchestrator()
434
 
435
  # Generate book concept