Skip to content

Agent extension points: LlmClient/ConversationMemory SPI, connector-backed agent tools, event triggers - #5408

Merged
qqeasonchen merged 1 commit into
apache:developfrom
qqeasonchen:feat/agent-connector-tools
Sep 21, 2026
Merged

qqeasonchen merged 1 commit into
apache:developfrom
qqeasonchen:feat/agent-connector-tools

Conversation

@qqeasonchen

Copy link
Copy Markdown
Contributor

Fixes #5407

Summary of changes

Agent extension points (connector-first):

  • LlmClient SPI (agent.llm): token stream() + function-calling chat() with ToolSpec/ToolCall/Completion records; OpenAiLlmClient implements it and gains non-streaming chat() (OpenAI tools wire format, mock-tested)
  • ConversationMemory SPI: ConversationStore (in-memory sliding window) now implements the interface; persistent backends can be constructor-injected
  • AgentTool SPI + ToolRegistry: name+schema+invoke tools; StreamingAgent constructor takes an optional registry and, when non-empty, runs a bounded function-calling loop (≤5 iterations, tool results fed back as messages). No tools registered → behavior unchanged token-by-token streaming
  • ConnectorToolAdapter: any SinkConnector → write tool (args object wrapped as one CloudEvent → put()+commit()); any SourceConnector → read tool (poll() one batch → JSON array). Class-based (system-property wired in AgentApplication) and instance-based overloads
  • Event-driven triggers: agent.subscribe.topics / agent.trigger.output.topic — each consumed CloudEvent becomes a prompt (trigger:<eventId> conversation), answered with tools, answer published to the output topic for sink delivery. Uses a dedicated CloudEventsClient so the agent lite-channel poller is untouched
  • Port drift fix: AgentApplication, ConnectorApplication, start-connector.sh, connector.properties, agent.properties, StreamingCallDemo — default runtime URL 8080 → 10105 (matches EventMeshApplication's default)
  • Docs: new docs/feature/agent-tools.md (extension surface table, connector-as-tools, triggers, embedder API, config reference) + docs/index.md entry

Agent boot wiring (AgentApplication): agent.tools.sink.<name>=<fqcn> / agent.tools.source.<name>=<fqcn> + agent.tools.props.<name>.* build the tool registry; connector jars just need to be on the agent classpath.

Tests

  • ToolRegistryTest — registry lookup, spec rendering, unknown-tool error (3 tests)
  • ConnectorToolAdapterTest — sink tool delivers args as CloudEvent (asserts serialized data), source tool returns batch as JSON array + empty batch (3 tests)
  • OpenAiLlmClientChatTest — tool-call parsing, plain-text completion, tools advertised in request body (3 tests, in-process mock)
  • All 9 green via JUnit launcher (hermetic; no external LLM)
  • ConversationStoreTest unchanged and still passing (interface extraction is source-compatible: ConversationStore still concrete)

Notes for reviewers

  • Record components ToolSpec/ToolCall are flagged [WARN] by local checkstyle 9.0 (MethodName on record decls); records are used across the codebase (e.g. AgentControlClient.RegisterResult) so CI's checker accepts them
  • eventmesh-agent now depends on eventmesh-connector-api (compile) + io.cloudevents:cloudevents-core — the architecture guard only constrains plugin→runtime, this direction is unconstrained
  • Tool-loop answers are published as a single chunk (per-token streaming of the final answer is a follow-up noted in the issue)

@qqeasonchen
qqeasonchen force-pushed the feat/agent-connector-tools branch from 779f117 to 92403fe Compare September 21, 2026 07:57
@qqeasonchen

Copy link
Copy Markdown
Contributor Author

Rebased onto the latest develop (2d1dc1a13) to resolve the conflict with #5406's agent hardening.

Conflict resolution notes:

…acked agent tools and event triggers

- Extract the LlmClient SPI (token streaming + function-calling chat);
  OpenAiLlmClient becomes its default implementation
- Extract the ConversationMemory SPI; ConversationStore (in-memory sliding
  window) implements it
- Add the AgentTool SPI + ToolRegistry and a bounded function-calling loop
  in StreamingAgent (streaming unchanged when no tools are registered)
- Add ConnectorToolAdapter: any sink connector becomes a write tool and any
  source connector a read tool, turning the 23 shipped connector plugins
  into the agent tool library
- Add an event-driven trigger path: agent.subscribe.topics -> prompt ->
  answer (with tools) -> published to agent.trigger.output.topic for sink
  delivery
- Fix runtime-port drift in agent/connector/examples defaults: 8080 -> 10105
- Docs: docs/feature/agent-tools.md + index entry
@qqeasonchen
qqeasonchen force-pushed the feat/agent-connector-tools branch from 92403fe to 69f5cc5 Compare September 21, 2026 08:15
@qqeasonchen
qqeasonchen merged commit 705163f into apache:develop Sep 21, 2026
8 of 10 checks passed
qqeasonchen added a commit that referenced this pull request Sep 21, 2026
…-runtime, connector-api under connector-plugin (follow-up to #5408) (#5409)

* AgentTool SPI: plugin-jar deployment for custom agent tools

- Annotate AgentTool with @EventMeshSPI (new AGENT_TOOL extension type);
  custom tools now register via META-INF/eventmesh/<AgentTool-FQCN> service
  files exactly like storage/connector plugins
- ToolRegistry.registerSpi(name) resolves implementations through
  EventMeshExtensionFactory (singleton semantics, classpath + plugin-jar
  loaders)
- AgentApplication: -Dagent.tools.spi=<name,...> enables SPI tools alongside
  connector-backed tools
- start-agent.sh: jars under plugin/agent/ join the agent classpath (same
  convention as plugin/connector/ in the connector runtime)
- Tests: AgentToolSpiTest drives the real factory + a test service file
  (echo-test) through registerSpi; unknown-name rejection path covered
- Docs: agent-tools.md SPI section + config-table entry; agent.properties
  key doc

* AgentTool SPI follow-up: module restructure — connector-api under connector-plugin, agent -> agent-runtime, new agent-plugin tree

- Move eventmesh-connector-api -> eventmesh-connector-plugin/eventmesh-connector-api
  (SPI jar lives with its plugin family, like storage/protocol)
- Rename eventmesh-agent -> eventmesh-agent-runtime (it is a process runtime, matching
  eventmesh-connector-runtime)
- New eventmesh-agent-plugin/ tree: umbrella + eventmesh-agent-plugin-http-fetch (first
  in-tree AgentTool plugin: fetch a URL, return truncated body; pluginType=agentTool,
  pluginName=http-fetch, META-INF service file included)
- dist-agent now installs pluginType==agentTool modules into dist-agent/plugin/agent/<name>/
  (same convention as dist-connector/plugin/connector)
- References updated: settings.gradle, root build.gradle (findProject paths), all 23
  connector plugin build files, architecture-guard, runtime test dep, docs

* Fix restructure: point connector-runtime at the moved connector-api module path

* Fix restructure: point all 23 connector plugins at the moved connector-api path (double-quote form)

* Fix restructure: update connector-api-split.md paths to the moved module

* Fix http-fetch plugin: drop unused @Slf4j/lombok, slim deps (spi types resolve via agent-runtime)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Agent extension points: LlmClient/ConversationMemory SPI, connector-backed agent tools, event triggers

1 participant