-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
python packaged server tests (FF-3494) (#88)
* python packaged server tests (FF-3494) * python does not support bandits or dynamic types * run package test on PR
- Loading branch information
1 parent
84b4e18
commit 3454761
Showing
11 changed files
with
263 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
bin | ||
lib | ||
venv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
FROM python:3.12 | ||
|
||
WORKDIR /app | ||
|
||
COPY requirements.txt . | ||
RUN pip install -r requirements.txt | ||
|
||
# Copy the source code | ||
COPY src/ ./src/ | ||
|
||
CMD ["python", "/app/src/server.py"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Python Testing Server | ||
|
||
Post test case files to this server and check the results against what's expected. | ||
|
||
## Running locally with Docker | ||
|
||
Build the docker image: | ||
|
||
```shell | ||
docker build -t Eppo-exp/python-sdk-relay . | ||
``` | ||
|
||
Run the docker container: | ||
|
||
```shell | ||
./docker-run.sh | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#!/usr/bin/env bash | ||
|
||
# Default is to use the latest build | ||
VERSION="${1:-latest}" | ||
|
||
echo "Starting deployment with version: $VERSION" | ||
|
||
if [ -e .env ]; then | ||
echo "Loading environment variables from .env file" | ||
source .env | ||
fi | ||
|
||
echo "Stopping existing container..." | ||
docker stop python-relay | ||
echo "Removing existing container..." | ||
docker remove python-relay | ||
|
||
echo "Building new image..." | ||
docker build . -t Eppo-exp/python-sdk-relay:$VERSION | ||
|
||
echo "Starting new container..." | ||
docker run -p $SDK_RELAY_PORT:$SDK_RELAY_PORT \ | ||
--add-host host.docker.internal:host-gateway \ | ||
-e SDK_REF \ | ||
-e EPPO_BASE_URL \ | ||
-e SDK_RELAY_PORT \ | ||
--name python-relay \ | ||
--rm \ | ||
-t Eppo-exp/python-sdk-relay:$VERSION; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
flask | ||
eppo-server-sdk==4.1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import eppo_client | ||
import eppo_client.bandit | ||
|
||
from flask import Flask, request, jsonify | ||
from os import environ | ||
from dataclasses import dataclass | ||
from eppo_client.config import Config, AssignmentLogger | ||
|
||
app = Flask(__name__) | ||
|
||
|
||
class LocalAssignmentLogger(AssignmentLogger): | ||
def log_assignment(self, assignment): | ||
print(f"Assignment: {assignment}") | ||
|
||
|
||
@dataclass | ||
class AssignmentRequest: | ||
flag: str | ||
subject_key: str | ||
subject_attributes: dict | ||
assignment_type: str | ||
default_value: any | ||
|
||
|
||
@app.route('/', methods=['GET']) | ||
def health_check(): | ||
return "OK" | ||
|
||
@app.route('/sdk/reset', methods=['POST']) | ||
def reset_sdk(): | ||
initialize_client_and_wait() | ||
|
||
return "Reset complete" | ||
|
||
@app.route('/sdk/details', methods=['GET']) | ||
def get_sdk_details(): | ||
return jsonify({ | ||
"sdkName": "python-sdk", | ||
"sdkVersion": "4.1.0", | ||
"supportsBandits": False, | ||
"supportsDynamicTyping": False | ||
}) | ||
|
||
@app.route('/flags/v1/assignment', methods=['POST']) | ||
def handle_assignment(): | ||
data = request.json | ||
request_obj = AssignmentRequest( | ||
flag=data['flag'], | ||
subject_key=data['subjectKey'], | ||
subject_attributes=data['subjectAttributes'], | ||
assignment_type=data['assignmentType'], | ||
default_value=data['defaultValue'] | ||
) | ||
print(f"Request object: {request_obj}") | ||
|
||
client = eppo_client.get_instance() | ||
|
||
try: | ||
match request_obj.assignment_type: | ||
case 'BOOLEAN': | ||
result = client.get_boolean_assignment( | ||
request_obj.flag, | ||
request_obj.subject_key, | ||
request_obj.subject_attributes, | ||
bool(request_obj.default_value) | ||
) | ||
case 'INTEGER': | ||
result = client.get_integer_assignment( | ||
request_obj.flag, | ||
request_obj.subject_key, | ||
request_obj.subject_attributes, | ||
int(request_obj.default_value) | ||
) | ||
case 'STRING': | ||
result = client.get_string_assignment( | ||
request_obj.flag, | ||
request_obj.subject_key, | ||
request_obj.subject_attributes, | ||
request_obj.default_value | ||
) | ||
case 'NUMERIC': | ||
result = client.get_numeric_assignment( | ||
request_obj.flag, | ||
request_obj.subject_key, | ||
request_obj.subject_attributes, | ||
float(request_obj.default_value) | ||
) | ||
case 'JSON': | ||
result = client.get_json_assignment( | ||
request_obj.flag, | ||
request_obj.subject_key, | ||
request_obj.subject_attributes, | ||
request_obj.default_value | ||
) | ||
|
||
response = { | ||
"result": result, | ||
"assignmentLog": [], | ||
"banditLog": [], | ||
"error": None | ||
} | ||
print(f"response: {response}") | ||
return jsonify(response) | ||
except Exception as e: | ||
print(f"Error processing assignment: {str(e)}") | ||
response = { | ||
"result": None, | ||
"assignmentLog": [], | ||
"banditLog": [], | ||
"error": str(e) | ||
} | ||
return jsonify(response) | ||
|
||
@dataclass | ||
class BanditActionRequest: | ||
flag: str | ||
subject_key: str | ||
subject_attributes: dict | ||
actions: list | ||
default_value: any | ||
|
||
|
||
@app.route('/bandits/v1/action', methods=['POST']) | ||
def handle_bandit(): | ||
data = request.json | ||
request_obj = BanditActionRequest( | ||
flag=data['flag'], | ||
subject_key=data['subjectKey'], | ||
subject_attributes=data['subjectAttributes'], | ||
default_value=data['defaultValue'], | ||
actions=data['actions'] | ||
) | ||
print(f"Request object: {request_obj}") | ||
|
||
# TODO: Implement bandit logic | ||
return jsonify({ | ||
"result": "action", | ||
"assignmentLog": [], | ||
"banditLog": [], | ||
"error": None | ||
}) | ||
|
||
def initialize_client_and_wait(): | ||
print("Initializing client") | ||
api_key = environ.get('EPPO_API_KEY', 'NOKEYSPECIFIED') | ||
base_url = environ.get('EPPO_BASE_URL', 'http://localhost:5000/api') | ||
|
||
client_config = Config( | ||
api_key=api_key, | ||
base_url=base_url, | ||
assignment_logger=LocalAssignmentLogger() | ||
) | ||
eppo_client.init(client_config) | ||
client = eppo_client.get_instance() | ||
client.wait_for_initialization() | ||
print("Client initialized") | ||
|
||
if __name__ == "__main__": | ||
initialize_client_and_wait() | ||
|
||
port = int(environ.get('SDK_RELAY_PORT', 7001)) | ||
host = environ.get('SDK_RELAY_HOST', '0.0.0.0') | ||
print(f"Starting server on {host}:{port}") | ||
app.run( | ||
host=host, | ||
port=port, | ||
debug=True # Add debug mode | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters