You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@flink.apache.org by "purushah (via GitHub)" <gi...@apache.org> on 2026/09/16 04:24:24 UTC

[PR] [integrations][java] Add OpenAI embedding model [flink-agents]

purushah opened a new pull request, #1126:
URL: https://github.com/apache/flink-agents/pull/1126

   Closes #1103.
   
   Adds a Java OpenAI embedding model integration, closing the Java/Python parity gap for embeddings: Python already had `OpenAIEmbeddingModelConnection`/`OpenAIEmbeddingModelSetup`; Java had only Ollama and Bedrock, so a Java agent needed the Python wrapper (and a Python runtime on the TaskManagers) for a plain HTTPS call.
   
   ## What's in the change
   
   - New module `integrations/embedding-models/openai` with `OpenAIEmbeddingModelConnection` and `OpenAIEmbeddingModelSetup`, built on `com.openai:openai-java` (already a dependency of the OpenAI chat module; no new third-party dependency).
   - Arguments mirror the Python classes one for one. Connection: `api_key` (required), `base_url`, `request_timeout` (seconds, `0` disables), `max_retries`, `organization`, `project`. Setup: `connection`, `model`, `encoding_format`, `dimensions`, `user`, `additional_kwargs` (forwarded as extra body properties, overriding the named ones like the Python `model_kwargs`).
   - A batch of texts is sent as one request; results are placed by the response `index` so input order is preserved even if the API reorders. `embedWithUsage` reports `prompt_tokens`/`total_tokens`, so the embedding token metrics from #870 cover this provider.
   - Registration: `ResourceName.EmbeddingModel.OPENAI_CONNECTION/OPENAI_SETUP`, the Python `ResourceName.EmbeddingModel.Java` mirror, YAML aliases (`openai`), `dist`, `ide-support`, and the e2e integration module.
   - Docs: `embedding_models.md` OpenAI section gains Java tabs (usage, connection and setup parameters); the Python-only hint is removed.
   - Tests: `OpenAIEmbeddingModelTest` (11 tests, SDK client mocked, following `BedrockEmbeddingModelTest`): defaults, explicit options, missing `api_key`, timeout/retry parsing bounds, setup parameters, single and batch embeddings with token usage, out-of-order response handling, parameter forwarding (encoding format, dimensions, user, extra body properties), mismatched response size, missing model. `EmbeddingIntegrationTest` gains an `OPENAI` provider (gated on `OPENAI_API_KEY`, like the chat model integration test).
   
   Naming follows the Python embedding connection (`base_url`, `request_timeout`) rather than the Java chat connection (`api_base_url`, `timeout`), as proposed in the issue, so the two embedding implementations align across languages.
   
   ## Test commands and results
   
   Unit tests for the new module:
   ```
   mvn -pl integrations/embedding-models/openai test
   # Tests run: 11, Failures: 0, Errors: 0, Skipped: 0 -- BUILD SUCCESS
   ```
   
   Complete Java unit suite (all non-e2e modules, as `tools/ut.sh` runs it):
   ```
   mvn test -pl '!e2e-test/flink-agents-end-to-end-tests-integration,!e2e-test/flink-agents-end-to-end-tests-resource-cross-language'
   # Tests run: 3564, Failures: 0, Errors: 0, Skipped: 0 -- BUILD SUCCESS
   ```
   
   Complete Python unit suite (e2e tests excluded, as `tools/ut.sh` does) plus the cross-language resource-name consistency check:
   ```
   cd python && uv sync --extra test && uv pip install 'apache-flink~=2.3.0'
   uv run --no-sync pytest flink_agents -k "not e2e_tests"
   # 1420 passed, 27 skipped, 82 deselected
   uv run --no-sync python ../e2e-test/test-scripts/check_resource_consistency.py
   # ResourceName Cross-language consistency check successful
   ```
   
   End-to-end, real OpenAI API, embedded Flink 2.3 cluster (`OPENAI_API_KEY` exported from a local file, never printed):
   ```
   mvn -pl e2e-test/flink-agents-end-to-end-tests-integration test -Dtest=EmbeddingIntegrationTest -Pflink-2.3
   # Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 -- BUILD SUCCESS
   # [OLLAMA] 10 embeddings, Dimension=768; [OPENAI] 10 embeddings, Dimension=256 (text-embedding-3-small, dimensions=256)
   ```
   
   End-to-end, real OpenAI API, standalone local Flink 2.3.0 cluster (`start-cluster.sh`, `flink-agents-dist-flink-2.3` in `lib/`, throwaway job submitted with `flink run`, results written by a `FileSink`):
   ```
   text=Apache Flink processes streams|dim=256|norm=1.000|batch=2|batchDim=256|promptTokens=5|totalTokens=13
   text=Embeddings map text to vectors|dim=256|norm=1.000|batch=2|batchDim=256|promptTokens=6|totalTokens=15
   text=Model routing picks a model per request|dim=256|norm=1.000|batch=2|batchDim=256|promptTokens=7|totalTokens=17
   # job openai-embedding-e2e FINISHED, no exceptions in JobManager/TaskManager logs
   ```
   The requested `dimensions` are honored, OpenAI's unit-normalized vectors come back with norm 1.000, batch calls return one vector per input, and token usage is reported for single and batch calls.
   
   Also run: `ruff check` and `ruff format --check` on the touched Python files, Hugo build of the docs, spotless on the touched modules.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [integrations][java] Add OpenAI embedding model [flink-agents]

