Package dyntabs.ai

Class AssistantBuilder<T>

java.lang.Object
dyntabs.ai.AssistantBuilder<T>
Type Parameters:
T - the assistant interface type

public class AssistantBuilder<T> extends Object
Builder for creating AI assistant proxies from annotated interfaces.

This builder creates a runtime proxy that forwards method calls to an AI model. You can optionally add tool objects (your existing Java services) that the AI can call, and document sources (RAG) that the AI can reference.

Use Case 1: Simple AI Assistant (No Tools)


 @EasyAIAssistant(systemMessage = "You are a helpful translator")
 public interface Translator {
     String translate(String text);
 }

 Translator t = EasyAI.assistant(Translator.class).build();
 String result = t.translate("Hello, how are you?");
 // result: "Bonjour, comment allez-vous?" (depending on system message)
 

Use Case 2: AI Assistant with Tools (AI Calls Your Java Code)

This is the most powerful feature. Pass your existing service objects to withTools(Object...), and the AI will call their methods when needed. No annotations are required on your service classes.


 // Your existing service - plain Java, no AI annotations
 public class WeatherService {
     public String getWeather(String city) {
         return weatherApi.fetch(city).toString();
     }
 }

 @EasyAIAssistant(systemMessage = "You are a weather assistant")
 public interface WeatherBot {
     String ask(String question);
 }

 // Wire it together
 WeatherBot bot = EasyAI.assistant(WeatherBot.class)
     .withTools(new WeatherService())
     .build();

 // AI automatically calls WeatherService.getWeather("London") behind the scenes
 String answer = bot.ask("What's the weather like in London?");
 

Use Case 3: Multiple Tool Services


 SupportBot bot = EasyAI.assistant(SupportBot.class)
     .withTools(orderService, userService, inventoryService)
     .build();

 // AI can call methods from ANY of the three services
 bot.ask("What is the status of order #123 for user john@example.com?");
 

Use Case 4: Jakarta EJB Beans as Tools

EJB beans (@Stateless, @Stateful, @Singleton) injected via @Inject work as tools out of the box. EasyAI automatically detects the EJB proxy and discovers business methods from the actual bean class. Method calls go through the container proxy, so transactions, security, and interceptors work normally.


 @Stateless
 public class OrderService {
     @PersistenceContext private EntityManager em;

     public String findOrder(String orderId) {
         return em.find(Order.class, orderId).toString();
     }
 }

 // In your CDI bean:
 @Inject OrderService orderService;   // EJB proxy from the container

 SupportBot bot = EasyAI.assistant(SupportBot.class)
     .withTools(orderService)          // just pass the injected proxy
     .build();

 bot.ask("Where is order #123?");
 // AI calls orderService.findOrder("123") through the EJB proxy
 

Use Case 5: Override Settings Per-Assistant


 Translator t = EasyAI.assistant(Translator.class)
     .withModel("gpt-4o")           // use a specific model
     .withMemory(50)                 // remember 50 messages
     .withSystemMessage("Translate everything to Serbian")
     .build();
 
