Library / SDK
imfangs/dify-java-client avatar
imfangs/dify-java-client

dify-java-client: calling the Dify platform from a Java application

Dify Java Client 是一个用于与 Dify 平台进行交互的 Java 客户端库。它提供了对 Dify 应用 API 和知识库 API 的完整支持,让 Java 开发者能够轻松地将 Dify 的生成式 AI 能力集成到自己的应用中。

693 stars159 forksJavaApache-2.0

At a glance

What is it?
dify-java-client is a Java library that wraps Dify's application and dataset APIs, with one client per application type, blocking and streaming modes, session management and knowledge base operations. It suits a Java team that wants Dify behind a typed API rather than hand-written HTTP calls, and it is a client for one specific platform rather than a general LLM SDK.
Who is it for?
dify-java-client fits a Java team integrating Dify into an existing Spring-style service that wants typed request and response objects, streaming callbacks and a single factory entry point rather than hand-built HTTP calls. It does not fit a team that needs to talk to several AI platforms through one abstraction, since the model is Dify's own API surface.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 16 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What the library covers: five clients for five Dify application types

dify-java-client is a Java client library for interacting with the Dify platform, providing complete support for the Dify application API and the knowledge base API so that Java developers can integrate Dify's generative AI capabilities into their own applications.

The design is organised around one client per application type, and the readme lists them:

- Chat applications, through DifyChatClient, with session management and message feedback - Completion applications, through DifyCompletionClient - Chatflow, workflow-orchestrated conversation, through DifyChatflowClient - Workflow applications, through DifyWorkflowClient - Datasets, through DifyDatasetsClient, for knowledge base, document and retrieval management

That split is the API's main idea. Dify itself distinguishes these application types, and each has its own request and response shapes, so a single generic client would mean optional fields everywhere. One client per type keeps the typed surface honest at the cost of more classes.

The audience is a Java developer with a Dify deployment to talk to. The library is not a general LLM SDK, and the readme does not describe support for any other provider, which is the single most important thing to establish before adopting it.

The factory and the configuration object that sets your timeouts

Client construction goes through a factory, and the readme shows both the short form and the long form.

java
// 创建完整的 Dify 客户端
DifyClient client = DifyClientFactory.createClient("https://api.dify.ai/v1", "your-api-key");

// 创建特定类型的客户端
DifyChatClient chatClient = DifyClientFactory.createChatClient("https://api.dify.ai/v1", "your-api-key");
DifyCompletionClient completionClient = DifyClientFactory.createCompletionClient("https://api.dify.ai/v1", "your-api-key");
DifyChatflowClient chatflowClient = DifyClientFactory.createChatWorkflowClient("https://api.dify.ai/v1", "your-api-key");
DifyWorkflowClient workflowClient = DifyClientFactory.createWorkflowClient("https://api.dify.ai/v1", "your-api-key");
DifyDatasetsClient datasetsClient = DifyClientFactory.createDatasetsClient("https://api.dify.ai/v1", "your-api-key");

// 使用自定义配置创建客户端
DifyConfig config = DifyConfig.builder()
    .baseUrl("https://api.dify.ai/v1")
    .apiKey("your-api-key")
    .connectTimeout(5000)
    .readTimeout(60000)
    .writeTimeout(30000)
    .build();

DifyClient clientWithConfig = DifyClientFactory.createClient(config);

Three things are worth noting. The base URL is a constructor argument everywhere, so a self-hosted Dify deployment is a different string rather than a different code path. The chatflow factory method is named createChatWorkflowClient while the client type is DifyChatflowClient, a small naming mismatch to watch for when you grep.

And the configuration object is where the operational decisions live. connectTimeout, readTimeout and writeTimeout are all separate, and the values in the example tell you what the author considered reasonable: five seconds to connect, sixty seconds to read, thirty seconds to write. A sixty second read timeout matters for a streaming response and for a long workflow run, and it is the setting you will want to change first if you are putting this behind a service with its own request deadline.

The configurable options are also listed as custom HTTP client support, so you can supply your own HTTP client implementation rather than accepting the default one.

Blocking and streaming, and the callback contract that changed in 1.7.0

The interaction modes are the second structural decision. Blocking mode calls the API synchronously and waits for the full response, and streaming mode delivers generated content through callbacks so a UI can render it as it arrives, which the readme describes as supporting a typewriter effect.

The streaming callback is an interface with four methods, and the shape is worth reading because it separates the three ways a stream can go wrong:

java
// 发送流式消息
chatClient.sendChatMessageStream(message, new ChatStreamCallback() {
    @Override
    public void onMessage(MessageEvent event) {
        System.out.println("收到消息片段: " + event.getAnswer());
    }

    @Override
    public void onMessageEnd(MessageEndEvent event) {
        System.out.println("消息结束,完整消息ID: " + event.getMessageId());
    }

    @Override
    public void onError(ErrorEvent event) {
        System.err.println("错误: " + event.getMessage());
    }

    @Override
    public void onException(Throwable throwable) {
        System.err.println("异常: " + throwable.getMessage());
    }
});

onMessage delivers a fragment, onMessageEnd reports the complete message id, onError carries a server-reported error, and onException carries a thrown exception. Keeping the last two separate is a real design decision: an application error returned inside the stream is not the same as a transport or parsing failure, and a caller that wants to retry only on the latter can.

The release note at the top of the readme matters here. Version 1.7.0 added stream-end and data-source event callbacks and fixed the handling of the Chatflow end event. The end-of-stream callback and the data source event are the two places a streaming client is most likely to be wrong about, so if you are on 1.6.x or earlier, that is the upgrade to prioritise. The recent release history shows v1.7.0 on 2026-10-09, v1.6.0 and v1.5.0 both on 2026-07-10, so the 1.7 line is a single release old.

