Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ public static final class ChatModel {
public static final String AZURE_OPENAI_SETUP =
"org.apache.flink.agents.integrations.chatmodels.openai.AzureOpenAIChatModelSetup";

// IBM watsonx.ai
public static final String WATSONX_CONNECTION =
"org.apache.flink.agents.integrations.chatmodels.watsonx.WatsonxChatModelConnection";
public static final String WATSONX_SETUP =
"org.apache.flink.agents.integrations.chatmodels.watsonx.WatsonxChatModelSetup";

// Python Wrapper
public static final String PYTHON_WRAPPER_CONNECTION =
"org.apache.flink.agents.api.chat.model.python.PythonChatModelConnection";
Expand Down Expand Up @@ -134,6 +140,12 @@ public static final class Python {
public static final String TONGYI_SETUP =
"flink_agents.integrations.chat_models.tongyi_chat_model.TongyiChatModelSetup";

// IBM watsonx.ai
public static final String WATSONX_CONNECTION =
"flink_agents.integrations.chat_models.watsonx.watsonx_chat_model.WatsonxChatModelConnection";
public static final String WATSONX_SETUP =
"flink_agents.integrations.chat_models.watsonx.watsonx_chat_model.WatsonxChatModelSetup";

private Python() {}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,14 @@ public final class Aliases {
chatConnJava.put("anthropic", ResourceName.ChatModel.ANTHROPIC_CONNECTION);
chatConnJava.put("azure", ResourceName.ChatModel.AZURE_CONNECTION);
chatConnJava.put("bedrock", ResourceName.ChatModel.BEDROCK_CONNECTION);
chatConnJava.put("watsonx", ResourceName.ChatModel.WATSONX_CONNECTION);
Map<String, String> chatConnPython = new HashMap<>();
chatConnPython.put("ollama", ResourceName.ChatModel.Python.OLLAMA_CONNECTION);
chatConnPython.put("openai", ResourceName.ChatModel.Python.OPENAI_COMPLETIONS_CONNECTION);
chatConnPython.put("anthropic", ResourceName.ChatModel.Python.ANTHROPIC_CONNECTION);
chatConnPython.put("tongyi", ResourceName.ChatModel.Python.TONGYI_CONNECTION);
chatConnPython.put("azure_openai", ResourceName.ChatModel.Python.AZURE_OPENAI_CONNECTION);
chatConnPython.put("watsonx", ResourceName.ChatModel.Python.WATSONX_CONNECTION);
ca.put(ResourceType.CHAT_MODEL_CONNECTION, buildLangBuckets(chatConnJava, chatConnPython));

// CHAT_MODEL
Expand All @@ -104,12 +106,14 @@ public final class Aliases {
chatJava.put("anthropic", ResourceName.ChatModel.ANTHROPIC_SETUP);
chatJava.put("azure", ResourceName.ChatModel.AZURE_SETUP);
chatJava.put("bedrock", ResourceName.ChatModel.BEDROCK_SETUP);
chatJava.put("watsonx", ResourceName.ChatModel.WATSONX_SETUP);
Map<String, String> chatPython = new HashMap<>();
chatPython.put("ollama", ResourceName.ChatModel.Python.OLLAMA_SETUP);
chatPython.put("openai", ResourceName.ChatModel.Python.OPENAI_COMPLETIONS_SETUP);
chatPython.put("anthropic", ResourceName.ChatModel.Python.ANTHROPIC_SETUP);
chatPython.put("tongyi", ResourceName.ChatModel.Python.TONGYI_SETUP);
chatPython.put("azure_openai", ResourceName.ChatModel.Python.AZURE_OPENAI_SETUP);
chatPython.put("watsonx", ResourceName.ChatModel.Python.WATSONX_SETUP);
ca.put(ResourceType.CHAT_MODEL, buildLangBuckets(chatJava, chatPython));

// EMBEDDING_MODEL_CONNECTION
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ void clazzAliasPythonBucket() {
assertThat(fqn).isEqualTo(ResourceName.ChatModel.Python.OLLAMA_CONNECTION);
}

@Test
void watsonxAliasesResolveForJavaAndPython() {
assertThat(
Aliases.resolveClazz(
"watsonx", ResourceType.CHAT_MODEL_CONNECTION, Language.JAVA))
.isEqualTo(ResourceName.ChatModel.WATSONX_CONNECTION);
assertThat(Aliases.resolveClazz("watsonx", ResourceType.CHAT_MODEL, Language.JAVA))
.isEqualTo(ResourceName.ChatModel.WATSONX_SETUP);
assertThat(
Aliases.resolveClazz(
"watsonx", ResourceType.CHAT_MODEL_CONNECTION, Language.PYTHON))
.isEqualTo(ResourceName.ChatModel.Python.WATSONX_CONNECTION);
assertThat(Aliases.resolveClazz("watsonx", ResourceType.CHAT_MODEL, Language.PYTHON))
.isEqualTo(ResourceName.ChatModel.Python.WATSONX_SETUP);
}

@Test
void clazzAliasMissPassesThrough() {
String fqn =
Expand Down
5 changes: 5 additions & 0 deletions dist/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ under the License.
<artifactId>flink-agents-integrations-chat-models-gemini</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-agents-integrations-chat-models-watsonx</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-agents-integrations-embedding-models-ollama</artifactId>
Expand Down
159 changes: 158 additions & 1 deletion docs/content/docs/development/chat_models.md
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,163 @@ Some popular options include:
Model availability and specifications may change. Always check the official DashScope documentation for the latest information before implementing in production.
{{< /hint >}}

### Watsonx (IBM watsonx.ai)

IBM watsonx.ai provides cloud-based chat models, including the IBM Granite series, with enterprise-grade governance and deployment options on IBM Cloud.

#### Prerequisites

1. Create an account on [IBM Cloud](https://cloud.ibm.com/) and provision a [watsonx.ai](https://www.ibm.com/products/watsonx-ai) instance
2. Create a watsonx.ai project or deployment space
2. On Developer access, select your Project ID and create API Key
4. Note the generated API Key, Project ID and watsonx.ai URL for configuring your connection

#### WatsonxChatModelConnection Parameters

{{< tabs "WatsonxChatModelConnection Parameters" >}}

{{< tab "Python" >}}

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `url` | str | `$WATSONX_URL` | watsonx.ai service endpoint of your region |
| `api_key` | str | `$WATSONX_API_KEY` | IBM Cloud API key; configure exactly one of `api_key` and `token` |
| `token` | str | `$WATSONX_TOKEN` | Bearer token; configure exactly one of `api_key` and `token` |
| `project_id` | str | `$WATSONX_PROJECT_ID` | watsonx.ai project id (or use `space_id`) |
| `space_id` | str | `$WATSONX_SPACE_ID` | Deployment space id, as an alternative to `project_id` |
| `request_timeout` | float | `120.0` | HTTP request timeout in seconds |
| `max_retries` | int | `3` | Maximum retries for transport failures and HTTP 408, 429, 500, 502, 503, and 504 responses |

{{< /tab >}}

{{< tab "Java" >}}

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `url` | String | `$WATSONX_URL` | watsonx.ai service endpoint of your region |
| `api_key` | String | `$WATSONX_API_KEY` | IBM Cloud API key; configure exactly one of `api_key` and `token` |
| `token` | String | `$WATSONX_TOKEN` | Bearer token; configure exactly one of `api_key` and `token` |
| `project_id` | String | `$WATSONX_PROJECT_ID` | watsonx.ai project id (or use `space_id`) |
| `space_id` | String | `$WATSONX_SPACE_ID` | Deployment space id, as an alternative to `project_id` |
| `api_version` | String | `"2025-04-23"` | watsonx.ai REST API version date |
| `iam_url` | String | `"https://iam.cloud.ibm.com"` | IAM endpoint used to exchange the API key for a token |
| `request_timeout` | double | `120.0` | HTTP request timeout in seconds |
| `max_retries` | int | `3` | Maximum retries for transport failures and HTTP 408, 429, 500, 502, 503, and 504 responses |

{{< /tab >}}

{{< /tabs >}}

#### WatsonxChatModelSetup Parameters

{{< tabs "WatsonxChatModelSetup Parameters" >}}

{{< tab "Python" >}}

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `connection` | str | Required | Reference to connection method name |
| `model` | str | `"ibm/granite-4-h-small"` | watsonx.ai model id to use |
| `prompt` | Prompt \| str | None | Prompt template or reference to prompt resource |
| `tools` | List[str] | None | List of tool names available to the model |
| `temperature` | float | `0.1` | Sampling temperature (0.0 to 2.0) |
| `max_tokens` | int | None | Maximum number of tokens to generate |
| `extract_reasoning` | bool | `False` | Extract reasoning content (e.g. `<think>` blocks) from response |
| `additional_kwargs` | dict | `{}` | Additional watsonx.ai chat parameters (e.g. `top_p`, `time_limit`, `seed`) |

{{< /tab >}}

{{< tab "Java" >}}

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `connection` | String | Required | Reference to connection method name |
| `model` | String | `"ibm/granite-4-h-small"` | watsonx.ai model id to use |
| `prompt` | Prompt \| String | None | Prompt template or reference to prompt resource |
| `tools` | List[String] | None | List of tool names available to the model |
| `temperature` | Double | `0.1` | Sampling temperature (0.0 to 2.0) |
| `max_tokens` | Integer | None | Maximum number of tokens to generate |
| `extract_reasoning` | Boolean | `false` | Extract reasoning content (e.g. `<think>` blocks) from response |
| `additional_kwargs` | Map | `{}` | Additional watsonx.ai chat parameters (e.g. `top_p`, `time_limit`, `seed`) |

{{< /tab >}}

{{< /tabs >}}

#### Usage Example

{{< tabs "Watsonx Usage Example" >}}

{{< tab "Python" >}}
```python
class MyAgent(Agent):

@chat_model_connection
@staticmethod
def watsonx_connection() -> ResourceDescriptor:
return ResourceDescriptor(
clazz=ResourceName.ChatModel.WATSONX_CONNECTION,
url="https://us-south.ml.cloud.ibm.com", # set WATSONX_URL env var
api_key="your-api-key-here", # set WATSONX_API_KEY env var
project_id="your-project-id", # set WATSONX_PROJECT_ID env var
)

@chat_model_setup
@staticmethod
def watsonx_chat_model() -> ResourceDescriptor:
return ResourceDescriptor(
clazz=ResourceName.ChatModel.WATSONX_SETUP,
connection="watsonx_connection",
model="ibm/granite-4-h-small",
temperature=0.1,
max_tokens=1024
)

...
```
{{< /tab >}}

{{< tab "Java" >}}
```java
public class MyAgent extends Agent {
@ChatModelConnection
public static ResourceDescriptor watsonxConnection() {
return ResourceDescriptor.Builder.newBuilder(ResourceName.ChatModel.WATSONX_CONNECTION)
.addInitialArgument("url", "https://us-south.ml.cloud.ibm.com")
.addInitialArgument("api_key", "your-api-key-here")
.addInitialArgument("project_id", "your-project-id")
.build();
}

@ChatModelSetup
public static ResourceDescriptor watsonxChatModel() {
return ResourceDescriptor.Builder.newBuilder(ResourceName.ChatModel.WATSONX_SETUP)
.addInitialArgument("connection", "watsonxConnection")
.addInitialArgument("model", "ibm/granite-4-h-small")
.addInitialArgument("temperature", 0.1)
.build();
}

...
}
```
{{< /tab >}}

{{< /tabs >}}

#### Available Models

Visit the [watsonx.ai foundation models documentation](https://www.ibm.com/products/watsonx-ai/foundation-models) for the complete and up-to-date list of available chat models.

Some popular options include:
- **ibm/granite** series (ibm/granite-4-h-small, ibm/granite-3-3-8b-instruct)
- **meta-llama** series (meta-llama/llama-3-3-70b-instruct)
- **mistralai** series (mistralai/mistral-large)

{{< hint warning >}}
Model availability and specifications may change. Always check the official IBM watsonx.ai documentation for the latest information before implementing in production.
{{< /hint >}}

## Using Cross-Language Providers

Flink Agents supports cross-language chat model integration, allowing you to use chat models implemented in one language (Java or Python) from agents written in the other language. This is particularly useful when a chat model provider is only available in one language (e.g., Tongyi is currently Python-only).
Expand Down Expand Up @@ -1370,4 +1527,4 @@ public class MyChatModelSetup extends BaseChatModelSetup {

The built-in `chat_model_action` listens to `ChatRequestEvent` and `ToolResponseEvent`. To request a chat completion, send a `ChatRequestEvent`. If the model returns a final answer, the action sends a `ChatResponseEvent`.

If the model asks to call tools, `chat_model_action` sends a `ToolRequestEvent` instead of a final `ChatResponseEvent`. After the tools finish, it receives the matching `ToolResponseEvent`, appends the tool results to the chat history, and calls the model again. This loop continues until the model returns a final response. For details on how tools are executed, see [Built-in Events and Actions in Tool Use]({{< ref "docs/development/tool_use#built-in-events-and-actions" >}}).
If the model asks to call tools, `chat_model_action` sends a `ToolRequestEvent` instead of a final `ChatResponseEvent`. After the tools finish, it receives the matching `ToolResponseEvent`, appends the tool results to the chat history, and calls the model again. This loop continues until the model returns a final response. For details on how tools are executed, see [Built-in Events and Actions in Tool Use]({{< ref "docs/development/tool_use#built-in-events-and-actions" >}}).
1 change: 1 addition & 0 deletions docs/content/docs/development/yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ Common chat-model aliases:
| `azure` | — | Azure OpenAI (Java) |
| `bedrock` | — | Bedrock (Java) |
| `tongyi` | Tongyi (Python) | — |
| `watsonx` | IBM watsonx.ai (Python) | IBM watsonx.ai (Java) |

Embedding-model aliases (apply to both `embedding_model_connections` and `embedding_model_setups`):

Expand Down
1 change: 1 addition & 0 deletions integrations/chat-models/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ under the License.
<module>gemini</module>
<module>ollama</module>
<module>openai</module>
<module>watsonx</module>
</modules>

</project>
64 changes: 64 additions & 0 deletions integrations/chat-models/watsonx/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
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-integrations-chat-models</artifactId>
<version>0.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

<artifactId>flink-agents-integrations-chat-models-watsonx</artifactId>
<name>Flink Agents : Integrations: Chat Models: IBM watsonx.ai</name>
<packaging>jar</packaging>

<!-- Talks to the watsonx.ai REST API with the JDK HttpClient. The official
IBM watsonx.ai Java SDK requires Java 17+, while this project targets
Java 11. -->
<dependencies>
<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-agents-api</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<dependency>
<groupId>org.apache.flink</groupId>
<artifactId>flink-annotations</artifactId>
<version>${flink.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

</project>
Loading
Loading