This is an automated email from the ASF dual-hosted git repository.
xtsong 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 d8f2eea [Feature][integration] Add Azure chat model integration in
java (#290)
d8f2eea is described below
commit d8f2eea5213f49ec463fc2f3be439d234a8e4b26
Author: tsaiggo <[email protected]>
AuthorDate: Wed Nov 5 09:40:51 2025 +0800
[Feature][integration] Add Azure chat model integration in java (#290)
---
e2e-test/integration-test/pom.xml | 5 +
.../agents/integration/test/AgentWithAzureAI.java | 126 ++++++++++++
.../integration/test/AgentWithAzureAIExample.java | 71 +++++++
.../chat-models/azureai}/pom.xml | 45 ++---
.../azureai/AzureAIChatModelConnection.java | 223 +++++++++++++++++++++
.../chatmodels/azureai/AzureAIChatModelSetup.java | 66 ++++++
integrations/chat-models/pom.xml | 1 +
7 files changed, 507 insertions(+), 30 deletions(-)
diff --git a/e2e-test/integration-test/pom.xml
b/e2e-test/integration-test/pom.xml
index c912248..1551595 100644
--- a/e2e-test/integration-test/pom.xml
+++ b/e2e-test/integration-test/pom.xml
@@ -59,6 +59,11 @@ under the License.
<artifactId>flink-clients</artifactId>
<version>${flink.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.flink</groupId>
+
<artifactId>flink-agents-integrations-chat-models-azureai</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-agents-integrations-chat-models-ollama</artifactId>
diff --git
a/e2e-test/integration-test/src/main/java/org/apache/flink/agents/integration/test/AgentWithAzureAI.java
b/e2e-test/integration-test/src/main/java/org/apache/flink/agents/integration/test/AgentWithAzureAI.java
new file mode 100644
index 0000000..e16f7e0
--- /dev/null
+++
b/e2e-test/integration-test/src/main/java/org/apache/flink/agents/integration/test/AgentWithAzureAI.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.flink.agents.integration.test;
+
+import org.apache.flink.agents.api.Agent;
+import org.apache.flink.agents.api.InputEvent;
+import org.apache.flink.agents.api.OutputEvent;
+import org.apache.flink.agents.api.annotation.*;
+import org.apache.flink.agents.api.chat.messages.ChatMessage;
+import org.apache.flink.agents.api.chat.messages.MessageRole;
+import org.apache.flink.agents.api.context.RunnerContext;
+import org.apache.flink.agents.api.event.ChatRequestEvent;
+import org.apache.flink.agents.api.event.ChatResponseEvent;
+import org.apache.flink.agents.api.resource.ResourceDescriptor;
+import
org.apache.flink.agents.integrations.chatmodels.azureai.AzureAIChatModelConnection;
+import
org.apache.flink.agents.integrations.chatmodels.azureai.AzureAIChatModelSetup;
+
+import java.util.Collections;
+
+public class AgentWithAzureAI extends Agent {
+
+ private static final String AZURE_ENDPOINT = "";
+ private static final String AZURE_API_KEY = "";
+
+ public static boolean callingRealMode() {
+ if (AZURE_ENDPOINT != null
+ && !AZURE_ENDPOINT.isEmpty()
+ && AZURE_API_KEY != null
+ && !AZURE_API_KEY.isEmpty()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @ChatModelConnection
+ public static ResourceDescriptor azureAIChatModelConnection() {
+ return
ResourceDescriptor.Builder.newBuilder(AzureAIChatModelConnection.class.getName())
+ .addInitialArgument("endpoint", AZURE_ENDPOINT)
+ .addInitialArgument("apiKey", AZURE_API_KEY)
+ .build();
+ }
+
+ @ChatModelSetup
+ public static ResourceDescriptor azureAIChatModel() {
+ System.out.println(
+ "Calling real Azure AI service. Make sure the endpoint and
apiKey are correct.");
+ return
ResourceDescriptor.Builder.newBuilder(AzureAIChatModelSetup.class.getName())
+ .addInitialArgument("connection", "azureAIChatModelConnection")
+ .addInitialArgument("model", "gpt-4o")
+ .build();
+ }
+
+ @Tool(description = "Converts temperature between Celsius and Fahrenheit")
+ public static double convertTemperature(
+ @ToolParam(name = "value", description = "Temperature value to
convert") Double value,
+ @ToolParam(
+ name = "fromUnit",
+ description = "Source unit ('C' for Celsius or 'F'
for Fahrenheit)")
+ String fromUnit,
+ @ToolParam(
+ name = "toUnit",
+ description = "Target unit ('C' for Celsius or 'F'
for Fahrenheit)")
+ String toUnit) {
+
+ fromUnit = fromUnit.toUpperCase();
+ toUnit = toUnit.toUpperCase();
+
+ if (fromUnit.equals(toUnit)) {
+ return value;
+ }
+
+ if (fromUnit.equals("C") && toUnit.equals("F")) {
+ return (value * 9 / 5) + 32;
+ } else if (fromUnit.equals("F") && toUnit.equals("C")) {
+ return (value - 32) * 5 / 9;
+ } else {
+ throw new IllegalArgumentException("Invalid temperature units. Use
'C' or 'F'");
+ }
+ }
+
+ @Tool(description = "Calculates Body Mass Index (BMI)")
+ public static double calculateBMI(
+ @ToolParam(name = "weightKg", description = "Weight in kilograms")
Double weightKg,
+ @ToolParam(name = "heightM", description = "Height in meters")
Double heightM) {
+
+ if (weightKg <= 0 || heightM <= 0) {
+ throw new IllegalArgumentException("Weight and height must be
positive values");
+ }
+ return weightKg / (heightM * heightM);
+ }
+
+ @Tool(description = "Create a random number")
+ public static double createRandomNumber() {
+ return Math.random();
+ }
+
+ @Action(listenEvents = {InputEvent.class})
+ public static void process(InputEvent event, RunnerContext ctx) throws
Exception {
+ ctx.sendEvent(
+ new ChatRequestEvent(
+ "azureAIChatModel",
+ Collections.singletonList(
+ new ChatMessage(MessageRole.USER, (String)
event.getInput()))));
+ }
+
+ @Action(listenEvents = {ChatResponseEvent.class})
+ public static void processChatResponse(ChatResponseEvent event,
RunnerContext ctx) {
+ ctx.sendEvent(new OutputEvent(event.getResponse().getContent()));
+ }
+}
diff --git
a/e2e-test/integration-test/src/main/java/org/apache/flink/agents/integration/test/AgentWithAzureAIExample.java
b/e2e-test/integration-test/src/main/java/org/apache/flink/agents/integration/test/AgentWithAzureAIExample.java
new file mode 100644
index 0000000..bb56a21
--- /dev/null
+++
b/e2e-test/integration-test/src/main/java/org/apache/flink/agents/integration/test/AgentWithAzureAIExample.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.flink.agents.integration.test;
+
+import org.apache.flink.agents.api.AgentsExecutionEnvironment;
+import org.apache.flink.api.java.functions.KeySelector;
+import org.apache.flink.streaming.api.datastream.DataStream;
+import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
+
+public class AgentWithAzureAIExample {
+ /** Runs the example pipeline. */
+ public static void main(String[] args) throws Exception {
+ if (!AgentWithAzureAI.callingRealMode()) {
+ // print warning information
+ System.err.println(
+ "Please set the AZURE_ENDPOINT and AZURE_API_KEY in the
AgentWithAzureAI class to run this example in real mode.");
+ System.err.println("Falling back to mock mode.");
+ AgentWithResourceExample.main(args);
+ return;
+ }
+ // Create the execution environment
+ StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
+ env.setParallelism(1);
+
+ // Use prompts that trigger different tool calls in the agent
+ DataStream<String> inputStream =
+ env.fromData(
+ "Convert 25 degrees Celsius to Fahrenheit",
+ "What is 98.6 Fahrenheit in Celsius?",
+ "Change 32 degrees Celsius to Fahrenheit",
+ "If it's 75 degrees Fahrenheit, what would that be in
Celsius?",
+ "Convert room temperature of 20C to F",
+ "Calculate BMI for someone who is 1.75 meters tall and
weighs 70 kg",
+ "What's the BMI for a person weighing 85 kg with
height 1.80 meters?",
+ "Can you tell me the BMI if I'm 1.65m tall and weigh
60kg?",
+ "Find BMI for 75kg weight and 1.78m height",
+ "Create me a random number please");
+
+ // Create agents execution environment
+ AgentsExecutionEnvironment agentsEnv =
+ AgentsExecutionEnvironment.getExecutionEnvironment(env);
+
+ // Apply agent to the DataStream and use the prompt itself as the key
+ DataStream<Object> outputStream =
+ agentsEnv
+ .fromDataStream(inputStream, (KeySelector<String,
String>) value -> value)
+ .apply(new AgentWithAzureAI())
+ .toDataStream();
+
+ // Print the results
+ outputStream.print();
+
+ // Execute the pipeline
+ agentsEnv.execute();
+ }
+}
diff --git a/e2e-test/integration-test/pom.xml
b/integrations/chat-models/azureai/pom.xml
similarity index 54%
copy from e2e-test/integration-test/pom.xml
copy to integrations/chat-models/azureai/pom.xml
index c912248..d3cbd05 100644
--- a/e2e-test/integration-test/pom.xml
+++ b/integrations/chat-models/azureai/pom.xml
@@ -19,14 +19,17 @@ under the License.
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
+
<parent>
<groupId>org.apache.flink</groupId>
- <artifactId>flink-agents-e2e-tests</artifactId>
+ <artifactId>flink-agents-integrations-chat-models</artifactId>
<version>0.2-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
</parent>
- <artifactId>flink-agents-integration-tests</artifactId>
- <name>Flink Agents : E2E Tests: Integration</name>
+ <artifactId>flink-agents-integrations-chat-models-azureai</artifactId>
+ <name>Flink Agents : Integrations: Chat Models: Azure AI</name>
+ <packaging>jar</packaging>
<dependencies>
<dependency>
@@ -36,38 +39,20 @@ under the License.
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
- <artifactId>flink-agents-runtime</artifactId>
+ <artifactId>flink-agents-plan</artifactId>
<version>${project.version}</version>
</dependency>
+
<dependency>
- <groupId>org.apache.flink</groupId>
- <artifactId>flink-streaming-java</artifactId>
- <version>${flink.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.flink</groupId>
- <artifactId>flink-table-api-java-bridge</artifactId>
- <version>${flink.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.flink</groupId>
- <artifactId>flink-table-planner_2.12</artifactId>
- <version>${flink.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.flink</groupId>
- <artifactId>flink-clients</artifactId>
- <version>${flink.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.flink</groupId>
-
<artifactId>flink-agents-integrations-chat-models-ollama</artifactId>
- <version>${project.version}</version>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-ai-inference</artifactId>
+ <version>1.0.0-beta.5</version>
</dependency>
+
<dependency>
- <groupId>org.apache.flink</groupId>
-
<artifactId>flink-agents-integrations-embedding-models-ollama</artifactId>
- <version>${project.version}</version>
+ <groupId>com.azure</groupId>
+ <artifactId>azure-identity</artifactId>
+ <version>1.13.3</version>
</dependency>
</dependencies>
diff --git
a/integrations/chat-models/azureai/src/main/java/org/apache/flink/agents/integrations/chatmodels/azureai/AzureAIChatModelConnection.java
b/integrations/chat-models/azureai/src/main/java/org/apache/flink/agents/integrations/chatmodels/azureai/AzureAIChatModelConnection.java
new file mode 100644
index 0000000..182c4c1
--- /dev/null
+++
b/integrations/chat-models/azureai/src/main/java/org/apache/flink/agents/integrations/chatmodels/azureai/AzureAIChatModelConnection.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.flink.agents.integrations.chatmodels.azureai;
+
+import com.azure.ai.inference.ChatCompletionsClient;
+import com.azure.ai.inference.ChatCompletionsClientBuilder;
+import com.azure.ai.inference.models.*;
+import com.azure.core.credential.AzureKeyCredential;
+import com.azure.core.util.BinaryData;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.nimbusds.jose.shaded.gson.Gson;
+import org.apache.flink.agents.api.chat.messages.ChatMessage;
+import org.apache.flink.agents.api.chat.messages.MessageRole;
+import org.apache.flink.agents.api.chat.model.BaseChatModelConnection;
+import org.apache.flink.agents.api.resource.Resource;
+import org.apache.flink.agents.api.resource.ResourceDescriptor;
+import org.apache.flink.agents.api.resource.ResourceType;
+import org.apache.flink.agents.api.tools.Tool;
+
+import java.util.*;
+import java.util.function.BiFunction;
+import java.util.stream.Collectors;
+
+/**
+ * A chat model integration for Azure AI Chat Completions service.
+ *
+ * <p>This implementation adapts the generic Flink Agents chat model interface
to the Azure AI Chat
+ * Completions API.
+ *
+ * <p>See also {@link BaseChatModelConnection} for the common resource
abstractions and lifecycle.
+ *
+ * <p>Example usage:
+ *
+ * <pre>{@code
+ * public class MyAgent extends Agent {
+ * // Register the chat model connection via @ChatModelConnection metadata.
+ * @ChatModelConnection
+ * public static ResourceDesc azureAI() {
+ * return
ResourceDescriptor.Builder.newBuilder(AzureAIChatModelConnection.class.getName())
+ * .addInitialArgument("endpoint", "<your-azure-ai-endpoint>")
+ * .addInitialArgument("apiKey", "<your-azure-ai-api-key>")
+ * .build();
+ * }
+ * }
+ * }</pre>
+ */
+public class AzureAIChatModelConnection extends BaseChatModelConnection {
+
+ private final Gson gson = new Gson();
+
+ private final ChatCompletionsClient client;
+
+ /**
+ * Creates a new AzureAI chat model connection.
+ *
+ * @param descriptor a resource descriptor contains the initial parameters
+ * @param getResource a function to resolve resources (e.g., tools) by
name and type
+ * @throws IllegalArgumentException if endpoint is null or empty
+ */
+ public AzureAIChatModelConnection(
+ ResourceDescriptor descriptor, BiFunction<String, ResourceType,
Resource> getResource) {
+ super(descriptor, getResource);
+
+ String endpoint = descriptor.getArgument("endpoint");
+ String apiKey = descriptor.getArgument("apiKey");
+ if (endpoint == null || endpoint.isEmpty()) {
+ throw new IllegalArgumentException("endpoint should not be null or
empty.");
+ }
+ this.client =
+ new ChatCompletionsClientBuilder()
+ .credential(new AzureKeyCredential(apiKey))
+ .endpoint(endpoint)
+ .buildClient();
+ }
+
+ private List<ChatCompletionsToolDefinition>
convertToAzureAITools(List<Tool> tools) {
+ final ObjectMapper mapper = new ObjectMapper();
+ final List<ChatCompletionsToolDefinition> azureAITools = new
ArrayList<>();
+ try {
+ for (Tool tool : tools) {
+ final Map<String, Object> schema =
+ mapper.readValue(
+ tool.getMetadata().getInputSchema(), new
TypeReference<>() {});
+
+ final FunctionDefinition functionDef =
+ new FunctionDefinition(tool.getName())
+ .setDescription(tool.getDescription())
+ .setParameters(BinaryData.fromObject(schema));
+
+ azureAITools.add(new
ChatCompletionsFunctionToolDefinition(functionDef));
+ }
+ return azureAITools;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private ChatRequestMessage convertToChatRequestMessage(ChatMessage
message) {
+ final String content = message.getContent();
+ final MessageRole role = message.getRole();
+ final List<Map<String, Object>> toolCalls = message.getToolCalls();
+ final Map<String, Object> extraArgs = message.getExtraArgs();
+ switch (role) {
+ case SYSTEM:
+ return new ChatRequestSystemMessage(content);
+ case USER:
+ return new ChatRequestUserMessage(content);
+ case ASSISTANT:
+ final List<ChatCompletionsToolCall> azureToolCalls =
+ toolCalls != null
+ ? transformToAzureToolCalls(toolCalls)
+ : Collections.emptyList();
+ return new
ChatRequestAssistantMessage(content).setToolCalls(azureToolCalls);
+ case TOOL:
+ String toolCallId =
+ extraArgs != null &&
extraArgs.containsKey("externalId")
+ ? extraArgs.get("externalId").toString()
+ : null;
+ return new
ChatRequestToolMessage(toolCallId).setContent(content);
+ default:
+ throw new IllegalArgumentException("Unsupported role: " +
role);
+ }
+ }
+
+ // the structure of toolCalls should be like the returned value of
Method:convertToAgentsTools
+ private List<ChatCompletionsToolCall> transformToAzureToolCalls(
+ List<Map<String, Object>> toolCalls) {
+ final List<ChatCompletionsToolCall> azureToolCalls = new ArrayList<>();
+ for (Map<String, Object> call : toolCalls) {
+ final String id = (String) call.get("id");
+ final String type = (String) call.get("type");
+
+ if ("function".equals(type)) {
+ final Map<String, Object> functionCall = (Map<String, Object>)
call.get("function");
+ final String functionName = (String) functionCall.get("name");
+ final Map<String, Object> functionArguments =
+ (Map<String, Object>) functionCall.get("arguments");
+ final String functionArgumentsJson =
gson.toJson(functionArguments);
+ ChatCompletionsFunctionToolCall function =
+ new ChatCompletionsFunctionToolCall(
+ id, new FunctionCall(functionName,
functionArgumentsJson));
+ azureToolCalls.add(function);
+ }
+ }
+ return azureToolCalls;
+ }
+
+ @Override
+ public ChatMessage chat(
+ List<ChatMessage> messages, List<Tool> tools, Map<String, Object>
arguments) {
+ try {
+ final List<ChatCompletionsToolDefinition> azureTools =
convertToAzureAITools(tools);
+ final List<ChatRequestMessage> chatMessages =
+ messages.stream()
+ .map(this::convertToChatRequestMessage)
+ .collect(Collectors.toList());
+
+ ChatCompletionsOptions options =
+ new ChatCompletionsOptions(chatMessages)
+ .setModel((String) arguments.get("model"))
+ .setTools(azureTools);
+
+ ChatCompletions completions = client.complete(options);
+ ChatChoice choice = completions.getChoices().get(0);
+ ChatResponseMessage responseMessage = choice.getMessage();
+
+ ChatMessage chatMessage =
ChatMessage.assistant(responseMessage.getContent());
+
+ List<ChatCompletionsToolCall> toolCalls =
responseMessage.getToolCalls();
+ if (toolCalls != null && !toolCalls.isEmpty()) {
+ List<Map<String, Object>> convertedToolCalls =
convertToAgentsTools(toolCalls);
+ chatMessage.setToolCalls(convertedToolCalls);
+ }
+
+ return chatMessage;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private List<Map<String, Object>> convertToAgentsTools(
+ List<ChatCompletionsToolCall> azureToolCalls) {
+ final List<Map<String, Object>> toolCalls = new
ArrayList<>(azureToolCalls.size());
+ for (ChatCompletionsToolCall toolCall : azureToolCalls) {
+ if (toolCall != null) {
+ final Map<String, Object> call =
+ Map.of(
+ // todo: I don't think the magic name is a
good idea here, need to
+ // unify later (maybe we can consider
standardizing tool call
+ // structure across different LLM integrations)
+ "id", toolCall.getId(),
+ "original_id", toolCall.getId(),
+ "type", toolCall.getType(),
+ "function",
+ Map.of(
+ "name",
toolCall.getFunction().getName(),
+ "arguments",
+ gson.fromJson(
+
toolCall.getFunction()
+
.getArguments(),
+ Map.class)));
+ toolCalls.add(call);
+ }
+ }
+ return toolCalls;
+ }
+}
diff --git
a/integrations/chat-models/azureai/src/main/java/org/apache/flink/agents/integrations/chatmodels/azureai/AzureAIChatModelSetup.java
b/integrations/chat-models/azureai/src/main/java/org/apache/flink/agents/integrations/chatmodels/azureai/AzureAIChatModelSetup.java
new file mode 100644
index 0000000..465cbc8
--- /dev/null
+++
b/integrations/chat-models/azureai/src/main/java/org/apache/flink/agents/integrations/chatmodels/azureai/AzureAIChatModelSetup.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.flink.agents.integrations.chatmodels.azureai;
+
+import org.apache.flink.agents.api.chat.model.BaseChatModelSetup;
+import org.apache.flink.agents.api.resource.Resource;
+import org.apache.flink.agents.api.resource.ResourceDescriptor;
+import org.apache.flink.agents.api.resource.ResourceType;
+
+/**
+ * A chat model integration for Azure AI Chat Completions service.
+ *
+ * <p>This implementation adapts the generic Flink Agents chat model interface
to the Azure AI Chat
+ * Completions API.
+ *
+ * <p>See also {@link BaseChatModelSetup} for the common resource abstractions
and lifecycle.
+ *
+ * <p>Example usage:
+ *
+ * <pre>{@code
+ * public class MyAgent extends Agent {
+ * // Register the chat model setup via @ChatModelSetup metadata.
+ * @ChatModelSetup
+ * public static ResourceDesc azureAI() {
+ * return
ResourceDescriptor.Builder.newBuilder(AzureAIChatModelSetup.class.getName())
+ * .addInitialArgument("model", "<your-azure-ai-model-name>")
+ * .addInitialArgument("prompt", "<your-prompt-template>")
+ * .addInitialArgument("tools", "<your-tool-list>")
+ * .addInitialArgument("key", "<your-azure-ai-key>")
+ * .addInitialArgument("endpoint", "<your-azure-ai-endpoint>")
+ * .build();
+ * }
+ * }
+ * }</pre>
+ */
+public class AzureAIChatModelSetup extends BaseChatModelSetup {
+
+ public AzureAIChatModelSetup(
+ ResourceDescriptor descriptor,
+ java.util.function.BiFunction<String, ResourceType, Resource>
getResource) {
+ super(descriptor, getResource);
+ }
+
+ // For any other specific parameters, please refer to
ChatCompletionsOptions
+ @Override
+ public java.util.Map<String, Object> getParameters() {
+ java.util.Map<String, Object> params = new java.util.HashMap<>();
+ params.put("model", model);
+ return params;
+ }
+}
diff --git a/integrations/chat-models/pom.xml b/integrations/chat-models/pom.xml
index bca4be1..dafeae9 100644
--- a/integrations/chat-models/pom.xml
+++ b/integrations/chat-models/pom.xml
@@ -31,6 +31,7 @@ under the License.
<packaging>pom</packaging>
<modules>
+ <module>azureai</module>
<module>ollama</module>
</modules>