Posted by "purushah (via GitHub)" <gi...@apache.org>.
purushah commented on code in PR #1126:
URL: https://github.com/apache/flink-agents/pull/1126#discussion_r4049391504


##########
python/flink_agents/integrations/embedding_models/openai_embedding_model.py:
##########
@@ -31,6 +35,166 @@
 DEFAULT_REQUEST_TIMEOUT = 30.0
 DEFAULT_BASE_URL = "https://api.openai.com/v1"
 DEFAULT_MAX_RETRIES = 3
+DEFAULT_ENCODING_FORMAT = "float"
+# Upper bounds shared with the OpenAI chat models and the Java connection.
+MAX_OPENAI_TIMEOUT_SECONDS = 2_147_483.647
+MAX_OPENAI_RETRIES = 2_147_483_647
+MAX_DIMENSIONS = 2_147_483_647  # int range, as the Java connection requires
+ENCODING_FORMATS = frozenset({"float", "base64"})
+# Typed request fields; additional_kwargs may not repeat them.
+RESERVED_ADDITIONAL_KWARGS = frozenset(
+    {"model", "input", "encoding_format", "dimensions", "user"}
+)
+
+
+def _blank_to_none(value: Any, name: str) -> str | None:
+    """Require a string or None; blank strings are treated as absent, as in Java."""
+    if value is None:
+        return None
+    if not isinstance(value, str):
+        msg = f"{name} must be a string, got: {type(value).__name__}"
+        raise TypeError(msg)
+    return value if value.strip() else None
+
+
+def _check_additional_kwargs(additional_kwargs: Any) -> Dict[str, Any]:
+    """Require a mapping with non-blank string keys and no typed request field."""
+    if not isinstance(additional_kwargs, Mapping):
+        msg = (
+            f"additional_kwargs must be a map, got: {type(additional_kwargs).__name__}"
+        )
+        raise TypeError(msg)
+    # Per-call keys are stringified as Java's Map keys are (the setup field already
+    # requires string keys); null and blank keys are rejected in both languages.
+    if any(key is None or not str(key).strip() for key in additional_kwargs):
+        msg = "additional_kwargs contains an empty key."
+        raise ValueError(msg)
+    additional_kwargs = {str(key): value for key, value in additional_kwargs.items()}
+    collisions = sorted(RESERVED_ADDITIONAL_KWARGS & additional_kwargs.keys())
+    if collisions:
+        msg = (
+            f"additional_kwargs must not contain the typed request fields {collisions}; "
+            "set them through the corresponding setup argument instead."
+        )
+        raise ValueError(msg)
+    return additional_kwargs
+
+
+def _check_encoding_format(encoding_format: Any) -> str:
+    """Validate encoding_format; a blank value means the default, as in Java."""
+    if _blank_to_none(encoding_format, "encoding_format") is None:
+        return DEFAULT_ENCODING_FORMAT
+    if encoding_format not in ENCODING_FORMATS:
+        msg = (
+            f"encoding_format must be one of {sorted(ENCODING_FORMATS)}, "
+            f"got: {encoding_format!r}"
+        )
+        raise ValueError(msg)
+    return encoding_format
+
+
+def _check_model(model: Any) -> str:
+    if not isinstance(model, str) or not model.strip():
+        msg = "OpenAI embedding requires a non-empty 'model' (setup or per-call parameter)."
+        raise ValueError(msg)
+    return model
+
+
+def _check_dimensions(dimensions: Any) -> int | None:
+    """Accept a positive integer (an integral float counts, as in Java)."""
+    if dimensions is None:
+        return None
+    if isinstance(dimensions, float) and dimensions.is_integer():
+        dimensions = int(dimensions)
+    if (
+        isinstance(dimensions, bool)
+        or not isinstance(dimensions, int)
+        or not 0 < dimensions <= MAX_DIMENSIONS
+    ):
+        msg = f"dimensions must be a positive integer, got: {dimensions!r}"
+        raise ValueError(msg)
+    return dimensions
+
+
+def _usage_count(value: Any) -> int | None:
+    """A token count, or None when a compatible server sends something unusable."""
+    if isinstance(value, str) and value.strip().isdecimal():
+        return int(value)
+    if isinstance(value, bool):
+        return None
+    if isinstance(value, int):
+        return value
+    if isinstance(value, float) and value.is_integer():
+        return int(value)
+    return None
+
+
+def _decode_embedding(value: Any, *, base64_requested: bool) -> list[float]:
+    # With encoding_format="base64" the SDK hands the base64 string through untouched.
+    if isinstance(value, str):
+        if not base64_requested:
+            msg = "a string vector was returned although encoding_format was float"
+            raise TypeError(msg)
+        # validate=True so corrupted input raises instead of being silently truncated;
+        # unpadded input is accepted as Java's decoder accepts it.
+        padded = value + "=" * (-len(value) % 4)

