Skip to content

Commit bdfdec0

Browse files
committed
Chat Streaming endpoint bare-minimum PoC
1 parent 6fdc215 commit bdfdec0

File tree

8 files changed

+138
-8
lines changed

8 files changed

+138
-8
lines changed
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import logging
2+
3+
import aiohttp
4+
from django.apps import apps
5+
from django.http import StreamingHttpResponse
6+
from rest_framework.decorators import parser_classes
7+
from rest_framework.parsers import JSONParser
8+
from rest_framework.views import APIView
9+
10+
from ansible_ai_connect.ai.api.model_pipelines.pipelines import ModelPipelineChatBot
11+
12+
logger = logging.getLogger(__name__)
13+
14+
15+
class StreamingChat(APIView):
16+
17+
def __init__(self):
18+
self.llm = apps.get_app_config("ai").get_model_pipeline(ModelPipelineChatBot)
19+
20+
async def call_chatservice(self, request):
21+
async with aiohttp.ClientSession(raise_for_status=True) as session:
22+
headers = {
23+
"Content-Type": "application/json",
24+
"Accept": "application/json,text/event-stream",
25+
}
26+
async with session.post(
27+
self.llm.config.inference_url + "/v1/streaming_query",
28+
json=request.data,
29+
headers=headers,
30+
) as r:
31+
async for chunk in r.content:
32+
logger.debug(chunk)
33+
yield chunk
34+
35+
@parser_classes([JSONParser])
36+
def post(self, request):
37+
return StreamingHttpResponse(
38+
self.call_chatservice(request),
39+
content_type="text/event-stream",
40+
)

ansible_ai_connect/ai/api/versions/v1/ai/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@
2424
path("generations/role/", views.GenerationRole.as_view(), name="generations/role"),
2525
path("feedback/", views.Feedback.as_view(), name="feedback"),
2626
path("chat/", views.Chat.as_view(), name="chat"),
27+
path("streaming_chat/", views.StreamingChat.as_view(), name="streaming_chat"),
2728
]

ansible_ai_connect/ai/api/versions/v1/ai/views.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from ansible_ai_connect.ai.api.streaming_chat import StreamingChat
1516
from ansible_ai_connect.ai.api.views import (
1617
Chat,
1718
Completions,
@@ -30,4 +31,5 @@
3031
"GenerationRole",
3132
"Feedback",
3233
"Chat",
34+
"StreamingChat",
3335
]

ansible_ai_connect/main/settings/base.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
# Application definition
5656

5757
INSTALLED_APPS = [
58+
"daphne",
5859
"django.contrib.admin",
5960
"django.contrib.auth",
6061
"django.contrib.contenttypes",
@@ -294,7 +295,7 @@ def is_ssl_enabled(value: str) -> bool:
294295
},
295296
},
296297
"handlers": {
297-
"console": {"class": "logging.StreamHandler", "formatter": "simple", "level": "INFO"},
298+
"console": {"class": "logging.StreamHandler", "formatter": "simple", "level": "DEBUG"},
298299
},
299300
"loggers": {
300301
"django": {
@@ -334,6 +335,11 @@ def is_ssl_enabled(value: str) -> bool:
334335
"level": "INFO",
335336
"propagate": False,
336337
},
338+
"ansible_ai_connect.ai.api.streaming_chat": {
339+
"handlers": ["console"],
340+
"level": "DEBUG",
341+
"propagate": False,
342+
},
337343
},
338344
"root": {
339345
"handlers": ["console"],
@@ -358,6 +364,7 @@ def is_ssl_enabled(value: str) -> bool:
358364
]
359365

360366
WSGI_APPLICATION = "ansible_ai_connect.main.wsgi.application"
367+
ASGI_APPLICATION = "ansible_ai_connect.main.asgi.application"
361368

362369
# Database
363370
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases

