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 )