Note also that the completion client has its own callback interface, CompletionStreamCallback, with the same four methods, so the two are not interchangeable.

Session management, feedback and the dataset side of the API

Beyond a single call, the library covers the conversation lifecycle. The readme's session management examples show reading history, listing conversations, renaming and deleting:

java
// 获取会话历史消息
MessageListResponse messages = chatClient.getMessages(conversationId, "user-123", null, 10);

// 获取会话列表
ConversationListResponse conversations = chatClient.getConversations("user-123", null, 10, "-updated_at");

// 重命名会话
Conversation renamedConversation = chatClient.renameConversation(conversationId, "新会话名称", false, "user-123");

// 删除会话
SimpleResponse deleteResponse = chatClient.deleteConversation(conversationId, "user-123");

The user identifier appears on every one of these calls, and that is a Dify convention rather than a Java one: it is how Dify scopes a conversation to an end user, which is what lets a server keep per-user history without a session store of its own. The list call takes a sort key such as -updated_at and a limit.

Message feedback and suggestions are there too:

java
// 发送消息反馈(点赞)
SimpleResponse feedbackResponse = chatClient.feedbackMessage(messageId, "like", "user-123", "这是一个很好的回答");

// 获取建议问题
SuggestedQuestionsResponse suggestedQuestions = chatClient.getSuggestedQuestions(messageId, "user-123");

And the multimedia calls, audio to text and text to audio:

java
// 语音转文字
AudioToTextResponse textResponse = chatClient.audioToText(audioFile, "user-123");
System.out.println("转换后的文本: " + textResponse.getText());

// 文字转语音
byte[] audioData = chatClient.textToAudio(null, "这是一段测试文本", "user-123");

The datasets client covers the knowledge base end to end: creating and managing knowledge bases, uploading and managing documents, segment management, and semantic retrieval. That is the part that makes the library more than a chat wrapper, because indexing documents is the workflow that usually needs the most custom code.

One thing you will not find in the readme is how the API key is protected. The examples pass "your-api-key" as a plain string, so where you keep that string, whether it comes from a secret manager, a Spring property or an environment variable, is your problem to solve.

Installation, Java baseline, and where the library fits in a build

The library is on Maven Central under the group id io.github.imfangs, and the readme gives the dependency in both build systems.

Maven:

xml
<dependency>
    <groupId>io.github.imfangs</groupId>
    <artifactId>dify-java-client</artifactId>
    <version>1.7.0</version>
</dependency>

Gradle:

groovy
implementation 'io.github.imfangs:dify-java-client:1.7.0'

The system requirements are Java 8 or higher, with Maven 3.x or Gradle 4.x and above. The Java 8 baseline is worth noting: it means the library can go into a long-lived service that cannot yet move to a newer JDK, and it also means you will not get newer language features in the API.

For a Spring application, the natural arrangement is to build one client bean per application type at startup and inject it, with the API key coming from configuration rather than from a literal in your code. The factory methods are static and the clients are presumably stateless with respect to the request, since the request objects carry the user and the mode, so a single instance per type should serve concurrent requests.

The repository is a plain Maven project with pom.xml and src/, and the top-level entries also include README_EN.md and README_JP.md alongside the Chinese README, plus docs/, scripts/, AGENTS.md, CURRENT.md and RELEASE_NOTES.md.

The multi-language readme and the agent instruction file and current-state file are a sign of a project that is being developed with tooling in the loop, and the presence of RELEASE_NOTES.md is what you should read before pinning a version, since the streaming behaviour changed in 1.7.0.

Editorial conclusion

dify-java-client fits a Java team integrating Dify into an existing Spring-style service that wants typed request and response objects, streaming callbacks and a single factory entry point rather than hand-built HTTP calls. It does not fit a team that needs to talk to several AI platforms through one abstraction, since the model is Dify's own API surface. Before adopting it, read the release notes for the version you pin, because the 1.7.0 changes added stream-end and data-source event callbacks and fixed Chatflow end-event handling, and confirm your base URL and API key handling match how your Dify deployment issues credentials.

Frequently asked questions

What does the dify-java-client library cover?

It is a Java client for the Dify platform with complete support for the Dify application API and the knowledge base API. It covers chat, completion, chatflow and workflow applications plus dataset management, with blocking and streaming interaction modes and session management.

How do I add dify-java-client to a Maven or Gradle project?

For Maven, add io.github.imfangs:dify-java-client:1.7.0 as a dependency. For Gradle, use implementation 'io.github.imfangs:dify-java-client:1.7.0'. The requirements are Java 8 or higher, with Maven 3.x or Gradle 4.x and above.

How do clients for the different Dify application types get created?

Through DifyClientFactory, either with a base URL and API key directly or with a DifyConfig built through its builder, which also carries connectTimeout, readTimeout and writeTimeout. There is a factory method for the full client and one per application type, such as createChatClient, createCompletionClient, createChatWorkflowClient, createWorkflowClient and createDatasetsClient.

What changed in version 1.7.0 of dify-java-client?

The readme release note says 1.7.0 added stream-end and data-source event callbacks and fixed the handling of the Chatflow end event. The version was published on 2026-10-09, and the two preceding releases, 1.6.0 and 1.5.0, both came out on 2026-07-10.

How does streaming differ from blocking mode in dify-java-client?

Blocking mode calls the API synchronously and waits for the complete response, while streaming mode delivers generated content through a callback so it can be rendered as it arrives. The stream callback interface has onMessage for a fragment, onMessageEnd for the complete message id, onError for a server-reported error and onException for a thrown exception.

Official sources

  1. imfangs/dify-java-client on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/imfangs-dify-java-client.svg)](https://hysenlabs.com/projects/imfangs-dify-java-client)

Was this useful?