Package dyntabs.ai

Class EasyAI

java.lang.Object
dyntabs.ai.EasyAI

public final class EasyAI extends Object
Main entry point for the EasyAI library.

EasyAI is a simple abstraction layer over LangChain4J. It hides all the low-level details (ChatLanguageModel, ChatMemory, AiServices, ToolSpecification, EmbeddingStore...) behind a clean builder-pattern API that any Java developer can use in minutes.

Quick Start

Step 1: Add your API key to easyai.properties on the classpath:

 easyai.provider=openai
 easyai.api-key=sk-YOUR-KEY
 easyai.model-name=gpt-4o-mini
 

Step 2: Start chatting!

Three Ways to Use EasyAI

1. Simple Chat (chat())

Send messages to AI and get responses. Optionally remembers conversation history.


 Conversation chat = EasyAI.chat()
     .withMemory(20)                              // remember last 20 messages
     .withSystemMessage("You are a helpful tutor") // set AI personality
     .build();

 String answer = chat.send("What is Java?");
 String follow = chat.send("Give me an example"); // AI remembers the context
 

2. AI Assistant with Tools (assistant(Class))

Define an interface, give it your existing service classes as "tools", and the AI will call your Java methods when needed.


 // 1. Define assistant interface
 @EasyAIAssistant(systemMessage = "You are an e-commerce support bot")
 public interface SupportBot {
     String ask(String question);
 }

 // 2. Your existing service (no AI annotations needed!)
 public class OrderService {
     public String findOrder(String orderId) {
         return database.findById(orderId).toString();
     }
 }

 // 3. Wire it together
 SupportBot bot = EasyAI.assistant(SupportBot.class)
     .withTools(orderService, userService)
     .build();

 // 4. The AI will automatically call orderService.findOrder("12345")
 String answer = bot.ask("Where is my order #12345?");
 

3. Document-Powered Assistant (RAG)

Let the AI answer questions based on your PDF, DOCX, or TXT files.


 @EasyRAG(source = "classpath:company-policy.pdf")
 @EasyAIAssistant(systemMessage = "Answer based on the company policy")
 public interface PolicyBot {
     String ask(String question);
 }

 PolicyBot bot = EasyAI.assistant(PolicyBot.class).build();
 String answer = bot.ask("What is the vacation policy?");
 // AI reads the PDF and answers based on its content
 

Overriding Configuration Per-Call

You can override any config property when building:


 Conversation chat = EasyAI.chat()
     .withProvider("ollama")                       // use local Ollama
     .withModel("llama3")                          // specific model
     .withBaseUrl("http://localhost:11434/v1/")     // custom endpoint
     .withTemperature(0.3)                          // less creative
     .withMaxTokens(500)                            // shorter answers
     .build();
 

CDI / Jakarta EE Integration

In a Jakarta EE application, assistants are automatically injectable:


 @Inject SupportBot bot;  // no manual build() needed
 
See Also:
  • Method Details

    • chat

      public static ConversationBuilder chat()
      Starts building a new Conversation for simple chat.
      
       Conversation chat = EasyAI.chat()
           .withMemory(20)
           .withSystemMessage("You are a helpful assistant")
           .build();
      
       String answer = chat.send("Hello!");
       
      Returns:
      a new ConversationBuilder
    • assistant

      public static <T> AssistantBuilder<T> assistant(Class<T> assistantInterface)
      Starts building an AI Assistant proxy for the given interface.

      The interface should have one or more methods that accept a String and return a String. Annotate it with @EasyAIAssistant for a system message.

      
       @EasyAIAssistant(systemMessage = "You are a code reviewer")
       public interface CodeReviewer {
           String review(String code);
       }
      
       CodeReviewer reviewer = EasyAI.assistant(CodeReviewer.class).build();
       String feedback = reviewer.review("public void foo() { ... }");
       
      Type Parameters:
      T - the assistant interface type
      Parameters:
      assistantInterface - the interface class to create a proxy for
      Returns:
      a new AssistantBuilder
    • agent

      public static AgentBuilder agent()
      Starts building an EasyAgent that autonomously plans and executes multi-step tasks by orchestrating calls to your registered Java services.

      Unlike assistant(Class), which answers a single question, an agent receives a complex task and breaks it into steps — calling your service methods in the right order, using the result of each step to decide what to do next, and adapting if a step fails.

      
       EasyAgent agent = EasyAI.agent()
           .withServices(inventoryService, paymentService, orderService)
           .withMaxSteps(10)
           .withPlanningPrompt(true)
           .withStepListener(step ->
               log.info("[AGENT] Step {}: {}({}) -> {}",
                   step.stepNumber(), step.toolName(),
                   step.arguments(), step.result()))
           .build();
      
       String result = agent.execute(
           "Order 2 laptops for user U123, apply loyalty credit, " +
           "use fallback warehouse WH-EU if out of stock."
       );
       
      Returns:
      a new AgentBuilder
    • configure

      public static void configure(EasyAIConfig config)
      Sets a global configuration that will be used as default for all new conversations and assistants (unless overridden per-builder).
      
       EasyAI.configure(EasyAIConfig.builder()
           .provider("openai")
           .apiKey("sk-...")
           .modelName("gpt-4o")
           .build());
       
      Parameters:
      config - the global configuration
    • getGlobalConfig

      public static EasyAIConfig getGlobalConfig()
      Returns the global configuration, or loads from easyai.properties if not set.
      Returns:
      the current global EasyAIConfig
    • extractErrorMessage

      public static String extractErrorMessage(Throwable t)
      Extracts a clean, human-readable error message from an AI exception.

      LangChain4J exceptions (especially from OpenAI-compatible providers like Groq, Azure OpenAI, and OpenAI itself) often carry raw JSON in their message, for example:

       {"error":{"message":"Failed to call a function...","type":"invalid_request_error",...}}
       

      This method parses the JSON and extracts only the error.message field. If the message is not JSON, it is returned as-is. If the exception is null, an empty string is returned.

      Typical usage in a JSF backing bean or REST endpoint:

      
       try {
           return bot.ask(userQuestion);
       } catch (Exception e) {
           log.error("AI call failed", e);
           return "Sorry, something went wrong: " + EasyAI.extractErrorMessage(e);
       }
       

      Common LangChain4J exception types to catch separately if needed:

      • dev.langchain4j.exception.AuthenticationException — wrong API key
      • dev.langchain4j.exception.RateLimitException — rate limit exceeded
      • dev.langchain4j.exception.InvalidRequestException — bad request, tool call failure
      • dev.langchain4j.exception.InternalServerException — provider server error
      Parameters:
      t - the exception thrown by an AI assistant or conversation call
      Returns:
      a clean, readable error message