From 0faef11b4aee8ee9d6b8b5428d12d92441b7c879 Mon Sep 17 00:00:00 2001 From: Vignesh Aigal Date: Tue, 10 Oct 2023 02:37:05 -0700 Subject: [PATCH 1/4] Fix DataSourceSelector --- .../datasource/DataSourceSelector.jsx | 60 +++++++++---------- .../providers/twilio/create_message.py | 2 +- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/llmstack/client/src/components/datasource/DataSourceSelector.jsx b/llmstack/client/src/components/datasource/DataSourceSelector.jsx index 934f25633c6..975d0a9e491 100644 --- a/llmstack/client/src/components/datasource/DataSourceSelector.jsx +++ b/llmstack/client/src/components/datasource/DataSourceSelector.jsx @@ -3,11 +3,8 @@ import { useRecoilValue } from "recoil"; import { dataSourcesState, orgDataSourcesState } from "../../data/atoms"; import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline"; import { AddDataSourceModal } from "./AddDataSourceModal"; -import { Chip } from "@mui/material"; +import { Autocomplete, TextField } from "@mui/material"; import FormControl from "@mui/material/FormControl"; -import Select from "@mui/material/Select"; -import MenuItem from "@mui/material/MenuItem"; -import Input from "@mui/material/Input"; export function DataSourceSelector(props) { const dataSources = useRecoilValue(dataSourcesState); @@ -24,11 +21,21 @@ export function DataSourceSelector(props) { return ( - } - renderValue={(selected) => ( -
- {(typeof selected === "string" ? [selected] : selected).map( - (value) => ( - ds.uuid === value)?.name || - value - } - style={{ margin: 2, borderRadius: 5 }} - /> - ), - )} -
+ renderInput={(params) => ( + )} - > - {uniqueDataSources.map((dataSource) => ( - - {dataSource.name} - - ))} - + onChange={(event, value) => { + props.onChange( + value.map((dataSource) => dataSource?.uuid || dataSource), + ); + }} + /> + + {showFilterBar && ( From 20a138fbba5cd273297cd8f86e4b40353ec256c4 Mon Sep 17 00:00:00 2001 From: Vignesh Aigal Date: Tue, 10 Oct 2023 10:05:44 -0700 Subject: [PATCH 4/4] Add history endpoint --- llmstack/processors/apis.py | 49 +++++++++++++++++++++++++++++++++++++ llmstack/processors/urls.py | 3 +++ 2 files changed, 52 insertions(+) diff --git a/llmstack/processors/apis.py b/llmstack/processors/apis.py index ac3d3187c4e..cae03b9116b 100644 --- a/llmstack/processors/apis.py +++ b/llmstack/processors/apis.py @@ -1,9 +1,12 @@ import asyncio +from datetime import datetime import json import logging import uuid from collections import namedtuple from django.conf import settings +from django.core.paginator import Paginator, EmptyPage +from flags.state import flag_enabled from django.contrib.auth import authenticate from django.contrib.auth import login @@ -389,6 +392,52 @@ def list(self, request): ) return DRFResponse(response.data) + + def get_csv(self, queryset): + yield ','.join([ + 'Request UUID', 'App UUID', 'Session Key', 'Request User Email', 'Request IP', 'Request Location', 'Request User Agent', 'Request Content Type', 'Request Body', 'Response Body', 'Created At', + ]) + '\n' + for entry in queryset: + logger.info(entry) + yield ','.join([ + entry.request_uuid, entry.app_uuid, entry.session_key if entry.session_key else '', entry.request_user_email, entry.request_ip, entry.request_location, entry.request_user_agent, entry.request_content_type, json.dumps(entry.request_body), json.dumps(entry.response_body), entry.created_at.strftime('%Y-%m-%d %H:%M:%S'), + ]) + '\n' + + def download(self, request): + if not flag_enabled('CAN_EXPORT_HISTORY', request=request): + return HttpResponseForbidden('You do not have permission to download history') + + app_uuid = request.data.get('app_uuid', None) + session_key = request.data.get('session_key', None) + request_user_email = request.data.get('request_user_email', None) + endpoint_uuid = request.data.get('endpoint_uuid', None) + page_number = request.data.get('page', 1) + + filters = { + 'owner': request.user, + } + if app_uuid and app_uuid != 'null': + filters['app_uuid'] = app_uuid + if session_key and session_key != 'null': + filters['session_key'] = session_key + if request_user_email and request_user_email != 'null': + filters['request_user_email'] = request_user_email + if endpoint_uuid and endpoint_uuid != 'null': + filters['endpoint_uuid'] = endpoint_uuid + + queryset = RunEntry.objects.all().filter(**filters).order_by('-created_at') + paginator = Paginator(queryset, self.paginate_by) + try: + page = paginator.page(page_number) + except EmptyPage: + page = paginator.page(paginator.num_pages) + + response = StreamingHttpResponse( + streaming_content=self.get_csv(page), content_type='text/csv', + ) + time_now = datetime.now().strftime('%Y-%m-%d') + response['Content-Disposition'] = f'attachment; filename="promptly_{time_now}_{page_number}.csv"' + return response def list_sessions(self, request): app_uuid = request.GET.get('app_uuid', None) diff --git a/llmstack/processors/urls.py b/llmstack/processors/urls.py index eb28e9f8eff..ea76b1a71ad 100644 --- a/llmstack/processors/urls.py +++ b/llmstack/processors/urls.py @@ -41,6 +41,9 @@ # History path('api/history', apis.HistoryViewSet.as_view({'get': 'list'})), + + path('api/history/download', apis.HistoryViewSet.as_view({'post': 'download'})), + path( 'api/history/sessions', apis.HistoryViewSet.as_view({'get': 'list_sessions'}),