Review Comment:
   Done: padding is now restored only for a completely unpadded value; partially padded input such as `AACAPw=` is passed to the decoder as-is and rejected (the same "malformed embedding" error as other corrupted vectors), matching Java's `Base64.getDecoder()`. A regression test for `AACAPw=` sits next to the unpadded `AACAPw` case.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [integrations][java] Add OpenAI embedding model [flink-agents]

Posted by "wenjin272 (via GitHub)" <gi...@apache.org>.
wenjin272 merged PR #1126:
URL: https://github.com/apache/flink-agents/pull/1126


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [integrations][java] Add OpenAI embedding model [flink-agents]

Posted by "wenjin272 (via GitHub)" <gi...@apache.org>.
wenjin272 commented on code in PR #1126:
URL: https://github.com/apache/flink-agents/pull/1126#discussion_r4036158920


##########
docs/content/docs/development/embedding_models.md:
##########
@@ -453,28 +452,106 @@ class MyAgent(Agent):
             encoding_format="float"
         )
 ```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+```java
+public class MyAgent extends Agent {
+
+    @EmbeddingModelConnection
+    public static ResourceDescriptor openaiConnection() {
+        return ResourceDescriptor.Builder.newBuilder(ResourceName.EmbeddingModel.OPENAI_CONNECTION)
+                .addInitialArgument("api_key", System.getenv("OPENAI_API_KEY"))
+                .addInitialArgument("request_timeout", 30)
+                .addInitialArgument("max_retries", 3)
+                .build();
+    }
+
+    @EmbeddingModelSetup
+    public static ResourceDescriptor openaiEmbedding() {
+        return ResourceDescriptor.Builder.newBuilder(ResourceName.EmbeddingModel.OPENAI_SETUP)
+                .addInitialArgument("connection", "openaiConnection")
+                .addInitialArgument("model", "text-embedding-3-small")
+                .addInitialArgument("dimensions", 512)
+                .build();
+    }
+
+    ...
+}
+```
+{{< /tab >}}
+
+{{< /tabs >}}
 
 #### OpenAIEmbeddingModelConnection Parameters
 
+{{< tabs "OpenAIEmbeddingModelConnection Parameters" >}}
+
+{{< tab "Python" >}}
+
 | Parameter | Type | Default | Description |
 |-----------|------|---------|-------------|
-| `api_key` | str | Required | OpenAI API key for authentication |
+| `api_key` | str | Required | OpenAI API key for authentication; a blank key is passed through to the SDK for unauthenticated OpenAI-compatible servers (the Java connection rejects a blank key) |
 | `base_url` | str | `"https://api.openai.com/v1"` | OpenAI API base URL |
-| `request_timeout` | float | `30.0` | HTTP request timeout in seconds |
+| `request_timeout` | float | `30.0` | HTTP request timeout in seconds; `0` disables the timeout |
 | `max_retries` | int | `3` | Maximum number of retry attempts |
-| `organization` | str | None | Optional organization ID |
-| `project` | str | None | Optional project ID |
+| `organization` | str | None | Optional organization ID; when unset or blank the Python SDK may read `OPENAI_ORG_ID` from the environment (the Java connection sends no organization) |
+| `project` | str | None | Optional project ID; when unset or blank the Python SDK may read `OPENAI_PROJECT_ID` from the environment (the Java connection sends no project) |
+
+{{< /tab >}}
+
+{{< tab "Java" >}}
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `api_key` | String | Required | OpenAI API key for authentication |
+| `base_url` | String | `"https://api.openai.com/v1"` | OpenAI API base URL |
+| `request_timeout` | Number | `30` | HTTP request timeout in seconds; `0` disables the timeout |
+| `max_retries` | Number | `3` | Maximum number of retry attempts, a non-negative integer |
+| `organization` | String | None | Optional organization ID |
+| `project` | String | None | Optional project ID |
+
+{{< /tab >}}
+
+{{< /tabs >}}
 
 #### OpenAIEmbeddingModelSetup Parameters
 
+{{< tabs "OpenAIEmbeddingModelSetup Parameters" >}}
+
+{{< tab "Python" >}}
+
 | Parameter | Type | Default | Description |
 |-----------|------|---------|-------------|
 | `connection` | str | Required | Reference to connection method name |
 | `model` | str | Required | OpenAI embedding model name |
-| `encoding_format` | str | `"float"` | Return format ("float" or "base64") |
+| `encoding_format` | str | `"float"` | Wire format, `"float"` or `"base64"`; base64 responses are decoded to floats |
 | `dimensions` | int | None | Output dimensions (text-embedding-3 models only) |
 | `user` | str | None | End-user identifier for monitoring |
-| `additional_kwargs` | dict | `{}` | Additional parameters for the OpenAI embeddings API |
+| `additional_kwargs` | dict | `{}` | Extra request body properties sent with every embeddings request; keys may not be `model`, `input`, `encoding_format`, `dimensions` or `user`. A per-call `additional_kwargs` replaces this map |
+
+Since 0.4, `additional_kwargs` entries are sent to the API (earlier releases dropped them), an entry that repeats a typed field fails validation instead of overriding it (set the typed argument instead), `model_kwargs` nests them under an `additional_kwargs` key instead of spreading them, and `dimensions` must be an integer.

Review Comment:
   Since 0.x does not guarantee API compatibility, could we keep this section focused on the current contract and remove the implementation-history wording (`Since 0.4`, `earlier releases`, and the `model_kwargs` shape change)? If an upgrade note is still useful, release notes would be a better place for it.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [integrations][java] Add OpenAI embedding model [flink-agents]

Posted by "wenjin272 (via GitHub)" <gi...@apache.org>.
wenjin272 commented on code in PR #1126:
URL: https://github.com/apache/flink-agents/pull/1126#discussion_r4044342115


##########
python/flink_agents/integrations/embedding_models/openai_embedding_model.py:
##########
@@ -31,6 +35,166 @@
 DEFAULT_REQUEST_TIMEOUT = 30.0
 DEFAULT_BASE_URL = "https://api.openai.com/v1"
 DEFAULT_MAX_RETRIES = 3
+DEFAULT_ENCODING_FORMAT = "float"
+# Upper bounds shared with the OpenAI chat models and the Java connection.
+MAX_OPENAI_TIMEOUT_SECONDS = 2_147_483.647
+MAX_OPENAI_RETRIES = 2_147_483_647
+MAX_DIMENSIONS = 2_147_483_647  # int range, as the Java connection requires
+ENCODING_FORMATS = frozenset({"float", "base64"})
+# Typed request fields; additional_kwargs may not repeat them.
+RESERVED_ADDITIONAL_KWARGS = frozenset(
+    {"model", "input", "encoding_format", "dimensions", "user"}
+)
+
+
+def _blank_to_none(value: Any, name: str) -> str | None:
+    """Require a string or None; blank strings are treated as absent, as in Java."""
+    if value is None:
+        return None
+    if not isinstance(value, str):
+        msg = f"{name} must be a string, got: {type(value).__name__}"
+        raise TypeError(msg)
+    return value if value.strip() else None
+
+
+def _check_additional_kwargs(additional_kwargs: Any) -> Dict[str, Any]:
+    """Require a mapping with non-blank string keys and no typed request field."""
+    if not isinstance(additional_kwargs, Mapping):
+        msg = (
+            f"additional_kwargs must be a map, got: {type(additional_kwargs).__name__}"
+        )
+        raise TypeError(msg)
+    # Per-call keys are stringified as Java's Map keys are (the setup field already
+    # requires string keys); null and blank keys are rejected in both languages.
+    if any(key is None or not str(key).strip() for key in additional_kwargs):
+        msg = "additional_kwargs contains an empty key."
+        raise ValueError(msg)
+    additional_kwargs = {str(key): value for key, value in additional_kwargs.items()}
+    collisions = sorted(RESERVED_ADDITIONAL_KWARGS & additional_kwargs.keys())
+    if collisions:
+        msg = (
+            f"additional_kwargs must not contain the typed request fields {collisions}; "
+            "set them through the corresponding setup argument instead."
+        )
+        raise ValueError(msg)
+    return additional_kwargs
+
+
+def _check_encoding_format(encoding_format: Any) -> str:
+    """Validate encoding_format; a blank value means the default, as in Java."""
+    if _blank_to_none(encoding_format, "encoding_format") is None:
+        return DEFAULT_ENCODING_FORMAT
+    if encoding_format not in ENCODING_FORMATS:
+        msg = (
+            f"encoding_format must be one of {sorted(ENCODING_FORMATS)}, "
+            f"got: {encoding_format!r}"
+        )
+        raise ValueError(msg)
+    return encoding_format
+
+
+def _check_model(model: Any) -> str:
+    if not isinstance(model, str) or not model.strip():
+        msg = "OpenAI embedding requires a non-empty 'model' (setup or per-call parameter)."
+        raise ValueError(msg)
+    return model
+
+
+def _check_dimensions(dimensions: Any) -> int | None:
+    """Accept a positive integer (an integral float counts, as in Java)."""
+    if dimensions is None:
+        return None
+    if isinstance(dimensions, float) and dimensions.is_integer():
+        dimensions = int(dimensions)
+    if (
+        isinstance(dimensions, bool)
+        or not isinstance(dimensions, int)
+        or not 0 < dimensions <= MAX_DIMENSIONS
+    ):
+        msg = f"dimensions must be a positive integer, got: {dimensions!r}"
+        raise ValueError(msg)
+    return dimensions
+
+
+def _usage_count(value: Any) -> int | None:
+    """A token count, or None when a compatible server sends something unusable."""
+    if isinstance(value, str) and value.strip().isdecimal():
+        return int(value)
+    if isinstance(value, bool):
+        return None
+    if isinstance(value, int):
+        return value
+    if isinstance(value, float) and value.is_integer():
+        return int(value)
+    return None
+
+
+def _decode_embedding(value: Any, *, base64_requested: bool) -> list[float]:
+    # With encoding_format="base64" the SDK hands the base64 string through untouched.
+    if isinstance(value, str):
+        if not base64_requested:
+            msg = "a string vector was returned although encoding_format was float"
+            raise TypeError(msg)
+        # validate=True so corrupted input raises instead of being silently truncated;
+        # unpadded input is accepted as Java's decoder accepts it.
+        padded = value + "=" * (-len(value) % 4)

Review Comment:
   Could we only restore padding for completely unpadded values? With the current logic, malformed partial padding such as `AACAPw=` is normalized to `AACAPw==` and accepted, while Java's `Base64.getDecoder()` rejects it. This breaks the intended Java/Python parity and weakens the corrupted-response validation. Please keep partial padding invalid and add a regression test for it.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [integrations][java] Add OpenAI embedding model [flink-agents]

Posted by "purushah (via GitHub)" <gi...@apache.org>.
purushah commented on PR #1126:
URL: https://github.com/apache/flink-agents/pull/1126#issuecomment-5725338640

   Commit history consolidated as requested, in this order: (1) `[e2e]` chat/embedding API-key gating and test cleanup, (2) `[integrations][python]` OpenAI embedding refactoring and hardening, (3) `[integrations][java]` OpenAI embedding implementation, registration, documentation and tests. The tree is unchanged apart from the docs wording above. Full Java and Python suites plus the local Flink E2E were rerun on the squashed tree before pushing.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] [integrations][java] Add OpenAI embedding model [flink-agents]

Posted by "purushah (via GitHub)" <gi...@apache.org>.
purushah commented on code in PR #1126:
URL: https://github.com/apache/flink-agents/pull/1126#discussion_r4043907540


##########
docs/content/docs/development/embedding_models.md:
##########
@@ -453,28 +452,106 @@ class MyAgent(Agent):
             encoding_format="float"
         )
 ```