See Also:
  • Method Details

    • withTools

      public AssistantBuilder<T> withTools(Object... tools)
      Adds tool objects whose public methods the AI can call.

      Accepts plain POJOs and Jakarta EJB proxies (@Stateless, @Stateful, @Singleton) obtained via @Inject. EJB proxies are detected automatically — business methods are discovered from the actual bean class, while invocations go through the proxy so that container services (transactions, security, interceptors) work normally.

      Parameters:
      tools - one or more service objects (POJOs or injected EJB proxies)
      Returns:
      this builder
    • withMemory

      public AssistantBuilder<T> withMemory(int maxMessages)
    • withSystemMessage

      public AssistantBuilder<T> withSystemMessage(String systemMessage)
    • withModel

      public AssistantBuilder<T> withModel(String modelName)
    • withApiKey

      public AssistantBuilder<T> withApiKey(String apiKey)
    • withRAG

      public AssistantBuilder<T> withRAG(String... sources)
      Enables RAG (document-powered AI) with the given document sources.

      This is the programmatic alternative to the @EasyRAG annotation. Use this when your document paths are not known at compile time, for example when they come from a database, user upload, or application configuration.

      In a web application, use file: prefix to point to documents on the server's file system, or pass any absolute path:

      
       // Documents on the server file system
       PolicyBot bot = EasyAI.assistant(PolicyBot.class)
           .withRAG("file:C:/app-data/docs/policy.pdf")
           .build();
      
       // Path from application config or database
       String docPath = appConfig.getDocumentPath();
       PolicyBot bot = EasyAI.assistant(PolicyBot.class)
           .withRAG(docPath)
           .build();
      
       // Multiple documents
       PolicyBot bot = EasyAI.assistant(PolicyBot.class)
           .withRAG("file:/data/policy.pdf", "file:/data/faq.pdf")
           .build();
       

      Supports the same path prefixes as @EasyRAG: classpath:, file:, or plain relative paths.

      Parameters:
      sources - one or more document paths
      Returns:
      this builder
      See Also:
    • withRAG

      public AssistantBuilder<T> withRAG(String[] sources, int maxResults, double minScore)
      Enables RAG with full control over retrieval parameters.
      
       PolicyBot bot = EasyAI.assistant(PolicyBot.class)
           .withRAG(
               new String[]{"file:/data/policy.pdf", "file:/data/terms.pdf"},
               5,     // return top 5 relevant segments
               0.7    // only segments with 70%+ relevance
           )
           .build();
       
      Parameters:
      sources - one or more document paths
      maxResults - maximum number of relevant segments to retrieve (default 3)
      minScore - minimum relevance score, 0.0 to 1.0 (default 0.5)
      Returns:
      this builder
    • withRAG

      public AssistantBuilder<T> withRAG(DocumentSource... sources)
      Enables RAG from in-memory document sources (byte arrays).

      Use this when your documents come from a DMS, database, REST API, or any source that provides content as byte[].

      
       // From a DMS
       byte[] pdfBytes = dmsClient.downloadDocument("DOC-12345");
       PolicyBot bot = EasyAI.assistant(PolicyBot.class)
           .withRAG(DocumentSource.of("policy.pdf", pdfBytes))
           .build();
      
       // From a database BLOB
       byte[] content = resultSet.getBytes("document_content");
       PolicyBot bot = EasyAI.assistant(PolicyBot.class)
           .withRAG(DocumentSource.of("terms.pdf", content))
           .build();
      
       // Plain text (no file needed)
       PolicyBot bot = EasyAI.assistant(PolicyBot.class)
           .withRAG(DocumentSource.ofText("policy", "All employees get 25 vacation days..."))
           .build();
      
       // Multiple documents from different sources
       PolicyBot bot = EasyAI.assistant(PolicyBot.class)
           .withRAG(
               DocumentSource.of("policy.pdf", dmsClient.download("policy")),
               DocumentSource.of("faq.txt", restApi.getFaqBytes()),
               DocumentSource.ofText("extra-rules", additionalRulesText)
           )
           .build();
       
      Parameters:
      sources - one or more document sources with content as byte arrays
      Returns:
      this builder
      See Also:
    • withRAG

      public AssistantBuilder<T> withRAG(List<DocumentSource> sources, int maxResults, double minScore)
      Enables RAG from in-memory document sources with tuning parameters.
      Parameters:
      sources - document sources with content as byte arrays
      maxResults - maximum number of relevant segments to retrieve
      minScore - minimum relevance score, 0.0 to 1.0
      Returns:
      this builder
    • withMilvus

      public AssistantBuilder<T> withMilvus(String host, int port, String collectionName)
      Connects this assistant to a persistent Milvus collection for retrieval, using explicit connection settings.

      This is the read-side counterpart to EasyAI.indexer(). Unlike withRAG(String...) — which loads and embeds documents fresh on every build() into an in-memory store — withMilvus points at a collection that was populated earlier (and stays populated). Think "query the existing database" versus "load a file into memory for this one request."

      When set, Milvus takes precedence over withRAG(...) and @EasyRAG. Retrieval tuning uses maxResults/minScore defaults (3 / 0.5) unless you call withMilvus(MilvusConfig, int, double).

      
       PolicyBot bot = EasyAI.assistant(PolicyBot.class)
           .withMilvus("localhost", 19530, "documents")
           .build();
       
      Parameters:
      host - Milvus server hostname
      port - Milvus server port (typically 19530)
      collectionName - the collection to retrieve from
      Returns:
      this builder
    • withMilvus

      public AssistantBuilder<T> withMilvus(MilvusConfig config)
      Connects this assistant to a persistent Milvus collection using a fully built MilvusConfig (for non-default dimension or credentials).
      Parameters:
      config - the Milvus connection settings
      Returns:
      this builder
    • withMilvus

      public AssistantBuilder<T> withMilvus(MilvusConfig config, int maxResults, double minScore)
      Connects this assistant to a persistent Milvus collection with explicit retrieval tuning.
      Parameters:
      config - the Milvus connection settings
      maxResults - maximum relevant segments to retrieve per query
      minScore - minimum relevance score, 0.0 to 1.0
      Returns:
      this builder
    • withMilvus

      public AssistantBuilder<T> withMilvus()
      Connects this assistant to a persistent Milvus collection configured entirely from easyai.properties (keys easyai.milvus.*).
      Returns:
      this builder
      See Also:
    • withChatModel

      public AssistantBuilder<T> withChatModel(dev.langchain4j.model.chat.ChatModel model)
    • withActivityContext

      public AssistantBuilder<T> withActivityContext(ActivityContext activityContext)
      Makes this assistant ambient-activity aware: before each call to any of the assistant's methods, the given context is re-rendered and folded into the system message, so the model already knows what the user has recently been doing in the UI (and can resolve "this"/"that" without being told).

      Familiar analogy: giving your assistant a glance at your desk before every question — it sees the order you just opened, so "email the customer about it" needs no further explanation.

      
       SupportBot bot = EasyAI.assistant(SupportBot.class)
           .withTools(orderService)
           .withActivityContext(ActivityContext.of(activityStore)
               .forSession(sessionId).forTab(tabId).build())
           .build();
      
       bot.ask("Cancel this order");   // "this" resolved from recent activity
       

      The context is combined with any withSystemMessage(String) value by SystemMessageComposer; passing null simply leaves the feature off.

      Parameters:
      activityContext - the ambient-activity descriptor to inject, or null to disable
      Returns:
      this builder
      See Also:
    • build

      public T build()