Skip to content

Commit 1d29cec

Browse files
authored
[HOS-701] update chat app tutorial docs (#1350)
* update chat app tutorail docs * update chat tutorial * trim sections --------- Co-authored-by: pourhakimi <[email protected]>
1 parent eeaef2a commit 1d29cec

File tree

1 file changed

+76
-83
lines changed

1 file changed

+76
-83
lines changed

docs/getting_started/chatapp_tutorial.md

Lines changed: 76 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ rx.container(
437437
```python
438438
# chatapp.py
439439
from chatapp.state import State
440-
...
440+
441441

442442
def chat() -> rx.Component:
443443
return rx.box(
@@ -447,7 +447,7 @@ def chat() -> rx.Component:
447447
)
448448
)
449449

450-
...
450+
451451

452452
def action_bar() -> rx.Component:
453453
return rx.hstack(
@@ -499,7 +499,6 @@ def action_bar() -> rx.Component:
499499

500500
```python
501501
# state.py
502-
503502
@rx.event
504503
def answer(self):
505504
# Our chatbot is not very smart right now...
@@ -536,7 +535,6 @@ rx.container(
536535
# state.py
537536
import asyncio
538537

539-
...
540538
async def answer(self):
541539
# Our chatbot is not very smart right now...
542540
answer = "I don't know!"
@@ -565,60 +563,51 @@ We will use OpenAI's API to give our chatbot some intelligence.
565563

566564
### Configure the OpenAI API Key
567565

568-
Ensure you have an active OpenAI subscription. Save your API key as an environment variable named `OPENAI_API_KEY`:
566+
First, ensure you have an active OpenAI subscription.
567+
Next, install the latest openai package:
568+
```bash
569+
pip install --upgrade openai
570+
```
569571

570-
```bash
571-
export OPENAI_API_KEY="your-api-key-here"
572-
```
572+
Direct Configuration of API in Code
573573

574-
Install the `openai` pypi package:
574+
Update the state.py file to include your API key directly:
575575

576-
```bash
577-
pip install openai
578-
```
576+
```python
577+
# state.py
578+
import os
579+
from openai import AsyncOpenAI
579580

580-
### Using the API
581+
import reflex as rx
581582

582-
We need to modify our event handler to send a request to the API.
583+
# Initialize the OpenAI client
584+
client = AsyncOpenAI(api_key="YOUR_OPENAI_API_KEY") # Replace with your actual API key
583585

584-
```python exec
585-
def qa(question: str, answer: str) -> rx.Component:
586-
return rx.box(
587-
rx.box(rx.text(question, style=style.question_style), text_align="right"),
588-
rx.box(rx.text(answer, style=style.answer_style), text_align="left"),
589-
margin_y="1em",
590-
)
586+
```
591587

588+
### Using the API
592589

593-
def chat1() -> rx.Component:
594-
return rx.box(
595-
rx.foreach(
596-
ChatappState.chat_history, lambda messages: qa(messages[0], messages[1])
597-
)
598-
)
590+
Making your chatbot intelligent requires connecting to a language model API. This section explains how to integrate with OpenAI's API to power your chatbot's responses.
599591

592+
1. First, the user types a prompt that is updated via the `on_change` event handler.
593+
2. Next, when a prompt is ready, the user can choose to submit it by clicking the `Ask` button which in turn triggers the `State.answer` method inside our `state.py` file.
594+
3. Finally, if the method is triggered, the `prompt` is sent via a request to OpenAI client and returns an answer that we can trim and use to update the chat history!
600595

601-
def action_bar3() -> rx.Component:
596+
597+
```python
598+
# chatapp.py
599+
def action_bar() -> rx.Component:
602600
return rx.hstack(
603601
rx.input(
604-
value=ChatappState.question,
602+
value=State.question,
605603
placeholder="Ask a question",
606-
on_change=ChatappState.set_question,
607-
style=style.input_style,
608-
),
609-
rx.button("Ask", on_click=ChatappState.answer4, style=style.button_style),
610-
)
611-
```
604+
# on_change event updates the input as the user types a prompt.
605+
on_change=State.set_question,
606+
style=style.input_style),
612607

613-
```python demo box
614-
rx.center(
615-
rx.vstack(
616-
chat1(),
617-
action_bar3(),
618-
align="center",
619-
width="100%",
608+
# on_click event triggers the API to send the prompt to OpenAI.
609+
rx.button("Ask", on_click=State.answer, style=style.button_style),
620610
)
621-
)
622611
```
623612

624613
```python
@@ -665,20 +654,36 @@ Finally, we have our chatbot!
665654

666655
### Final Code
667656

668-
We wrote all our code in three files, which you can find below.
657+
This application is a simple, interactive chatbot built with Reflex that leverages OpenAI's API for intelligent responses. The chatbot features a clean interface with streaming responses for a natural conversation experience.
658+
659+
Key Features
660+
661+
1. Real-time streaming responses
662+
2. Clean, visually distinct chat bubbles for questions and answers
663+
3. Simple input interface with question field and submit button
664+
665+
Project Structure
666+
667+
Below is the full chatbot code with a commented title that corresponds to the filename.
668+
669+
```text
670+
chatapp/
671+
├── chatapp.py # UI components and app setup
672+
├── state.py # State management and API integration
673+
└── style.py # Styling definitions
674+
```
675+
676+
The `chatapp.py` file:
669677

670678
```python
671-
# chatapp.py
672679
import reflex as rx
673-
674680
from chatapp import style
675681
from chatapp.state import State
676682

677-
678683
def qa(question: str, answer: str) -> rx.Component:
679684
return rx.box(
680-
rx.box(rx.text(question, style=style.question_style),text_align="right"),
681-
rx.box(rx.text(answer, style=style.answer_style),text_align="left"),
685+
rx.box(rx.text(question, style=style.question_style), text_align="right"),
686+
rx.box(rx.text(answer, style=style.answer_style), text_align="left"),
682687
margin_y="1em",
683688
)
684689

@@ -690,7 +695,6 @@ def chat() -> rx.Component:
690695
)
691696
)
692697

693-
694698
def action_bar() -> rx.Component:
695699
return rx.hstack(
696700
rx.input(
@@ -706,7 +710,6 @@ def action_bar() -> rx.Component:
706710
),
707711
)
708712

709-
710713
def index() -> rx.Component:
711714
return rx.center(
712715
rx.vstack(
@@ -716,66 +719,58 @@ def index() -> rx.Component:
716719
)
717720
)
718721

719-
720722
app = rx.App()
721723
app.add_page(index)
722724
```
723725

726+
727+
The `state.py` file:
728+
724729
```python
725-
# state.py
726730
import os
727-
728731
from openai import AsyncOpenAI
729-
730732
import reflex as rx
731733

732734
class State(rx.State):
733-
734-
# The current question being asked.
735735
question: str
736-
737-
# Keep track of the chat history as a list of (question, answer) tuples.
738-
chat_history: list[tuple[str, str]]
736+
chat_history: list[tuple[str, str]] = []
739737

740738
async def answer(self):
741-
# Our chatbot has some brains now!
742739
client = AsyncOpenAI(api_key=os.environ["OPENAI_API_KEY"])
743-
740+
741+
# Start streaming completion from OpenAI
744742
session = await client.chat.completions.create(
745743
model="gpt-4o-mini",
746-
messages=[\{"role": "user", "content": self.question}],
747-
stop=None,
744+
messages=[
745+
\{"role": "user", "content": self.question}
746+
],
748747
temperature=0.7,
749748
stream=True,
750749
)
751750

752-
# Add to the answer as the chatbot responds.
751+
# Initialize response and update UI
753752
answer = ""
754753
self.chat_history.append((self.question, answer))
755-
756-
# Clear the question input.
757754
self.question = ""
758-
# Yield here to clear the frontend input before continuing.
759755
yield
760-
756+
757+
# Process streaming response
761758
async for item in session:
762759
if hasattr(item.choices[0].delta, "content"):
763760
if item.choices[0].delta.content is None:
764-
# presence of 'None' indicates the end of the response
765761
break
766762
answer += item.choices[0].delta.content
767-
self.chat_history[-1] = (
768-
self.chat_history[-1][0],
769-
answer,
770-
)
763+
self.chat_history[-1] = (self.chat_history[-1][0], answer)
771764
yield
772765
```
773766

767+
768+
The `style.py` file:
769+
774770
```python
775-
# style.py
776771
import reflex as rx
777772

778-
# Common styles for questions and answers.
773+
# Common style base
779774
shadow = "rgba(0, 0, 0, 0.15) 0px 2px 8px"
780775
chat_margin = "20%"
781776
message_style = dict(
@@ -787,7 +782,7 @@ message_style = dict(
787782
display="inline-block",
788783
)
789784

790-
# Set specific styles for questions and answers.
785+
# Styles for questions and answers
791786
question_style = message_style | dict(
792787
margin_left=chat_margin,
793788
background_color=rx.color("gray", 4),
@@ -797,18 +792,16 @@ answer_style = message_style | dict(
797792
background_color=rx.color("accent", 8),
798793
)
799794

800-
# Styles for the action bar.
795+
# Styles for input elements
801796
input_style = dict(border_width="1px", padding="0.5em", box_shadow=shadow, width="350px")
802-
button_style = dict(
803-
background_color=rx.color("accent", 10), box_shadow=shadow
804-
)
805-
797+
button_style = dict(background_color=rx.color("accent", 10), box_shadow=shadow)
806798
```
807799

800+
808801
### Next Steps
809802

810803
Congratulations! You have built your first chatbot. From here, you can read through the rest of the documentations to learn about Reflex in more detail. The best way to learn is to build something, so try to build your own app using this as a starting point!
811804

812805
### One More Thing
813806

814-
With our hosting service, you can deploy this app with a single command within minutes. Check out our [Hosting Quick Start]({hosting.deploy_quick_start.path}).
807+
With our hosting service, you can deploy this app with a single command within minutes. Check out our [Hosting Quick Start]({hosting.deploy_quick_start.path}).

0 commit comments

Comments
 (0)