File size: 16,427 Bytes
080af87
 
 
 
 
 
0722d11
388a202
6492325
080af87
bc0b6ce
 
 
 
 
080af87
 
 
388a202
 
080af87
388a202
 
f2be5a7
6492325
388a202
f2be5a7
6492325
 
 
 
 
 
 
 
388a202
 
 
6492325
 
 
 
 
 
 
388a202
6492325
 
 
 
 
 
f2be5a7
6492325
 
 
 
388a202
 
 
 
 
080af87
f2be5a7
080af87
388a202
080af87
 
 
bc0b6ce
0722d11
f2be5a7
6492325
bc0b6ce
f2be5a7
bc0b6ce
 
f2be5a7
bc0b6ce
 
f2be5a7
6492325
0722d11
bc0b6ce
 
 
 
 
388a202
bc0b6ce
6492325
bc0b6ce
6492325
 
 
f2be5a7
6492325
 
 
f2be5a7
6492325
 
 
f2be5a7
6492325
 
 
 
f2be5a7
6492325
 
 
bc0b6ce
 
 
 
 
 
 
 
f2be5a7
bc0b6ce
 
 
 
f2be5a7
bc0b6ce
 
f2be5a7
bc0b6ce
0722d11
bc0b6ce
 
 
 
 
5df3314
 
 
bc0b6ce
2dc1810
0722d11
2dc1810
0722d11
 
 
f2be5a7
388a202
 
 
 
 
0722d11
f2be5a7
bc0b6ce
0722d11
bc0b6ce
0722d11
 
f2be5a7
0722d11
2dc1810
 
0722d11
bc0b6ce
080af87
 
 
 
6492325
 
 
 
 
 
 
 
 
 
f2be5a7
6492325
 
 
 
 
 
 
 
 
f2be5a7
6492325
 
 
 
 
 
 
 
 
f2be5a7
6492325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f2be5a7
6492325
f2be5a7
6492325
 
 
bc0b6ce
080af87
f2be5a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bc0b6ce
388a202
f2be5a7
bc0b6ce
6492325
 
f2be5a7
6492325
 
 
 
 
 
f2be5a7
6492325
f2be5a7
6492325
 
 
 
 
f2be5a7
6492325
 
91ee4dd
6492325
 
f2be5a7
6492325
 
 
 
 
 
 
 
f2be5a7
6492325
 
91ee4dd
6492325
 
f2be5a7
6492325
 
 
 
 
 
f2be5a7
6492325
bc0b6ce
f2be5a7
6492325
bc0b6ce
f2be5a7
6492325
2dc1810
 
 
f2be5a7
bc0b6ce
f2be5a7
 
 
 
bc0b6ce
388a202
6492325
388a202
6492325
388a202
 
f2be5a7
388a202
 
 
 
 
 
 
f2be5a7
388a202
 
 
 
 
f2be5a7
388a202
f2be5a7
388a202
 
 
 
 
 
 
 
 
 
 
 
 
 
f2be5a7
388a202
 
f2be5a7
388a202
 
 
2dc1810
6492325
0722d11
2dc1810
 
 
0722d11
f2be5a7
2dc1810
 
f2be5a7
2dc1810
f2be5a7
0722d11
2dc1810
 
5df3314
f2be5a7
0722d11
 
 
 
 
 
f2be5a7
0722d11
 
 
 
f2be5a7
0722d11
 
 
f2be5a7
0722d11
 
 
f2be5a7
0722d11
 
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
# File: multi_agent_book_workflow.py
# Description: Core multi-agent book writing orchestration system

import os
import uuid
import numpy as np
import streamlit as st
from typing import Dict, List, Any, Optional
from pydantic import BaseModel, Field

# Vector Store and Embedding
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.docstore.document import Document

# Agent and LLM Frameworks
from crewai import Agent, Task, Crew
from langchain_openai import ChatOpenAI
from langchain_core.language_models.chat_models import BaseChatModel
from langchain.schema import AIMessage, HumanMessage
from langchain.memory import ConversationBufferMemory
import anthropic


