Skip to content

TIKA-4766: minimal typed Document parse contract for tika-grpc (stage 1) - #2961

Draft
krickert wants to merge 21 commits into
apache:mainfrom
ai-pipestream:TIKA-4766-document-contract-minimal
Draft

krickert wants to merge 21 commits into
apache:mainfrom
ai-pipestream:TIKA-4766-document-contract-minimal

Conversation

@krickert

@krickert krickert commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

First slice of the typed Document contract — stage 1 of the sequence proposed by @nddipiazza in #2921: the Document envelope, Dublin Core DocumentMetadata, the lossless tagged metadata tail, and ParseStatus. Generic transformer only; no blocks/markdown, no embedded recursion, no FormatCategory; ParseBytes is deferred to its own JIRA.

The reply is additive. FetchAndParseReply.fields/status are untouched and document is a new field alongside — no client breaks (the "stage the break" approach from #2916). The legacy fields map keeps carrying the flat content until the content tree lands in a later stage.

Tail values mirror Tika's String[]-backed metadata model: every entry is a typed array, tagged by the declared Property element type, so declared integer sequences (pdf:charsPerPage, tiff:BitsPerSample) arrive as int64s per element and dates as Timestamps. Untyped keys stay strings, never guessed; if any element refuses its declared type the whole entry falls back to strings. No reflection anywhere — the mapping is an explicit compile-time table (TikaCoreProperties.TITLE → metadata.title), so reviewing it is reading one file.

Coverage: 31 mapper unit tests (tagging contract, DC mapping, builder edge cases, a registered-transformer test demonstrating the stage-2 mechanism), 10 in-process tika-grpc tests including a unary round trip through the real forked pipes worker, and a live-server e2e case asserting typed booleans/integers/strings over the wire.

Deferred to follow-up PRs per the staged plan: per-format transformers, the structured content tree, embedded-document recursion.

@krickert
krickert marked this pull request as ready for review July 21, 2026 19:39
@krickert

Copy link
Copy Markdown
Contributor Author

@nddipiazza new try -

Minimal scope, good document shape, no reflection. Followed your feedback - most of the code is tests.

The mapping is a greppable, compile-time-checked table (TikaCoreProperties.TITLE → metadata.title)

Let's give it another look?

@tballison

Copy link
Copy Markdown
Contributor

From a quick read, this looks great. I'm struggling with how to migrate from what we have to a more structured approach given what I'm hoping is a 4.0.0 release fairly soon.

I personally don't want to hold 4.0.0 for this, but I do think this is important.

Apologies if you've done this in previous iterations, but would it be possible to keep grpc as is and then work on a tika.v2 service that covers this work and the potential follow on work. We can then release tika.v2 when this is finalized enough.

Is that too complicated?

@krickert and @nddipiazza wdyt?

@krickert

Copy link
Copy Markdown
Contributor Author

From a quick read, this looks great. I'm struggling with how to migrate from what we have to a more structured approach given what I'm hoping is a 4.0.0 release fairly soon.

I personally don't want to hold 4.0.0 for this, but I do think this is important.

Apologies if you've done this in previous iterations, but would it be possible to keep grpc as is and then work on a tika.v2 service that covers this work and the potential follow on work. We can then release tika.v2 when this is finalized enough.

Is that too complicated?

@krickert and @nddipiazza wdyt?

The v2 idea is spot on. The strange lesson I had by transitioning from a java kingdom to a serialized schema IDL is that the models should never break. I used to purposefully break my APIs to force customers to write the data how I wanted them to. Now I see the light.

Protobufs provide an "out" for most of it, and the ultimate out you get is when you break the contract and just add a "v2" so your "v1" shapes can still serialize. Then it'll be up to you to map it. Otherwise data structures are always additive and you deprecate or retire old fields.

So your suggestion is exactly what the "v{n}" package name is for. We can release a new point version of the tika engine separately from the model version since they play well together.

And I don't mind as many iterations as we need. The API shape is something we get stuck with, and the Tika team is the one that defines the shape after parsing.

There are two schools of data structuring that come out of parsing: data shape (the physical look of it like bounding boxes) and then the data surface (NLP analysis, tokens, the chunks plus body, and governed metadata). I've found in parser-land that it's best to separate these two concerns. Docling falls more into the first category (layout-first, NLP second) while tika seems to follow the second (NLP first, layout second). Both sorta flirt with each other as far as data goes, but that tends to be where each parser aims to be.

There are ways to more accurately combine them, but don't ever try to pull an Icarus and fly too close to a unified single model. If you try, you'll end up just angering both camps to use it. The idea is that you share metadata or link between the two data views.

A lot of theory there, but hope this makes sense..

BTW - if you want instant docling-looking data using diskless OCR, this is a grpc service I slapped together that's faster than docling (nvidia/IBM gfx compatible) but returns the same data and this is a grpc service that wraps poi into grpc.

@nddipiazza

nddipiazza commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

@krickert i agree a v2 is needed. we had people using v1 so that was going to be needed regardless because actually tika v3 had a grpc contract that a few groups are using which is compatible with the contract we have in 4.x

@krickert

Copy link
Copy Markdown
Contributor Author

@krickert i agree a v2 is needed. we had people using v1 so that was going to be needed regardless because actually tika v3 had a grpc contract that a few groups are using which is compatible with the contract we have in 4.x

I'll go ahead and make the protos in a new package. I'd encourage you to Mark the old ones as deprecated but I'll leave that up to you.

Are you okay if that's what I do with this pass or is there any additional things you'd like me to change before I start.

@dpol1
dpol1 force-pushed the TIKA-4766-document-contract-minimal branch from 4e04de5 to 69666e2 Compare August 18, 2026 09:33
@dpol1

dpol1 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

rebased on today's main: squashed the commits and fixed the conflicts, plus the API moves (HttpHeaders split, tk: minting, the ClimateForecast rename, CreativeCommons removal). one real fix rode along: the digest key had drifted (SHA256 vs the javaName SHA-256) so origin.sha256 was always empty - now derived from DigestDef.

@dpol1
dpol1 force-pushed the TIKA-4766-document-contract-minimal branch from c431b61 to 80469fc Compare September 2, 2026 11:09
@dpol1

dpol1 commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Rebased on main 1e3d8f8. One thing changed inside the first commit: #3108's parse_context_json gate (buildRequestParseContext) got a package-private overload so the v2 RPCs go through it too, and runFetchAndParse takes the validated ParseContext instead of the two JSON strings. Commits 2 and 3 are byte-identical.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

There are confirmed RPC correctness issues (v2 server-side streaming never completes, and interruption handling can produce no reply) plus a status-mapping misclassification for PARTIAL_TIMEOUT.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Introduces an experimental v2 gRPC parse surface for Apache Tika that returns a typed org.apache.tika.grpc.v2.Document envelope (typed Dublin Core DocumentMetadata, lossless typed extra tail, and ParseStatus) while keeping the existing v1 fields-map API intact, and wires this into the server plus new API/mapper modules and tests.

Changes:

  • Add new tika-grpc-api module defining the v2 protobuf contract (document.proto, tika_v2.proto) and descriptor bundling.
  • Add new tika-grpc-mapper module that maps Tika Metadata into the typed Document contract (typed DC + tagged tail).
  • Update tika-grpc server to host both v1 and v2 services on the same port and add v1/v2 coverage (unit + in-process + e2e).
File summaries
File Description
tika-grpc/src/test/java/org/apache/tika/pipes/grpc/TikaGrpcServerTest.java Extends in-process server tests to exercise v2 typed Document alongside v1 fields behavior.
tika-grpc/src/main/proto/tika.proto Minor comment indentation cleanup in v1 proto.
tika-grpc/src/main/java/org/apache/tika/pipes/grpc/TikaGrpcV2ServerImpl.java Adds v2 gRPC service implementation delegating to existing pipes runtime and building typed Document replies.
tika-grpc/src/main/java/org/apache/tika/pipes/grpc/TikaGrpcServerImpl.java Refactors shared fetch+parse path into a reusable outcome for both v1 and v2 surfaces.
tika-grpc/src/main/java/org/apache/tika/pipes/grpc/TikaGrpcServer.java Registers v2 service alongside v1 in the production server.
tika-grpc/README.md Documents v1 vs v2 surfaces and points to API/mapper modules and build command.
tika-grpc/pom.xml Adds dependencies on new tika-grpc-api and tika-grpc-mapper modules.
tika-grpc-mapper/src/test/java/org/apache/tika/grpc/mapper/transform/MetadataTaggerTest.java Unit tests for tagged-tail typing rules and “never guess” behavior.
tika-grpc-mapper/src/test/java/org/apache/tika/grpc/mapper/transform/GenericDocumentTransformerTest.java Unit tests for generic transformer Dublin Core mapping and consumed-key behavior.
tika-grpc-mapper/src/test/java/org/apache/tika/grpc/mapper/transform/DocumentTransformersTest.java Unit tests for transformer selection/fallback and tail losslessness.
tika-grpc-mapper/src/test/java/org/apache/tika/grpc/mapper/ParseFixtureSupport.java Shared test harness to parse real Tika fixtures and map to Document.
tika-grpc-mapper/src/test/java/org/apache/tika/grpc/mapper/DocumentBuilderTest.java Coverage for DocumentBuilder envelope/status, tail typing, and duplication avoidance.
tika-grpc-mapper/src/main/java/org/apache/tika/grpc/mapper/transform/TransformSupport.java Centralizes common DC field mapping helpers and consumed-key tracking.
tika-grpc-mapper/src/main/java/org/apache/tika/grpc/mapper/transform/MetadataTagger.java Implements typed-array tagging for extra based on Tika declared Property types.
tika-grpc-mapper/src/main/java/org/apache/tika/grpc/mapper/transform/GenericDocumentTransformer.java Always-applicable fallback transformer for DC mapping.
tika-grpc-mapper/src/main/java/org/apache/tika/grpc/mapper/transform/DocumentTransformers.java Transformer registry/fallback runner + single tail append.
tika-grpc-mapper/src/main/java/org/apache/tika/grpc/mapper/transform/DocumentTransformer.java Defines the transformer contract used to map metadata into typed document fields.
tika-grpc-mapper/src/main/java/org/apache/tika/grpc/mapper/DocumentBuilder.java Builds full typed Document (envelope + typed DC + tagged tail + status mapping).
tika-grpc-mapper/pom.xml Introduces new mapper module build + dependencies and test setup.
tika-grpc-api/src/main/proto/org/apache/tika/grpc/v2/tika_v2.proto Defines experimental v2 service endpoints returning typed Document.
tika-grpc-api/src/main/proto/org/apache/tika/grpc/v2/document.proto Defines the typed Document envelope, DocumentMetadata, tagged tail, and ParseStatus.
tika-grpc-api/README.md Documents how clients depend on the v2 API module and the Document shape.
tika-grpc-api/pom.xml Introduces new API module build (protobuf generation + descriptor set packaging).
tika-grpc-api/buf.yaml Adds buf lint/breaking configuration for the new proto module.
tika-e2e-tests/tika-grpc/src/test/java/org/apache/tika/pipes/filesystem/HandlerTypeTest.java Adds live-server e2e coverage asserting typed tail values over the wire and v1 independence.
tika-e2e-tests/tika-grpc/src/test/java/org/apache/tika/pipes/ExternalTestBase.java Extends test fixture copying to include testPDF.pdf for e2e coverage.
tika-bom/pom.xml Adds new gRPC API/mapper artifacts to the BOM.
pom.xml Adds tika-grpc-api and tika-grpc-mapper to the reactor build.
Review details
  • Files reviewed: 27/29 changed files
  • Comments generated: 3
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@dpol1
dpol1 force-pushed the TIKA-4766-document-contract-minimal branch from 80469fc to e690d9f Compare September 11, 2026 07:49
@dpol1
dpol1 force-pushed the TIKA-4766-document-contract-minimal branch from e690d9f to 7b4f9c3 Compare September 24, 2026 16:07
krickert and others added 5 commits October 6, 2026 16:54
…rpc.v2 lives in one module

Signed-off-by: Davide Polato <[email protected]>
…stream

PARTIAL_TIMEOUT is a success-category PipesResult status (the deadline hit,
what was parsed is emitted); the mapper reported it as FAILED.
fetchAndParseServerSideStreaming never called onCompleted(), so a v2
streaming client waited for its deadline after the reply.

Signed-off-by: Davide Polato <[email protected]>
@dpol1
dpol1 force-pushed the TIKA-4766-document-contract-minimal branch from 7b4f9c3 to 70d658c Compare October 7, 2026 07:40
@krickert

Copy link
Copy Markdown
Contributor Author

@tballison can you take another look at this one? This is the one that we discussed a few months back to do into two parts.

@dpol1 has done a great job keeping up with it. It'll allow for external parsers to bring their own data shape and map it to tika without mucking up the need to maintain more objects.

@THausherr
THausherr requested a lite review from Copilot October 23, 2026 02:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

…tus, interrupted calls

- MetadataTagger: a declared-boolean value that is not true/false, yes/no or
  1/0 now falls the whole entry back to strings, like the other declared
  types, instead of arriving as a guessed false.
- DocumentBuilder: PARSE_EXCEPTION_NO_EMIT maps to FAILED. Under the
  ON_PARSE_EXCEPTION.SKIP tika-grpc sends it is a parse that threw and
  carries no metadata, so there is nothing partial to return.
- v1 and v2 FetchAndParse: when the fetch and parse is interrupted there is
  no outcome; close the call with UNAVAILABLE instead of completing a unary
  call without a reply.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@krickert

Copy link
Copy Markdown
Contributor Author

I went over the copilot review and issued the fixes

Here's a summary, followed by the more detailed summary produced by AI:

  • Fixed the booleans fall back to string.
  • Fixed the edge case of a skip vs failed and updated the javadoc
  • Better error handling when paring closed early

AI specifics:

Addressed the Copilot review in ffe0add:

  • Unrecognized booleans fall back to strings (MetadataTagger). parseBoolean now returns null for anything other than true/false, yes/no or 1/0 (any case). tagByDeclaredType(BOOLEAN, ...) then returns null, so the whole entry falls back to strings, as integers, numbers and dates already do, instead of arriving as a guessed false. Tests: booleanParsingAcceptsCommonSpellings now also checks that maybe and N/A give null, and the new unrecognizedBooleanFallsBackToStrings covers the fallback.
  • PARSE_EXCEPTION_NO_EMIT maps to FAILED (DocumentBuilder). tika-grpc sends ON_PARSE_EXCEPTION.SKIP, so this status means the parse threw. It comes back with only the stack trace and no metadata list, so there is nothing partial to return. The Javadoc is updated, and the new parseExceptionWithNothingEmittedIsFailedNotPartial covers it.
  • An interrupted FetchAndParse closes the call with UNAVAILABLE (TikaGrpcServerImpl, TikaGrpcV2ServerImpl). When runFetchAndParse is interrupted it has no outcome. The unary and server-streaming calls used to complete anyway with no reply, and a unary client saw INTERNAL: No value received for unary call. fetchAndParseImpl now closes the call through a shared closeInterrupted and returns false, and the caller skips onCompleted(). This applies to v1 and v2, and to the bidi streams. The new testInterruptedFetchAndParseClosesWithUnavailable interrupts the handler thread before the call (in-process transport, direct executor) and checks that v1 and v2 both send no reply, no completion and one UNAVAILABLE error.

Tested with ./mvnw test -pl tika-grpc-mapper,tika-grpc: 33/33 and 29/29 pass, and checkstyle is clean.

krickert and others added 2 commits October 23, 2026 16:33
buf lint reported 17 errors, all in the new v2 protos (v1 and the pipes
protos are untouched):

- ParseStatus.Status values take the STATUS_ prefix, and the zero value is
  STATUS_UNSPECIFIED.
- The service is TikaV2Service, so the generated class is
  TikaV2ServiceGrpc.
- Each RPC has its own request and response message
  (FetchAndParseRequest/Response, FetchAndParseServerSideStreaming...,
  FetchAndParseBiDirectionalStreaming...). They carry the same fields
  under the same numbers as before, so the wire format does not change.

TikaGrpcV2ServerImpl now builds every reply through one helper, and the
tests, READMEs and CHANGES follow the new names.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@THausherr
THausherr requested a lite review from Copilot October 23, 2026 18:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

5 open issues
9 resolved since last review

Review effort: Lite

Comment on lines +127 to +135
String[] parsedByFull = primary.getValues(TikaCoreProperties.TIKA_PARSED_BY_FULL_SET);
if (parsedByFull == null || parsedByFull.length == 0) {
parsedByFull = primary.getValues(TikaCoreProperties.TIKA_PARSED_BY);
}
for (String parser : parsedByFull) {
if (parser != null && !parser.isBlank()) {
status.addParsersUsed(parser.trim());
}
}
Comment on lines +97 to +105
@Override
public void onError(Throwable throwable) {
LOG.error("Parse error occurred", throwable);
}

@Override
public void onCompleted() {
responseObserver.onCompleted();
}
Comment on lines +47 to +55
message FetchAndParseRequest {
// ID of a fetcher previously saved through the v1 Tika service.
string fetcher_id = 1;
string fetch_key = 2;
string additional_fetch_config_json = 3;
string emitter_id = 4;
// Optional JSON object to configure the ParseContext for this request.
string parse_context_json = 5;
}
Comment on lines +105 to +114
static {
for (Class<?> vocabulary : PROPERTY_VOCABULARIES) {
try {
// A class literal alone does NOT initialize the class; forName(…, true, …) does.
Class.forName(vocabulary.getName(), true, vocabulary.getClassLoader());
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
}
}
Comment on lines +827 to +840
try {
v2.fetchAndParseServerSideStreaming(org.apache.tika.grpc.v2.FetchAndParseServerSideStreamingRequest
.newBuilder()
.setFetcherId(fetcherId)
.setFetchKey(fetchKey)
.build(), observer);

assertEquals(1, replies.size(), "one reply for one fetch key");
assertEquals(PipesResult.RESULT_STATUS.PARSE_SUCCESS.name(),
replies.get(0).getDocument().getStatus().getPipesStatus(),
"the fixture must actually parse, or this test proves nothing");
assertEquals(0, errors.get(), "no error on the happy path");
assertEquals(1, completions.get(),
"server streaming must send a terminal signal");
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Comment thread tika-grpc-api/README.md Resolved
Keeps both sides of the TikaGrpcServerTest addition (the v2 terminal
signal tests and the TIKA-5001 file store test) and files the TIKA-4766
CHANGES entry under SERVER, PIPES AND OPERATIONS.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01C3uD3iXWuDQ3wg2AiEbD6t
v2 returns the Document and never emits, so the field was accepted and
silently ignored. Field 4 and the name are reserved so they can return later.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01BGnDTWcJiFwxgpvRQGSJ6L
- v2 FetchAndParse puts the pipes message (fetch failure, parse exception,
  timeout, crash) or the container stack trace of a partial parse into
  ParseStatus.errors instead of a generic "No metadata returned".
- A client cancel interrupts the parse running for the call (through the call
  Context, which fires while the handler is blocked), so a cancelled call no
  longer holds a pipes worker; later requests on a cancelled call do not start.
- The bidirectional stream drops requests after it closed the call with an
  error instead of calling onNext/onCompleted on a closed call.
- v2 answers NOT_FOUND for an unknown fetcher and INTERNAL (cause logged, not
  returned) for unexpected failures.
- Mapper: a multi-valued title/description/rights/created/modified keeps every
  value in the tagged tail instead of dropping all but the first.
- Tests for a failing document, a blank document, EMPTY_OUTPUT, cancellation
  and bidi after an error.

The ParseBytes half of this change goes on the TIKA-4795 branch.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01C3uD3iXWuDQ3wg2AiEbD6t
.github/workflows/buf.yml runs buf lint on both proto modules and buf
breaking against the PR base branch (npx @bufbuild/buf, no extra action).
The breaking check compares <module>/src/main/proto so a base without a
buf.yaml doesn't report every file as deleted.

tika-grpc/buf.yaml covers the v1 tika.proto: STANDARD minus the six
naming rules its wire API can't meet without breaking existing clients,
each commented. v1 is frozen; behaviour changes go to v2. The v2 module
(tika-grpc-api) passes STANDARD with no exceptions; its unused
googleapis dep is dropped so lint needs no BSR access.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01C3uD3h7V1SgZzdVcFGJK8q
… on cancel

A busy pool, a pipes runtime or fetcher that failed to start, and a fetcher
the worker cannot find now close a v2 call with RESOURCE_EXHAUSTED,
UNAVAILABLE or NOT_FOUND, so retry policies keyed on status codes see them.
A document the worker timed out, ran out of memory or crashed on stays a
STATUS_FAILED Document.

Parses take a permit per pipes client before asking the pipes parser for a
client, so waiting for a worker and running on one are told apart. A
cancelled or expired v2 call stops waiting at once; a parse already on a
worker finishes and its result is dropped, instead of being interrupted,
which in per-client mode restarted the worker JVM. grpc.cancelRunningParse
restores the interrupt.

v2 errors carry the first line of the pipes diagnostic, cut to 1024
characters, unless parse-context.exception-reporting is configured.

v1 replies are unchanged: pool exhaustion is still an in-band
CLIENT_UNAVAILABLE_WITHIN_MS.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01C3uD3mcUt3NdoxRq6LzNYB
- A fetcher that does not exist is NOT_FOUND; one that exists but cannot
  be built or read from its store is UNAVAILABLE (was NOT_FOUND for
  both). additional_fetch_config_json for a missing fetcher is NOT_FOUND,
  checked before the config is merged.
- v1 error_message for fetch failures is scrubbed to the first line, as
  v2's errors are, unless parse-context.exception-reporting is set.
- The server loads pipes config with PipesConfig.load, so the parent sees
  exception-reporting and timeout limits for the results it builds.
- Cancellation.end always clears the thread's interrupt, so a shutdown
  interrupt does not leak into the next call on a pooled thread.
- A FAILED or PARTIAL Document always carries at least one error.
- SourceOrigin.byte_size is optional: unset is unknown, 0 is empty.
- buf workflow: checkout pinned by SHA, buf binary checked by sha256,
  breaking check also on push, base buf.yaml used for the check.
- tika.proto is byte-identical to main.
- Tests for UNAVAILABLE fetchers, FETCHER_INITIALIZATION_EXCEPTION, status
  codes on server-streaming and bidi, CRLF diagnostics, interrupt
  clearing, the v1 scrub and old clients sending emitter_id.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01C3uD3mcUt3NdoxRq6LzNYB
resolveFetcherId looks the fetcher up once and returns its configured id, so
the fetch-and-parse paths (v1 and v2) share a single call site that will
release a lease once getFetcher returns one (TIKA-5001 step 2).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01C3uD3mcUt3NdoxRq6LzNYB
tika-grpc/docs/v2-document-contract.md explains why v2 exists, how values
get their types, which outcomes are a Document status and which a gRPC
status, cancel behaviour, and how buf keeps v1 frozen. The README links it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01C3uD3jQgutA3ZS6Ttgy8hR
@tballison

Copy link
Copy Markdown
Contributor

I pulled out the v1 fixes here: #3448

@tballison

Copy link
Copy Markdown
Contributor

Let me know what you think of this? #3451

Does it get you enough of what you need?

@krickert

krickert commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Let me know what you think of this? #3451

Does it get you enough of what you need?

Did you get a chance to see my latest reply from the other thread?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants