diff --git a/app/api/api.py b/app/api/api.py index 44384150..2ef30ca5 100644 --- a/app/api/api.py +++ b/app/api/api.py @@ -273,7 +273,7 @@ def _get_app( add_exception_handlers(app) instrumentator = Instrumentator( - excluded_handlers=["/docs", "/redoc", "/metrics", "/openapi.json", "/favicon.ico", "none"] + excluded_handlers=["/docs", "/redoc", "/metrics", "/openapi.json", "/favicon.png", "none"] ).instrument(app) if msd_overwritten is not None: @@ -309,7 +309,7 @@ async def swagger_doc(req: Request) -> HTMLResponse: title="CogStack ModelServe", oauth2_redirect_url=oauth2_redirect_url, init_oauth=app.swagger_ui_init_oauth, - swagger_favicon_url="/static/images/favicon.ico", + swagger_favicon_url="/static/images/favicon.png", swagger_ui_parameters=app.swagger_ui_parameters, ) @@ -320,7 +320,7 @@ async def redoc_doc(req: Request) -> HTMLResponse: return get_redoc_html( openapi_url=openapi_url, title="CogStack ModelServe", - redoc_favicon_url="/static/images/favicon.ico", + redoc_favicon_url="/static/images/favicon.png", ) @app.get("/", include_in_schema=False) diff --git a/app/api/routers/invocation.py b/app/api/routers/invocation.py index 3c6f5c12..c1d8e3c1 100644 --- a/app/api/routers/invocation.py +++ b/app/api/routers/invocation.py @@ -45,7 +45,7 @@ PATH_PROCESS_BULK_FILE = "/process_bulk_file" PATH_REDACT = "/redact" PATH_REDACT_WITH_ENCRYPTION = "/redact_with_encryption" -PATH_OPENAI_EMBEDDINGS = "/v1/embeddings" +PATH_EMBEDDINGS = "/embeddings" router = APIRouter() config = get_settings() @@ -359,13 +359,13 @@ def get_redacted_text_with_encryption( @router.post( - PATH_OPENAI_EMBEDDINGS, - tags=[Tags.OpenAICompatible.name], + PATH_EMBEDDINGS, + tags=[Tags.Embeddings.name], response_model=None, dependencies=[Depends(cms_globals.props.current_active_user)], - description="Create embeddings based on text(s), similar to OpenAI's /v1/embeddings endpoint", + description="Create embeddings based on text(s)", ) -def embed_texts( +def get_text_embeddings( request: Request, request_data: Annotated[OpenAIEmbeddingsRequest, Body( description="Text(s) to be embedded", media_type="application/json" @@ -374,7 +374,7 @@ def embed_texts( model_service: AbstractModelService = Depends(cms_globals.model_service_dep) ) -> JSONResponse: """ - Embeds text or a list of texts, mimicking OpenAI's /v1/embeddings endpoint. + Embeds text or a list of texts. Args: request (Request): The request object. diff --git a/app/api/static/images/favicon.ico b/app/api/static/images/favicon.ico deleted file mode 100644 index 0a2a28a2..00000000 Binary files a/app/api/static/images/favicon.ico and /dev/null differ diff --git a/app/api/static/images/favicon.png b/app/api/static/images/favicon.png new file mode 100644 index 00000000..40901132 Binary files /dev/null and b/app/api/static/images/favicon.png differ diff --git a/app/domain.py b/app/domain.py index 7b9c1413..6dc050c1 100644 --- a/app/domain.py +++ b/app/domain.py @@ -24,6 +24,7 @@ class Tags(str, Enum): Redaction = "Redact the extracted NER entities" Rendering = "Preview embeddable annotation snippet in HTML" Training = "Trigger model training on input annotations" + Embeddings = "Generate embeddings for input texts" Evaluating = "Evaluate the deployed model with trainer export" Authentication = "Authenticate registered users" Generative = "Generate text based on the input prompt" diff --git a/app/mcp/server.py b/app/mcp/server.py index 0781faf1..43701085 100644 --- a/app/mcp/server.py +++ b/app/mcp/server.py @@ -140,7 +140,7 @@ def create_server() -> Starlette: oauth_manager=oauth_manager, public_paths=[ "/authorize", - "/favicon.ico", + "/favicon.png", "/oauth/", "/.well-known", "/.well-known/", diff --git a/docker-compose-proxy.yml b/docker-compose-proxy.yml index 7ee7d53c..b1f1efb3 100644 --- a/docker-compose-proxy.yml +++ b/docker-compose-proxy.yml @@ -33,7 +33,7 @@ services: - 28202:28202 # grafana - 28203:28203 # graylog healthcheck: - test: ["CMD", "curl", "-f", "http://localhost/health"] + test: ["CMD", "curl", "-f", "-k", "https://localhost/health"] interval: 60s timeout: 20s retries: 3 diff --git a/tests/integration/features/serving.feature b/tests/integration/features/serving.feature index 4b270fd6..a2bd48a8 100644 --- a/tests/integration/features/serving.feature +++ b/tests/integration/features/serving.feature @@ -93,6 +93,14 @@ Feature: | /redact_with_encryption | {"text": "Spinal stenosis", "public_key_pem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3ITkTP8Tm/5FygcwY2EQ7LgVsuCF0OH7psUqvlXnOPNCfX86CobHBiSFjG9o5ZeajPtTXaf1thUodgpJZVZSqpVTXwGKo8r0COMO87IcwYigkZZgG/WmZgoZART+AA0+JvjFGxflJAxSv7puGlf82E+u5Wz2psLBSDO5qrnmaDZTvPh5eX84cocahVVI7X09/kI+sZiKauM69yoy1bdx16YIIeNm0M9qqS3tTrjouQiJfZ8jUKSZ44Na/81LMVw5O46+5GvwD+OsR43kQ0TexMwgtHxQQsiXLWHCDNy2ZzkzukDYRwA3V2lwVjtQN0WjxHg24BTBDBM+v7iQ7cbweQIDAQAB\n-----END PUBLIC KEY-----"} | application/json | Then the response should contain encrypted labels + @embeddings + Scenario: Generate embeddings for a single text + Given CMS app is up and running + When I send a POST request with the following content + | endpoint | data | content_type | + | /embeddings | {"input": "Spinal stenosis", "model": "cms-model"} | application/json | + Then the response should contain embeddings + @preview Scenario: Extract and preview entities Given CMS app is up and running diff --git a/tests/integration/steps/test_steps.py b/tests/integration/steps/test_steps.py index fe41011b..144c1f0c 100644 --- a/tests/integration/steps/test_steps.py +++ b/tests/integration/steps/test_steps.py @@ -284,3 +284,15 @@ def check_response_annotation_stats(context): assert len(response_lines) > 1 assert "concept,anno_count,anno_unique_counts,anno_ignorance_counts" == response_lines[0] context["response"].close() + +@then("the response should contain embeddings") +def check_response_embeddings(context): + assert context["response"].status_code == 200 + assert context["response"].headers["Content-Type"] == "application/json" + response_json = context["response"].json() + assert "data" in response_json + assert isinstance(response_json["data"], list) + assert len(response_json["data"]) > 0 + assert "embedding" in response_json["data"][0] + assert isinstance(response_json["data"][0]["embedding"], list) + context["response"].close()