Skip to content

Commit 38be419

Browse files
authored
Merge pull request #458 from Jdubrick/add-dynamic-feedback-toggle
[RHDHPAI-1027] Add dynamic feedback status update endpoint
2 parents eaa6a8a + d683993 commit 38be419

File tree

5 files changed

+234
-7
lines changed

5 files changed

+234
-7
lines changed

docs/openapi.json

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,46 @@
386386
}
387387
}
388388
}
389+
},
390+
"put": {
391+
"tags": [
392+
"feedback"
393+
],
394+
"summary": "Update Feedback Status",
395+
"description": "Handle feedback status update requests.\n\nTakes a request with the desired state of the feedback status.\nReturns the updated state of the feedback status based on the request's value.\nThese changes are for the life of the service and are on a per-worker basis.\n\nReturns:\n StatusResponse: Indicates whether feedback is enabled.",
396+
"operationId": "update_feedback_status_v1_feedback_status_put",
397+
"requestBody": {
398+
"content": {
399+
"application/json": {
400+
"schema": {
401+
"$ref": "#/components/schemas/FeedbackStatusUpdateRequest"
402+
}
403+
}
404+
},
405+
"required": true
406+
},
407+
"responses": {
408+
"200": {
409+
"description": "Successful Response",
410+
"content": {
411+
"application/json": {
412+
"schema": {
413+
"$ref": "#/components/schemas/FeedbackStatusUpdateResponse"
414+
}
415+
}
416+
}
417+
},
418+
"422": {
419+
"description": "Validation Error",
420+
"content": {
421+
"application/json": {
422+
"schema": {
423+
"$ref": "#/components/schemas/HTTPValidationError"
424+
}
425+
}
426+
}
427+
}
428+
}
389429
}
390430
},
391431
"/v1/conversations": {
@@ -712,6 +752,7 @@
712752
"title": "Actions"
713753
}
714754
},
755+
"additionalProperties": false,
715756
"type": "object",
716757
"required": [
717758
"role",
@@ -843,6 +884,7 @@
843884
]
844885
}
845886
},
887+
"additionalProperties": false,
846888
"type": "object",
847889
"title": "AuthenticationConfiguration",
848890
"description": "Authentication configuration."
@@ -857,6 +899,7 @@
857899
"title": "Access Rules"
858900
}
859901
},
902+
"additionalProperties": false,
860903
"type": "object",
861904
"title": "AuthorizationConfiguration",
862905
"description": "Authorization configuration."
@@ -933,6 +976,7 @@
933976
]
934977
}
935978
},
979+
"additionalProperties": false,
936980
"type": "object",
937981
"title": "CORSConfiguration",
938982
"description": "CORS configuration."
@@ -1000,6 +1044,7 @@
10001044
"default": {}
10011045
}
10021046
},
1047+
"additionalProperties": false,
10031048
"type": "object",
10041049
"required": [
10051050
"name",
@@ -1223,6 +1268,7 @@
12231268
"title": "System Prompt"
12241269
}
12251270
},
1271+
"additionalProperties": false,
12261272
"type": "object",
12271273
"title": "Customization",
12281274
"description": "Service customization."
@@ -1250,6 +1296,7 @@
12501296
]
12511297
}
12521298
},
1299+
"additionalProperties": false,
12531300
"type": "object",
12541301
"title": "DatabaseConfiguration",
12551302
"description": "Database configuration."
@@ -1446,6 +1493,47 @@
14461493
}
14471494
]
14481495
},
1496+
"FeedbackStatusUpdateRequest": {
1497+
"properties": {
1498+
"status": {
1499+
"type": "boolean",
1500+
"title": "Status",
1501+
"description": "Desired state of feedback enablement, must be False or True",
1502+
"default": false,
1503+
"examples": [
1504+
true,
1505+
false
1506+
]
1507+
}
1508+
},
1509+
"type": "object",
1510+
"title": "FeedbackStatusUpdateRequest",
1511+
"description": "Model representing a feedback status update request.\n\nAttributes:\n status: Value of the desired feedback enabled state.\n\nExample:\n ```python\n feedback_request = FeedbackRequest(\n status=false\n )\n ```"
1512+
},
1513+
"FeedbackStatusUpdateResponse": {
1514+
"properties": {
1515+
"status": {
1516+
"additionalProperties": true,
1517+
"type": "object",
1518+
"title": "Status"
1519+
}
1520+
},
1521+
"type": "object",
1522+
"required": [
1523+
"status"
1524+
],
1525+
"title": "FeedbackStatusUpdateResponse",
1526+
"description": "Model representing a response to a feedback status update request.\n\nAttributes:\n status: The previous and current status of the service and who updated it.\n\nExample:\n ```python\n status_response = StatusResponse(\n status={\n \"previous_status\": true,\n \"updated_status\": false,\n \"updated_by\": \"user/test\"\n },\n )\n ```",
1527+
"examples": [
1528+
{
1529+
"status": {
1530+
"previous_status": true,
1531+
"updated_by": "user/test",
1532+
"updated_status": false
1533+
}
1534+
}
1535+
]
1536+
},
14491537
"ForbiddenResponse": {
14501538
"properties": {
14511539
"detail": {
@@ -1503,6 +1591,7 @@
15031591
"title": "Default Provider"
15041592
}
15051593
},
1594+
"additionalProperties": false,
15061595
"type": "object",
15071596
"title": "InferenceConfiguration",
15081597
"description": "Inference configuration."
@@ -1569,6 +1658,7 @@
15691658
}
15701659
}
15711660
},
1661+
"additionalProperties": false,
15721662
"type": "object",
15731663
"required": [
15741664
"url"
@@ -1596,6 +1686,7 @@
15961686
"title": "Role Rules"
15971687
}
15981688
},
1689+
"additionalProperties": false,
15991690
"type": "object",
16001691
"title": "JwtConfiguration",
16011692
"description": "JWT configuration."
@@ -1625,6 +1716,7 @@
16251716
"title": "Roles"
16261717
}
16271718
},
1719+
"additionalProperties": false,
16281720
"type": "object",
16291721
"required": [
16301722
"jsonpath",
@@ -1701,6 +1793,7 @@
17011793
"title": "Library Client Config Path"
17021794
}
17031795
},
1796+
"additionalProperties": false,
17041797
"type": "object",
17051798
"title": "LlamaStackConfiguration",
17061799
"description": "Llama stack configuration."
@@ -1721,6 +1814,7 @@
17211814
"title": "Url"
17221815
}
17231816
},
1817+
"additionalProperties": false,
17241818
"type": "object",
17251819
"required": [
17261820
"name",
@@ -1828,6 +1922,7 @@
18281922
"title": "Ca Cert Path"
18291923
}
18301924
},
1925+
"additionalProperties": false,
18311926
"type": "object",
18321927
"required": [
18331928
"db",
@@ -2131,6 +2226,7 @@
21312226
"title": "Db Path"
21322227
}
21332228
},
2229+
"additionalProperties": false,
21342230
"type": "object",
21352231
"required": [
21362232
"db_path"
@@ -2192,6 +2288,7 @@
21922288
}
21932289
}
21942290
},
2291+
"additionalProperties": false,
21952292
"type": "object",
21962293
"title": "ServiceConfiguration",
21972294
"description": "Service configuration."
@@ -2263,6 +2360,7 @@
22632360
"title": "Tls Key Password"
22642361
}
22652362
},
2363+
"additionalProperties": false,
22662364
"type": "object",
22672365
"title": "TLSConfiguration",
22682366
"description": "TLS configuration."
@@ -2321,6 +2419,7 @@
23212419
"title": "Transcripts Storage"
23222420
}
23232421
},
2422+
"additionalProperties": false,
23242423
"type": "object",
23252424
"title": "UserDataCollection",
23262425
"description": "User data collection configuration."

