Releases: tabforgeai/tabforge-ai
v2.2.0 — Ambient Activity Memory
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 viewedTwo 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
"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)
- EasyAI.chat() — conversations with optional memory
- EasyAI.assistant(X.class) — method-calling assistants (no annotations)
- @EasyRAG — document question-answering (PDF/DOCX/TXT)
- EasyAI.agent() — multi-step autonomous tasks with step limits
- EasyAI.indexer() — persistent Milvus RAG
- 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
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
- EasyAI.chat() — simple conversations (optional memory)
- EasyAI.assistant(X.class) — assistants that call your Java methods (zero annotations)
- @EasyRAG — answer questions from your documents (PDF/DOCX/TXT)
- EasyAI.agent() — autonomous multi-step tasks, with step cap + live trace
- EasyAI.indexer() — new: persistent RAG in Milvus
- 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
- EasyAI Developer Guide (https://github.com/tabforgeai/tabforge-ai/blob/main/docs/easyai_guide.txt) — all 6 use
cases, with examples - DynTabs Developer Guide (https://github.com/tabforgeai/tabforge-ai/blob/main/docs/dyntabs_guide.txt)
- JavaDoc (https://tabforgeai.github.io/tabforge-ai/apidocs/index.html)
- Starter project (https://github.com/tabforgeai/tabforge-ai-starter-) · Demo app
(https://github.com/tabforgeai/tabforge-ai-demo)
v1.1.0 — EasyAgent
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
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:
- EasyAI.chat()
- EasyAI.assistant()
- 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
