This is an automated email from the ASF dual-hosted git repository.
wenjin272 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/flink-agents.git
The following commit(s) were added to refs/heads/main by this push:
new 064341a3 [hotfix] Register all resource types added via
Agent.addResource (#668)
064341a3 is described below
commit 064341a3c7c49234117991f082911a377696e717
Author: Eugene <[email protected]>
AuthorDate: Thu May 14 15:18:24 2026 +0800
[hotfix] Register all resource types added via Agent.addResource (#668)
---
.../org/apache/flink/agents/plan/AgentPlan.java | 56 ++++++++++++------
.../apache/flink/agents/plan/AgentPlanTest.java | 69 ++++++++++++++++++++++
2 files changed, 106 insertions(+), 19 deletions(-)
diff --git a/plan/src/main/java/org/apache/flink/agents/plan/AgentPlan.java
b/plan/src/main/java/org/apache/flink/agents/plan/AgentPlan.java
index 3fb15109..3a77f866 100644
--- a/plan/src/main/java/org/apache/flink/agents/plan/AgentPlan.java
+++ b/plan/src/main/java/org/apache/flink/agents/plan/AgentPlan.java
@@ -246,6 +246,18 @@ public class AgentPlan implements Serializable {
}
}
+ private static ResourceDescriptor requireResourceDescriptor(
+ String name, ResourceType type, Object value) {
+ if (!(value instanceof ResourceDescriptor)) {
+ throw new IllegalStateException(
+ String.format(
+ "Resource '%s' of type %s must be a
ResourceDescriptor when added via"
+ + " Agent.addResource, but got %s",
+ name, type, value == null ? "null" :
value.getClass().getName()));
+ }
+ return (ResourceDescriptor) value;
+ }
+
private void extractResource(ResourceType type, Method method) throws
Exception {
extractResource(type, method, null);
}
@@ -462,25 +474,7 @@ public class AgentPlan implements Serializable {
for (Map.Entry<ResourceType, Map<String, Object>> entry :
agent.getResources().entrySet()) {
ResourceType type = entry.getKey();
- if (type == ResourceType.CHAT_MODEL || type ==
ResourceType.CHAT_MODEL_CONNECTION) {
- for (Map.Entry<String, Object> kv :
entry.getValue().entrySet()) {
- ResourceProvider provider;
- if (PythonResourceWrapper.class.isAssignableFrom(
- Class.forName(
- ((ResourceDescriptor)
kv.getValue()).getClazz(),
- true,
-
Thread.currentThread().getContextClassLoader()))) {
- provider =
- new PythonResourceProvider(
- kv.getKey(), type,
(ResourceDescriptor) kv.getValue());
- } else {
- provider =
- new JavaResourceProvider(
- kv.getKey(), type,
(ResourceDescriptor) kv.getValue());
- }
- addResourceProvider(provider);
- }
- } else if (type == PROMPT) {
+ if (type == PROMPT) {
for (Map.Entry<String, Object> kv :
entry.getValue().entrySet()) {
JavaSerializableResourceProvider provider =
JavaSerializableResourceProvider.createResourceProvider(
@@ -500,6 +494,30 @@ public class AgentPlan implements Serializable {
skillsObjects.put(kv.getKey(), (Skills) kv.getValue());
}
}
+ } else if (type == MCP_SERVER) {
+ if (!entry.getValue().isEmpty()) {
+ throw new UnsupportedOperationException(
+ "Adding an MCP server via Agent.addResource is not
supported."
+ + " Declare the MCP server with a
@MCPServer-annotated static"
+ + " method on your Agent class so its
tools and prompts can be"
+ + " discovered.");
+ }
+ } else {
+ for (Map.Entry<String, Object> kv :
entry.getValue().entrySet()) {
+ ResourceDescriptor descriptor =
+ requireResourceDescriptor(kv.getKey(), type,
kv.getValue());
+ ResourceProvider provider;
+ if (PythonResourceWrapper.class.isAssignableFrom(
+ Class.forName(
+ descriptor.getClazz(),
+ true,
+
Thread.currentThread().getContextClassLoader()))) {
+ provider = new PythonResourceProvider(kv.getKey(),
type, descriptor);
+ } else {
+ provider = new JavaResourceProvider(kv.getKey(), type,
descriptor);
+ }
+ addResourceProvider(provider);
+ }
}
}
diff --git a/plan/src/test/java/org/apache/flink/agents/plan/AgentPlanTest.java
b/plan/src/test/java/org/apache/flink/agents/plan/AgentPlanTest.java
index 29196620..7e7d0870 100644
--- a/plan/src/test/java/org/apache/flink/agents/plan/AgentPlanTest.java
+++ b/plan/src/test/java/org/apache/flink/agents/plan/AgentPlanTest.java
@@ -33,6 +33,7 @@ import
org.apache.flink.agents.api.resource.SerializableResource;
import org.apache.flink.agents.api.resource.python.PythonResourceAdapter;
import org.apache.flink.agents.api.resource.python.PythonResourceWrapper;
import org.apache.flink.agents.plan.actions.Action;
+import org.apache.flink.agents.plan.resourceprovider.JavaResourceProvider;
import
org.apache.flink.agents.plan.resourceprovider.JavaSerializableResourceProvider;
import org.apache.flink.agents.plan.resourceprovider.PythonResourceProvider;
import org.apache.flink.agents.plan.resourceprovider.ResourceProvider;
@@ -356,4 +357,72 @@ public class AgentPlanTest {
assertThat(pythonChatModelProvider.getName()).isEqualTo("pythonChatModel");
assertThat(pythonChatModelProvider.getType()).isEqualTo(ResourceType.CHAT_MODEL);
}
+
+ @Test
+ public void testAddResourceRegistersEmbeddingModelProvider() throws
Exception {
+ Agent agent = new Agent();
+ ResourceDescriptor descriptor =
+
ResourceDescriptor.Builder.newBuilder(TestPythonResource.class.getName())
+ .addInitialArgument("pythonClazz",
"test.module.EmbeddingClazz")
+ .build();
+ agent.addResource("myEmbedding", ResourceType.EMBEDDING_MODEL,
descriptor);
+
+ AgentPlan plan = new AgentPlan(agent);
+
+ Map<String, ResourceProvider> providers =
+ plan.getResourceProviders().get(ResourceType.EMBEDDING_MODEL);
+ assertThat(providers).isNotNull();
+ assertThat(providers).containsKey("myEmbedding");
+
+ ResourceProvider provider = providers.get("myEmbedding");
+ // TestPythonResource implements PythonResourceWrapper, so a Python
provider is expected.
+ assertThat(provider).isInstanceOf(PythonResourceProvider.class);
+ assertThat(provider.getName()).isEqualTo("myEmbedding");
+ assertThat(provider.getType()).isEqualTo(ResourceType.EMBEDDING_MODEL);
+ }
+
+ @Test
+ public void testAddResourceRegistersVectorStoreJavaProvider() throws
Exception {
+ Agent agent = new Agent();
+ // A non-Python-wrapper class triggers JavaResourceProvider — String
is fine for this
+ // structural check; we never call provide() here.
+ ResourceDescriptor descriptor =
+
ResourceDescriptor.Builder.newBuilder(String.class.getName()).build();
+ agent.addResource("myVectorStore", ResourceType.VECTOR_STORE,
descriptor);
+
+ AgentPlan plan = new AgentPlan(agent);
+
+ ResourceProvider provider =
+
plan.getResourceProviders().get(ResourceType.VECTOR_STORE).get("myVectorStore");
+ assertThat(provider).isInstanceOf(JavaResourceProvider.class);
+ assertThat(provider.getType()).isEqualTo(ResourceType.VECTOR_STORE);
+ }
+
+ @Test
+ public void testAddResourceRejectsNonDescriptorForUnsupportedType() {
+ Agent agent = new Agent();
+ // SerializableResource for EMBEDDING_MODEL is illegal — only PROMPT /
TOOL / SKILLS accept
+ // non-descriptors. The new code path must reject it with a clear
message instead of CCE.
+ agent.addResource(
+ "badEmbedding",
+ ResourceType.EMBEDDING_MODEL,
+ new TestSerializableChatModel("badEmbedding"));
+
+ Assertions.assertThrows(IllegalStateException.class, () -> new
AgentPlan(agent));
+ }
+
+ @Test
+ public void testAddResourceMCPServerRejectedWithGuidance() {
+ Agent agent = new Agent();
+ ResourceDescriptor descriptor =
+ ResourceDescriptor.Builder.newBuilder("dummy.MCPServer")
+ .addInitialArgument("endpoint",
"http://127.0.0.1:0/mcp")
+ .build();
+ agent.addResource("addedMcpServer", ResourceType.MCP_SERVER,
descriptor);
+
+ UnsupportedOperationException ex =
+ Assertions.assertThrows(
+ UnsupportedOperationException.class, () -> new
AgentPlan(agent));
+ assertThat(ex.getMessage()).contains("@MCPServer");
+ }
}