Skip to content

Releases: tabforgeai/tabforge-ai

v2.2.0 — Ambient Activity Memory

24 Jun 14:21

Choose a tag to compare

Your assistant now knows what the user is doing — without being told.

Open an order on screen and type "cancel this", and it cancels the right order.
Switch tabs and ask "what am I looking at?", and it answers. No ids, no copy-paste,
no re-explaining context in the prompt.

To our knowledge, no other web app framework ships this out of the box.
It's possible because TabForge lives inside your Jakarta EE app: with TabScoped
it already knows which tab, which entity, and which action the user touched — so it
feeds the AI semantic, typed, in-app activity, never OCR or screen-scraping.

This is the mirror image of the 2.1 live event stream: where 2.1 streamed AI → UI,
2.2 streams UI → AI.


What's new: Ambient Activity Memory

A short, in-memory timeline of the user's recent actions is rendered into the assistant's
system message on every call — so the model resolves "this"/"that" on its own.

1. Capture (opt-in):

// navigation — records tab open/select automatically:
@DynTab(name = "OrdersDynTab", uniqueIdentifier = "Orders", trackActivity = true)
public class OrdersBean extends BaseDyntabCdiBean { ... }

// business actions — records "viewed order ORD-002":
@ActivityTracked(type = Type.BUSINESS_ACTION, verb = "view",
                 entityType = "order", entityIdParams = "orderId")
public String viewOrder(String orderId) { ... }

2. Inject (one builder call):
ActivityContext ctx = ActivityContext.of(activityStore)
    .forSession(sessionId).forTab(tabId).build();   // omit forTab() for app-wide scope

OrdersAssistant bot = EasyAI.assistant(OrdersAssistant.class)
    .withTools(orderService)        // so it can ACT on "this"
    .withActivityContext(ctx)       // so it KNOWS what "this" is
    .build();

bot.ask("cancel this");             // resolves to the order just viewed

Two ways to scope it:

  • Per-tab deixis (forSession + forTab) → "cancel this order"
  • Session-wide orientation (forSession only) → "what tab am I on?"

In-memory, per-session, and ephemeral by default (ActivityStore SPI,
InMemoryActivityStore default). Privacy-first: only labels and ids are stored, and the
timeline evaporates with the session. Nothing is captured unless you opt in.


Also in this release

  • LangChain4J 1.15.1 → 1.16.3
  • PrimeFaces 13.0.10 → 15.0.16 (jakarta classifier)
  • Docs: new USE CASE 11 in the EasyAI Developer Guide + fully refreshed llms.txt
  • Fix: menu-opened tabs now correctly record navigation activity
    (DynTabManager.addTab carries over the trackActivity flag)

Compatibility

Additive minor release — no breaking changes. Everything new is opt-in
(@ActivityTracked, @DynTab(trackActivity=true), .withActivityContext(...),
package dyntabs.ai.activity).

  <dependency>
      <groupId>io.github.tabforgeai</groupId>
      <artifactId>tabforge-ai</artifactId>
      <version>2.2.0</version>
  </dependency>

See it live

Demo app (Orders deixis tab + global "where am I" panel):
https://github.com/tabforgeai/tabforge-ai-demo

TabForge AI 2.1.0

08 Jun 12:44

Choose a tag to compare

"Watch your AI work — stream every chat, agent step, retrieval, and extraction live into your UI, with three lines of
code."

2.1.0 adds a transport-agnostic live event stream to EasyAI. Every capability can now narrate what it's doing in real
time — which tool an agent is calling, what came back, retries, progress, the final answer — without coupling the
library to any UI or transport. The reference starter wires this straight into a live Activity panel over SSE.

This is a minor, fully additive release. No breaking changes — existing 2.0 code compiles and runs unchanged.

