Skip to content

Break api into 2 parts#36

Open
edeandrea wants to merge 3 commits into
langfuse:mainfrom
edeandrea:separate-api
Open

Break api into 2 parts#36
edeandrea wants to merge 3 commits into
langfuse:mainfrom
edeandrea:separate-api

Conversation

@edeandrea
Copy link
Copy Markdown

@edeandrea edeandrea commented May 28, 2026

Summary

Separates the Langfuse Java SDK into a multi-module Maven project with generated API interfaces, a reference HTTP client, and Testcontainers support for integration testing.

It is mostly dependency-free. It does not use/require OkHttp, instead using Java's built-in HttpClient. It requires either Jackson 2 or 3, but does not force a user into one over the other, nor does it force which version to use.

The API has been completely decoupled to the underlying http transport, meaning the api can be re-used with other http transports.

New modules

  • langfuse-java-api -- API interfaces, model types, and SPI generated from the Langfuse OpenAPI spec using openapi-generator with custom Mustache templates. Most code is generated at
    build time and never checked into version control.
  • langfuse-java-client -- Reference HTTP client built on java.net.http.HttpClient with dual Jackson 2/3 support, request/response logging with sensitive header masking, and automatic HTTP/1.1 fallback for plain HTTP connections.
  • langfuse-java-testcontainers -- LangfuseContainer that orchestrates a full Langfuse environment (PostgreSQL, ClickHouse, Redis, MinIO, web server, worker) for testing via Testcontainers.
  • langfuse-java-legacy -- Existing fern-generated SDK preserved as-is for backward compatibility.

Key design decisions

  • Build-time generation over checked-in code: Uses the Fern-generated OpenAPI spec as input to openapi-generator with custom templates, because Fern's generated Java code lacks builder patterns, dual Jackson support, request parameter objects, and JPMS modules.
  • SPI-based client discovery: LangfuseApi.builder() uses ServiceLoader to find the client implementation, allowing framework-specific implementations (Spring, Quarkus) without depending on the reference client.
  • Request parameter objects: All API methods with parameters use a request object with a builder (via useSingleRequestParameter), eliminating long parameter lists (e.g. scoresGetMany had 21 parameters).
  • Protected constructors + builders: Model constructors are protected; all creation goes through builder().
  • Empty container defaults: All List/Map/Set fields default to empty (never null), with @JsonInclude(NON_EMPTY) at the class level so empty optional containers are omitted from serialized JSON.
  • Bean validation: @NotNull, @Size, etc. annotations generated from OpenAPI schema constraints.
  • Dual Jackson 2 + 3: Models carry both com.fasterxml.jackson and tools.jackson annotations. The client auto-detects which Jackson version is on the classpath at runtime.
  • Sync + async APIs: Every API operation has both a synchronous and CompletionStage-based async variant.

Testcontainers

  • Parallel infrastructure startup via Startables.deepStart
  • Web and worker containers start in parallel after infrastructure is ready
  • Singleton container pattern for sharing across test classes
  • getAllLogs() returns a Map<String, String> of all container logs for diagnostics
  • Configurable via builder API with sensible defaults aligned to docker-compose.yml
  • Optional<Duration> for ingestion queue delay and ClickHouse write interval settings

Test coverage

tests (sync + async) across 19 API areas, all running against a real Langfuse environment via Testcontainers:

Health, Ingestion, Traces, Prompts, Prompt Versions, Scores, Score Configs, Datasets, Dataset Items, Dataset Run Items, Models, Projects, Observations, Sessions, Comments, Annotation Queues, plus LegacyScoreV1 and LegacyObservationsV1 used as helpers.

Other changes

  • JPMS module-info.java for API and client modules
  • Request/response logging with logRequests(), logResponses(), prettyPrint() builder options
  • HTTP/1.1 fallback in ApiClient for plain http URLs (avoids HTTP/2 upgrade issues)
  • README documentation for root project and all three new modules
  • Awaitility added for eventual-consistency assertions in tests
  • Container labels (com.langfuse.testcontainers.service) on all Docker containers

Closes #31

@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 28, 2026

CLA assistant check
All committers have signed the CLA.

@edeandrea
Copy link
Copy Markdown
Author

edeandrea commented May 28, 2026

Hi @Steffen911 Please see my initial implementation of the separation. Its a lot and I'm happy to iterate on it. The biggest thing I tried to achieve is to keep as much build-time auto-generation as possible.

Fern itself is not very customizeable, so I started from an OpenAPI document, which can be generated from fern. There is very little code actually checked into version control - most of the power comes from the fact it is generated at build time.

Happy to answer any questions you may have.

You might think that 491 file changes is a lot, but more than half of that is simply moving what was there (generated by fern) into a subdirectory so it can be preserved.

…guration options, and testing setup with Testcontainers

Add integration tests for all major APIs, including async and legacy modes

Refactor configuration handling and improve ingestion-related tests

Add async tests for all major APIs

Enable Bean Validation support in `langfuse-java-api`.

Define module-info files for `langfuse-java-api` and `langfuse-java-client`.

Tests

Adding request/response logging

Adding testcontainers

Adding additional apis

Initial separation
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.

Break api into 2 parts

2 participants