+{{< /tab >}}
+
+{{< tab "Java" >}}
+```java
+public class MyAgent extends Agent {
+
+    @EmbeddingModelConnection
+    public static ResourceDescriptor openaiConnection() {
+        return ResourceDescriptor.Builder.newBuilder(ResourceName.EmbeddingModel.OPENAI_CONNECTION)
+                .addInitialArgument("api_key", System.getenv("OPENAI_API_KEY"))
+                .addInitialArgument("request_timeout", 30)
+                .addInitialArgument("max_retries", 3)
+                .build();
+    }
+
+    @EmbeddingModelSetup
+    public static ResourceDescriptor openaiEmbedding() {
+        return ResourceDescriptor.Builder.newBuilder(ResourceName.EmbeddingModel.OPENAI_SETUP)
+                .addInitialArgument("connection", "openaiConnection")
+                .addInitialArgument("model", "text-embedding-3-small")
+                .addInitialArgument("dimensions", 512)
+                .build();
+    }
+
+    ...
+}
+```
+{{< /tab >}}
+
+{{< /tabs >}}
 
 #### OpenAIEmbeddingModelConnection Parameters
 
+{{< tabs "OpenAIEmbeddingModelConnection Parameters" >}}
+
+{{< tab "Python" >}}
+
 | Parameter | Type | Default | Description |
 |-----------|------|---------|-------------|
