java-core
java-core defines the protocol’s data model and the Agent abstraction. It has no third-party runtime dependencies. Streaming uses the JDK’s java.util.concurrent.Flow, and JSON is left to a pluggable Serializer.
Packages
The Agent interface
Agent: your own implementation, the remote HttpAgent client, and the Spring AI SpringAiAgent. This means a client and a server agent are interchangeable. For example, you can put a remote agent behind your own server, or test a front-end integration against a local stub.
The contract is:
- Each subscription to the returned publisher is one run.
- The publisher emits the run’s events in order, then calls
onComplete. - If the run fails, the publisher calls
onError. The server modules convert this into a terminalRUN_ERRORevent for the client.
Flow.Publisher is the JDK’s copy of the Reactive Streams interface, you can convert it at the edges of your application:
Implementing an agent
Without a reactive library,SubmissionPublisher is the easiest way to produce events. Create one per subscription so that each subscriber gets its own run:
Flux and convert it once:
RUN_STARTED and ends with RUN_FINISHED or RUN_ERROR. See Events for the full set of events.
Serialization
The core does not depend on a JSON library. Client and server components take aSerializer:
- Handle the sealed hierarchies polymorphically:
Eventuses thetypediscriminator ("RUN_STARTED", …),Messageusesrole("user", …), andRunOutcomeusestype("success","interrupt"). - Write enums (
Role,EventType,OutcomeType,ResumeStatus) by their wire value. - Be thread-safe.
- Wrap library-specific failures in
SerializationException. The server modules map this exception to400 Bad Request.
JacksonSerializer
You usually don’t need to write a serializer yourself.JacksonSerializer in ag-ui-spring-server-core does all of the above with Jackson. Despite the artifact name, it does not depend on Spring:
null fields, so a RunAgentInput serializes as:
ObjectMapper the same way, call JacksonSerializer.configure(mapper).