F5 AI Gateway Python SDK Quickstart¶
Note
Contact your F5 representative for access to evaluate the SDK.
About processors¶
There are three types of alterations which a processor may make on a request.
Annotation
Rejection
Modification
A processor can only make these types of alterations when a specific parameter is provided.
If there is an attempt to make a modification without the required parameter, the SDK will drop the modification, and it will log a warning. For example:
WARNING:root:CatClassifierProcessor tried to annotate request with tags when parameters.annotate was set to false, tags will be dropped
Annotation¶
A processor can add tags to a request which can be used by AI Gateway to route the request. This is controlled by the parameter annotate
, which defaults to true
.
Rejection¶
A processor can reject a request which causes a rejection message to be returned to the client. This is controlled by the parameter reject
, which defaults to false
.
Modification¶
A processor can modify the prompt that is sent by a client or modify the response from an upstream model. This is controlled by the parameter modify
, which defaults to false
.
Before you begin¶
To follow the steps in this guide you will need:
A Python 3.11+ environment with pip or an alternative installed
An ASGI server
In this example we will use uvicorn as our ASGI server. Install it first if you don’t have it already or adjust the commands to use the server of your choice.
Note
You may want to use a Python virtual environment to manage your dependencies. Below is an example of setting up and activating a virtual environment.
python3 -m venv .venv
source .venv/bin/activate
pip install uvicorn
Install the SDK¶
The F5 AI Gateway Python SDK is designed to handle requests and responses between the F5 AI Gateway and your application, allowing you to focus on developing your processor logic.
pip install f5-ai-gateway-sdk
Create a basic processor which annotates a request¶
In this tutorial we create a simple processor that inspects the prompt and response for any mentions of a ‘cat’ and tags it accordingly. Tags can be used in the AI Gateway configuration to route to a specific upstream LLM or run a particular set of processors.
Create a file called cat_classifier.py
with the following contents.
from f5_ai_gateway_sdk.parameters import Parameters
from f5_ai_gateway_sdk.processor import Processor
from f5_ai_gateway_sdk.processor_routes import ProcessorRoutes
from f5_ai_gateway_sdk.request_input import Message, MessageRole
from f5_ai_gateway_sdk.result import Result
from f5_ai_gateway_sdk.signature import BOTH_SIGNATURE
from f5_ai_gateway_sdk.tags import Tags
from f5_ai_gateway_sdk.type_hints import Metadata
from starlette.applications import Starlette
class CatClassifierProcessor(Processor):
"""
A simple processor which adds a tag when a cat is found in the prompt or response
"""
def __init__(self):
super().__init__(
name="cat-classifier",
version="v1",
namespace="tutorial",
signature = BOTH_SIGNATURE
)
def process(self, prompt, response, metadata, parameters, request):
my_tags = Tags()
cat_found = False
if response:
cat_found = any(
"cat" in choice.message.content for choice in response.choices
)
elif prompt:
cat_found = any("cat" in message.content for message in prompt.messages)
result = Metadata({"cat_found": cat_found})
if cat_found:
my_tags.add_tag("animals-found", "cat")
return Result(
processor_result=result, tags=my_tags if parameters.annotate else None
)
app = Starlette(
routes=ProcessorRoutes([CatClassifierProcessor()]),
)
Note
This example contains all of the imports that will be needed for this tutorial so your editor may warn about unused imports.
Run the processor locally¶
Run the processor locally using uvicorn; this will start a server on port 9999
. Ensure that you run this command from the directory where your cat_classifier.py
file is located.
python -m uvicorn cat_classifier:app --host 127.0.0.1 --port 9999 --reload
The --reload
flag will automatically restart the server when you make changes to your code.
We can check if the server is running by sending a curl request to the signature endpoint of the processor.
Request
curl -i http://localhost:9999/api/v1/signature/tutorial/cat-classifier
Response
{
"fields": [
{
"type": "response.choices",
"required": false
},
{
"type": "input.messages",
"required": false
}
],
"parameters": {
"description": "Default empty parameters class for processors that do not require parameters.",
"properties": {
"annotate": {
"default": true,
"description": "Whether the processor can annotate the input with tags.",
"title": "Annotate",
"type": "boolean"
},
"modify": {
"default": false,
"description": "Whether the processor can modify the input.",
"title": "Modify",
"type": "boolean"
},
"reject": {
"default": false,
"description": "Whether the processor can reject requests.",
"title": "Reject",
"type": "boolean"
}
},
"title": "Default Parameters",
"type": "object"
}
}
In the above endpoint, tutorial
is the namespace of your processor, and cat-classifier
is the name of your processor.
The response should be similar to the following example and contains information that is used by AI Gateway to send correctly formatted requests to the processor:
Request
curl -i -X POST http://localhost:9999/api/v1/execute/tutorial/cat-classifier \
-H "Content-Type: multipart/form-data" \
--form 'input.parameters={};type=application/json' \
--form 'metadata={};type=application/json' \
--form 'input.messages={
"messages": [
{
"content": "Curious cats gracefully roam through cozy homes, chasing shadows and basking in warm sunlit windows."
}
]
};type=application/json'
Response
HTTP/1.1 200 OK
date: Fri, 22 Nov 2024 16:33:10 GMT
server: uvicorn
content-type: multipart/form-data;charset=utf-8;boundary="3zWBuRUjyupW5lUbCEITz3riZBm2dPztxregsT4fMZJBHjmoEDYUq5fqAXGCKHr6"
Transfer-Encoding: chunked
--3zWBuRUjyupW5lUbCEITz3riZBm2dPztxregsT4fMZJBHjmoEDYUq5fqAXGCKHr6
Content-Disposition: form-data; name="metadata"
Content-Type: application/json
{"processor_id": "example:cat-classifier", "processor_version": "v1", "processor_result": {"cat_found": true}, "tags": {"animals-found": ["cat"]}}
--3zWBuRUjyupW5lUbCEITz3riZBm2dPztxregsT4fMZJBHjmoEDYUq5fqAXGCKHr6--
Use the processor with AI Gateway¶
To configure this processor in AI Gateway add the following to your aigw.yml
.
processors:
- name: cat-classifier
type: external
config:
endpoint: "localhost:9999"
namespace: tutorial
version: 1
See the configure processors page for more details.
Configure the processor to reject a request¶
Processors can be configured on a per-request basis by setting the params
object in either the processors
or steps
sections of the AI Gateway configuration file.
Let’s use our default parameters to reject any requests which don’t contain cats. Update the end of the process
function in cat_classifier.py
to add the reject
variable and reference it in the return
statement:
reject = parameters.reject and not cat_found
return Result(
processor_result=result,
tags=my_tags if parameters.annotate else None,
rejected=reject,
)
We can now make a request to the processor with the reject
parameter set to true
:
Request
curl -i -X POST http://localhost:9999/api/v1/execute/tutorial/cat-classifier \
-H "Content-Type: multipart/form-data" \
--form 'input.parameters={"reject":true};type=application/json' \
--form 'metadata={};type=application/json' \
--form 'input.messages={
"messages": [
{
"content": "Curious dogs gracefully roam through cozy homes, chasing shadows and basking in warm sunlit windows."
}
]
};type=application/json'
Response
HTTP/1.1 422 Unprocessable Content
date: Fri, 22 Nov 2024 16:32:45 GMT
server: uvicorn
content-type: multipart/form-data;charset=utf-8;boundary="dkQBsMcvc3JXNWKnlTdRw8dOfsD3KWjrZnJvgZ6z0nFS8hNBCnSmiJ2eRldNXTnR"
Transfer-Encoding: chunked
--dkQBsMcvc3JXNWKnlTdRw8dOfsD3KWjrZnJvgZ6z0nFS8hNBCnSmiJ2eRldNXTnR
Content-Disposition: form-data; name="metadata"
Content-Type: application/json
{"processor_id": "example:cat-classifier", "processor_version": "v1", "processor_result": {"cat_found": false}}
--dkQBsMcvc3JXNWKnlTdRw8dOfsD3KWjrZnJvgZ6z0nFS8hNBCnSmiJ2eRldNXTnR--
The 422
status code is used to indicate that the request was rejected by the processor.
Configure the processor to modify a request¶
Processors also have the ability to make modifications to the prompt or response before it is routed to the next stage. Make the following changes to add a system message to the request if the processor is used in an input stage.
Additional parameters can be added to the processor by defining a class that extends f5_ai_gateway_sdk.parameters.Parameters
. Add the following after the imports section at the top of the cat_classifier.py
file:
class CatClassifierParameters(Parameters):
enforce_cat_message: str = "Remember to talk about cats in your response"
We then need to update the __init__
function for the processor to specify this class.
def __init__(self):
super().__init__(
name="cat-classifier",
version="v1",
namespace="tutorial",
signature=BOTH_SIGNATURE,
parameters_class=CatClassifierParameters
)
If we make a request to the signature endpoint, we can see the newly added parameter.
Request
curl -i http://localhost:9999/api/v1/signature/tutorial/cat-classifier
Response
{
"fields": [
{
"type": "input.messages",
"required": false
},
{
"type": "response.choices",
"required": false
}
],
"parameters": {
"properties": {
"annotate": {
"default": true,
"description": "Whether the processor can annotate the input with tags.",
"title": "Annotate",
"type": "boolean"
},
"modify": {
"default": false,
"description": "Whether the processor can modify the input.",
"title": "Modify",
"type": "boolean"
},
"reject": {
"default": false,
"description": "Whether the processor can reject requests.",
"title": "Reject",
"type": "boolean"
},
"enforce_cat_message": {
"default": "Remember to talk about cats in your response",
"title": "Enforce Cat Message",
"type": "string"
}
},
"title": "CatClassifierParameters",
"type": "object"
}
}
Note
Due to the CatClassifierParameters
used in this example being a subclass of Parameters
, it automatically has access to the common parameters of annotate
, reject
, and modify
.
Add the following to the process function before the return statement
if (
not reject # don't attempt to add a message if we are already rejecting
and parameters.modify
and prompt
):
prompt.messages.append(
Message(
content=parameters.enforce_cat_message,
role=MessageRole.SYSTEM,
)
)
return Result(
processor_result=result,
tags=my_tags if parameters.annotate else None,
rejected=reject,
modified_prompt=prompt,
)
Verify that the system message has been added to the request.
Request
curl -i -X POST http://localhost:9999/api/v1/execute/tutorial/cat-classifier \
-H "Content-Type: multipart/form-data" \
--form 'input.parameters={"modify":true,"enforce_cat_message":"Remember to talk about how cute cats are in your response"};type=application/json' \
--form 'metadata={};type=application/json' \
--form 'input.messages={
"messages": [
{
"content": "Curious dogs gracefully roam through cozy homes, chasing shadows and basking in warm sunlit windows."
}
]
};type=application/json'
Response
HTTP/1.1 200 OK
date: Thu, 12 Dec 2024 16:03:26 GMT
server: uvicorn
content-type: multipart/form-data;charset=utf-8;boundary="iBDsmhfPKiowYNQTb9bbfPT76c26q3y8Zj9o7qFHrgDIsIlLqDQi5E5bQaqjIkLy"
Transfer-Encoding: chunked
--iBDsmhfPKiowYNQTb9bbfPT76c26q3y8Zj9o7qFHrgDIsIlLqDQi5E5bQaqjIkLy
Content-Disposition: form-data; name="input.messages"
Content-Type: text/plain;charset=utf-8
{"messages":[{"content":"Curious dogs gracefully roam through cozy homes, chasing shadows and basking in warm sunlit windows.","role":"user"},{"content":"Remember to talk about how cute cats are in your response","role":"system"}]}
--iBDsmhfPKiowYNQTb9bbfPT76c26q3y8Zj9o7qFHrgDIsIlLqDQi5E5bQaqjIkLy
Content-Disposition: form-data; name="metadata"
Content-Type: application/json
{"processor_id": "tutorial:cat-classifier", "processor_version": "v1", "processor_result": {"cat_found": false}}
--iBDsmhfPKiowYNQTb9bbfPT76c26q3y8Zj9o7qFHrgDIsIlLqDQi5E5bQaqjIkLy--
Completed tutorial processor¶
from f5_ai_gateway_sdk.parameters import Parameters
from f5_ai_gateway_sdk.processor import Processor
from f5_ai_gateway_sdk.processor_routes import ProcessorRoutes
from f5_ai_gateway_sdk.request_input import Message, MessageRole
from f5_ai_gateway_sdk.result import Result
from f5_ai_gateway_sdk.signature import BOTH_SIGNATURE
from f5_ai_gateway_sdk.tags import Tags
from f5_ai_gateway_sdk.type_hints import Metadata
from starlette.applications import Starlette
class CatClassifierParameters(Parameters):
enforce_cat_message: str = "Remember to talk about cats in your response"
class CatClassifierProcessor(Processor):
"""
A simple processor which adds a tag when a cat is found in the prompt or response
"""
def __init__(self):
super().__init__(
name="cat-classifier",
version="v1",
namespace="tutorial",
signature=BOTH_SIGNATURE,
parameters_class=CatClassifierParameters,
)
def process(self, prompt, response, metadata, parameters, request):
my_tags = Tags()
cat_found = False
if response:
cat_found = any(
"cat" in choice.message.content for choice in response.choices
)
elif prompt:
cat_found = any("cat" in message.content for message in prompt.messages)
result = Metadata({"cat_found": cat_found})
if cat_found:
my_tags.add_tag("animals-found", "cat")
reject = parameters.reject and not cat_found
if (
not reject # don't attempt to add a message if we are already rejecting
and parameters.modify
and prompt
):
prompt.messages.append(
Message(
content=parameters.enforce_cat_message,
role=MessageRole.SYSTEM,
)
)
return Result(
processor_result=result,
tags=my_tags if parameters.annotate else None,
rejected=reject,
modified_prompt=prompt,
)
return Result(
processor_result=result,
tags=my_tags if parameters.annotate else None,
rejected=reject,
)
app = Starlette(
routes=ProcessorRoutes([CatClassifierProcessor()]),
)