second draft

This commit is contained in:
isaac hershenson
2024-07-04 10:53:50 -07:00
parent be9c601223
commit 65069c12f4
4 changed files with 170 additions and 167 deletions
+10 -10
View File
@@ -12,23 +12,23 @@ async def main():
st.title("App Information")
st.write("This app is a quick example showing how you can use LangGraph Cloud in your development applications. \
Before playing with the app, I highly recommend reading through this info guide to gain a better understanding of how it works.")
st.write("This app is a quick example showing how you can use LangGraph Cloud in your applications. \
Before using the app, I highly recommend reading through this info guide to gain a better understanding of how it works.")
st.header("Background")
st.write("This app was designed to show off some LangGraph Cloud features in a fun, interactive way. This app is designed to allow users to write a story \
with the help of a LangGraph agent. The app allows users to edit chapters they have written already, or continue the story by writing \
the next chapter as well. This means the user could have multiple versions of the same chapter number (for example chapter #3) and can select the \
one they like most to continue writing chpater #4. At the beginning the user provides the graph information on the summary of the story, the writing style \
they want, and any additional details important to the story. From that point on they just need to provide edit and continue instructions to steer the \
the next chapter. This means the user can have multiple versions of the same chapter number and can select the \
one they like most to continue the story from. At the beginning the user provides the graph information on the summary of the story, the writing style \
they want, and any additional details important to the story. From that point they just need to provide edit and continue instructions to steer the \
agent in the desired direction. \n \nNote: This app is a prototype and not ready for deployment. There might be bugs/poor results from the agent.")
st.header("The Graph State")
st.write("One of the coolest features of LangGraph Cloud is the ability to have a persistent state across many runs of the graph. \
In this case we are able to retain information about the story as the user continues to write it. In our case, we keep an overall \
chapter state graph, which is just a dictionary containing the different chapter written so far. Each time you edit or continue the \
In this case we are able to retain information about the story as the graph continues to write it. In our case, we keep an overall \
chapter state graph, which is just a dictionary containing the different chapters written so far. Each time you edit or continue the \
story a new chapter is added to the graph. Each chapter keeps information about its content, title, and the relationship it has with \
the other chapters in the story (i.e. what chapters are siblings, children, parents, or cousins to it). Below is an example of what the \
chapter graph would look like after a user has been using the Story Writing tool for a little bit:")
@@ -41,7 +41,7 @@ async def main():
then created Node 3 by editing the chapter that was contained in Node 1. You can follow the rest of the story creation on your own by tracking the \
increasing node numbers.")
st.write("When using the story app, you can navigate between previous chapters, next chapters, current chapters. It can be a little confusing to understand \
st.write("When using the story app, you can navigate between previous chapters, next chapters, current chapters. It can be a little hard to understand \
what chapters show up where, so let's take a look at an example where the user is currently viewing the chapter in Node 5. The following diagram \
highlights the relationships Node 5 has with other nodes, and the explanation below dives into how these relationships work and how they inform \
what previous, next, and current chapter options we have to choose from:")
@@ -53,8 +53,8 @@ async def main():
red arrows representing all of the other nodes the user could move to. \n \nThere is one \"Next Chapter\" option, Node 8, because Node 5 only has \
one child. If we were to press \"Continue\" again from Node 5 to create another child, there would then be two options for the \"Next Chapter\". \n \nThere\
are three current chapter options. The first is Node 5 itself (the chapter you are viewing is always an option to be the current chapter!) \
and then Nodes 6 and 7 are also options. Node 7 is a \"Sibling\" of Node 5 because it was created by editing from Node 5. If we were to further make \
an edit to Node 7, that new node would also be a siblig of Node 5. Any nodes that are direct \"edit descendants\" of a node are considered \"Siblings\" \
and then Nodes 6 and 7 are also options. Node 7 is a \"Sibling\" of Node 5 because it was created by editing from Node 5. If we were to make \
further edits to Node 7, that new node would also be a siblig of Node 5. Any nodes that are direct \"edit descendants\" of a node are considered \"Siblings\" \
of that node. \n \nNode 6 is what we call a \"Cousin\" node because it originates from the same node as Node 5 (namely Node 4) but is not directly \
connected to it on our flow chart. Any nodes that originate from the same parent as a particular node are considered \"Cousin\" nodes. To summarize: \
the \"Current Chapter\" options consist of the current node itself, all of its \"Sibling\" nodes, and all of its \"Cousin\" nodes. \n\
+80 -65
View File
@@ -7,6 +7,7 @@ from langchain_core.prompts import ChatPromptTemplate
from copy import deepcopy
title_llm = ChatOpenAI(model="gpt-4o",metadata={"name":"title_llm"})
chapter_title_llm = ChatOpenAI(model="gpt-4o",metadata={"name":"chapter_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"})
@@ -71,58 +72,23 @@ 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
title: str
children: list
siblings: list
cousins: list
parent: int
def update_chapter_graph(old_chapter_graph, new_chapter_graph):
if isinstance(new_chapter_graph,dict):
old_chapter_graph.update(new_chapter_graph)
return old_chapter_graph
class State(TypedDict):
# User's initial inputs (could be updated later)
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 = ""
edit_prompt = """Here is the current state of the new chapter:
<Draft>
{draft}
</Draft>
Here are some edits I want to make to that chapter:
<EditInstructions>
{edit}
</EditInstructions>"""
continue_prompt = """Here is what I want in the next chapter:
<Instructions>
{instructions}
</Instructions>"""
def summarize_current_story(state,chapter_id):
if chapter_id == '-1':
return ""
current_chapter_id = chapter_id
chapters_currently_selected_text = [state['chapter_graph'][current_chapter_id]['content']]
while state['chapter_graph'][current_chapter_id]['parent'] != '-1':
chapters_currently_selected_text.append(state['chapter_graph'][current_chapter_id]['content'])
current_chapter_id = state['chapter_graph'][current_chapter_id]['parent']
chapters_str = "\n\n".join(
[f"Chapter {i}\n\n{chapters_currently_selected_text[i]}" for i in range(len(chapters_currently_selected_text))]
).strip()
return summary_chain.invoke({"chapters_str": chapters_str})
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'], \
@@ -133,7 +99,7 @@ def write_chapter(user_message, chapters_summary, state):
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("\"","")
chapter_title = chapter_title_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("\"","")
return chapter_content, chapter_title
def get_title(state,first_chapter):
@@ -157,18 +123,17 @@ def write_first_chapter(state):
state['story_title'] = get_title(state,chapter_content)
return state
def summarize_current_story(state,chapter_id):
if chapter_id == '-1':
return ""
current_chapter_id = chapter_id
chapters_currently_selected_text = [state['chapter_graph'][current_chapter_id]['content']]
while state['chapter_graph'][current_chapter_id]['parent'] != '-1':
chapters_currently_selected_text.append(state['chapter_graph'][current_chapter_id]['content'])
current_chapter_id = state['chapter_graph'][current_chapter_id]['parent']
chapters_str = "\n\n".join(
[f"Chapter {i}\n\n{chapters_currently_selected_text[i]}" for i in range(len(chapters_currently_selected_text))]
).strip()
return summary_chain.invoke({"chapters_str": chapters_str})
edit_prompt = """Here is the current state of the new chapter:
<Draft>
{draft}
</Draft>
Here are some edits I want to make to that chapter:
<EditInstructions>
{edit}
</EditInstructions>"""
def edit_chapter(state):
chapters_summary = summarize_current_story(state,state['chapter_graph'][state["chapter_id_viewing"]]['parent'])
@@ -182,11 +147,11 @@ def edit_chapter(state):
#create new chapter
state['chapter_graph'][str(int(state["current_chapter_id"])+1)] = Chapter(content=chapter_content,title=chapter_title,children=[], \
siblings=deepcopy(state["chapter_graph"][state["chapter_id_viewing"]]['siblings']+[state["chapter_id_viewing"]]), \
cousins=deepcopy(state["chapter_graph"][state["chapter_id_viewing"]]['children']), \
parent=deepcopy(state["chapter_graph"][state["chapter_id_viewing"]]['parent']))
siblings=deepcopy(state["chapter_graph"][state["chapter_id_viewing"]]['siblings']+[state["chapter_id_viewing"]]), \
cousins=deepcopy(state["chapter_graph"][state["chapter_id_viewing"]]['cousins']), \
parent=deepcopy(state["chapter_graph"][state["chapter_id_viewing"]]['parent']))
#update siblings
for sibling in state["chapter_graph"][state["chapter_id_viewing"]]['siblings']:
for sibling in state["chapter_graph"][str(int(state["chapter_id_viewing"]+1))]['siblings']:
state["chapter_graph"][sibling]['siblings'].append(str(int(state["current_chapter_id"])+1))
state["chapter_graph"][state["chapter_id_viewing"]]['siblings'].append(str(int(state["current_chapter_id"])+1))
@@ -194,6 +159,11 @@ def edit_chapter(state):
state["chapter_id_viewing"] = deepcopy(state["current_chapter_id"])
return state
continue_prompt = """Here is what I want in the next chapter:
<Instructions>
{instructions}
</Instructions>"""
def continue_chapter(state):
chapters_summary = summarize_current_story(state,state["chapter_id_viewing"])
@@ -216,6 +186,52 @@ def continue_chapter(state):
state["chapter_id_viewing"] = deepcopy(state["current_chapter_id"])
return state
# State Definitions
class Chapter(TypedDict):
content: str
"""Content of the chapter"""
title: str
"""Title of the chapter"""
children: list
"""Direct descendants of the chapter - chapters you get by clicking Continue"""
siblings: list
"""Direct edit descendants of the chapter - chapters you get by clicking Edit"""
cousins: list
"""Chapters that originated from the same parent but are not directly related"""
parent: int
"""For chapter number n, it is the node of chapter number n-1 that is directly related"""
def update_chapter_graph(old_chapter_graph, new_chapter_graph):
# Always add nodes to the overall graph
if isinstance(new_chapter_graph,dict):
old_chapter_graph.update(new_chapter_graph)
return old_chapter_graph
class State(TypedDict):
summary: str
"""Summary user passes in at beginning of story"""
details: str
"""Details provided by user at beginning of story"""
style: str
"""Desired writing style from the user"""
summary_request: str = ""
"""Prompt engineered summary instructions for LLM"""
detail_request: str = ""
"""Prompt engineered detial instructions for LLM"""
style_request: str = ""
"""Prompt engineered writing style instructions for LLM"""
chapter_graph: Annotated[dict[str, Chapter], update_chapter_graph]
"""Graph containing all of our chapter"""
chapter_id_viewing: str
"""What node in the graph the user is currently viewing"""
current_chapter_id: str
"""What is the current highest node (i.e. how many chapters have been written so far)"""
rewrite_instructions: str
"""Instructions to edit a chapter from the user"""
continue_instructions: str
"""Instructions to write the next chapter from the suer"""
story_title: str = ""
"""Overall title of the story"""
def router(state):
if len(state.get('chapter_graph', [])) == 0:
@@ -225,7 +241,6 @@ def router(state):
else:
return "continue"
graph = StateGraph(State)
graph.set_conditional_entry_point(router)
graph.add_node("first", write_first_chapter)
+1 -1
View File
@@ -7,4 +7,4 @@
"agent": "./agent.py:graph"
},
"env": ".env"
}
}
+79 -91
View File
@@ -8,28 +8,10 @@ from streamlit_extras.stylable_container import stylable_container
import requests
st.set_page_config(layout="wide")
# Langsmith feedback client
feedback_client = Client(api_url="https://beta.api.smith.langchain.com")
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-monthly-attachment-15-c1f10ef48e7c5c198bda57ca-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(metadata={"user":ip_address})
assistant = assistants[0]
return [client,thread,assistant]
# Find run id for giving feedback
async def get_run_id_corresponding_to_node(client, thread, node_id):
'''Get the run id corresponding to the chapter written'''
runs = await client.runs.list(thread_id=thread['thread_id'])
@@ -39,25 +21,22 @@ async def get_run_id_corresponding_to_node(client, thread, node_id):
return r['run_id']
return None
async def get_new_thread(client,ip_address):
thread = await client.threads.create(metadata={"user":ip_address})
return thread
# Create the agent
async def start_agent(session_id):
client = get_client(url="https://ht-monthly-attachment-15-c1f10ef48e7c5c198bda57ca-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(metadata={"user":session_id})
assistant = assistants[0]
return [client,thread,assistant]
# Find a story that we had previously written
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
# Find stories user has written
async def get_user_threads(client,session_id):
threads = await client.threads.search(metadata={"user":session_id})
untitled_count = 1
for t in threads:
@@ -69,58 +48,55 @@ async def get_user_threads(client,ip_address):
untitled_count += 1
return threads
async def run_graph_with_input(client,thread,assistant,input,metadata={}):
# Need to add streaming capability
data = []
async for chunk in client.runs.stream(
thread['thread_id'], assistant['assistant_id'], input=input, config={'configurable':metadata}, stream_mode="updates", multitask_strategy="rollback",
):
if chunk.data and 'run_id' not in chunk.data:
for t in ['rewrite','continue','first']:
try:
data = chunk.data[t]['chapters']
except:
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..."
"title_llm": "Generating a title for the story...",
"chapter_title_llm": "Generating title for chapter..."
}
# Streaming chapter writing
async def generate_answer(placeholder, placeholder_title, input, client, thread, assistant, metadata = {}):
current_llm = "starting"
placeholder_title.markdown(f"<h4 style='text-align: center; color: rgb(206,234,253);'> \
{llm_to_title[current_llm]} \
</h4>",unsafe_allow_html=True)
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.markdown(f"<h4 style='text-align: center; color: rgb(206,234,253);'> \
{llm_to_title[current_llm]} \
</h4>",unsafe_allow_html=True)
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:])
current_llm = "starting"
chapter_written = False
placeholder_title.markdown(f"<h4 style='text-align: center; color: rgb(206,234,253);'> \
{llm_to_title[current_llm]} \
</h4>",unsafe_allow_html=True)
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", multitask_strategy="rollback"
):
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.markdown(f"<h4 style='text-align: center; color: rgb(206,234,253);'> \
{llm_to_title[current_llm]} \
</h4>",unsafe_allow_html=True)
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:])
# Update variables after chapter has been written
async def get_current_state(client,thread):
current_state = await client.threads.get_state(thread_id=thread['thread_id'])
return current_state
# When user selects a different chapter to view
async def update_current_state(client,thread,values):
updated_state = await client.threads.update_state(thread_id=thread['thread_id'],values=values)
await client.threads.update_state(thread_id=thread['thread_id'],values=values)
# Create new thread for new story
async def get_new_thread(client,session_id):
thread = await client.threads.create(metadata={"user":session_id})
return thread
# Make sure event loop never closes
async def call_async_function_safely(func,*args):
try:
# Try to run the async function using the existing event loop
@@ -135,6 +111,7 @@ async def call_async_function_safely(func,*args):
raise e
return result
# Helper function for chapter options
def transform_titles_into_options(titles):
name_counts = {}
for name in set(titles):
@@ -152,6 +129,7 @@ def transform_titles_into_options(titles):
return transformed_titles[::-1]
# Update variables after writing
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']
@@ -162,8 +140,9 @@ async def update_session_variables():
st.session_state.current_chapter_options = st.session_state.chapter_graph[st.session_state.currently_selected_chapter]['siblings'] \
+ 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(ip_address):
# Reset variables on new story
async def reset_session_variables():
st.session_state.chapter_graph = {"-1":{'content':"Click Start Story to begin writing!", 'title':"Pre-start Chapter"}}
st.session_state.currently_selected_chapter = "-1"
st.session_state.chapter_number = 0
@@ -185,7 +164,7 @@ async def main():
</style>
""", unsafe_allow_html=True)
if "story_title" not in st.session_state or st.session_state.story_title == "":
st.markdown("<h1 class='centered-title'>Story Writing with Langgraph</h1>", unsafe_allow_html=True)
st.markdown("<h1 class='centered-title'>Story Writing with LangGraph</h1>", unsafe_allow_html=True)
else:
st.markdown(f"<h1 class='centered-title'>{st.session_state.story_title}</h1>", unsafe_allow_html=True)
@@ -195,11 +174,13 @@ async def main():
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 "session_id" not in st.session_state:
st.session_state.session_id = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
if st.session_state.page_loaded == False:
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.client,st.session_state.thread,st.session_state.assistant = await call_async_function_safely(start_agent,st.session_state.session_id)
await reset_session_variables()
st.session_state.page_loaded = True
if "story_started" not in st.session_state:
@@ -217,7 +198,7 @@ async def main():
if "show_load_story" not in st.session_state:
st.session_state.show_load_story = False
# Starting/New story
if st.session_state.show_start_input:
summary_text = st.sidebar.text_area("Summary")
detail_text = st.sidebar.text_area("Details")
@@ -233,21 +214,19 @@ async def main():
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,st.session_state.ip_address)
await reset_session_variables(st.session_state.ip_address)
st.session_state.thread = await call_async_function_safely(get_new_thread,st.session_state.client,st.session_state.session_id)
await reset_session_variables()
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.story_started = True
await update_session_variables()
st.session_state.show_start_input = False
st.session_state.writing = False
st.session_state.chapter_number = 1
st.rerun()
# Editing story
elif st.session_state.show_edit_input:
edit_chapter_text = st.sidebar.text_area("Edit Instructions")
col1, col2 = st.sidebar.columns([1, 1])
@@ -258,15 +237,14 @@ async def main():
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 update_session_variables()
st.session_state.show_edit_input = False
st.session_state.writing = False
st.rerun()
# Continuing story
elif st.session_state.show_continue_input:
next_chapter_text = st.sidebar.text_area("Next Chapter Instructions")
col1, col2 = st.sidebar.columns([1, 1])
@@ -277,8 +255,6 @@ async def main():
st.rerun()
with col2:
if st.button("Submit",key="continue-submit"):
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})
@@ -287,10 +263,11 @@ async def main():
st.session_state.writing = False
st.session_state.chapter_number += 1
st.rerun()
# Loading story
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']]
threads = await call_async_function_safely(get_user_threads,st.session_state.client,st.session_state.session_id)
threads_without_current = [t for t in threads if t['thread_id'] != st.session_state.thread['thread_id'] and 'Untitled' not in t['story_title']]
options = [t['story_title'] for t in threads_without_current]
if len(options) > 0:
@@ -304,15 +281,20 @@ async def main():
st.session_state.thread = threads_without_current[options.index(selected_story)]
await update_session_variables()
st.session_state.show_load_story = False
st.session_state.chapter_number = 1
cur_chapter = st.session_state.currently_selected_chapter
while st.session_state.chapter_graph[cur_chapter]['parent'] != '-1':
st.session_state.chapter_number += 1
cur_chapter = st.session_state.chapter_graph[cur_chapter]['parent']
st.rerun()
with col1:
if st.button("Back",key="load-story-back"):
st.session_state.show_load_story = False
st.rerun()
# Default Navigation pane
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()
@@ -334,7 +316,7 @@ async def main():
<style>
div[data-testid="stHorizontalBlock"] > * {
max-height: 300px;
overflow-y: auto;
overflow-y: scroll;
}
</style>
""", unsafe_allow_html=True)
@@ -342,6 +324,7 @@ async def main():
col1, _, col3 = st.columns([1, 2, 1])
if st.session_state.writing == False:
# Previous chapter options
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:
@@ -362,6 +345,7 @@ async def main():
await call_async_function_safely(update_session_variables)
st.session_state.chapter_number -= 1
st.rerun()
# Next chapter options
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:
@@ -412,6 +396,7 @@ async def main():
_, col2, _ = st.columns([1, 2, 1])
if st.session_state.writing == False:
# Current chapter options
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 chapter_id != "-1"])
if len(options) > 0:
@@ -432,8 +417,10 @@ async def main():
await call_async_function_safely(update_session_variables)
st.rerun()
# Feedback options
if st.session_state.current_node_id != '1':
_, col2a,col2b, _ = st.columns([1, 1,1, 1])
# Bad writing
with col2a:
with stylable_container(
key="red_button",
@@ -455,6 +442,7 @@ async def main():
score=0.0,
comment="comment",
)
# Good writing
with col2b:
with stylable_container(
key="green_button",