Skip to main content

Write custom service helpers

A service helper is a Spring bean annotated with @HelperService. It is made available inside Thymeleaf prompt templates as a named expression object. Prompt templates call helper methods to inject dynamic data such as document content, user profile information, or data from an external system.

How helpers work in templates​

When a prompt template is rendered, the ThymeleafTemplateEngineHelper receives a context that includes all registered @HelperService beans. A template accesses a helper by the name declared in the annotation:

[[${documentService.extractTextualContent(documentId)}]]

Here documentService is the bean name from @HelperService(name = "documentService").

Example: ARender DocumentService​

The DocumentService helper in the ARender plugin extracts text from documents via the ARender DSB:

@Service
@Primary
@HelperService(name = "documentService")
public class DocumentService implements OcrDocumentParser {

private final RenditionService renditionService;

public DocumentService(RenditionService renditionService) {
this.renditionService = renditionService;
}

public String extractTextualContent(String documentId) {
// Calls RenditionService to fetch OCR text from the ARender DSB
// ...
}
}

The name in @HelperService(name = "documentService") is the identifier used in templates.

Writing a custom helper​

1. Create the helper class​

import com.uxopian.ai.model.annotation.helper.HelperService;
import org.springframework.stereotype.Service;

@Service
@HelperService(name = "myHelper")
public class MyHelperService {

public String getUserProfile(String userId) {
// Call your internal API or database
return "Profile for user: " + userId;
}

public List<String> getRecentDocuments(String folderId) {
// Return recent document IDs from your system
return List.of("doc-1", "doc-2");
}
}

The name value is the identifier you use in prompt templates. Choose a short, descriptive name. Do not use a name already used by a built-in helper (documentService, fdService).

2. Inject dependencies​

Your helper can autowire other Spring beans that are also in the plugin JAR. Internal @Service and @Component beans in the same JAR are registered before @HelperService beans, so they are available for injection.

@Service
@HelperService(name = "myHelper")
public class MyHelperService {

private final MyApiClient apiClient;

public MyHelperService(MyApiClient apiClient) {
this.apiClient = apiClient;
}
}

3. Use the helper in a prompt template​

- id: userContextPrompt
role: USER
content: |
The user profile is as follows:
[[${myHelper.getUserProfile(userId)}]]

Their recent documents are:
[# th:each="docId : ${myHelper.getRecentDocuments(folderId)}"]
- [[${docId}]]
[/]

The variables userId and folderId must be passed in the request payload.

4. Package and deploy​

Package the helper as a shaded JAR (same process as custom tools) and place it in the plugins/ directory. Restart uxopian-ai.

mvn clean package
cp target/my-helper-1.0.0.jar ./plugins/
docker compose restart uxopian-ai

Constraints​

  • @HelperService bean names must be unique. If a name conflicts with an existing bean, IntegrationLoader logs a warning and skips the duplicate.
  • The interface rule: IntegrationLoader registers the bean under its first interface type if the class implements one. This allows different implementations to share an interface.
  • Helpers must not depend on another @HelperService. If you need shared logic between helpers, place it in a regular @Service class in the same JAR.
  • Helper methods are called synchronously during prompt rendering. Long-running methods will delay the LLM request.