class CustomChatAnthropic(BaseChatModel, BaseModel):
    """Custom Chat Model for Anthropic"""

    anthropic_api_key: str = Field(...)
    model_name: str = Field(...)
    temperature: float = Field(default=0.7)
    client: Any = None

    def __init__(self, **data):
        super().__init__(**data)
        self.client = anthropic.Anthropic(api_key=self.anthropic_api_key)

    def _generate(self, messages, stop=None, run_manager=None, **kwargs):
        """Generate a response"""
        try:
            message_content = []
            for message in messages:
                if isinstance(message, HumanMessage):
                    message_content.append({"role": "user", "content": message.content})
                elif isinstance(message, AIMessage):
                    message_content.append({"role": "assistant", "content": message.content})

            response = self.client.messages.create(
                model=self.model_name,
                messages=message_content,
                temperature=self.temperature,
                max_tokens=1000
            )

            return AIMessage(content=response.content[0].text)
        except Exception as e:
            st.error(f"Error generating response: {str(e)}")
            raise

    @property
    def _llm_type(self) -> str:
        """Return type of llm."""
        return "anthropic"


class BookWritingOrchestrator:
    def __init__(self, api_key: Optional[str] = None):
        """
        Initialize the book writing orchestrator with multi-agent setup
        """
        # Generate a unique project identifier
        self.project_id = str(uuid.uuid4())

        # Set up API keys and debug info
        self._setup_api_keys(api_key)

        # Initialize context management
        self._initialize_context_store()

        # Setup AI models
        self._setup_ai_models()

        # Initialize agents
        try:
            self.setup_agents()
        except Exception as e:
            st.error(f"Agent setup failed: {e}")
            raise RuntimeError("Could not initialize book writing agents") from e

    def _setup_api_keys(self, api_key: Optional[str] = None):
        """
        Validate and set up API keys with comprehensive checks for Hugging Face Spaces
        """
        try:
            # Debug: Print available environment variables
            print("Available environment variables:", [k for k in os.environ.keys()])

            # OpenAI API Key
            self.openai_api_key = os.getenv('OPENAI_API_KEY')
            print("OpenAI API Key found:", bool(self.openai_api_key))

            # Anthropic API Key
            self.anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')
            print("Anthropic API Key found:", bool(self.anthropic_api_key))

            if not self.openai_api_key:
                raise ValueError("OpenAI API key not found in environment variables")
            if not self.anthropic_api_key:
                raise ValueError("Anthropic API key not found in environment variables")

        except Exception as e:
            st.error(f"Error accessing API keys: {str(e)}")
            raise

    def _initialize_context_store(self):
        """
        Set up FAISS vector store for context management
        """
        try:
            # Initialize embeddings
            self.embeddings = OpenAIEmbeddings(api_key=self.openai_api_key)

            # Create initial empty FAISS vector store
            self.context_store = FAISS.from_documents(
                documents=[
                    Document(
                        page_content="Initial project context",
                        metadata={"project_id": self.project_id}
                    )
                ],
                embedding=self.embeddings
            )
        except Exception as e:
            st.error(f"Context store initialization failed: {e}")
            raise

    def _setup_ai_models(self):
        """
        Configure AI models with robust error handling
        """
        try:
            # OpenAI Configuration
            self.openai_llm = ChatOpenAI(
                model="gpt-4-turbo-preview",
                temperature=0.7,
                api_key=self.openai_api_key
            )

            # Anthropic Configuration with custom wrapper
            self.anthropic_llm = CustomChatAnthropic(
                model_name="claude-3-sonnet-20240229",
                temperature=0.7,
                anthropic_api_key=self.anthropic_api_key
            )

            # Global memory for cross-chapter context
            self.global_memory = ConversationBufferMemory(
                memory_key="book_writing_context",
                return_messages=True
            )

        except Exception as e:
            st.error(f"AI model setup failed: {str(e)}")
            print(f"Detailed error: {str(e)}")
            raise

    def setup_agents(self):
        """
        Create specialized agents for book writing workflow
        """
        try:
            # Concept Development Agent
            self.concept_agent = Agent(
                role='Book Concept Architect',
                goal='Develop a comprehensive and compelling book concept',
                backstory='An expert literary strategist who transforms raw ideas into structured book frameworks',
                verbose=True,
                llm=self.openai_llm,
                memory=True
            )

            # Research Agent
            self.research_agent = Agent(
                role='Literary Research Specialist',
                goal='Conduct in-depth research to support the book\'s narrative and authenticity',
                backstory='A meticulous researcher with expertise in gathering and synthesizing complex information',
                verbose=True,
                llm=self.anthropic_llm,
                memory=True
            )

            # Content Generation Agent
            self.writing_agent = Agent(
                role='Master Storyteller',
                goal='Create engaging, coherent, and stylistically consistent narrative content',
                backstory='A versatile writer capable of crafting compelling prose across various genres',
                verbose=True,
                llm=self.openai_llm,
                memory=True
            )

            # Editing Agent
            self.editing_agent = Agent(
                role='Narrative Refinement Specialist',
                goal='Ensure narrative consistency, quality, and stylistic excellence',
                backstory='A seasoned editor with a keen eye for storytelling nuance and structural integrity',
                verbose=True,
                llm=self.anthropic_llm,
                memory=True
            )
        except Exception as e:
            st.error(f"Error setting up agents: {str(e)}")
            raise

    def _fallback_book_concept(self, initial_prompt: str) -> Dict[str, Any]:
        """Provide fallback book concept"""
        return {
            "title": "Untitled Project",
            "genre": "General Fiction",
            "target_audience": "General Adult",
            "core_premise": initial_prompt,
            "chapter_outline": ["Chapter 1: Introduction"],
            "narrative_approach": "Standard Third-Person Narrative",
            "status": "fallback_generated"
        }

    def _fallback_chapter_content(self, book_concept: Dict[str, Any], chapter_number: int) -> str:
        """Provide fallback chapter content"""
        return f"""
        Chapter {chapter_number}

        [Placeholder content for {book_concept.get('title', 'Untitled')}]

        This is auto-generated fallback content due to an error in chapter generation.
        Please try regenerating this chapter or contact support if the issue persists.
        """

    def generate_book_concept(self, initial_prompt: str) -> Dict[str, Any]:
        """
        Generate a comprehensive book concept using multi-agent collaboration
        """
        try:
            # Concept Development Task
            concept_task = Task(
                description=f"""
                Develop a comprehensive book concept based on this initial idea:
                {initial_prompt}

                Provide detailed outputs including:
                1. Title: A unique and compelling book title
                2. Genre: Primary genre and any relevant subgenres
                3. Target Audience: Specific demographic and reader profile
                4. Core Premise: The central concept or hook
                5. Chapter Outline: Brief outline of proposed chapters
                6. Narrative Approach: Point of view and stylistic elements
                7. Key Themes: Major themes to be explored

                Format the output as a structured JSON object.
                """,
                expected_output="A detailed JSON-formatted book concept with title, genre, target audience, and other key elements",
                agent=self.concept_agent
            )

            # Research Validation Task
            research_task = Task(
                description="""
                Review and enhance the generated book concept with:
                1. Market analysis and genre conventions
                2. Comparable successful titles
                3. Unique selling points
                4. Potential areas for deeper exploration

                Add these insights to the concept structure.
                """,
                expected_output="A detailed analysis and enhancement of the book concept with market insights and comparative analysis",
                agent=self.research_agent
            )

            # Create Crew for Collaborative Processing
            book_concept_crew = Crew(
                agents=[self.concept_agent, self.research_agent],
                tasks=[concept_task, research_task],
                verbose=True
            )

            # Execute Collaborative Workflow
            result = book_concept_crew.kickoff()

            # Parse and structure the result
            parsed_concept = self._parse_concept(result)

            # Store Context
            self._store_context('book_concept', str(parsed_concept))

            return parsed_concept

        except Exception as e:
            st.error(f"Book concept generation failed: {e}")
            return self._fallback_book_concept(initial_prompt)

    def generate_chapter_content(self, book_concept: Dict[str, Any], chapter_number: int) -> str:
        """Generate content for a specific chapter"""
        try:
            # Get previous context
            previous_context = self._retrieve_context(chapter_number - 1) if chapter_number > 1 else ""

            # Writing Task
            writing_task = Task(
                description=f"""
                Write Chapter {chapter_number} for:
                Title: {book_concept.get('title', 'Untitled')}
                Genre: {book_concept.get('genre', 'Unspecified')}

                Previous Context: {previous_context}

                Requirements:
                1. Follow the established narrative style
                2. Maintain consistency with previous chapters
                3. Advance the plot or themes meaningfully
                4. Include appropriate scene-setting and character development

                Generate a complete, polished chapter.
                """,
                expected_output="A complete, polished chapter with narrative continuity and character development",
                agent=self.writing_agent
            )

            # Editing Task
            editing_task = Task(
                description="""
                Review and refine the chapter focusing on:
                1. Narrative flow and pacing
                2. Character consistency
                3. Thematic development
                4. Language and style polish

                Provide a final, edited version.
                """,
                expected_output="A refined and polished version of the chapter with improved narrative quality",
                agent=self.editing_agent
            )

            # Create Crew for Chapter Generation
            chapter_crew = Crew(
                agents=[self.writing_agent, self.editing_agent],
                tasks=[writing_task, editing_task],
                verbose=True
            )

            # Generate Chapter
            chapter_content = chapter_crew.kickoff()

            # Store Context
            self._store_context(f'chapter_{chapter_number}', chapter_content)

            # Update Memory
            self.global_memory.chat_memory.add_user_message(
                f"Chapter {chapter_number} Summary: {chapter_content[:500]}..."
            )

            return chapter_content

        except Exception as e:
            st.error(f"Chapter generation failed: {e}")
            return self._fallback_chapter_content(book_concept, chapter_number)

    def _parse_concept(self, raw_concept: str) -> Dict[str, Any]:
        """Parse the raw concept output into a structured format"""
        try:
            # Split content into sections
            sections = raw_concept.strip().split('\n\n')
            concept = {}

            # Extract key-value pairs
            for section in sections:
                lines = section.strip().split('\n')
                for line in lines:
                    if ':' in line:
                        key, value = line.split(':', 1)
                        concept[key.strip()] = value.strip()

            # Ensure required fields
            required_fields = ['title', 'genre', 'target_audience', 'core_premise']
            for field in required_fields:
                if field not in concept:
                    concept[field] = "Unspecified"

            return concept

        except Exception as e:
            st.error(f"Concept parsing failed: {e}")
            return self._fallback_book_concept("Failed to parse concept")

    def _store_context(self, context_key: str, content: str):
        """Store context in FAISS vector store"""
        try:
            document = Document(
                page_content=content,
                metadata={
                    "project_id": self.project_id,
                    "context_key": context_key
                }
            )

            new_store = FAISS.from_documents([document], self.embeddings)
            self.context_store.merge_from(new_store)

        except Exception as e:
            st.error(f"Context storage failed: {e}")

    def _retrieve_context(self, chapter_number: int) -> str:
        """Retrieve context for a specific chapter"""
        try:
            search_results = self.context_store.similarity_search(
                f"chapter_{chapter_number}",
                k=1
            )

            if search_results:
                return search_results[0].page_content

            return ""

        except Exception as e:
            st.error(f"Context retrieval failed: {e}")
            return ""


def main():
    """
    Demonstration of BookWritingOrchestrator
    """
    try:
        orchestrator = BookWritingOrchestrator()

        # Generate book concept
        initial_prompt = "A science fiction story about space exploration"
        book_concept = orchestrator.generate_book_concept(initial_prompt)
        print("Book Concept:", book_concept)

        # Generate first chapter
        first_chapter = orchestrator.generate_chapter_content(book_concept, 1)
        print("\nFirst Chapter:\n", first_chapter)

    except Exception as e:
        print(f"An error occurred: {e}")


if __name__ == "__main__":
    main()