This is an automated email from the ASF dual-hosted git repository.
tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git
The following commit(s) were added to refs/heads/main by this push:
new 18b948be33 TIKA-4889: a request may not supply the embedding filters;
they are deprecated (#3171)
18b948be33 is described below
commit 18b948be339940f305948b7ad52eb23e34edcb9c
Author: Tim Allison <[email protected]>
AuthorDate: Tue Sep 15 07:38:26 2026 -0400
TIKA-4889: a request may not supply the embedding filters; they are
deprecated (#3171)
---
CHANGES.txt | 8 ++++++++
.../ROOT/pages/configuration/metadata-filters.adoc | 11 +++++++----
.../pages/migration-to-4x/migrating-to-4x.adoc | 10 ++++++++++
.../tika/inference/AbstractEmbeddingFilter.java | 14 ++++++++++++++
.../apache/tika/inference/JinaEmbeddingFilter.java | 2 ++
.../tika/inference/OpenAIEmbeddingFilter.java | 2 ++
.../tika/serialization/ComponentNameResolver.java | 21 +++++++++++++++++++++
.../serdes/ParseContextDeserializer.java | 3 ++-
.../WireRestrictedParseContextTest.java | 10 ++++++++++
.../tika/server/standard/TikaResourceTest.java | 22 ++++++++++++++++++++++
10 files changed, 98 insertions(+), 5 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index 5f0746ff9c..31bfb2e562 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,13 @@
Release 4.1.0 - unreleased
+ * A request can no longer supply openai-embedding-filter or
+ jina-embedding-filter in a per-request metadata-filters list: 4.0.x
+ built the filter from the request, endpoint and key included, and
+ posted the document's text there. The flat per-request skipEmbedding
+ override is unchanged. The two filters are deprecated; configure the
+ endpoint under "engines" and bind it with a TEXT inference binding.
+ Removal in 4.2.0 (TIKA-4889).
+
* PDF: "pdf-parser": {"text": ...} says where a page's text comes from:
EXTRACT, AUTO (default), EXTRACT_AND_OCR, OCR, or the new NONE, which
writes no text from any source while pages are still rendered for
diff --git a/docs/modules/ROOT/pages/configuration/metadata-filters.adoc
b/docs/modules/ROOT/pages/configuration/metadata-filters.adoc
index dc19f1fd8b..e1770b4c47 100644
--- a/docs/modules/ROOT/pages/configuration/metadata-filters.adoc
+++ b/docs/modules/ROOT/pages/configuration/metadata-filters.adoc
@@ -115,10 +115,13 @@ Adding the module to the classpath is enough to make the
name resolvable.
|`openai-embedding-filter`, `jina-embedding-filter`
|`tika-inference`
-|Chunks `tk:content` and calls an embeddings endpoint, putting the vectors on
`tk:chunks`, one
-document per request. Since 4.1.0 a `TEXT`
xref:configuration/inference.adoc#text[inference
-binding] does the same for the whole tree in batched requests; it runs before
this chain and
-is not part of it. See xref:migration-to-4x/chunk-strategies.adoc[Chunk
Strategies].
+|*Deprecated in 4.1.0, removed in 4.2.0.* Chunks `tk:content` and calls an
embeddings endpoint,
+putting the vectors on `tk:chunks`, one document per request. Configure the
endpoint under
+`engines` and bind it with a `TEXT`
xref:configuration/inference.adoc#text[inference binding]
+instead: the whole tree in batched requests, before this chain. A request may
not supply these
+filters in a `metadata-filters` list (they would post text to a caller-chosen
endpoint); the
+flat `skipEmbedding` override below still works. See
+xref:migration-to-4x/chunk-strategies.adoc[Chunk Strategies].
|`charsoup-metadata-filter`, `optimaize-metadata-filter`,
`open-nlp-metadata-filter`
|`tika-langdetect-charsoup`, `-optimaize`, `-opennlp`
diff --git a/docs/modules/ROOT/pages/migration-to-4x/migrating-to-4x.adoc
b/docs/modules/ROOT/pages/migration-to-4x/migrating-to-4x.adoc
index 6799a2579b..f74222b56a 100644
--- a/docs/modules/ROOT/pages/migration-to-4x/migrating-to-4x.adoc
+++ b/docs/modules/ROOT/pages/migration-to-4x/migrating-to-4x.adoc
@@ -256,6 +256,16 @@ What changes for you:
* VLM parsers gain `textRecognizer` (default `true`); set it to `false` for a
captioning or tagging
prompt so the PDF parser's `AUTO` OCR never replaces extracted text with the
VLM's output.
+=== Embedding filters deprecated (4.1.0+)
+
+`openai-embedding-filter` and `jina-embedding-filter` are deprecated and will
be removed in
+4.2.0. Configure the endpoint once under `engines` and bind it with a `TEXT`
+xref:configuration/inference.adoc#text[inference binding]; it embeds the whole
document tree in
+batched requests. Until then they keep working from the server-side config. A
request can no
+longer name them in a per-request `metadata-filters` list (4.0.x accepted a
caller-supplied
+`baseUrl` there); the flat per-request `{"openai-embedding-filter":
{"skipEmbedding": true}}`
+form is unchanged.
+
=== PDF text policy: `ocr.strategy` becomes `text` (4.1.0+)
The PDF parser's `"ocr": {"strategy": ...}` is now the parser's own `"text"`,
since it decides
diff --git
a/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/AbstractEmbeddingFilter.java
b/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/AbstractEmbeddingFilter.java
index b134dd0da8..a40448189b 100644
---
a/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/AbstractEmbeddingFilter.java
+++
b/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/AbstractEmbeddingFilter.java
@@ -55,9 +55,15 @@ import org.apache.tika.parser.ParseContext;
* Thread safety: instances are safe for concurrent {@link #filter} calls once
* fully constructed. Setters must not be called concurrently with
* {@link #filter}.
+ *
+ * @deprecated since 4.1.0, removed in 4.2.0: configure the endpoint once under
+ * {@code "engines"} and bind it with a {@code TEXT} {@code "inference"}
binding, which embeds
+ * the whole document tree in batched requests.
*/
+@Deprecated
public abstract class AbstractEmbeddingFilter extends MetadataFilter
implements SelfConfiguring {
+
private static final long serialVersionUID = 1L;
private static final String REQUIRED_HANDLER_TYPE = "MARKDOWN";
@@ -68,10 +74,18 @@ public abstract class AbstractEmbeddingFilter extends
MetadataFilter implements
private InferenceConfig defaultConfig = new InferenceConfig();
protected AbstractEmbeddingFilter() {
+ warnDeprecated();
}
protected AbstractEmbeddingFilter(InferenceConfig config) {
this.defaultConfig = config;
+ warnDeprecated();
+ }
+
+ private void warnDeprecated() {
+ LOG.warn("{} is deprecated since 4.1.0 and will be removed in 4.2.0:
configure the "
+ + "endpoint under \"engines\" and bind it with a TEXT
\"inference\" binding",
+ getClass().getSimpleName());
}
/**
diff --git
a/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/JinaEmbeddingFilter.java
b/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/JinaEmbeddingFilter.java
index 2985c77498..e684ed16fb 100644
---
a/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/JinaEmbeddingFilter.java
+++
b/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/JinaEmbeddingFilter.java
@@ -39,7 +39,9 @@ import org.apache.tika.utils.StringUtils;
* Configuration key: {@code "jina-embedding-filter"}
*
* @since Apache Tika 4.0
+ * @deprecated since 4.1.0, removed in 4.2.0; see {@link
AbstractEmbeddingFilter}.
*/
+@Deprecated
@TikaComponent(name = "jina-embedding-filter", spi = false)
public class JinaEmbeddingFilter extends OpenAIEmbeddingFilter {
diff --git
a/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/OpenAIEmbeddingFilter.java
b/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/OpenAIEmbeddingFilter.java
index 54afc1712b..c03bb28ac5 100644
---
a/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/OpenAIEmbeddingFilter.java
+++
b/tika-parsers/tika-parsers-ml/tika-inference/src/main/java/org/apache/tika/inference/OpenAIEmbeddingFilter.java
@@ -45,7 +45,9 @@ import org.apache.tika.utils.StringUtils;
* Configuration key: {@code "openai-embedding-filter"}
*
* @since Apache Tika 4.0
+ * @deprecated since 4.1.0, removed in 4.2.0; see {@link
AbstractEmbeddingFilter}.
*/
+@Deprecated
@TikaComponent(name = "openai-embedding-filter", spi = false)
public class OpenAIEmbeddingFilter extends AbstractEmbeddingFilter {
diff --git
a/tika-serialization/src/main/java/org/apache/tika/serialization/ComponentNameResolver.java
b/tika-serialization/src/main/java/org/apache/tika/serialization/ComponentNameResolver.java
index 91a1872d7b..c73d67c293 100644
---
a/tika-serialization/src/main/java/org/apache/tika/serialization/ComponentNameResolver.java
+++
b/tika-serialization/src/main/java/org/apache/tika/serialization/ComponentNameResolver.java
@@ -96,6 +96,14 @@ public final class ComponentNameResolver {
* either: operator policy a caller must not be able to relax.
*/
private static final Set<Class<?>> WIRE_BLOCKED_CONFIG_KEYS = new
HashSet<>();
+ /**
+ * Implementations of a wire-allowed type that break its promise: a wire
request may name
+ * them nowhere it could instantiate one. The embedding filters POST
document text to a
+ * configured endpoint, so a request must not supply the endpoint. Their
flat per-request
+ * config ({@code skipEmbedding}) is unaffected: it tunes the loaded
instance and is
+ * guarded by the filter's RuntimeConfig.
+ */
+ private static final Set<String> WIRE_BLOCKED_COMPONENT_NAMES = new
HashSet<>();
static {
// Allowed: bounded to this request's metadata/output; no exec or IO.
@@ -115,6 +123,9 @@ public final class ComponentNameResolver {
WIRE_BLOCKED_CONTEXT_KEYS.add(EmbeddedDocumentExtractor.class);
WIRE_BLOCKED_CONFIG_KEYS.add(ExceptionReporting.class);
+
+ WIRE_BLOCKED_COMPONENT_NAMES.add("openai-embedding-filter");
+ WIRE_BLOCKED_COMPONENT_NAMES.add("jina-embedding-filter");
}
private static final Map<String, ComponentRegistry> REGISTRIES = new
ConcurrentHashMap<>();
@@ -311,6 +322,16 @@ public final class ComponentNameResolver {
return Collections.unmodifiableSet(WIRE_INSTANTIABLE_CONTEXT_KEYS);
}
+ /** Component names a wire ParseContext may not instantiate although their
type is allowed. */
+ public static Set<String> getWireBlockedComponentNames() {
+ return Collections.unmodifiableSet(WIRE_BLOCKED_COMPONENT_NAMES);
+ }
+
+ /** True if a wire ParseContext must not instantiate this component by
name. */
+ public static boolean isWireBlockedName(String friendlyName) {
+ return WIRE_BLOCKED_COMPONENT_NAMES.contains(friendlyName);
+ }
+
/** True if a wire ParseContext may bind this context-key type
(default-deny). */
public static boolean isWireInstantiable(Class<?> contextKey) {
return WIRE_INSTANTIABLE_CONTEXT_KEYS.contains(contextKey);
diff --git
a/tika-serialization/src/main/java/org/apache/tika/serialization/serdes/ParseContextDeserializer.java
b/tika-serialization/src/main/java/org/apache/tika/serialization/serdes/ParseContextDeserializer.java
index cd3da8d8c7..9b79b412ce 100644
---
a/tika-serialization/src/main/java/org/apache/tika/serialization/serdes/ParseContextDeserializer.java
+++
b/tika-serialization/src/main/java/org/apache/tika/serialization/serdes/ParseContextDeserializer.java
@@ -221,7 +221,8 @@ public class ParseContextDeserializer extends
JsonDeserializer<ParseContext> {
return;
}
Class<?> contextKey =
ComponentNameResolver.determineContextKey(infoOpt.get());
- if (ComponentNameResolver.isWireBlocked(contextKey)) {
+ if (ComponentNameResolver.isWireBlocked(contextKey)
+ || ComponentNameResolver.isWireBlockedName(name)) {
throw new IOException(wireBlockedMessage(name, contextKey));
}
}
diff --git
a/tika-serialization/src/test/java/org/apache/tika/serialization/WireRestrictedParseContextTest.java
b/tika-serialization/src/test/java/org/apache/tika/serialization/WireRestrictedParseContextTest.java
index 8282c82d4c..8d5a41864d 100644
---
a/tika-serialization/src/test/java/org/apache/tika/serialization/WireRestrictedParseContextTest.java
+++
b/tika-serialization/src/test/java/org/apache/tika/serialization/WireRestrictedParseContextTest.java
@@ -71,6 +71,16 @@ public class WireRestrictedParseContextTest {
}
}
+ /** Names blocked although their type is allowed: the embedding filters,
which do network IO. */
+ @Test
+ public void embeddingFiltersAreBlockedByName() {
+ Set<String> names =
ComponentNameResolver.getWireBlockedComponentNames();
+ assertTrue(names.contains("openai-embedding-filter"));
+ assertTrue(names.contains("jina-embedding-filter"));
+
assertTrue(ComponentNameResolver.isWireBlockedName("openai-embedding-filter"));
+
assertFalse(ComponentNameResolver.isWireBlockedName("include-field-metadata-filter"));
+ }
+
@Test
public void restrictedRejectsTypedParserInjection() {
// A blocked component nested under a wrapper key must be refused
regardless of the wrapper.
diff --git
a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaResourceTest.java
b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaResourceTest.java
index 3b510f5635..3ad98d20e2 100644
---
a/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaResourceTest.java
+++
b/tika-server/tika-server-standard/src/test/java/org/apache/tika/server/standard/TikaResourceTest.java
@@ -17,6 +17,7 @@
package org.apache.tika.server.standard;
import static org.apache.cxf.helpers.HttpHeaderHelper.CONTENT_ENCODING;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
@@ -448,6 +449,27 @@ public class TikaResourceTest extends CXFTestBase {
"expected wire-blocked rejection, got: " + rootMessage(e));
}
+ /**
+ * MetadataFilter is wire-instantiable on the promise that no
implementation does IO; the
+ * embedding filters POST document text to their baseUrl, so they are
blocked by name.
+ */
+ @Test
+ public void testConfigRejectsEmbeddingFilterFromTheWire() {
+ TikaObjectMapperFactory.getMapper();
+ for (String name : new String[]{"openai-embedding-filter",
"jina-embedding-filter"}) {
+ String configJson = "{\"metadata-filters\":[{\"" + name
+ + "\":{\"baseUrl\":\"http://127.0.0.1:1/\"}}]}";
+ Exception e = assertThrows(Exception.class,
+ () -> TikaResource.mergeParseContextFromConfig(configJson,
new ParseContext()),
+ name);
+ assertTrue(rootMessage(e).contains("may not be supplied via a
request parseContext"),
+ name + ": expected wire-blocked rejection, got: " +
rootMessage(e));
+ }
+ // the flat per-request config tunes the loaded filter and stays
allowed
+ assertDoesNotThrow(() -> TikaResource.mergeParseContextFromConfig(
+ "{\"openai-embedding-filter\":{\"skipEmbedding\":true}}", new
ParseContext()));
+ }
+
private static String rootMessage(Throwable t) {
Throwable r = t;
while (r.getCause() != null && r.getCause() != r) {