1+
2+ """
3+ code from https://github.com/hyperdiv/hyperdiv-apps/tree/main/gpt-chatbot
4+
5+ python >= 3.9
6+
7+ pip3 install -r requirements.txt
8+
9+ """
10+
11+ import sys
12+ import os
13+ import openai
14+ import hyperdiv as hd
15+
16+
17+
18+ api_key = os .environ .get ("OPENAI_API_KEY" ,"<your OpenAI API key if not set as env var>" )
19+
20+
21+
22+ def add_message (role , content , state , gpt_model ):
23+ state .messages += (
24+ dict (role = role , content = content , id = state .message_id , gpt_model = gpt_model ),
25+ )
26+ state .message_id += 1
27+
28+
29+ def request (gpt_model , state ):
30+ response = openai .chat .completions .create (
31+ model = gpt_model ,
32+ messages = [dict (role = m ["role" ], content = m ["content" ]) for m in state .messages ],
33+ temperature = 0 ,
34+ stream = True ,
35+ )
36+
37+ for chunk in response :
38+ message = chunk .choices [0 ].delta .content
39+ state .current_reply += str (message )
40+
41+ add_message ("assistant" , state .current_reply , state , gpt_model )
42+ state .current_reply = ""
43+
44+
45+ def render_user_message (content , gpt_model ):
46+ with hd .hbox (
47+ align = "center" ,
48+ padding = 0.5 ,
49+ border_radius = "medium" ,
50+ background_color = "neutral-50" ,
51+ font_color = "neutral-600" ,
52+ justify = "space-between" ,
53+ ):
54+ with hd .hbox (gap = 0.5 , align = "center" ):
55+ hd .icon ("chevron-right" , shrink = 0 )
56+ hd .text (content )
57+ hd .badge (gpt_model )
58+
59+
60+ def main ():
61+ state = hd .state (messages = (), current_reply = "" , gpt_model = "gpt-4" , message_id = 0 )
62+
63+ task = hd .task ()
64+
65+ template = hd .template (title = "GPT Chatbot" , sidebar = False )
66+
67+ with template .body :
68+ # Render the messages so far, if any.
69+ if len (state .messages ) > 0 :
70+ # We use a vertical-reverse box to render the messages, so
71+ # they naturally stay 'stuck' to the bottom and auto-scroll up.
72+ with hd .box (direction = "vertical-reverse" , gap = 1.5 , vertical_scroll = True ):
73+ # The current reply is the most recent message
74+ if state .current_reply :
75+ hd .markdown (state .current_reply )
76+
77+ # Render the rest of the messages in reverse. The
78+ # `vertical-reverse` direction will re-reverse them,
79+ # rendering them in expected order.
80+ for e in reversed (state .messages ):
81+ with hd .scope (e ["id" ]):
82+ if e ["role" ] == "system" :
83+ continue
84+ if e ["role" ] == "user" :
85+ render_user_message (e ["content" ], e ["gpt_model" ])
86+ else :
87+ hd .markdown (e ["content" ])
88+
89+ with hd .box (align = "center" , gap = 1.5 ):
90+ # Render the input form.
91+ with hd .form (direction = "horizontal" , width = "100%" ) as form :
92+ with hd .box (grow = 1 ):
93+ prompt = form .text_input (
94+ placeholder = "Talk to the AI" ,
95+ autofocus = True ,
96+ disabled = task .running ,
97+ name = "prompt" ,
98+ )
99+
100+ model = form .select (
101+ options = ("gpt-3.5-turbo" , "gpt-4" , "gpt-4-1106-preview" ),
102+ value = "gpt-4" ,
103+ name = "gpt-model" ,
104+ )
105+
106+ if form .submitted :
107+ add_message ("user" , prompt .value , state , model .value )
108+ prompt .reset ()
109+ task .rerun (request , model .value , state )
110+
111+ # Render a small button that when clicked, resets the message history.
112+ if len (state .messages ) > 0 :
113+ if hd .button (
114+ "Start Over" , size = "small" , variant = "text" , disabled = task .running
115+ ).clicked :
116+ state .messages = ()
117+
118+
119+ hd .run (main )
0 commit comments