Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
332 changes: 328 additions & 4 deletions CLAUDE.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -845,7 +845,7 @@ describe('DotContentTypesEditComponent', () => {
contentTypeForm = de.query(By.css('dot-content-types-form'));
});

it('should udpate content type', () => {
it('should update content type', () => {
const responseContentType = Object.assign({}, fakeContentType, {
fields: [{ hello: 'world' }]
});
Expand Down
19 changes: 12 additions & 7 deletions dotCMS/src/main/java/com/dotcms/ai/rest/CompletionsResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.dotcms.ai.rest.forms.CompletionsForm;
import com.dotcms.ai.util.LineReadingOutputStream;
import com.dotcms.rest.WebResource;
import com.dotcms.rest.annotation.SwaggerCompliant;
import com.dotmarketing.beans.Host;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.business.web.WebAPILocator;
Expand All @@ -22,6 +23,7 @@
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.ws.rs.Consumes;
import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.server.JSONP;

Expand All @@ -46,8 +48,9 @@
* The CompletionsResource class provides REST endpoints for interacting with the AI completions service.
* It includes methods for generating completions based on a given prompt.
*/
@SwaggerCompliant(value = "Modern APIs and specialized services", batch = 7)
@Path("/v1/ai/completions")
@Tag(name = "AI", description = "AI-powered content generation and analysis endpoints")
@Tag(name = "AI")
public class CompletionsResource {

/**
Expand All @@ -60,7 +63,8 @@ public class CompletionsResource {
*/
@POST
@JSONP
@Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Consumes(MediaType.APPLICATION_JSON)
@Operation(
operationId = "summarizeFromContent",
summary = "Generate AI completions from content",
Expand All @@ -69,7 +73,7 @@ public class CompletionsResource {
responses = {
@ApiResponse(responseCode = "200", description = "Completion generated successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Object.class))),
schema = @Schema(type = "object", description = "AI completion response containing generated text and metadata"))),
@ApiResponse(responseCode = "400", description = "Bad request - Missing or invalid prompt"),
@ApiResponse(responseCode = "401", description = "Unauthorized - User not authenticated"),
@ApiResponse(responseCode = "500", description = "Internal server error")
Expand Down Expand Up @@ -102,7 +106,8 @@ public final Response summarizeFromContent(@Context final HttpServletRequest req
@Path("/rawPrompt")
@POST
@JSONP
@Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Consumes(MediaType.APPLICATION_JSON)
@Operation(
operationId = "rawPrompt",
summary = "Generate AI completions from raw prompt",
Expand All @@ -111,7 +116,7 @@ public final Response summarizeFromContent(@Context final HttpServletRequest req
responses = {
@ApiResponse(responseCode = "200", description = "Raw completion generated successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Object.class))),
schema = @Schema(type = "object", description = "AI completion response containing generated text and metadata"))),
@ApiResponse(responseCode = "400", description = "Bad request - Missing or invalid prompt"),
@ApiResponse(responseCode = "401", description = "Unauthorized - User not authenticated"),
@ApiResponse(responseCode = "500", description = "Internal server error")
Expand Down Expand Up @@ -143,7 +148,7 @@ public final Response rawPrompt(@Context final HttpServletRequest request,
@GET
@JSONP
@Path("/config")
@Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Operation(
operationId = "getAiConfig",
summary = "Get AI service configuration",
Expand All @@ -152,7 +157,7 @@ public final Response rawPrompt(@Context final HttpServletRequest request,
responses = {
@ApiResponse(responseCode = "200", description = "Configuration retrieved successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(implementation = Map.class))),
schema = @Schema(type = "object", description = "AI service configuration with available models and settings"))),
@ApiResponse(responseCode = "401", description = "Unauthorized - User not authenticated"),
@ApiResponse(responseCode = "500", description = "Internal server error")
}
Expand Down
123 changes: 116 additions & 7 deletions dotCMS/src/main/java/com/dotcms/ai/rest/EmbeddingsResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.dotcms.ai.rest.forms.CompletionsForm;
import com.dotcms.ai.rest.forms.EmbeddingsForm;
import com.dotcms.rest.WebResource;
import com.dotcms.rest.annotation.SwaggerCompliant;
import com.dotmarketing.business.APILocator;
import com.dotmarketing.business.Role;
import com.dotmarketing.common.model.ContentletSearch;
Expand All @@ -21,6 +22,7 @@
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import javax.ws.rs.Consumes;
import org.glassfish.jersey.server.JSONP;

import javax.servlet.http.HttpServletRequest;
Expand All @@ -45,8 +47,9 @@
* It includes endpoints for creating, deleting, and retrieving embeddings, as well as additional utility operations.
* This class requires user authentication and certain operations require specific user roles.
*/
@SwaggerCompliant(value = "Modern APIs and specialized services", batch = 7)
@Path("/v1/ai/embeddings")
@Tag(name = "AI", description = "AI-powered content generation and analysis endpoints")
@Tag(name = "AI")
public class EmbeddingsResource {

private final ContentletAPI contentletAPI;
Expand All @@ -62,6 +65,14 @@ public EmbeddingsResource() {
* @param response the HttpServletResponse object.
* @return a Response object containing a map with "type" as key and "embeddings" as value.
*/
@Operation(
summary = "Test AI embeddings service",
description = "Returns a test response to verify the AI embeddings service is operational"
)
@ApiResponse(responseCode = "200",
description = "Test response returned successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Simple key-value map indicating embeddings service type")))
@GET
@JSONP
@Path("/test")
Expand All @@ -80,12 +91,30 @@ public final Response textResource(@Context final HttpServletRequest request,
* @param embeddingsForm the form data for creating embeddings.
* @return a Response object containing the result of the embeddings creation.
*/
@Operation(
summary = "Create AI embeddings",
description = "Creates embeddings for content based on the provided form data, processing up to 10,000 content items"
)
@ApiResponse(responseCode = "200",
description = "Embeddings created successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Embeddings operation result containing timing and count information")))
@ApiResponse(responseCode = "401",
description = "Unauthorized - backend user authentication required",
content = @Content(mediaType = "application/json"))
@ApiResponse(responseCode = "500",
description = "Internal server error during embeddings creation",
content = @Content(mediaType = "application/json"))
@POST
@JSONP
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_JSON})
@Consumes(MediaType.APPLICATION_JSON)
public final Response embed(@Context final HttpServletRequest request,
@Context final HttpServletResponse response,
@RequestBody(description = "Embeddings form containing query, limit, offset, indexName, model, velocityTemplate, and fields",
required = true,
content = @Content(schema = @Schema(implementation = EmbeddingsForm.class)))
final EmbeddingsForm embeddingsForm) {

// force authentication
Expand Down Expand Up @@ -141,12 +170,27 @@ public final Response embed(@Context final HttpServletRequest request,
* @param json the JSON object containing the data for the embeddings to be deleted.
* @return a Response object containing the result of the embeddings' deletion.
*/
@Operation(
summary = "Delete AI embeddings",
description = "Deletes embeddings based on provided criteria such as query, identifier, inode, or content type"
)
@ApiResponse(responseCode = "200",
description = "Embeddings deleted successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Deletion result containing count of deleted items")))
@ApiResponse(responseCode = "401",
description = "Unauthorized - backend user authentication required",
content = @Content(mediaType = "application/json"))
@DELETE
@JSONP
@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_JSON})
@Consumes(MediaType.APPLICATION_JSON)
public final Response delete(@Context final HttpServletRequest request,
@Context final HttpServletResponse response,
@RequestBody(description = "JSON object containing deletion criteria such as query, identifier, inode, or content type",
required = true,
content = @Content(schema = @Schema(type = "object", description = "Deletion criteria including query, identifier, inode, or content type")))
final JSONObject json) {

final User user = new WebResource.InitBuilder(request, response).requiredBackendUser(true).init().getUser();
Expand Down Expand Up @@ -181,12 +225,30 @@ public final Response delete(@Context final HttpServletRequest request,
* @param json the JSON object containing the data for the operation.
* @return a Response object containing the result of the operation.
*/
@Operation(
summary = "Drop and recreate embeddings tables",
description = "Drops and recreates the embeddings database tables. Requires CMS Administrator role."
)
@ApiResponse(responseCode = "200",
description = "Tables dropped and recreated successfully",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Table creation result status")))
@ApiResponse(responseCode = "401",
description = "Unauthorized - backend user authentication required",
content = @Content(mediaType = "application/json"))
@ApiResponse(responseCode = "403",
description = "Forbidden - CMS Administrator role required",
content = @Content(mediaType = "application/json"))
@DELETE
@JSONP
@Path("/db")
@Produces(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_JSON})
@Consumes(MediaType.APPLICATION_JSON)
public final Response dropAndRecreateTables(@Context final HttpServletRequest request,
@Context final HttpServletResponse response,
@RequestBody(description = "Empty JSON object to trigger table recreation",
required = true,
content = @Content(schema = @Schema(type = "object", description = "Empty JSON object for triggering table recreation")))
final JSONObject json) {

new WebResource.InitBuilder(request, response)
Expand Down Expand Up @@ -215,18 +277,36 @@ public final Response dropAndRecreateTables(@Context final HttpServletRequest re
* @param fieldVar the fieldVar parameter.
* @return a Response object containing the count of embeddings.
*/
@Operation(
summary = "Count embeddings (GET)",
description = "Counts embeddings based on provided query parameters such as site, content type, index name, etc."
)
@ApiResponse(responseCode = "200",
description = "JSON object containing embeddingsCount key",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Count results containing embedding statistics")))
@ApiResponse(responseCode = "401",
description = "Unauthorized - backend user authentication required",
content = @Content(mediaType = "application/json"))
@GET
@JSONP
@Path("/count")
@Produces(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_JSON})
public final Response count(@Context final HttpServletRequest request,
@Context final HttpServletResponse response,
@Parameter(description = "Site identifier")
@QueryParam("site") final String site,
@Parameter(description = "Content type")
@QueryParam("contentType") final String contentType,
@Parameter(description = "Index name")
@QueryParam("indexName") final String indexName,
@Parameter(description = "Language identifier")
@QueryParam("language") final String language,
@Parameter(description = "Content identifier")
@QueryParam("identifier") final String identifier,
@Parameter(description = "Content inode")
@QueryParam("inode") final String inode,
@Parameter(description = "Field variable name")
@QueryParam("fieldVar") final String fieldVar) {

new WebResource.InitBuilder(request, response).requiredBackendUser(true).init().getUser();
Expand All @@ -249,12 +329,27 @@ public final Response count(@Context final HttpServletRequest request,
* @param form the form data for counting embeddings.
* @return a Response object containing the count of embeddings.
*/
@Operation(
summary = "Count embeddings (POST)",
description = "Counts embeddings based on provided form data containing search criteria"
)
@ApiResponse(responseCode = "200",
description = "JSON object containing embeddingsCount key",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Count results containing embedding statistics")))
@ApiResponse(responseCode = "401",
description = "Unauthorized - backend user authentication required",
content = @Content(mediaType = "application/json"))
@POST
@JSONP
@Path("/count")
@Produces(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_JSON})
@Consumes(MediaType.APPLICATION_JSON)
public final Response count(@Context final HttpServletRequest request,
@Context final HttpServletResponse response,
@RequestBody(description = "Completion form containing search criteria for counting embeddings",
required = false,
content = @Content(schema = @Schema(implementation = CompletionsForm.class)))
final CompletionsForm form) {

new WebResource.InitBuilder(request, response).requiredBackendUser(true).init().getUser();
Expand All @@ -273,10 +368,24 @@ public final Response count(@Context final HttpServletRequest request,
* @param response the HttpServletResponse response.
* @return a Response object containing the count of embeddings by index.
*/
@Operation(
summary = "Count embeddings by index",
description = "Returns count of embeddings grouped by index name. Requires CMS Administrator role."
)
@ApiResponse(responseCode = "200",
description = "JSON Object containing indexCount key",
content = @Content(mediaType = "application/json",
schema = @Schema(type = "object", description = "Count results containing index statistics grouped by index name")))
@ApiResponse(responseCode = "401",
description = "Unauthorized - backend user authentication required",
content = @Content(mediaType = "application/json"))
@ApiResponse(responseCode = "403",
description = "Forbidden - CMS Administrator role required",
content = @Content(mediaType = "application/json"))
@GET
@JSONP
@Path("/indexCount")
@Produces(MediaType.APPLICATION_JSON)
@Produces({MediaType.APPLICATION_JSON})
public final Response indexCount(@Context final HttpServletRequest request,
@Context final HttpServletResponse response) {
new WebResource.InitBuilder(request, response)
Expand Down
Loading
Loading