See it live (https://youtu.be/qNDD9mfFEJk) — an autonomous agent's tool calls streaming into the Activity panel in
real time.

Headline Feature: Live Observability

withEventListener(...) — one hook, every capability

A new event API lets you observe AI operations as they happen:

  • EasyAIEvent — an immutable event carrying Source (CHAT, ASSISTANT, RAG, AGENT, INDEXER, EXTRACT), Phase (STARTED,
    STEP_STARTED, STEP, PROGRESS, RESULT, RETRY, FINISHED, ERROR), Status, a monotonic sequence number, timestamp, and
    step/tool details.
  • EasyAIListener — a @FunctionalInterface; receive events with a single onEvent(...).
  • .withEventListener(...) — added to the chat, agent, indexer, and extract builders.

The core stays transport-agnostic — it never imports SSE, WebSocket, or any UI schema. You decide where events go: a
log, a metric, a WebSocket, or an SSE stream to the browser.

  var conversation = EasyAI.chat()
          .withChatModel(model)
          .withEventListener(e ->
              log.info("[{}] {} #{}", e.getSource(), e.getPhase(), e.getSequence()))
          .build();

Live Activity panel in the starter

The tabforge-ai-starter ships the full "batteries-included" path: an SSE transport (AiEventChannel +
AiStreamResource), an EasyAiActivityBridge that maps EasyAIEvent phases onto the pf-modern-template Activity panel,
and the frontend already wired. Clone it and an autonomous agent's tool calls stream into the UI as it runs — generate
→ call tool → result → finish, live.

Lighting it up in your own code is a three-line recipe:

  @Inject AiEventChannel channel;

  var bridge = new EasyAiActivityBridge(channel, sessionId);
  agent.withEventListener(bridge);

Six Core Capabilities (unchanged)

  1. EasyAI.chat() — conversations with optional memory
  2. EasyAI.assistant(X.class) — method-calling assistants (no annotations)
  3. @EasyRAG — document question-answering (PDF/DOCX/TXT)
  4. EasyAI.agent() — multi-step autonomous tasks with step limits
  5. EasyAI.indexer() — persistent Milvus RAG
  6. EasyAI.extract(X.class) — document-to-object conversion

All six now emit live events via .withEventListener(...).

Dependencies

Requires Java 21+ and Jakarta EE 11+; PrimeFaces 13+ for DynTabs. Built on LangChain4J 1.15.1.

  <dependency>
      <groupId>io.github.tabforgeai</groupId>
      <artifactId>tabforge-ai</artifactId>
      <version>2.1.0</version>
  </dependency>

TabForge AI 2.0.0

03 Jun 13:12

Choose a tag to compare

Add AI to a Jakarta EE app — assistants, document Q&A, autonomous agents, a persistent vector store, and structured
data extraction — without the boilerplate.

EasyAI is a thin, Jakarta-native layer over LangChain4J: no Spring, no @tool schemas, no manual wiring. Pass it a
plain POJO or an @Inject-ed EJB and it just works. (DynTabs, the other module, gives PrimeFaces a real one-instance-per-open-tab CDI scope.)

This is a major release: EasyAI now runs on LangChain4J 1.15.1, and adds two headline capabilities — persistent Milvus
RAG and structured extraction.

What's new in 2.0

Persistent vector store (Milvus) — index once, query forever

RAG that survives restarts and is shared across sessions and server nodes. Embed your documents once, then any
assistant queries them instantly — no re-embedding per request.

  // Write side — once (a batch job, or on each upload). Accepts paths or byte[].
  EasyAI.indexer().toMilvus("localhost", 19530, "company_kb")
        .index("file:/data/handbook.pdf");

  // Read side — any assistant, any time
  KbBot bot = EasyAI.assistant(KbBot.class)
        .withMilvus("localhost", 19530, "company_kb")
        .build();

Structured extraction — text or PDF → typed Java object

Turn an email, a chat message, or a PDF into a populated record/POJO in one call. Parsing, retry on malformed output,
and optional Jakarta Bean Validation are built in. After it returns, there's no AI left — just data your code already
understands.

  record Invoice(String vendor, String invoiceNumber, LocalDate date,
                 BigDecimal total, List<LineItem> items) {}

  Invoice inv = EasyAI.extract(Invoice.class)
          .from(DocumentSource.of("invoice.pdf", pdfBytes));

  em.persist(inv);   // it's just a typed object from here on

LangChain4J 1.15.1

Upgraded from 1.0.0-beta2 to the stable 1.15.1 line — newer providers, fixes, and a stable API surface underneath
EasyAI.

Breaking changes

LangChain4J removed its ChatLanguageModel type, so two names changed. Migration is a simple rename — nothing else:

┌──────────────────────────────┬──────────────────────┐
│ Before │ After │
├──────────────────────────────┼──────────────────────┤
│ type ChatLanguageModel │ ChatModel │
├──────────────────────────────┼──────────────────────┤
│ withChatLanguageModel(model) │ withChatModel(model) │
└──────────────────────────────┴──────────────────────┘

EasyAI in one minute — the 6 things

  1. EasyAI.chat() — simple conversations (optional memory)
  2. EasyAI.assistant(X.class) — assistants that call your Java methods (zero annotations)
  3. @EasyRAG — answer questions from your documents (PDF/DOCX/TXT)
  4. EasyAI.agent() — autonomous multi-step tasks, with step cap + live trace
  5. EasyAI.indexer() — new: persistent RAG in Milvus
  6. EasyAI.extract(X.class) — new: text/document → typed object

Get it

  <dependency>
      <groupId>io.github.tabforgeai</groupId>
      <artifactId>tabforge-ai</artifactId>
      <version>2.0.0</version>
  </dependency>

Requires Java 21+ and Jakarta EE 11+. PrimeFaces 13+ for the DynTabs module.

Docs

v1.1.0 — EasyAgent

17 Mar 15:08

Choose a tag to compare

What's new in 1.1.0

EasyAgent — Autonomous Multi-Step Task Execution

EasyAI now includes a built-in agent that autonomously plans and executes multi-step tasks across your Java services —
no manual orchestration required.

EasyAgent agent = EasyAI.agent()
    .withServices(orderService, paymentService, shippingService)
    .withMaxSteps(10)
    .withPlanningPrompt(true)
    .withStepListener(step -> log.info("Step {}: {}", step.stepNumber(), step.toolName()))
    .build();

String result = agent.execute("Process order #42: verify stock, charge the card, schedule delivery");

Key features:

  • withMaxSteps(n) — hard safety cap on tool calls; returns a final answer when reached
  • withStepListener() — typed callback fired after every tool call with tool name, arguments, and result
  • withPlanningPrompt(true) — instructs the model to plan before acting, reducing hallucinations
  • Zero annotations — pass any POJO or EJB directly, same as the rest of EasyAI

Also in this release

  • InvocationTargetException unwrapping fix — tool errors now report the real cause instead of null
  • Structured error feedback to the LLM: TOOL_ERROR | tool=X | error=Y | suggestion=...

Maven Central

<dependency>
    <groupId>io.github.tabforgeai</groupId>
    <artifactId>tabforge-ai</artifactId>
    <version>1.1.0</version>
</dependency>

TabForge AI 1.0.0 — first public release

08 Mar 16:04

Choose a tag to compare

TabForge AI

Two powerful modules for Jakarta EE developers — ship in minutes, not days.

DynTabs enables development of PrimeFaces Single Page Applications with dynamic tabs, bringing true user multi-tasking
to enterprise web apps: multiple tabs open simultaneously, parallel editing and data entry across different records,
all within a single page — delivering high ergonomics for end users. At the core is a proper TabScoped CDI scope: one
isolated bean instance per open tab, automatic lifecycle, inter-tab messaging, workflow patterns, and declarative
security. No more ViewScoped hacks, no more ID collisions.

EasyAI frees developers from learning and working with the LangChain4J low-level API entirely. Three simple calls:

  1. EasyAI.chat()
  2. EasyAI.assistant()
  3. EasyRAG

cover 90% of real-world AI use cases. Jakarta EE Stateless, Stateful, and Singleton beans plug in as AI tool objects with zero additional annotations — no Tool, no schema, no configuration.

EasyAI assistants are fully injectable as standard CDI beans via Inject, integrating naturally into any Jakarta EE application without breaking container services, transactions, or security.

Available on Maven Central: io.github.tabforgeai:tabforge-ai:1.0.0
https://central.sonatype.com/artifact/io.github.tabforgeai/tabforge-ai/1.0.0