Spaces:
Paused
Paused
:gem: [Feature] Enable OpenAI API call from fastapi with EventSourceResponse of sse_starlette
Browse files- apis/chat_api.py +25 -12
- examples/chat_with_openai.py +7 -13
- examples/chat_with_post.py +1 -1
- networks/message_outputer.py +16 -7
- networks/message_parser.py +3 -4
apis/chat_api.py
CHANGED
@@ -1,13 +1,25 @@
|
|
1 |
import uvicorn
|
2 |
|
3 |
from fastapi import FastAPI
|
4 |
-
from fastapi.responses import StreamingResponse
|
5 |
from pydantic import BaseModel, Field
|
6 |
from conversations import (
|
7 |
ConversationConnector,
|
8 |
ConversationCreator,
|
9 |
ConversationSession,
|
10 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
|
12 |
|
13 |
class ChatAPIApp:
|
@@ -131,14 +143,14 @@ class ChatAPIApp:
|
|
131 |
description="(int) Invocation ID",
|
132 |
)
|
133 |
|
134 |
-
|
135 |
-
connector = ConversationConnector(
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
)
|
142 |
|
143 |
if item.invocation_id == 0:
|
144 |
# TODO: History Messages Merger
|
@@ -146,9 +158,10 @@ class ChatAPIApp:
|
|
146 |
else:
|
147 |
prompt = item.messages[-1]["content"]
|
148 |
|
149 |
-
return
|
150 |
-
connector.stream_chat(prompt=prompt, yield_output=True),
|
151 |
-
|
|
|
152 |
)
|
153 |
|
154 |
def setup_routes(self):
|
|
|
1 |
import uvicorn
|
2 |
|
3 |
from fastapi import FastAPI
|
|
|
4 |
from pydantic import BaseModel, Field
|
5 |
from conversations import (
|
6 |
ConversationConnector,
|
7 |
ConversationCreator,
|
8 |
ConversationSession,
|
9 |
)
|
10 |
+
from networks import OpenaiStreamOutputer
|
11 |
+
from sse_starlette.sse import EventSourceResponse
|
12 |
+
|
13 |
+
|
14 |
+
def mock_stream_chat(prompt):
|
15 |
+
outputer = OpenaiStreamOutputer()
|
16 |
+
for i in range(10):
|
17 |
+
output = outputer.output(content=f"MSG {i} ", content_type="Completions")
|
18 |
+
print(output)
|
19 |
+
yield output
|
20 |
+
output = outputer.output(content="", content_type="Finished")
|
21 |
+
print(output)
|
22 |
+
yield output
|
23 |
|
24 |
|
25 |
class ChatAPIApp:
|
|
|
143 |
description="(int) Invocation ID",
|
144 |
)
|
145 |
|
146 |
+
def chat_completions(self, item: ChatCompletionsPostItem):
|
147 |
+
# connector = ConversationConnector(
|
148 |
+
# conversation_style=item.model,
|
149 |
+
# sec_access_token=item.sec_access_token,
|
150 |
+
# client_id=item.client_id,
|
151 |
+
# conversation_id=item.conversation_id,
|
152 |
+
# invocation_id=item.invocation_id,
|
153 |
+
# )
|
154 |
|
155 |
if item.invocation_id == 0:
|
156 |
# TODO: History Messages Merger
|
|
|
158 |
else:
|
159 |
prompt = item.messages[-1]["content"]
|
160 |
|
161 |
+
return EventSourceResponse(
|
162 |
+
# connector.stream_chat(prompt=prompt, yield_output=True),
|
163 |
+
mock_stream_chat(prompt),
|
164 |
+
media_type="text/event-stream",
|
165 |
)
|
166 |
|
167 |
def setup_routes(self):
|
examples/chat_with_openai.py
CHANGED
@@ -22,17 +22,11 @@ response = client.chat.completions.create(
|
|
22 |
extra_body=extra_body,
|
23 |
)
|
24 |
|
25 |
-
print(response)
|
26 |
-
|
27 |
-
for chunk in response:
|
28 |
-
# print(chunk.choices[0].delta)
|
29 |
-
print("??")
|
30 |
-
|
31 |
-
# print(response.choices[0].message)
|
32 |
# print(response)
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
|
|
|
22 |
extra_body=extra_body,
|
23 |
)
|
24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
# print(response)
|
26 |
+
for chunk in response:
|
27 |
+
if chunk.choices[0].delta.content is not None:
|
28 |
+
print(chunk.choices[0].delta.content, end="", flush=True)
|
29 |
+
elif chunk.choices[0].finish_reason == "stop":
|
30 |
+
print()
|
31 |
+
else:
|
32 |
+
print(chunk)
|
examples/chat_with_post.py
CHANGED
@@ -3,7 +3,7 @@ import httpx
|
|
3 |
import json
|
4 |
import re
|
5 |
|
6 |
-
|
7 |
chat_api = "http://localhost:22222"
|
8 |
api_key = "sk-xxxxx"
|
9 |
requests_headers = {}
|
|
|
3 |
import json
|
4 |
import re
|
5 |
|
6 |
+
# If runnning this service with proxy, you might need to unset `http(s)_proxy`.
|
7 |
chat_api = "http://localhost:22222"
|
8 |
api_key = "sk-xxxxx"
|
9 |
requests_headers = {}
|
networks/message_outputer.py
CHANGED
@@ -7,14 +7,21 @@ class OpenaiStreamOutputer:
|
|
7 |
* https://platform.openai.com/docs/api-reference/chat/create
|
8 |
"""
|
9 |
|
10 |
-
def data_to_string(self, data, content_type=""):
|
11 |
# return (json.dumps(data) + "\n").encode("utf-8")
|
12 |
-
data_str = f"
|
13 |
-
|
14 |
-
|
|
|
|
|
15 |
return data_str
|
16 |
|
17 |
-
def output(
|
|
|
|
|
|
|
|
|
|
|
18 |
data = {
|
19 |
"created": 1677825464,
|
20 |
"id": "chatcmpl-bing",
|
@@ -37,6 +44,8 @@ class OpenaiStreamOutputer:
|
|
37 |
"InternalSearchResult",
|
38 |
"SuggestedResponses",
|
39 |
]:
|
|
|
|
|
40 |
data["choices"] = [
|
41 |
{
|
42 |
"index": 0,
|
@@ -56,8 +65,8 @@ class OpenaiStreamOutputer:
|
|
56 |
data["choices"] = [
|
57 |
{
|
58 |
"index": 0,
|
59 |
-
"delta": {
|
60 |
"finish_reason": None,
|
61 |
}
|
62 |
]
|
63 |
-
return self.data_to_string(data, content_type)
|
|
|
7 |
* https://platform.openai.com/docs/api-reference/chat/create
|
8 |
"""
|
9 |
|
10 |
+
def data_to_string(self, data={}, content_type="", media_type=None):
|
11 |
# return (json.dumps(data) + "\n").encode("utf-8")
|
12 |
+
data_str = f"{json.dumps(data)}\n"
|
13 |
+
|
14 |
+
if media_type == "text/event-stream":
|
15 |
+
data_str = f"data: {data_str}"
|
16 |
+
|
17 |
return data_str
|
18 |
|
19 |
+
def output(
|
20 |
+
self,
|
21 |
+
content=None,
|
22 |
+
content_type=None,
|
23 |
+
media_type=None,
|
24 |
+
) -> bytes:
|
25 |
data = {
|
26 |
"created": 1677825464,
|
27 |
"id": "chatcmpl-bing",
|
|
|
44 |
"InternalSearchResult",
|
45 |
"SuggestedResponses",
|
46 |
]:
|
47 |
+
if content_type in ["InternalSearchQuery", "InternalSearchResult"]:
|
48 |
+
content += "\n"
|
49 |
data["choices"] = [
|
50 |
{
|
51 |
"index": 0,
|
|
|
65 |
data["choices"] = [
|
66 |
{
|
67 |
"index": 0,
|
68 |
+
"delta": {},
|
69 |
"finish_reason": None,
|
70 |
}
|
71 |
]
|
72 |
+
return self.data_to_string(data, content_type, media_type)
|
networks/message_parser.py
CHANGED
@@ -50,7 +50,7 @@ class MessageParser:
|
|
50 |
# Message: Search Query
|
51 |
elif message_type in ["InternalSearchQuery"]:
|
52 |
message_hidden_text = message["hiddenText"]
|
53 |
-
search_str = f"
|
54 |
logger.note(search_str)
|
55 |
if return_output:
|
56 |
return self.outputer.output(
|
@@ -58,7 +58,7 @@ class MessageParser:
|
|
58 |
)
|
59 |
# Message: Internal Search Results
|
60 |
elif message_type in ["InternalSearchResult"]:
|
61 |
-
analysis_str = f"
|
62 |
logger.note(analysis_str)
|
63 |
if return_output:
|
64 |
return self.outputer.output(
|
@@ -85,5 +85,4 @@ class MessageParser:
|
|
85 |
f"Not Supported Message Type: {message_type}"
|
86 |
)
|
87 |
|
88 |
-
|
89 |
-
return b""
|
|
|
50 |
# Message: Search Query
|
51 |
elif message_type in ["InternalSearchQuery"]:
|
52 |
message_hidden_text = message["hiddenText"]
|
53 |
+
search_str = f"[Searching: [{message_hidden_text}]]"
|
54 |
logger.note(search_str)
|
55 |
if return_output:
|
56 |
return self.outputer.output(
|
|
|
58 |
)
|
59 |
# Message: Internal Search Results
|
60 |
elif message_type in ["InternalSearchResult"]:
|
61 |
+
analysis_str = f"[Analyzing search results ...]"
|
62 |
logger.note(analysis_str)
|
63 |
if return_output:
|
64 |
return self.outputer.output(
|
|
|
85 |
f"Not Supported Message Type: {message_type}"
|
86 |
)
|
87 |
|
88 |
+
return self.outputer.output(content="", content_type="NotImplemented")
|
|