ansible_ai_connect_chatbot/src/useChatbot/useChatbot.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -369,13 +369,12 @@ export const useChatbot = () => {
369369
method: "POST",
370370
headers: {
371371
"Content-Type": "application/json",
372+
Accept: "application/json,text/event-stream",
372373
"X-CSRFToken": csrfToken!,
373374
},
374375
body: JSON.stringify(chatRequest),
375376
async onopen(resp: any) {
376-
if (resp.ok && resp.status === 200) {
377-
setIsLoading(false);
378-
} else if (
377+
if (
379378
resp.status >= 400 &&
380379
resp.status < 500 &&
381380
resp.status !== 429
@@ -394,6 +393,7 @@ export const useChatbot = () => {
394393
setConversationId(message.data.conversation_id);
395394
}
396395
} else if (message.event === "token") {
396+
setIsLoading(false);
397397
appendMessageChunk(message.data.token);
398398
} else if (message.event === "end") {
399399
if (message.data.referenced_documents.length > 0) {

requirements-aarch64.txt

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
aiohappyeyeballs==2.3.5
88
# via aiohttp
99
aiohttp==3.10.11
10-
# via langchain
10+
# via
11+
# -r requirements.in
12+
# langchain
1113
aiosignal==1.3.1
1214
# via aiohttp
1315
annotated-types==0.6.0
@@ -29,14 +31,22 @@ anyio==4.6.2.post1
2931
argparse==1.4.0
3032
# via uwsgi-readiness-check
3133
asgiref==3.8.1
32-
# via django
34+
# via
35+
# daphne
36+
# django
3337
asttokens==2.4.1
3438
# via stack-data
3539
attrs==23.2.0
3640
# via
3741
# aiohttp
3842
# jsonschema
3943
# referencing
44+
# service-identity
45+
# twisted
46+
autobahn==24.4.2
47+
# via daphne
48+
automat==24.8.1
49+
# via twisted
4050
backcall==0.2.0
4151
# via ipython
4252
backoff==2.2.1
@@ -68,13 +78,19 @@ charset-normalizer==3.3.2
6878
# via requests
6979
click==8.1.7
7080
# via black
81+
constantly==23.10.4
82+
# via twisted
7183
cryptography==43.0.1
7284
# via
7385
# -r requirements.in
7486
# ansible-core
87+
# autobahn
7588
# jwcrypto
7689
# pyopenssl
90+
# service-identity
7791
# social-auth-core
92+
daphne==4.1.2
93+
# via -r requirements.in
7894
decorator==5.1.1
7995
# via ipython
8096
defusedxml==0.8.0rc2
@@ -167,13 +183,21 @@ httpx==0.27.2
167183
# via
168184
# langsmith
169185
# ollama
186+
hyperlink==21.0.0
187+
# via
188+
# autobahn
189+
# twisted
170190
idna==3.7
171191
# via
172192
# -r requirements.in
173193
# anyio
174194
# httpx
195+
# hyperlink
175196
# requests
197+
# twisted
176198
# yarl
199+
incremental==24.7.2
200+
# via twisted
177201
inflection==0.5.1
178202
# via drf-spectacular
179203
ipython==8.10.0
@@ -307,10 +331,12 @@ pyasn1==0.6.0
307331
# oauth2client
308332
# pyasn1-modules
309333
# rsa
334+
# service-identity
310335
pyasn1-modules==0.4.0
311336
# via
312337
# google-auth
313338
# oauth2client
339+
# service-identity
314340
pycparser==2.21
315341
# via cffi
316342
pydantic==2.9.2
@@ -336,6 +362,7 @@ pyopenssl==24.2.1
336362
# via
337363
# -r requirements.in
338364
# pydrive2
365+
# twisted
339366
pyparsing==3.1.2
340367
# via httplib2
341368
pyrfc3339==1.1
@@ -410,6 +437,8 @@ segment-analytics-python==2.2.2
410437
# via -r requirements.in
411438
semver==3.0.2
412439
# via launchdarkly-server-sdk
440+
service-identity==24.2.0
441+
# via twisted
413442
six==1.16.0
414443
# via
415444
# asttokens
@@ -455,6 +484,10 @@ traitlets==5.14.3
455484
# via
456485
# ipython
457486
# matplotlib-inline
487+
twisted[tls]==24.11.0
488+
# via daphne
489+
txaio==23.1.1
490+
# via autobahn
458491
typing-extensions==4.11.0
459492
# via
460493
# django-test-migrations
@@ -464,6 +497,7 @@ typing-extensions==4.11.0
464497
# pydantic
465498
# pydantic-core
466499
# sqlalchemy
500+
# twisted
467501
uritemplate==4.1.1
468502
# via
469503
# drf-spectacular
@@ -490,3 +524,8 @@ yamllint==1.35.1
490524
# via ansible-lint
491525
yarl==1.17.2
492526
# via aiohttp
527+
zope-interface==7.2
528+
# via twisted
529+
530+
# The following packages are considered to be unsafe in a requirements file:
531+
# setuptools

requirements-x86_64.txt

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
aiohappyeyeballs==2.3.5
88
# via aiohttp
99
aiohttp==3.10.11
10-
# via langchain
10+
# via
11+
# -r requirements.in
12+
# langchain
1113
aiosignal==1.3.1
1214
# via aiohttp
1315
annotated-types==0.6.0
@@ -29,14 +31,22 @@ anyio==4.6.2.post1
2931
argparse==1.4.0
3032
# via uwsgi-readiness-check
3133
asgiref==3.8.1
32-
# via django
34+
# via
35+
# daphne
36+
# django
3337
asttokens==2.4.1
3438
# via stack-data
3539
attrs==23.2.0
3640
# via
3741
# aiohttp
3842
# jsonschema
3943
# referencing
44+
# service-identity
45+
# twisted
46+
autobahn==24.4.2
47+
# via daphne
48+
automat==24.8.1
49+
# via twisted
4050
backcall==0.2.0
4151
# via ipython
4252
backoff==2.2.1
@@ -68,13 +78,19 @@ charset-normalizer==3.3.2
6878
# via requests
6979
click==8.1.7
7080
# via black
81+
constantly==23.10.4
82+
# via twisted
7183
cryptography==43.0.1
7284
# via
7385
# -r requirements.in
7486
# ansible-core
87+
# autobahn
7588
# jwcrypto
7689
# pyopenssl
90+
# service-identity
7791
# social-auth-core
92+
daphne==4.1.2
93+
# via -r requirements.in
7894
decorator==5.1.1
7995
# via ipython
8096
defusedxml==0.8.0rc2
@@ -167,13 +183,21 @@ httpx==0.27.2
167183
# via
168184
# langsmith
169185
# ollama
186+
hyperlink==21.0.0
187+
# via
188+
# autobahn
189+
# twisted
170190
idna==3.7
171191
# via
172192
# -r requirements.in
173193
# anyio
174194
# httpx
195+
# hyperlink
175196
# requests
197+
# twisted
176198
# yarl
199+
incremental==24.7.2
200+
# via twisted
177201
inflection==0.5.1
178202
# via drf-spectacular
179203
ipython==8.10.0
@@ -307,10 +331,12 @@ pyasn1==0.6.0
307331
# oauth2client
308332
# pyasn1-modules
309333
# rsa
334+
# service-identity
310335
pyasn1-modules==0.4.0
311336
# via
312337
# google-auth
313338
# oauth2client
339+
# service-identity
314340
pycparser==2.21
315341
# via cffi
316342
pydantic==2.9.2
@@ -336,6 +362,7 @@ pyopenssl==24.2.1
336362
# via
337363
# -r requirements.in
338364
# pydrive2
365+
# twisted
339366
pyparsing==3.1.2
340367
# via httplib2
341368
pyrfc3339==1.1
@@ -410,6 +437,8 @@ segment-analytics-python==2.2.2
410437
# via -r requirements.in
411438
semver==3.0.2
412439
# via launchdarkly-server-sdk
440+
service-identity==24.2.0
441+
# via twisted
413442
six==1.16.0
414443
# via
415444
# asttokens
@@ -455,6 +484,10 @@ traitlets==5.14.3
455484
# via
456485
# ipython
457486
# matplotlib-inline
487+
twisted[tls]==24.11.0
488+
# via daphne
489+
txaio==23.1.1
490+
# via autobahn
458491
typing-extensions==4.11.0
459492
# via
460493
# django-test-migrations
@@ -464,6 +497,7 @@ typing-extensions==4.11.0
464497
# pydantic
465498
# pydantic-core
466499
# sqlalchemy
500+
# twisted
467501
uritemplate==4.1.1
468502
# via
469503
# drf-spectacular
@@ -490,3 +524,8 @@ yamllint==1.35.1
490524
# via ansible-lint
491525
yarl==1.17.2
492526
# via aiohttp
527+
zope-interface==7.2
528+
# via twisted
529+
530+
# The following packages are considered to be unsafe in a requirements file:
531+
# setuptools

0 commit comments

Comments
 (0)