Skip to content
Merged
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
47 changes: 43 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,23 @@
This repository contains an auto-generated Langfuse API client for Java based on our [API specification](https://github.com/langfuse/langfuse/tree/main/fern/apis/server).
See the [Langfuse API reference](https://api.reference.langfuse.com) for more details on the available endpoints.

**Note:** We recommend to solve tracing via the [OpenTelemetry Instrumentation](https://langfuse.com/docs/opentelemetry/get-started) instead of using the Ingestion API directly. You can use the [OpenTelemetry Java SDK](https://github.com/open-telemetry/opentelemetry-java) and export spans to the [Langfuse OTel endpoint](https://langfuse.com/integrations/native/opentelemetry).
This allows for a more detailed and standardized tracing experience without the need to handle batching and updates internally.
Check out our [Spring AI Example](https://langfuse.com/docs/integrations/spring-ai) for more details.
## Use OpenTelemetry for tracing

**OpenTelemetry is the recommended — and, going forward, the only supported — way to send tracing data to Langfuse from Java.**
This client no longer exposes the legacy ingestion API; it is deprecated and is removed in [Langfuse v4](https://langfuse.com/docs/v4).

Instrument your application with the [OpenTelemetry Java SDK](https://github.com/open-telemetry/opentelemetry-java) and export spans over OTLP/HTTP to the [Langfuse OTel endpoint](https://langfuse.com/integrations/native/opentelemetry):

```bash
OTEL_EXPORTER_OTLP_ENDPOINT="https://cloud.langfuse.com/api/public/otel"
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic ${AUTH_STRING},x-langfuse-ingestion-version=4"
```

The `x-langfuse-ingestion-version: 4` header selects the v4 ingestion path. It is not sufficient on its own — spans also have to follow the v4 span format. Read [Migrate custom ingestion to Langfuse v4](https://langfuse.com/integrations/native/opentelemetry/migration-to-v4) before switching production traffic; it covers the [v4-ready checklist](https://langfuse.com/integrations/native/opentelemetry/migration-to-v4#v4-ready-ingestion-checklist), the [legacy event → OTel span mapping](https://langfuse.com/integrations/native/opentelemetry/migration-to-v4#migrate-from-the-legacy-ingestion-api), and [propagating trace context to every observation](https://langfuse.com/integrations/native/opentelemetry/migration-to-v4#propagate-trace-context-to-observations).

Going through OpenTelemetry also means you do not have to handle batching, retries, or observation updates yourself. Check out our [Spring AI example](https://langfuse.com/integrations/frameworks/spring-ai) for a full setup.

Use this client for everything that is *not* tracing ingestion: prompts, datasets, scores, models, comments, annotation queues, and the reading APIs.

## Installation

Expand Down Expand Up @@ -50,6 +64,30 @@ try {
}
```

## Langfuse v4: removed endpoints

Langfuse v4 uses an [observations-first data model](https://langfuse.com/docs/v4). The endpoints built on the v3 model are deprecated: Langfuse Cloud serves them until **November 16, 2026**, and they become unavailable in self-hosted deployments as soon as those upgrade to v4.

Those endpoints have been **removed from this client** so that it only exposes the API surface that survives the v4 cutover. The canonical, per-endpoint migration reference is [Migration of deprecated APIs](https://langfuse.com/faq/all/deprecated-api-migration); the [Versions & Compatibility matrix](https://langfuse.com/docs/compatibility) lists which endpoints work against which server version, and [Make your project ready for the upgrade to Langfuse v4](https://langfuse.com/faq/all/upgrade-to-langfuse-v4) is the step-by-step upgrade guide.

| Removed from this client | Deprecated endpoint | Replacement |
| --------------------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client.ingestion().batch(...)` | `POST /api/public/ingestion` | [OpenTelemetry ingestion](https://langfuse.com/faq/all/deprecated-api-migration#ingestion) — see above |
| `client.observations().get(...)` / `.getMany(...)` | `GET /api/public/observations`, `/observations/{id}` | `client.observationsV2().getMany(...)` ([Observations API v2](https://langfuse.com/faq/all/deprecated-api-migration#observations)) |
| `client.trace().get(...)` / `.list(...)` | `GET /api/public/traces`, `/traces/{id}` | `client.observationsV2().getMany(...)` filtered by `traceId` ([Traces](https://langfuse.com/faq/all/deprecated-api-migration#traces)) |
| `client.sessions().get(...)` / `.list(...)` | `GET /api/public/sessions`, `/sessions/{id}` | `client.observationsV2().getMany(...)` filtered by `sessionId` ([Sessions](https://langfuse.com/faq/all/deprecated-api-migration#sessions)) |
| `client.metrics().metrics(...)` | `GET /api/public/metrics` | `client.metricsV2().metrics(...)` ([Metrics API v2](https://langfuse.com/faq/all/deprecated-api-migration#metrics)) |
| `client.scoreV2().get(...)` / `.getById(...)` | `GET /api/public/v2/scores`, `/v2/scores/{id}` | [Scores API v3](https://langfuse.com/faq/all/deprecated-api-migration#scores) — **not generated yet**, call `GET /api/public/v3/scores` directly for now |
| `client.datasets().getRuns(...)` / `.getRun(...)` / `.deleteRun(...)` | `GET`/`DELETE /api/public/datasets/{name}/runs[/{runName}]` | [Experiments API](https://langfuse.com/faq/all/deprecated-api-migration#dataset-runs) — **not generated yet**, call `GET /api/public/experiments` directly for now |
| `client.datasetRunItems().create(...)` / `.list(...)` | `POST`/`GET /api/public/dataset-run-items` | [Experiment Items API](https://langfuse.com/faq/all/deprecated-api-migration#dataset-runs) / OTel experiment attributes — **not generated yet** |

Notes:

- **Score writes are unaffected.** `client.score().create(...)` (`POST /api/public/scores`) and `client.score().delete(...)` stay supported after the v4 cutover. Only score *reads* move to v3.
- **Trace deletion is not deprecated.** `client.trace().delete(...)` and `client.trace().deleteMultiple(...)` are kept; they are also how you delete experiment data now that `DELETE /datasets/{name}/runs/{runName}` is gone.
- **Datasets themselves are not deprecated.** `client.datasets()` still exposes the `/api/public/v2/datasets` endpoints; only the dataset *run* endpoints were removed.
- The `/api/public/v3/scores`, `/api/public/experiments`, and `/api/public/experiment-items` endpoints exist in the Langfuse API but are not part of this client yet. They will be picked up by the next regeneration; until then, call them directly.

## Testing

### Unit tests
Expand Down Expand Up @@ -117,4 +155,5 @@ To publish to Maven Central, you need to configure the following secrets in your
4. Manually set the `package` across all files to `com.langfuse.client`.
5. Verify that `LangfuseClientBuilder.setAuthentication()` uses `Basic` auth (not `Bearer`).
6. Adjust Javadoc strings with HTML properties as the apidocs package does not support them.
7. Commit the changes in langfuse-java and push them to the repository.
7. Re-apply the deprecated-endpoint prune. Regeneration reintroduces every endpoint marked `availability: status: deprecated` in the API definition — the ones listed under [Langfuse v4: removed endpoints](#langfuse-v4-removed-endpoints). Drop those resource packages and methods again, remove the now-orphaned types under `resources/commons/types`, and unwire them from `LangfuseClient`/`AsyncLangfuseClient`.
8. Commit the changes in langfuse-java and push them to the repository.
48 changes: 0 additions & 48 deletions src/main/java/com/langfuse/client/AsyncLangfuseClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,12 @@
import com.langfuse.client.resources.blobstorageintegrations.AsyncBlobStorageIntegrationsClient;
import com.langfuse.client.resources.comments.AsyncCommentsClient;
import com.langfuse.client.resources.datasetitems.AsyncDatasetItemsClient;
import com.langfuse.client.resources.datasetrunitems.AsyncDatasetRunItemsClient;
import com.langfuse.client.resources.datasets.AsyncDatasetsClient;
import com.langfuse.client.resources.health.AsyncHealthClient;
import com.langfuse.client.resources.ingestion.AsyncIngestionClient;
import com.langfuse.client.resources.llmconnections.AsyncLlmConnectionsClient;
import com.langfuse.client.resources.media.AsyncMediaClient;
import com.langfuse.client.resources.metrics.AsyncMetricsClient;
import com.langfuse.client.resources.metricsv2.AsyncMetricsV2Client;
import com.langfuse.client.resources.models.AsyncModelsClient;
import com.langfuse.client.resources.observations.AsyncObservationsClient;
import com.langfuse.client.resources.observationsv2.AsyncObservationsV2Client;
import com.langfuse.client.resources.opentelemetry.AsyncOpentelemetryClient;
import com.langfuse.client.resources.organizations.AsyncOrganizationsClient;
Expand All @@ -28,8 +24,6 @@
import com.langfuse.client.resources.scim.AsyncScimClient;
import com.langfuse.client.resources.score.AsyncScoreClient;
import com.langfuse.client.resources.scoreconfigs.AsyncScoreConfigsClient;
import com.langfuse.client.resources.scorev2.AsyncScoreV2Client;
import com.langfuse.client.resources.sessions.AsyncSessionsClient;
import com.langfuse.client.resources.trace.AsyncTraceClient;

public class AsyncLangfuseClient {
Expand All @@ -43,28 +37,20 @@ public class AsyncLangfuseClient {

protected final Supplier<AsyncDatasetItemsClient> datasetItemsClient;

protected final Supplier<AsyncDatasetRunItemsClient> datasetRunItemsClient;

protected final Supplier<AsyncDatasetsClient> datasetsClient;

protected final Supplier<AsyncHealthClient> healthClient;

protected final Supplier<AsyncIngestionClient> ingestionClient;

protected final Supplier<AsyncLlmConnectionsClient> llmConnectionsClient;

protected final Supplier<AsyncMediaClient> mediaClient;

protected final Supplier<AsyncMetricsV2Client> metricsV2Client;

protected final Supplier<AsyncMetricsClient> metricsClient;

protected final Supplier<AsyncModelsClient> modelsClient;

protected final Supplier<AsyncObservationsV2Client> observationsV2Client;

protected final Supplier<AsyncObservationsClient> observationsClient;

protected final Supplier<AsyncOpentelemetryClient> opentelemetryClient;

protected final Supplier<AsyncOrganizationsClient> organizationsClient;
Expand All @@ -79,12 +65,8 @@ public class AsyncLangfuseClient {

protected final Supplier<AsyncScoreConfigsClient> scoreConfigsClient;

protected final Supplier<AsyncScoreV2Client> scoreV2Client;

protected final Supplier<AsyncScoreClient> scoreClient;

protected final Supplier<AsyncSessionsClient> sessionsClient;

protected final Supplier<AsyncTraceClient> traceClient;

public AsyncLangfuseClient(ClientOptions clientOptions) {
Expand All @@ -93,27 +75,21 @@ public AsyncLangfuseClient(ClientOptions clientOptions) {
this.blobStorageIntegrationsClient = Suppliers.memoize(() -> new AsyncBlobStorageIntegrationsClient(clientOptions));
this.commentsClient = Suppliers.memoize(() -> new AsyncCommentsClient(clientOptions));
this.datasetItemsClient = Suppliers.memoize(() -> new AsyncDatasetItemsClient(clientOptions));
this.datasetRunItemsClient = Suppliers.memoize(() -> new AsyncDatasetRunItemsClient(clientOptions));
this.datasetsClient = Suppliers.memoize(() -> new AsyncDatasetsClient(clientOptions));
this.healthClient = Suppliers.memoize(() -> new AsyncHealthClient(clientOptions));
this.ingestionClient = Suppliers.memoize(() -> new AsyncIngestionClient(clientOptions));
this.llmConnectionsClient = Suppliers.memoize(() -> new AsyncLlmConnectionsClient(clientOptions));
this.mediaClient = Suppliers.memoize(() -> new AsyncMediaClient(clientOptions));
this.metricsV2Client = Suppliers.memoize(() -> new AsyncMetricsV2Client(clientOptions));
this.metricsClient = Suppliers.memoize(() -> new AsyncMetricsClient(clientOptions));
this.modelsClient = Suppliers.memoize(() -> new AsyncModelsClient(clientOptions));
this.observationsV2Client = Suppliers.memoize(() -> new AsyncObservationsV2Client(clientOptions));
this.observationsClient = Suppliers.memoize(() -> new AsyncObservationsClient(clientOptions));
this.opentelemetryClient = Suppliers.memoize(() -> new AsyncOpentelemetryClient(clientOptions));
this.organizationsClient = Suppliers.memoize(() -> new AsyncOrganizationsClient(clientOptions));
this.projectsClient = Suppliers.memoize(() -> new AsyncProjectsClient(clientOptions));
this.promptVersionClient = Suppliers.memoize(() -> new AsyncPromptVersionClient(clientOptions));
this.promptsClient = Suppliers.memoize(() -> new AsyncPromptsClient(clientOptions));
this.scimClient = Suppliers.memoize(() -> new AsyncScimClient(clientOptions));
this.scoreConfigsClient = Suppliers.memoize(() -> new AsyncScoreConfigsClient(clientOptions));
this.scoreV2Client = Suppliers.memoize(() -> new AsyncScoreV2Client(clientOptions));
this.scoreClient = Suppliers.memoize(() -> new AsyncScoreClient(clientOptions));
this.sessionsClient = Suppliers.memoize(() -> new AsyncSessionsClient(clientOptions));
this.traceClient = Suppliers.memoize(() -> new AsyncTraceClient(clientOptions));
}

Expand All @@ -133,10 +109,6 @@ public AsyncDatasetItemsClient datasetItems() {
return this.datasetItemsClient.get();
}

public AsyncDatasetRunItemsClient datasetRunItems() {
return this.datasetRunItemsClient.get();
}

public AsyncDatasetsClient datasets() {
return this.datasetsClient.get();
}
Expand All @@ -145,10 +117,6 @@ public AsyncHealthClient health() {
return this.healthClient.get();
}

public AsyncIngestionClient ingestion() {
return this.ingestionClient.get();
}

public AsyncLlmConnectionsClient llmConnections() {
return this.llmConnectionsClient.get();
}
Expand All @@ -161,10 +129,6 @@ public AsyncMetricsV2Client metricsV2() {
return this.metricsV2Client.get();
}

public AsyncMetricsClient metrics() {
return this.metricsClient.get();
}

public AsyncModelsClient models() {
return this.modelsClient.get();
}
Expand All @@ -173,10 +137,6 @@ public AsyncObservationsV2Client observationsV2() {
return this.observationsV2Client.get();
}

public AsyncObservationsClient observations() {
return this.observationsClient.get();
}

public AsyncOpentelemetryClient opentelemetry() {
return this.opentelemetryClient.get();
}
Expand Down Expand Up @@ -205,18 +165,10 @@ public AsyncScoreConfigsClient scoreConfigs() {
return this.scoreConfigsClient.get();
}

public AsyncScoreV2Client scoreV2() {
return this.scoreV2Client.get();
}

public AsyncScoreClient score() {
return this.scoreClient.get();
}

public AsyncSessionsClient sessions() {
return this.sessionsClient.get();
}

public AsyncTraceClient trace() {
return this.traceClient.get();
}
Expand Down
Loading
Loading