Skip to content

Commit 7eb1ce5

Browse files
authored
Support prometheus 3 (#3734)
### 🛠 Summary Validated with both: - prometheus-2.55.1 - prometheus-3.5.0 It works for both after this change ### 🧪 Checklist - [x] Unit tests added. - [ ] The documentation updated. - [x] Change follows security best practices.
1 parent 0d1cea9 commit 7eb1ce5

File tree

4 files changed

+40
-5
lines changed

4 files changed

+40
-5
lines changed

src/http_rest_api_handler.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ void HttpRestApiHandler::registerAll() {
226226
return processV3(uri, request_components, response, request_body, std::move(serverReaderWriter), std::move(multiPartParser));
227227
});
228228
registerHandler(Metrics, [this](const std::string_view uri, const HttpRequestComponents& request_components, std::string& response, const std::string& request_body, HttpResponseComponents& response_components, std::shared_ptr<HttpAsyncWriter> serverReaderWriter, std::shared_ptr<MultiPartParser> multiPartParser) -> Status {
229-
return processMetrics(request_components, response, request_body);
229+
return processMetrics(request_components, response_components, response, request_body);
230230
});
231231
registerHandler(Options, [this](const std::string_view uri, const HttpRequestComponents& request_components, std::string& response, const std::string& request_body, HttpResponseComponents& response_components, std::shared_ptr<HttpAsyncWriter> serverReaderWriter, std::shared_ptr<MultiPartParser> multiPartParser) -> Status {
232232
return processOptions(request_components, response, request_body);
@@ -741,14 +741,16 @@ Status HttpRestApiHandler::processV3(const std::string_view uri, const HttpReque
741741
#endif
742742
}
743743

744-
Status HttpRestApiHandler::processMetrics(const HttpRequestComponents& request_components, std::string& response, const std::string& request_body) {
744+
Status HttpRestApiHandler::processMetrics(const HttpRequestComponents& request_components, HttpResponseComponents& response_components, std::string& response, const std::string& request_body) {
745745
auto module = this->ovmsServer.getModule(METRICS_MODULE_NAME);
746746
if (nullptr == module) {
747747
SPDLOG_ERROR("Failed to process metrics - metrics module is missing");
748748
return StatusCode::INTERNAL_ERROR;
749749
}
750750
auto& metricConfig = this->modelManager.getMetricConfig();
751751

752+
response_components.contentType = ContentType::PLAIN_TEXT; // Prometheus exposition format, since v3 does not ignore quietly
753+
752754
if (!metricConfig.metricsEnabled) {
753755
return StatusCode::REST_INVALID_URL;
754756
}

src/http_rest_api_handler.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,14 @@ struct HttpRequestComponents {
7474
std::unordered_map<std::string, std::string> headers;
7575
};
7676

77+
enum class ContentType {
78+
JSON,
79+
PLAIN_TEXT
80+
};
81+
7782
struct HttpResponseComponents {
7883
std::optional<int> inferenceHeaderContentLength;
84+
ContentType contentType = ContentType::JSON;
7985
};
8086

8187
using HandlerCallbackFn = std::function<Status(
@@ -231,7 +237,7 @@ class HttpRestApiHandler {
231237
Status processModelMetadataKFSRequest(const HttpRequestComponents& request_components, std::string& response, const std::string& request_body);
232238
Status processModelReadyKFSRequest(const HttpRequestComponents& request_components, std::string& response, const std::string& request_body);
233239
Status processInferKFSRequest(const HttpRequestComponents& request_components, std::string& response, const std::string& request_body, std::optional<int>& inferenceHeaderContentLength);
234-
Status processMetrics(const HttpRequestComponents& request_components, std::string& response, const std::string& request_body);
240+
Status processMetrics(const HttpRequestComponents& request_components, HttpResponseComponents& response_components, std::string& response, const std::string& request_body);
235241
Status processOptions(const HttpRequestComponents& request_components, std::string& response, const std::string& request_body);
236242

237243
Status processServerReadyKFSRequest(const HttpRequestComponents& request_components, std::string& response, const std::string& request_body);

src/http_server.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,12 @@ std::unique_ptr<DrogonHttpServer> createAndStartDrogonHttpServer(const std::stri
216216
output = buffer.GetString();
217217
}
218218
auto resp = drogon::HttpResponse::newHttpResponse();
219-
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
219+
220+
if (responseComponents.contentType == ContentType::PLAIN_TEXT) {
221+
resp->setContentTypeCode(drogon::CT_TEXT_PLAIN);
222+
} else {
223+
resp->setContentTypeCode(drogon::CT_APPLICATION_JSON);
224+
}
220225

221226
if (responseComponents.inferenceHeaderContentLength.has_value()) {
222227
resp->addHeader("inference-header-content-length", std::to_string(responseComponents.inferenceHeaderContentLength.value()));

src/test/http_openai_handler_test.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class HttpOpenAIHandlerTest : public ::testing::Test {
4040

4141
std::unordered_map<std::string, std::string> headers{{"content-type", "application/json"}};
4242
ovms::HttpRequestComponents comp;
43-
const std::string endpoint = "/v3/chat/completions";
43+
std::string endpoint = "/v3/chat/completions";
4444
std::shared_ptr<MockedServerRequestInterface> writer;
4545
std::shared_ptr<MockedMultiPartParser> multiPartParser;
4646
std::string response;
@@ -1152,6 +1152,28 @@ TEST_F(HttpOpenAIHandlerTest, V3ApiWithNonLLMCalculator) {
11521152
ASSERT_EQ(status, ovms::StatusCode::MEDIAPIPE_GRAPH_ADD_PACKET_INPUT_STREAM);
11531153
}
11541154

1155+
TEST_F(HttpOpenAIHandlerTest, DefaultContentTypeJSON) {
1156+
std::string requestBody = "";
1157+
endpoint = "/v3/chat/completions";
1158+
ASSERT_EQ(handler->parseRequestComponents(comp, "POST", endpoint, headers), ovms::StatusCode::OK);
1159+
ASSERT_NE( // Not equal because we do not expect for the workload to be processed
1160+
handler->dispatchToProcessor(endpoint, requestBody, &response, comp, responseComponents, writer, multiPartParser),
1161+
ovms::StatusCode::OK);
1162+
1163+
ASSERT_EQ(responseComponents.contentType, ovms::ContentType::JSON);
1164+
}
1165+
1166+
TEST_F(HttpOpenAIHandlerTest, MetricsEndpointContentTypePlainText) {
1167+
std::string requestBody = "";
1168+
endpoint = "/metrics";
1169+
ASSERT_EQ(handler->parseRequestComponents(comp, "GET", endpoint, headers), ovms::StatusCode::OK);
1170+
ASSERT_NE( // Not equal because we do not expect for the workload to be processed
1171+
handler->dispatchToProcessor(endpoint, requestBody, &response, comp, responseComponents, writer, multiPartParser),
1172+
ovms::StatusCode::OK);
1173+
1174+
ASSERT_EQ(responseComponents.contentType, ovms::ContentType::PLAIN_TEXT);
1175+
}
1176+
11551177
TEST_F(HttpOpenAIHandlerParsingTest, responseFormatValid) {
11561178
std::string json = R"({
11571179
"model": "llama",

0 commit comments

Comments
 (0)