-| `api_key` | str | Required | OpenAI API key for authentication |
+| `api_key` | str | Required | OpenAI API key for authentication; a blank key is passed through to the SDK for unauthenticated OpenAI-compatible servers (the Java connection rejects a blank key) |
 | `base_url` | str | `"https://api.openai.com/v1"` | OpenAI API base URL |
-| `request_timeout` | float | `30.0` | HTTP request timeout in seconds |
+| `request_timeout` | float | `30.0` | HTTP request timeout in seconds; `0` disables the timeout |
 | `max_retries` | int | `3` | Maximum number of retry attempts |
-| `organization` | str | None | Optional organization ID |
-| `project` | str | None | Optional project ID |
+| `organization` | str | None | Optional organization ID; when unset or blank the Python SDK may read `OPENAI_ORG_ID` from the environment (the Java connection sends no organization) |
+| `project` | str | None | Optional project ID; when unset or blank the Python SDK may read `OPENAI_PROJECT_ID` from the environment (the Java connection sends no project) |
+
+{{< /tab >}}
+
+{{< tab "Java" >}}
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `api_key` | String | Required | OpenAI API key for authentication |
+| `base_url` | String | `"https://api.openai.com/v1"` | OpenAI API base URL |
+| `request_timeout` | Number | `30` | HTTP request timeout in seconds; `0` disables the timeout |
+| `max_retries` | Number | `3` | Maximum number of retry attempts, a non-negative integer |
+| `organization` | String | None | Optional organization ID |
+| `project` | String | None | Optional project ID |
+
+{{< /tab >}}
+
+{{< /tabs >}}
 
 #### OpenAIEmbeddingModelSetup Parameters
 
