All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.composum.ai.backend.slingbase.experimential.impl.GetPageMarkdownAITool Maven / Gradle / Ivy

package com.composum.ai.backend.slingbase.experimential.impl;

import java.util.Locale;
import java.util.Map;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.composum.ai.backend.base.service.chat.GPTCompletionCallback;
import com.composum.ai.backend.slingbase.ApproximateMarkdownService;
import com.composum.ai.backend.slingbase.experimential.AITool;
import com.composum.ai.backend.slingbase.model.SlingGPTExecutionContext;
import com.google.gson.Gson;

@Component(service = AITool.class, configurationPolicy = ConfigurationPolicy.REQUIRE)
@Designate(ocd = GetPageMarkdownAITool.Config.class)
public class GetPageMarkdownAITool implements AITool {
    private static final Logger LOG = LoggerFactory.getLogger(GetPageMarkdownAITool.class);
    private Config config;
    private Gson gson = new Gson();

    @Reference
    private ApproximateMarkdownService markdownService;

    @Override
    public @Nonnull String getName(@Nullable Locale locale) {
        return "Get Text of Page";
    }

    @Override
    public @Nonnull String getDescription(@Nullable Locale locale) {
        return "Returns a markdown representation of the text of a given page";
    }

    @Override
    public @Nonnull String getToolName() {
        return "get_pagetext";
    }

    @Override
    public @Nonnull String getToolDeclaration() {
        return "{\n" +
                "  \"type\": \"function\",\n" +
                "  \"function\": {\n" +
                "    \"name\": \"get_pagetext\",\n" +
                "    \"description\": \"Get the text of a page with a given path.\",\n" +
                "    \"parameters\": {\n" +
                "      \"type\": \"object\",\n" +
                "      \"properties\": {\n" +
                "        \"path\": {\n" +
                "          \"type\": \"string\",\n" +
                "          \"description\": \"The path of the page to get the text of.\"\n" +
                "        }\n" +
                "      },\n" +
                "      \"required\": [\"path\"],\n" +
                "      \"additionalProperties\": false\n" +
                "    }\n" +
                "  },\n" +
                "  \"strict\": true\n" +
                "}";
    }

    @Override
    public boolean isAllowedFor(@Nonnull Resource resource) {
        return config != null && config.allowedPathsRegex() != null &&
                config.allowedPathsRegex().matches(resource.getPath());
    }

    /**
     * Does a query with lucene and then rates the results with the embedding.
     */
    @Override
    public @Nonnull String execute(@Nullable String arguments, @Nonnull Resource resource,
                                   @Nullable GPTCompletionCallback.GPTToolExecutionContext context) {
        try {
            SlingHttpServletRequest request = ((SlingGPTExecutionContext) context).getRequest();
            SlingHttpServletResponse response = ((SlingGPTExecutionContext) context).getResponse();
            Map parsedArguments = gson.fromJson(arguments, Map.class);
            String path = (String) parsedArguments.get("path");
            if (path == null || path.isEmpty()) {
                return "Missing path parameter";
            }
            if (!config.allowedPathsRegex().matches(path)) {
                return "Path not allowed";
            }
            ResourceResolver resolver = resource.getResourceResolver();
            Resource pathResource = resolver.getResource(path);
            String markdown = markdownService.approximateMarkdown(pathResource, request, response);
            LOG.debug("Markdown of {} : {}", path, StringUtils.abbreviate(markdown, 80));
            return markdown;
        } catch (Exception e) {
            LOG.error("Error in search page AI tool", e);
            return "Error in search page AI tool: " + e;
        }
    }

    // activate and deactivate methods
    @Activate
    @Modified
    protected void activate(Config config) {
        this.config = config;
    }

    @Deactivate
    protected void deactivate() {
        this.config = null;
    }

    @ObjectClassDefinition(name = "Composum AI Tool Get Page Markdown",
            description = "Provides the AI with a tool to search for page paths. Needs a lucene index for all pages." +
                    "If there is no configuration, the tool is not active.")
    public @interface Config {

        @AttributeDefinition(name = "Allowed paths regex",
                description = "A regex to match the paths that this tool is allowed to be used on. Default: /content/.*")
        String allowedPathsRegex() default "/content/.*";

    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy