first try

This commit is contained in:
isaac hershenson
2024-06-19 15:41:18 -07:00
parent 6c63c24787
commit 118aafc017
4 changed files with 354 additions and 208 deletions
+98 -90
View File
@@ -6,14 +6,13 @@ from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from copy import deepcopy
'''
llm = ChatAnthropic(model="claude-3-haiku-20240307", max_tokens_to_sample=4000)
summary_llm = ChatAnthropic(model="claude-3-sonnet-20240229")
'''
llm = ChatOpenAI(model="gpt-4o")
summary_llm = ChatOpenAI(model="gpt-4o")
title_llm = ChatOpenAI(model="gpt-4o",metadata={"name":"title_llm"})
summary_llm = ChatOpenAI(model="gpt-4o",metadata={"name":"summary_llm"})
brainstorm_llm = ChatOpenAI(model="gpt-4o",temperature=1,metadata={"name":"brainstorm_llm"})
plan_llm = ChatAnthropic(model="claude-3-haiku-20240307",metadata={"name":"plan_llm"})
write_llm = ChatAnthropic(model="claude-3-sonnet-20240229",metadata={"name":"write_llm"})
messages = [
summary_messages = [
("system", "You are an assistant solely focused on summarizing books. Your goal \
is to summarize so that all logical dependencies are captured. It is not important for \
you to summarize minute details but rather focus on important things like character names, \
@@ -22,9 +21,62 @@ messages = [
("human", "Please help me summarize the following book: {chapters_str}"),
]
prompt = ChatPromptTemplate.from_messages(messages)
brainstorm_messages = [
("system", "You are an assistant tasked with brainstorming ideas for brainstorming ideas for \
a chapter in a story. You should brainstorm ideas relevant to the plotline and in accordance with \
the users wishes for the next chapter. You should brainstorm multiple ideas for what the chapter could \
be about, making detailed descriptions of all your ideas. Do not return anything other than a numbered list of ideas."),
("human", "{summary_request}"),
("human", "{detail_request}"),
("human", "{style_request}"),
("human", "This is the summary of the story up to this point: {story_summary}"),
("human", "I would like to {action}. Can you please help me brainstorm ideas for that?")
]
summary_chain = prompt | summary_llm | StrOutputParser()
outline_messages = [
("system", "You are an assistant tasked with outlining a new chapter in a story. You will be provided with \
some potential ideas for the chapter. You should choose one of those ideas, and then write a clear outline \
for it. Your outline should include a beginning, middle, and end. You should only return the outline of the \
story, not any other information or text. Here is an example of what you should return: \
\
I. Introduction\n- **Setting Description:**\n - The old mansion at the end of Hawthorn Lane, shrouded in mystery and ivy.\n - Historical significance: Passed down through generations in Emma's family.\n- **Character Introduction:**\n - Emma, 27 years old, determined to uncover family secrets.\n - Mention of the secret room rumored by her great-grandmother.\n\n#### II. Emma's Curiosity and Determination\n- **Great-Grandmother's Mention:**\n - Flashback to Emma's childhood memory of great-grandmother hinting at the secret room.\n- **Emma's Motivation:**\n - Transition from childhood curiosity to adult determination to uncover the secret.\n\n#### III. The Stormy Evening\n- **Setting the Scene:**\n - Description of the stormy evening: rain, wind, ancient trees.\n- **Preparation:**\n - Emma armed with a flashlight and an old blueprint found in the attic.\n - Description of the blueprint: yellowed, frayed, delicate.\n\n#### IV. Discovery of the Unmarked Space\n- **Blueprint Examination:**\n - Emma tracing the lines and discovering the unmarked space between the library and the drawing-room.\n- **Realization:**\n - Heart skipping a beat; determination to investigate further.\n\n#### V. The Library\n- **Description of the Library:**\n - Cavernous room, floor-to-ceiling bookshelves, scent of aged paper.\n- **Search for Irregularities:**\n - Emma scanning the walls, finding the worn bookshelf.\n- **The Lost Histories Book:**\n - Discovery of the out-of-place leather-bound volume.\n - Pulling the book to reveal the secret passage.\n\n#### VI. The Hidden Passage\n- **Bookshelf Mechanism:**\n - Bookshelf swinging open to reveal a narrow passage.\n- **Initial Hesitation:**\n - Emmas breath catching, moment of hesitation.\n- **Descent:**\n - Flashlight beam, steep spiral staircase, mix of fear and excitement.\n\n#### VII. The Secret Room\n- **Room Description:**\n - Small, dimly lit, musty air.\n- **The Wooden Chest:**\n - Intricately carved surface, Emmas trembling fingers lifting the lid.\n- **Contents of the Chest:**\n - Faded photographs, letters tied with ribbon, ornate key.\n\n#### VIII. Discoveries and Revelations\n- **Photographs:**\n - Black-and-white image of great-grandmother and an unknown man.\n - Noting the secret happiness in their eyes.\n- **Love Letters:**\n - Untying the ribbon, reading the first letter.\n - Story of forbidden love and a promise to protect their secret.\n\n#### IX. Emotional Connection\n- **Emmas Reaction:**\n - Eyes filling with tears, realization of the rooms significance.\n- **Legacy of Love:**\n - Understanding the room as a sanctuary of love and resilience.\n- **The Ornate Key:**\n - Speculation about what the key might unlock.\n\n#### X. Emma's Resolution\n- **Vow to Uncover the Full Story:**\n - Determination to piece together the past.\n - Honoring the legacy of love and courage.\n- **Emerging from the Secret Room:**\n - Returning up the spiral staircase, storm waning, dawn breaking.\n\n#### XI. Conclusion\n- **Newfound Connection:**\n - Deeper connection to heritage.\n - Understanding the importance of discovering, cherishing, and passing on family secrets.\n- **End of a Mystery:**\n - Mansion holds one less mystery.\n - Walls whispering a new story of love and resilience.\n\n### Themes and Motifs\n- **Heritage and Legacy:**\n - Importance of family history and secrets.\n- **Love and Resilience:**\n - Enduring nature of love and strength across generations.\n- **Curiosity and Discovery:**\n - Emma's journey from curiosity to discovery and understanding.\n \n### Literary Devices\n- **Imagery:**\n - Vivid descriptions of the mansion, storm, and secret room.\n- **Foreshadowing:**\n - Hints from great-grandmother about the secret room.\n- **Symbolism:**\n - The ornate key symbolizing unlocking the past and hidden truths."),
("human", "{summary_request}"),
("human", "{detail_request}"),
("human", "{style_request}"),
("human", "This is the summary of the story up to this point: {story_summary}"),
("human", "Here are a list of ideas for the chapter I would like you to outline: {brainstorm_ideas}"),
("human", "I would like to {action}. Can you please make a clear outline for that chapter?")
]
write_messages = [
("system", "You are an assistant tasked with writing book chapters. You will receive an outline \
of the chapter and you should return the content of the chapter only. Do not return the chapter numnber, the chapter title, or any other information \
related to the chapter. Just write the words that would appear on the page. You should \
return the content like the following example: \
\
\
The old mansion stood at the end of Hawthorn Lane, shrouded in mystery and ivy. It had been in Emma\'s family for generations, passed down from one enigmatic ancestor to another. Though the house had always been a source of curiosity, none of the family\'s secrets intrigued Emma quite as much as the rumored secret room.\n\nHer great-grandmother had once mentioned it in passing, her eyes twinkling with a mixture of mischief and nostalgia. But Emma had been too young to press for details then. Now, at twenty-seven, her curiosity had matured into a determination.\n\nIt was a stormy evening when Emma decided to search for the room. The rain battered against the windows, and the wind howled through the ancient trees surrounding the mansion. Armed with a flashlight and an old, dusty blueprint of the house she found in the attic, she made her way through the labyrinthine corridors.\n\nThe blueprint had been yellowed with age, its edges frayed and delicate. Emma traced her finger along the lines, noting the familiar rooms and passages. Her heart skipped a beat when she noticed a small, unmarked space between the library and the drawing-room—an area that didnt correspond with any door or window she knew of.\n\nShe hurried to the library, her footsteps echoing in the vast, empty halls. The library was a cavernous room, filled with floor-to-ceiling bookshelves and the comforting scent of aged paper. She scanned the walls, searching for any irregularities. Her eyes landed on a particular bookshelf, slightly more worn than the others, its wood darker and dustier.\n\nEmma approached it cautiously, running her fingers along the spines of the old books. One of the books, a leather-bound volume titled \"The Lost Histories,\" seemed oddly out of place. She pulled it, and with a soft click, the entire bookshelf swung open to reveal a narrow passage.\n\nHer breath caught in her throat. The flashlight beam sliced through the darkness, revealing a steep, spiral staircase. She hesitated only for a moment before descending, her heart pounding with a mix of fear and excitement.\n\nThe staircase led to a small, dimly lit room. The air was musty, filled with the scent of forgotten memories. In the center of the room stood a wooden chest, its surface intricately carved with symbols Emma didn\'t recognize. She knelt beside it, her fingers trembling as she lifted the lid.\n\nInside the chest were relics of the past: faded photographs, letters tied with ribbon, and a peculiar, ornate key. Emma picked up one of the photographs. It was a black-and-white image of her great-grandmother as a young woman, standing beside a man Emma had never seen before. They were smiling, their eyes filled with a secret happiness.\n\nShe turned her attention to the letters, carefully untying the ribbon. The delicate parchment crackled as she unfolded the first one. It was a love letter, written in elegant, flowing script. As she read, a story unfolded—a tale of forbidden love, hidden meetings, and a promise to protect their secret at all costs.\n\nEmma\'s eyes filled with tears. The secret room was more than just a hidden space; it was a sanctuary of love, a testament to the resilience of her great-grandmother\'s spirit. The ornate key, she realized, must unlock something even more precious.\n\nWith newfound determination, Emma vowed to uncover the full story. She would piece together the fragments of the past, honoring the legacy of love and courage that had been hidden away for so long.\n\nAs she made her way back up the spiral staircase, the storm outside began to wane, the first rays of dawn breaking through the clouds. The secret room had given her more than just answers; it had bestowed upon her a deeper connection to her heritage, a reminder that some secrets are meant to be discovered, cherished, and passed on.\n\nAnd thus, the old mansion at the end of Hawthorn Lane held one less mystery, but its walls whispered a new story—a story of love, hidden away but never forgotten. \
\
Please remember to always only return the chapter writing itself. Do not return any other text."),
("human", "{summary_request}"),
("human", "{detail_request}"),
("human", "{style_request}"),
("human", "This is the summary of the story up to this point: {story_summary}"),
("human", "Here is the outline I would like you to follow when writing the chapter: {outline}"),
("human", "I would like to {action}. Can you please write the chapter for me, remebering to follow the outline I just provided? Pleease remember to return the chapter text only, not any commentary to the user or additional text.")
]
summary_prompt = ChatPromptTemplate.from_messages(summary_messages)
brainstorm_prompt = ChatPromptTemplate.from_messages(brainstorm_messages)
outline_prompt = ChatPromptTemplate.from_messages(outline_messages)
write_prompt = ChatPromptTemplate.from_messages(write_messages)
# Turn these into nodes with llm as router to run tests on each of the chains
# Add few shot prompting to improve the performance (get results from Langsmith)
summary_chain = summary_prompt | summary_llm | StrOutputParser()
brainstorm_chain = brainstorm_prompt | brainstorm_llm | StrOutputParser()
outline_chain = outline_prompt | plan_llm | StrOutputParser()
write_chain = write_prompt | write_llm | StrOutputParser()
class Chapter(TypedDict):
content: str
@@ -44,106 +96,65 @@ class State(TypedDict):
summary: str
details: str
style: str
summary_request: str = ""
detail_request: str = ""
style_request: str = ""
chapter_graph: Annotated[dict[str, Chapter], update_chapter_graph]
chapter_id_viewing: str
current_chapter_id: str
rewrite_instructions: str
continue_instructions: str
story_title: str = ""
instructions = """Your task is to collaborate with the user to write a novel, chapter by chapter, using the following process:
First, gather the initial information from the user:
<init>
Novel concept summary: {summary}
Additional details to include: {details}
Preferred writing style: {style}
</init>
Then, for each chapter:
<Chapter>
<brainstorm>
Review the novel concept, details, and previously written chapters. Brainstorm multiple ideas for plot, character development, symbols, allusion, and themes. Create a rough outline of the major events and scenes. Make notes on the chapter's beginning, middle, and end. Make multiple ideas for each part of the chapter.
</brainstorm>
<filtering>
Filter the brainstorm, only using the ideas that fit logically in the plot and follow the users instructions fairly rigidly.
</filtering>
<content>
Write a full, final draft of the chapter (2000-5000 words). Follow the outline and notes, maintaining the desired style and details. Focus on engaging storytelling, vivid descriptions, and realistic dialogue. Make sure there are no logical inconsitencies in the story as well. Do not include any of the brainstorm or the title of the chapter in the content, just the actual content of the chapter.
</content>
<chaptertitle>
Based on the final draft of the chapter, select a short chapter title that fits well. Plase don't include newline characters in the chapter title.
</chaptertitle>
</Chapter>
The keys to success are:
- Communicate clearly and work collaboratively with the user. Be well-attuned to their preferences and vision.
- Be organized and keep the novel's concept summary in mind, do not deviate too far from the summary and other prompts the user enters.
- Incorporate feedback graciously while preserving story integrity.
- Try to use the writig style and sound as similar to it as you can.
- Pay attention to detail in writing, revising and proofreading, make sure it reads like a novel not a blog post or short story.
- Keep the formatting strict, write the chapter content inbetween <content> </content> tags, and write the chapter title in between <chaptertitle> </chaptertitle> tags. Do not write the chapter title at the start of the the <content> </content> tags.
Always respond using the 'Chapter' function and then immediately stop. Do not respond by saying something along the lines of: I understand the task and am ready to help. Always return a written chapter, following the <Chapter> function instructions."""
edit_prompt = """Here's what we have so far:
<Progress>
{chapters_summary}
</Progress>
Here is the current state of the new chapter:
edit_prompt = """Here is the current state of the new chapter:
<Draft>
{draft}
</Draft>
Here are some edits we want to make to that chapter:
Here are some edits I want to make to that chapter:
<EditInstructions>
{edit}
</EditInstructions>
</EditInstructions>"""
Rewrite the the chapter with those instructions in mind. Reminder to respond in the correct <Chapter> form.
That means writing both <content> and a new <chaptertitle>. Please make sure to only return text that is in the story, not any comments you have to the user.
Ensure that any edits do not detract from the overall plot of the chapter unless you were specifically instructed to do so by the user."""
continue_prompt = """Here's what we have so far:
<Progress>
{chapters_summary}
</Progress>
Write the next chapter in this story, keeping the following instructions in mind
continue_prompt = """Here is what I want in the next chapter:
<Instructions>
{instructions}
</Instructions>
</Instructions>"""
Write the next chapter with those instructions in mind.
This chapter should pick up seamlessly from the previous chapters.
It should be a logical follow up.
Reminder to respond in the correct <Chapter> form."""
def write_chapter(user_message, chapters_summary, state):
brainstorm_ideas = brainstorm_chain.invoke({'story_summary':chapters_summary,'action':user_message,'summary_request':state['summary_request'], \
'detail_request':state['detail_request'],'style_request':state['style_request']})
outline = outline_chain.invoke({'story_summary':chapters_summary,'action':user_message,'summary_request':state['summary_request'], \
'detail_request':state['detail_request'],'style_request':state['style_request'],'brainstorm_ideas':brainstorm_ideas})
def parse(txt: str):
chapter_txt = txt
if "<content>" in txt and "</content>" in txt:
chapter_txt = txt.split("<content>")[1]
chapter_txt = chapter_txt.split("</content>")[0]
title = ""
if "<chaptertitle>" in txt and "</chaptertitle>" in txt:
title = txt.split("<chaptertitle>")[1]
title = title.split("</chaptertitle>")[0]
return chapter_txt, title
response = write_chain.invoke({'story_summary':chapters_summary,'action':user_message,'summary_request':state['summary_request'], \
'detail_request':state['detail_request'],'style_request':state['style_request'],'outline':outline})
chapter_content = response
chapter_title = summary_llm.invoke(f"Please come up with a title for the following chapter: {chapter_content}. The title should be 6 words or less.").content.replace("\"","").replace("\'","")
return chapter_content, chapter_title
def get_title(state,first_chapter):
return title_llm.invoke(f"Please come up with a short title, less than 6 words, for a story. The story has the following overall plot {state['summary']}, and here is the first chapter {first_chapter}").content.replace("\"","").replace("\'","")
def write_first_chapter(state):
prompt = instructions.format(summary=state['summary'], details=state['details'], style=state['style'])
response = llm.invoke(prompt)
chapter_content, chapter_title = parse(response.content)
if state['summary']:
state['summary_request'] = f"I would like the overall plot of the story to be {state['summary']}"
if state['details']:
state['detail_request'] = f"I would like you to keep the following details in mind when writing {state['details']}"
if state['style']:
state['style_request'] = f"Please make sure to use the following writing style {state['style']}"
chapter_content, chapter_title = write_chapter("Please write the first chapter of this story.", "no story up to this point, this is the first chapter!", state)
state['current_chapter_id'] = '1'
state['chapter_id_viewing'] = '1'
state['chapter_graph'] = {'1':Chapter(content=chapter_content,title=chapter_title,children=[],siblings=[],cousins=[],parent='-1')}
state['story_title'] = get_title(state,chapter_content)
return state
def summarize_current_story(state,chapter_id):
@@ -166,9 +177,8 @@ def edit_chapter(state):
draft=state['chapter_graph'][state['chapter_id_viewing']]['content'],
edit=state['rewrite_instructions']
)
prompt = instructions.format(summary=state['summary'], details=state['details'], style=state['style'])
response = llm.invoke([{"role": "system", "content": prompt}, {"role": "user", "content": user_message}])
chapter_content, chapter_title = parse(response.content)
chapter_content, chapter_title = write_chapter(user_message, chapters_summary, state)
#create new chapter
state['chapter_graph'][str(int(state["current_chapter_id"])+1)] = Chapter(content=chapter_content,title=chapter_title,children=[], \
@@ -191,10 +201,8 @@ def continue_chapter(state):
chapters_summary=chapters_summary,
instructions=state['continue_instructions']
)
prompt = instructions.format(summary=state['summary'], details=state['details'], style=state['style'])
response = llm.invoke([{"role": "system", "content": prompt}, {"role": "user", "content": user_message}])
chapter_content, chapter_title = parse(response.content)
chapter_content, chapter_title = write_chapter(user_message, chapters_summary, state)
#create new chapter
state['chapter_graph'][str(int(state["current_chapter_id"])+1)] = Chapter(content=chapter_content,title=chapter_title,children=[],siblings=[], \
cousins=deepcopy(state["chapter_graph"][state["chapter_id_viewing"]]['children']),\
+2
View File
@@ -3,6 +3,8 @@
"dependencies": [
"langchain_anthropic",
"langgraph",
"langchain_openai",
"langchain_core",
"."
],
"graphs": {
-1
View File
@@ -4,5 +4,4 @@ langchain_openai
langgraph_cli
langsmith
langgraph_sdk
streamlit_navigation_bar
streamlit_extras
+254 -117
View File
@@ -1,16 +1,31 @@
import random
import string
import streamlit as st
from langgraph_sdk import get_client
from streamlit_navigation_bar import st_navbar
import asyncio
from langsmith import Client
from streamlit_extras.stylable_container import stylable_container
import requests
feedback_client = Client(api_url="https://beta.api.smith.langchain.com")
async def start_agent():
client = get_client(url="https://ht-respectful-sundial-15-fbf5e59442b15c23a9306227-g3ps4aazkq-uc.a.run.app")
def get_public_ip():
try:
# Use an external API to fetch public IP address
response = requests.get('https://api64.ipify.org?format=json')
data = response.json()
ip_address = data['ip']
return ip_address
except requests.RequestException as e:
print(f"Error fetching IP address: {e}")
return None
async def start_agent(ip_address):
client = get_client()
#url="https://ht-respectful-sundial-15-fbf5e59442b15c23a9306227-g3ps4aazkq-uc.a.run.app")
assistants = await client.assistants.search()
assistants = [a for a in assistants if not a['config']]
thread = await client.threads.create()
thread = await client.threads.create(metadata={"user":ip_address})
assistant = assistants[0]
return [client,thread,assistant]
@@ -24,10 +39,36 @@ async def get_run_id_corresponding_to_node(client, thread, node_id):
return r['run_id']
return None
async def get_new_thread(client):
thread = await client.threads.create()
async def get_new_thread(client,ip_address):
thread = await client.threads.create(metadata={"user":ip_address})
return thread
async def get_thread_state(client,thread_id):
return await client.threads.get_state(thread_id)
async def get_user_threads(client,ip_address):
try:
# Try to run the async function using the existing event loop
threads = await client.threads.search(metadata={"user":ip_address})
except RuntimeError as e:
if "Event loop is closed" in str(e):
# If the event loop is closed, create a new one and run the async function
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
threads = await client.threads.search(metadata={"user":ip_address})
else:
raise e
untitled_count = 1
for t in threads:
t_state = await get_thread_state(client,t['thread_id'])
try:
t['story_title'] = t_state['values']['story_title']
except:
t['story_title'] = f"Untitled story #{untitled_count}"
untitled_count += 1
return threads
async def run_graph_with_input(client,thread,assistant,input,metadata={}):
# Need to add streaming capability
data = []
@@ -42,6 +83,33 @@ async def run_graph_with_input(client,thread,assistant,input,metadata={}):
pass
return data
llm_to_title = {
"starting":"Waiting for user input ...",
"brainstorm_llm": "Brainstorming ideas for chapter...",
"plan_llm": "Planning outline for chapter...",
"summary_llm": "Summarizing story so far...",
"write_llm": "Writing the chapter...",
"title_llm": "Generating a title for the story..."
}
async def generate_answer(placeholder, placeholder_title, input, client, thread, assistant, metadata = {}):
current_llm = "starting"
placeholder_title.write(llm_to_title[current_llm])
current_ind = 0
ans = ""
async for chunk in client.runs.stream(
thread['thread_id'], assistant['assistant_id'], input=input, config={"configurable":metadata}, \
stream_mode="messages",
):
if chunk.data and 'run_id' not in chunk.data:
if isinstance(chunk.data,dict):
current_llm = chunk.data[list(chunk.data.keys())[0]]['metadata']['name']
placeholder_title.write(llm_to_title[current_llm])
elif current_llm == "write_llm" and chunk.data[0]['content']:
ans += chunk.data[0]['content'][current_ind:]
placeholder.info(ans)
current_ind += len(chunk.data[0]['content'][current_ind:])
async def get_current_state(client,thread):
current_state = await client.threads.get_state(thread_id=thread['thread_id'])
return current_state
@@ -83,6 +151,7 @@ def transform_titles_into_options(titles):
async def update_session_variables():
current_state = await call_async_function_safely(get_current_state,st.session_state.client,st.session_state.thread)
st.session_state.chapter_graph = current_state['values']['chapter_graph']
st.session_state.story_title = current_state['values']['story_title']
st.session_state.currently_selected_chapter = str(current_state['values']['chapter_id_viewing'])
st.session_state.current_node_id = str(int(current_state['values']['current_chapter_id']) + 1)
st.session_state.next_chapter_options = st.session_state.chapter_graph[st.session_state.currently_selected_chapter]['children']
@@ -90,25 +159,35 @@ async def update_session_variables():
+ st.session_state.chapter_graph[st.session_state.currently_selected_chapter]['cousins'] + [st.session_state.currently_selected_chapter]
st.session_state.previous_chapter_options = [x for x in [st.session_state.chapter_graph[st.session_state.currently_selected_chapter]['parent']] if x!= '-1']
async def reset_session_variables():
async def reset_session_variables(ip_address):
st.session_state.chapter_graph = {"-1":{'content':"Click Start Story to begin writing!", 'title':"Pre-start Chapter"}}
st.session_state.client,st.session_state.thread,st.session_state.assistant = await call_async_function_safely(start_agent)
st.session_state.currently_selected_chapter = "-1"
st.session_state.current_node_id = '1'
st.session_state.story_title = ""
st.session_state.current_chapter_options = ["-1"]
st.session_state.previous_chapter_options, st.session_state.next_chapter_options = [],[]
async def stream(*args):
await asyncio.gather(call_async_function_safely(generate_answer,*args))
async def main():
st.title("Story Writing with Langgraph")
if "story_title" not in st.session_state or st.session_state.story_title == "":
st.title("Story Writing with Langgraph")
else:
st.title(st.session_state.story_title)
if "page_loaded" not in st.session_state:
st.session_state.page_loaded = False
st.session_state.selected_previous_chapter, st.session_state.selected_next_chapter, st.session_state.selected_current_chapter = None,None,None
st.session_state.story_title = ""
st.session_state.num_selected = 0
st.session_state.writing = False
st.session_state.ip_address = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
if st.session_state.page_loaded == False:
await reset_session_variables()
st.session_state.client,st.session_state.thread,st.session_state.assistant = await call_async_function_safely(start_agent,st.session_state.ip_address)
await reset_session_variables(st.session_state.ip_address)
st.session_state.page_loaded = True
if "story_started" not in st.session_state:
st.session_state.story_started = False
@@ -122,33 +201,40 @@ async def main():
if "show_continue_input" not in st.session_state:
st.session_state.show_continue_input = False
if "show_load_story" not in st.session_state:
st.session_state.show_load_story = False
if st.session_state.show_start_input:
summary_text = st.sidebar.text_area("Summary")
detail_text = st.sidebar.text_area("Details")
style_text = st.sidebar.text_area("Writing Style")
col1, col2 = st.sidebar.columns([1, 1])
col1, col2 = st.sidebar.columns([1, 1])
with col1:
if st.button("Back",key="start-back"):
st.session_state.show_start_input = False
st.session_state.writing = False
st.rerun()
with col2:
if st.button("Submit",key="start-submit"):
st.session_state.page_loaded = True
st.session_state.writing = True
# If we start a new story, reset all of our session variables
if st.session_state.story_started:
st.session_state.thread = await call_async_function_safely(get_new_thread,st.session_state.client)
await reset_session_variables()
st.session_state.thread = await call_async_function_safely(get_new_thread,st.session_state.client,st.session_state.ip_address)
await reset_session_variables(st.session_state.ip_address)
await call_async_function_safely(run_graph_with_input,st.session_state.client,st.session_state.thread,
st.session_state.assistant,{'summary':summary_text,'details':detail_text,
'style':style_text},{"node_id":st.session_state.current_node_id})
await stream(st.session_state.box,st.session_state.box_title,{'summary':summary_text,'details':detail_text,'style':style_text},st.session_state.client,st.session_state.thread,
st.session_state.assistant,{"node_id":st.session_state.current_node_id})
st.session_state.box_title.write("Saving chapter and returning to story view")
await asyncio.sleep(5)
st.session_state.story_started = True
await update_session_variables()
st.session_state.show_start_input = False
st.session_state.writing = False
st.rerun()
elif st.session_state.show_edit_input:
edit_chapter_text = st.sidebar.text_area("Edit Instructions")
@@ -156,16 +242,19 @@ async def main():
with col1:
if st.button("Back",key="edit-back"):
st.session_state.show_edit_input = False
st.session_state.writing = False
st.rerun()
with col2:
if st.button("Submit",key="edit-submit"):
await stream(st.session_state.box,st.session_state.box_title,{'rewrite_instructions':edit_chapter_text},st.session_state.client,st.session_state.thread,
st.session_state.assistant,{"node_id":st.session_state.current_node_id})
await call_async_function_safely(run_graph_with_input,st.session_state.client,st.session_state.thread,
st.session_state.assistant,{'rewrite_instructions':edit_chapter_text}
,{"node_id":st.session_state.current_node_id})
st.session_state.box_title.write("Saving chapter and returning to story view")
await asyncio.sleep(5)
await update_session_variables()
st.session_state.show_edit_input = False
st.session_state.writing = False
st.rerun()
elif st.session_state.show_continue_input:
next_chapter_text = st.sidebar.text_area("Next Chapter Instructions")
@@ -173,133 +262,181 @@ async def main():
with col1:
if st.button("Back",key="continue-back"):
st.session_state.show_continue_input = False
st.session_state.writing = False
st.rerun()
with col2:
if st.button("Submit",key="continue-submit"):
await call_async_function_safely(run_graph_with_input,st.session_state.client,st.session_state.thread,
st.session_state.assistant,{'continue_instructions':next_chapter_text}
,{"node_id":st.session_state.current_node_id})
await stream(st.session_state.box,st.session_state.box_title,{'continue_instructions':next_chapter_text},st.session_state.client,st.session_state.thread,
st.session_state.assistant,{"node_id":st.session_state.current_node_id})
st.session_state.box_title.write("Saving chapter and returning to story view")
await asyncio.sleep(5)
await update_session_variables()
st.session_state.show_continue_input = False
st.session_state.writing = False
st.rerun()
elif st.session_state.show_load_story:
col1, col2 = st.sidebar.columns([1, 1])
threads = await call_async_function_safely(get_user_threads,st.session_state.client,st.session_state.ip_address)
threads_without_current = [t for t in threads if t['thread_id'] != st.session_state.thread['thread_id']]
options = [t['story_title'] for t in threads_without_current]
if len(options) > 0:
selected_story = st.sidebar.selectbox("",options,index=None,placeholder="Select story", \
label_visibility="collapsed",key=f"story_selector")
else:
selected_story = None
st.sidebar.write("No alternate stories!")
if selected_story is not None:
st.session_state.thread = threads_without_current[options.index(selected_story)]
await update_session_variables()
st.session_state.show_load_story = False
st.rerun()
with col1:
if st.button("Back",key="load-story-back"):
st.session_state.show_load_story = False
st.rerun()
else:
st.sidebar.header("Navigation")
if st.sidebar.button("New Story" if st.session_state.story_started else "Start Story"):
st.session_state.story_title = ""
st.session_state.writing = True
st.session_state.show_start_input = True
st.rerun()
elif st.sidebar.button("Edit") and st.session_state.story_started and st.session_state.story_started:
st.session_state.show_edit_input = True
st.session_state.writing = True
st.rerun()
elif st.sidebar.button("Continue") and st.session_state.story_started:
st.session_state.show_continue_input = True
st.session_state.writing = True
st.rerun()
elif st.sidebar.button("Load Story"):
st.session_state.show_load_story = True
st.rerun()
st.sidebar.write(" ")
col1, _, col3 = st.columns([1, 2, 1])
with col1:
options = transform_titles_into_options([st.session_state.chapter_graph[chapter_id]['title'] for chapter_id in st.session_state.previous_chapter_options])
if len(options) > 0:
st.session_state.selected_previous_chapter = st.selectbox("",options,index=None,placeholder="Select previous chapter", \
label_visibility="collapsed",key=f"previous_chapter_{st.session_state.num_selected}")
else:
st.session_state.selected_previous_chapter = None
st.write("No previous chapters!")
if st.session_state.writing == False:
with col1:
options = transform_titles_into_options([st.session_state.chapter_graph[chapter_id]['title'] for chapter_id in st.session_state.previous_chapter_options])
if len(options) > 0:
st.session_state.selected_previous_chapter = st.selectbox("",options,index=None,placeholder="Select previous chapter", \
label_visibility="collapsed",key=f"previous_chapter_{st.session_state.num_selected}")
else:
st.session_state.selected_previous_chapter = None
st.write("No previous chapters!")
if st.session_state.selected_previous_chapter is not None:
st.session_state.num_selected += 1
new_chapter_selected = st.session_state.previous_chapter_options[options.index(st.session_state.selected_previous_chapter)]
await call_async_function_safely(update_current_state,st.session_state.client,st.session_state.thread,{'chapter_id_viewing':new_chapter_selected})
await call_async_function_safely(update_session_variables)
st.rerun()
with col3:
options = transform_titles_into_options([st.session_state.chapter_graph[chapter_id]['title'] for chapter_id in st.session_state.next_chapter_options])
if len(options) > 0:
st.session_state.selected_next_chapter = st.selectbox("",options,index=None,placeholder="Select next chapter", \
label_visibility="collapsed",key=f"next_chapter_{st.session_state.num_selected}")
else:
st.session_state.selected_next_chapter = None
st.write("No next chapters!")
if st.session_state.selected_next_chapter is not None:
if st.session_state.selected_previous_chapter is not None:
st.session_state.num_selected += 1
new_chapter_selected = st.session_state.next_chapter_options[options.index(st.session_state.selected_next_chapter)]
new_chapter_selected = st.session_state.previous_chapter_options[options.index(st.session_state.selected_previous_chapter)]
await call_async_function_safely(update_current_state,st.session_state.client,st.session_state.thread,{'chapter_id_viewing':new_chapter_selected})
await call_async_function_safely(update_session_variables)
st.rerun()
with col3:
options = transform_titles_into_options([st.session_state.chapter_graph[chapter_id]['title'] for chapter_id in st.session_state.next_chapter_options])
if len(options) > 0:
st.session_state.selected_next_chapter = st.selectbox("",options,index=None,placeholder="Select next chapter", \
label_visibility="collapsed",key=f"next_chapter_{st.session_state.num_selected}")
else:
st.session_state.selected_next_chapter = None
st.write("No next chapters!")
st.markdown(f"<h2 style='text-align: center; color: white;'> \
{st.session_state.chapter_graph[st.session_state.currently_selected_chapter]['title']} \
</h2>",unsafe_allow_html=True)
st.text_area(" ", value=st.session_state.chapter_graph[st.session_state.currently_selected_chapter]['content'], height=450)
if st.session_state.selected_next_chapter is not None:
st.session_state.num_selected += 1
new_chapter_selected = st.session_state.next_chapter_options[options.index(st.session_state.selected_next_chapter)]
await call_async_function_safely(update_current_state,st.session_state.client,st.session_state.thread,{'chapter_id_viewing':new_chapter_selected})
await call_async_function_safely(update_session_variables)
st.rerun()
_, col_middle_title, _ = st.columns([1, 6, 1])
if "box_title" not in st.session_state:
st.session_state.box_title = col_middle_title.empty()
elif st.session_state.writing == True:
with col_middle_title:
st.write("Waiting for user input...")
_, col_middle, _ = st.columns([1, 6, 1])
if "box" not in st.session_state:
st.session_state.box = col_middle.empty()
if st.session_state.writing == False:
st.session_state.chapter_title = st.markdown(f"<h2 style='text-align: center; color: white;'> \
{st.session_state.chapter_graph[st.session_state.currently_selected_chapter]['title']} \
</h2>",unsafe_allow_html=True)
st.session_state.chapter_content = st.text_area(" ", value=st.session_state.chapter_graph[st.session_state.currently_selected_chapter]['content'], height=450)
_, col2, _ = st.columns([1, 2, 1])
with col2:
options = transform_titles_into_options([st.session_state.chapter_graph[chapter_id]['title'] for chapter_id in st.session_state.current_chapter_options])
if len(options) > 0:
st.session_state.selected_current_chapter = st.selectbox("",options,index=None,placeholder="Select current chapter", \
label_visibility="collapsed",key=f"current_chapter_{st.session_state.num_selected}")
else:
st.session_state.selected_current_chapter = None
st.write("No alternate current chapters!")
if st.session_state.writing == False:
with col2:
options = transform_titles_into_options([st.session_state.chapter_graph[chapter_id]['title'] for chapter_id in st.session_state.current_chapter_options])
if len(options) > 0:
st.session_state.selected_current_chapter = st.selectbox("",options,index=None,placeholder="Select current chapter", \
label_visibility="collapsed",key=f"current_chapter_{st.session_state.num_selected}")
else:
st.session_state.selected_current_chapter = None
st.write("No alternate current chapters!")
if st.session_state.selected_current_chapter is not None:
st.session_state.num_selected += 1
new_chapter_selected = st.session_state.current_chapter_options[options.index(st.session_state.selected_current_chapter)]
await call_async_function_safely(update_current_state,st.session_state.client,st.session_state.thread,{'chapter_id_viewing':new_chapter_selected})
await call_async_function_safely(update_session_variables)
st.rerun()
if st.session_state.selected_current_chapter is not None:
st.session_state.num_selected += 1
new_chapter_selected = st.session_state.current_chapter_options[options.index(st.session_state.selected_current_chapter)]
await call_async_function_safely(update_current_state,st.session_state.client,st.session_state.thread,{'chapter_id_viewing':new_chapter_selected})
await call_async_function_safely(update_session_variables)
st.rerun()
if st.session_state.current_node_id != '1':
_, col2a,col2b, _ = st.columns([1, 1,1, 1])
with col2a:
with stylable_container(
key="red_button",
css_styles="""
button {
background-color: red;
color: white;
border-radius: 20px;
}
""",
):
if st.button("Bad writing",key = "red_button"):
run_id = await call_async_function_safely(get_run_id_corresponding_to_node,st.session_state.client, \
st.session_state.thread, str(int(st.session_state.current_node_id)-1))
feedback_client.create_feedback(
run_id=run_id,
key="feedback-key",
score=0.0,
comment="comment",
)
with col2b:
with stylable_container(
key="green_button",
css_styles="""
button {
background-color: green;
color: white;
border-radius: 20px;
}
""",
):
if st.button("Good writing",key = "green_button"):
run_id = await call_async_function_safely(get_run_id_corresponding_to_node,st.session_state.client, \
st.session_state.thread, str(int(st.session_state.current_node_id)-1))
feedback_client.create_feedback(
run_id=run_id,
key="feedback-key",
score=1.0,
comment="comment",
)
if st.session_state.current_node_id != '1':
_, col2a,col2b, _ = st.columns([1, 1,1, 1])
with col2a:
with stylable_container(
key="red_button",
css_styles="""
button {
background-color: red;
color: white;
border-radius: 20px;
}
""",
):
if st.button("Bad writing",key = "red_button"):
run_id = await call_async_function_safely(get_run_id_corresponding_to_node,st.session_state.client, \
st.session_state.thread, str(int(st.session_state.current_node_id)-1))
feedback_client.create_feedback(
run_id=run_id,
key="feedback-key",
score=0.0,
comment="comment",
)
with col2b:
with stylable_container(
key="green_button",
css_styles="""
button {
background-color: green;
color: white;
border-radius: 20px;
}
""",
):
if st.button("Good writing",key = "green_button"):
run_id = await call_async_function_safely(get_run_id_corresponding_to_node,st.session_state.client, \
st.session_state.thread, str(int(st.session_state.current_node_id)-1))
feedback_client.create_feedback(
run_id=run_id,
key="feedback-key",
score=1.0,
comment="comment",
)
if __name__ == "__main__":
asyncio.run(main())