src/app/endpoints/feedback.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Handler for REST API endpoint for user feedback."""
22

33
import logging
4+
import threading
45
from typing import Annotated, Any
56
from pathlib import Path
67
import json
@@ -12,10 +13,11 @@
1213
from authorization.middleware import authorize
1314
from configuration import configuration
1415
from models.config import Action
15-
from models.requests import FeedbackRequest
16+
from models.requests import FeedbackRequest, FeedbackStatusUpdateRequest
1617
from models.responses import (
1718
ErrorResponse,
1819
FeedbackResponse,
20+
FeedbackStatusUpdateResponse,
1921
StatusResponse,
2022
UnauthorizedResponse,
2123
ForbiddenResponse,
@@ -25,6 +27,7 @@
2527
logger = logging.getLogger(__name__)
2628
router = APIRouter(prefix="/feedback", tags=["feedback"])
2729
auth_dependency = get_auth_dependency()
30+
feedback_status_lock = threading.Lock()
2831

2932
# Response for the feedback endpoint
3033
feedback_response: dict[int | str, dict[str, Any]] = {
@@ -174,3 +177,42 @@ def feedback_status() -> StatusResponse:
174177
return StatusResponse(
175178
functionality="feedback", status={"enabled": feedback_status_enabled}
176179
)
180+
181+
182+
@router.put("/status")
183+
@authorize(Action.ADMIN)
184+
async def update_feedback_status(
185+
feedback_update_request: FeedbackStatusUpdateRequest,
186+
auth: Annotated[AuthTuple, Depends(auth_dependency)],
187+
) -> FeedbackStatusUpdateResponse:
188+
"""
189+
Handle feedback status update requests.
190+
191+
Takes a request with the desired state of the feedback status.
192+
Returns the updated state of the feedback status based on the request's value.
193+
These changes are for the life of the service and are on a per-worker basis.
194+
195+
Returns:
196+
StatusResponse: Indicates whether feedback is enabled.
197+
"""
198+
user_id, _, _ = auth
199+
requested_status = feedback_update_request.get_value()
200+
201+
with feedback_status_lock:
202+
previous_status = (
203+
configuration.user_data_collection_configuration.feedback_enabled
204+
)
205+
configuration.user_data_collection_configuration.feedback_enabled = (
206+
requested_status
207+
)
208+
updated_status = (
209+
configuration.user_data_collection_configuration.feedback_enabled
210+
)
211+
212+
return FeedbackStatusUpdateResponse(
213+
status={
214+
"previous_status": previous_status,
215+
"updated_status": updated_status,
216+
"updated_by": user_id,
217+
}
218+
)

src/models/requests.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,3 +380,28 @@ def check_feedback_provided(self) -> Self:
380380
"'sentiment', 'user_feedback', or 'categories'"
381381
)
382382
return self
383+
384+
385+
class FeedbackStatusUpdateRequest(BaseModel):
386+
"""Model representing a feedback status update request.
387+
388+
Attributes:
389+
status: Value of the desired feedback enabled state.
390+
391+
Example:
392+
```python
393+
feedback_request = FeedbackRequest(
394+
status=false
395+
)
396+
```
397+
"""
398+
399+
status: bool = Field(
400+
False,
401+
description="Desired state of feedback enablement, must be False or True",
402+
examples=[True, False],
403+
)
404+
405+
def get_value(self) -> bool:
406+
"""Return the value of the status attribute."""
407+
return self.status

src/models/responses.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,3 +570,40 @@ class ErrorResponse(BaseModel):
570570
]
571571
}
572572
}
573+
574+
575+
class FeedbackStatusUpdateResponse(BaseModel):
576+
"""
577+
Model representing a response to a feedback status update request.
578+
579+
Attributes:
580+
status: The previous and current status of the service and who updated it.
581+
582+
Example:
583+
```python
584+
status_response = StatusResponse(
585+
status={
586+
"previous_status": true,
587+
"updated_status": false,
588+
"updated_by": "user/test"
589+
},
590+
)
591+
```
592+
"""
593+
594+
status: dict
595+
596+
# provides examples for /docs endpoint
597+
model_config = {
598+
"json_schema_extra": {
599+
"examples": [
600+
{
601+
"status": {
602+
"previous_status": True,
603+
"updated_status": False,
604+
"updated_by": "user/test",
605+
},
606+
}
607+
]
608+
}
609+
}

0 commit comments

Comments
 (0)