1+ '''
2+ wrtn.py
3+ reverse proxy api for wrtn
4+ '''
5+ import json
6+ from util import Http , APIRequester
7+ from fake_useragent import UserAgent
8+ import inspect
9+
10+ class ChunkDecoder :
11+ def __init__ (self ):
12+ self .buffer = ""
13+
14+ def decode (self , chunk_tuple ):
15+ for chunk in chunk_tuple :
16+ if (isinstance (chunk , bytes )):
17+ return self .decode_chunks (chunk )
18+
19+ def decode_chunks (self , chunk_byte ):
20+ chunk_str = self .buffer + chunk_byte .decode (encoding = "utf-8" , errors = "ignore" )
21+ chunks = chunk_str .split ('\n \n ' )
22+ last_chunk = chunks [- 1 ]
23+ self .buffer = "" if last_chunk == "" else last_chunk
24+ json_objects = [json .loads ((line .split (": " , 1 )[1 ]))
25+ for line in chunks [:- 1 ] if line .strip ().startswith ('data: {' )]
26+ res_text = "" .join (obj ['chunk' ] for obj in json_objects if 'chunk' in obj if obj ['chunk' ] is not None )
27+ return res_text
28+
29+ #TODO: Divide WrtnAPI to Chat, Studio, Tool/ChatBot
30+ class WrtnAPI :
31+ '''
32+ WrtnAPI: needs json path
33+ '''
34+ def __init__ (self , debug = False ) -> None :
35+ self .debug = debug
36+ self .header = {
37+ "Init" : {
38+ "Refresh" : "" ,
39+ "Content-Type" : "application/json"
40+ },
41+ "Auth" : {
42+ "User-Agent" : UserAgent ().safari
43+ },
44+ }
45+ self .wrtn_requester = APIRequester ("https://api.wow.wrtn.ai" , debug = debug )
46+ self .wrtn_studio_requester = APIRequester ("https://studio-api.wow.wrtn.ai" , debug = debug )
47+
48+ async def login (self , account ):
49+ response = await self .wrtn_requester .post (
50+ info = Http (
51+ url = "/auth/local" ,
52+ header = self .header ["Init" ],
53+ payload = {
54+ "email" : account ['id' ],
55+ "password" : account ['pw' ]
56+ },
57+ ),
58+ )
59+ self .header ["Init" ] |= {"Refresh" : response ['data' ]['refreshToken' ]}
60+ return response ['data' ]['refreshToken' ]
61+
62+ async def access_token (self ):
63+ '''refresh access_token with refresh_key'''
64+ response = await self .wrtn_requester .post (
65+ info = Http (
66+ url = "/auth/refresh" ,
67+ header = self .header ["Init" ],
68+ ),
69+ )
70+ return {"Authorization" : "Bearer " + response ['data' ]['accessToken' ]}
71+
72+ async def chat_by_json (self , _id : str , msg : str , oldmsg : list [dict ], model :str = 'gpt-3.5-turbo' ):
73+ '''chat method with json. currently: gpt-3.5 and gpt-4, context length is unknown'''
74+
75+ self .header ["Auth" ] |= await self .access_token ()
76+ decoder = ChunkDecoder ()
77+ async for response in self .wrtn_studio_requester .stream (
78+ info = Http (
79+ url = f"/store/chat-bot/{ _id } /generate" ,
80+ header = self .header ["Auth" ],
81+ payload = {
82+ "content" : msg ,
83+ "model" : model ,
84+ "oldMessages" : oldmsg ,
85+ },
86+ ), callback = decoder .decode
87+ ):
88+ yield response
89+ await self .delete_chatbot (_id )
90+
91+ async def get_chatbot (self ) -> list :
92+ '''loads all chatbots from this user'''
93+
94+
95+ self .header ["Auth" ] |= await self .access_token ()
96+ response = await self .wrtn_studio_requester .get (
97+ info = Http (
98+ url = "/studio/chat-bot" ,
99+ header = self .header ["Auth" ]
100+ ),
101+ )
102+ return response ['data' ]
103+
104+ async def delete_chatbot (self , _id : str ):
105+ '''delete a chatbot with specific id. You must delete your chatbot after use.'''
106+
107+
108+ self .header ["Auth" ] |= await self .access_token ()
109+ response = await self .wrtn_studio_requester .delete (
110+ info = Http (
111+ url = f"/studio/chat-bot/{ _id } " ,
112+ header = self .header ["Auth" ],
113+ ),
114+ )
115+ return response
116+
117+ async def get_tool (self ) -> list :
118+ '''loads all tools from this user'''
119+
120+
121+ self .header ["Auth" ] |= await self .access_token ()
122+ response = await self .wrtn_studio_requester .get (
123+ info = Http (
124+ url = "/studio/tool" ,
125+ header = self .header ["Auth" ],
126+ ),
127+ )
128+ return response ['data' ]
129+
130+ async def delete_tool (self , _id : str ):
131+ '''delete a tool with specific id. You must delete your chatbot after use.'''
132+
133+
134+ self .header ["Auth" ] |= await self .access_token ()
135+ response = await self .wrtn_studio_requester .delete (
136+ info = Http (
137+ url = f"/studio/tool/{ _id } " ,
138+ header = self .header ["Auth" ],
139+ ),
140+ )
141+ return response
142+
143+ async def make_chatbot (self ):
144+ '''
145+ make a chatbot with specific option.
146+ Must register a chatbot with passing an toxicity test, to use a json prompt with system role.
147+ '''
148+ # toolList = await self.get_tool()
149+ # for tool in toolList['toolList']:
150+ # await self.delete_tool(tool['id'])
151+ # chatBotList = await self.get_chatbot()
152+ # for chatBot in chatBotList['chatBotList']:
153+ # await self.delete_chatbot(chatBot['id'])
154+
155+
156+ self .header ["Auth" ] |= await self .access_token ()
157+ payload = {
158+ "difficulty" :"hard" ,
159+ "icon" :"faceSmile" ,
160+ "title" :" " ,
161+ "description" :" " ,
162+ "category" :[" " ],
163+ "firstMessage" :" " ,
164+ "selectTypeForExampleQuestion" :" " ,
165+ "exampleQuestion" :["" ],
166+ "promptForEasy" :{
167+ "role" :"" ,"personality" :"" ,"requirement" :""
168+ },
169+ "promptForDifficult" :"" ,
170+ "userName" :" " ,
171+ "isDeleted" :False ,
172+ # "additionalInformation": "",
173+ "isTemporarySave" :False ,
174+ "openType" :"비공개" ,
175+ "priceType" :"무료" ,
176+ }
177+ response = await self .wrtn_studio_requester .post (
178+ info = Http (
179+ url = "/studio/chat-bot" ,
180+ header = self .header ["Auth" ],
181+ ),
182+ )
183+
184+ _id = response ['data' ]['chatBotList' ][0 ]['id' ]
185+ payload ["userId" ] = response ['data' ]['chatBotList' ][0 ]['userId' ]
186+ payload ["chatBotId" ] = _id
187+ chatbot = await self .wrtn_studio_requester .post (
188+ info = Http (
189+ url = f"/studio/chat-bot/{ _id } " ,
190+ payload = payload ,
191+ header = self .header ["Auth" ],
192+ ),
193+ )
194+ return chatbot ['data' ]['chatBotList' ][0 ]['id' ]
0 commit comments