+{{< tabs "OpenAIEmbeddingModelSetup Parameters" >}}
+
+{{< tab "Python" >}}
+
 | Parameter | Type | Default | Description |
 |-----------|------|---------|-------------|
 | `connection` | str | Required | Reference to connection method name |
 | `model` | str | Required | OpenAI embedding model name |
-| `encoding_format` | str | `"float"` | Return format ("float" or "base64") |
+| `encoding_format` | str | `"float"` | Wire format, `"float"` or `"base64"`; base64 responses are decoded to floats |
 | `dimensions` | int | None | Output dimensions (text-embedding-3 models only) |
 | `user` | str | None | End-user identifier for monitoring |
-| `additional_kwargs` | dict | `{}` | Additional parameters for the OpenAI embeddings API |
+| `additional_kwargs` | dict | `{}` | Extra request body properties sent with every embeddings request; keys may not be `model`, `input`, `encoding_format`, `dimensions` or `user`. A per-call `additional_kwargs` replaces this map |
+
+Since 0.4, `additional_kwargs` entries are sent to the API (earlier releases dropped them), an entry that repeats a typed field fails validation instead of overriding it (set the typed argument instead), `model_kwargs` nests them under an `additional_kwargs` key instead of spreading them, and `dimensions` must be an integer.

Review Comment:
   Done: the sentence now states the current contract only ("`additional_kwargs` entries are sent to the API as request body properties; an entry that repeats a typed field fails validation (set the typed argument instead), and `dimensions` must be an integer."). The upgrade notes stay in the PR description for the release notes.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscribe@flink.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org