Skip to content

Commit a96e57b

Browse files
committed
fix: align datasets/app/workflow APIs per 2025-11-17 audit
- datasets: getDatasetTags use GET; child chunks add keyword/page/limit - datasets: DocMetadata.count mapping; deprecate getUploadFile (no server route) - datasets: CreateDatasetRequest add embedding/retrieval; UpdateDatasetRequest add external_* - documents: update-by-text/file support retrieval_model - chat: add GET /app/feedbacks; add PUT conversation variable update; drop unused variable_name param - workflow: add run-by-id; extend logs filters (created_at__*, created_by_*)
1 parent 03f208e commit a96e57b

12 files changed

Lines changed: 403 additions & 8 deletions

scripts/release.sh

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
BLUE="\033[1;34m"
5+
YELLOW="\033[1;33m"
6+
RED="\033[1;31m"
7+
NC="\033[0m"
8+
9+
log() { echo -e "[${BLUE}INFO${NC}] $*"; }
10+
warn() { echo -e "[${YELLOW}WARN${NC}] $*"; }
11+
error() { echo -e "[${RED}ERROR${NC}] $*" >&2; }
12+
13+
usage() {
14+
cat <<'EOF'
15+
release.sh - 自动打 Tag 并发布到 Maven Central(Central Publishing 插件)
16+
17+
用法:
18+
bash scripts/release.sh -v <version> [-n <notes>] [-f <notes_file>] [选项]
19+
20+
必选参数:
21+
-v, --version <version> 版本号,例如 1.2.1
22+
23+
可选参数(二选一,若都省略则使用默认说明):
24+
-n, --notes "<text>" 发行说明(多行请用 \n 分隔)
25+
-f, --notes-file <file> 从文件读取发行说明
26+
27+
其他选项:
28+
-b, --branch <name> 目标分支(默认:main)
29+
--no-push 仅创建本地 tag,不推送
30+
--no-publish 不执行 Maven 发布
31+
--allow-dirty 允许工作区不干净
32+
--skip-remote-check 跳过与 origin/<branch> 的差异检查
33+
--dry-run 仅打印将要执行的操作,不落地
34+
-y, --yes 跳过交互确认
35+
-h, --help 显示本帮助
36+
37+
前置要求:
38+
- gpg 已配置(maven-gpg-plugin 可用)
39+
- ~/.m2/settings.xml 配置了 serverId=central 的 Usertoken
40+
- 可访问 https://central.sonatype.com
41+
EOF
42+
}
43+
44+
require_cmd() {
45+
if ! command -v "$1" >/dev/null 2>&1; then
46+
error "缺少命令:$1$2"
47+
exit 1
48+
fi
49+
}
50+
51+
# 默认参数
52+
BRANCH="main"
53+
DO_PUSH=1
54+
DO_PUBLISH=1
55+
ALLOW_DIRTY=0
56+
SKIP_REMOTE_CHECK=0
57+
DRY_RUN=0
58+
YES=0
59+
VERSION=""
60+
NOTES=""
61+
NOTES_FILE=""
62+
63+
# 解析参数
64+
while [[ $# -gt 0 ]]; do
65+
case "$1" in
66+
-v|--version) VERSION="${2:-}"; shift 2;;
67+
-n|--notes) NOTES="${2:-}"; shift 2;;
68+
-f|--notes-file) NOTES_FILE="${2:-}"; shift 2;;
69+
-b|--branch) BRANCH="${2:-}"; shift 2;;
70+
--no-push) DO_PUSH=0; shift;;
71+
--no-publish) DO_PUBLISH=0; shift;;
72+
--allow-dirty) ALLOW_DIRTY=1; shift;;
73+
--skip-remote-check) SKIP_REMOTE_CHECK=1; shift;;
74+
--dry-run) DRY_RUN=1; shift;;
75+
-y|--yes) YES=1; shift;;
76+
-h|--help) usage; exit 0;;
77+
*) error "未知参数:$1"; usage; exit 2;;
78+
esac
79+
done
80+
81+
if [[ -z "${VERSION}" ]]; then
82+
error "必须指定版本号,例如:-v 1.2.1"
83+
usage
84+
exit 2
85+
fi
86+
87+
require_cmd git "请安装 Git。"
88+
require_cmd mvn "请安装 Maven。"
89+
90+
# 进入仓库根目录
91+
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
92+
if [[ -z "${REPO_ROOT}" ]]; then
93+
error "当前目录不在 Git 仓库中。"
94+
exit 1
95+
fi
96+
cd "${REPO_ROOT}"
97+
98+
log "仓库根目录:${REPO_ROOT}"
99+
100+
# 检查分支
101+
CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
102+
if [[ "${CURRENT_BRANCH}" != "${BRANCH}" ]]; then
103+
error "当前分支为 ${CURRENT_BRANCH},预期 ${BRANCH}。使用 -b 切换目标分支或在正确分支运行。"
104+
exit 1
105+
fi
106+
107+
# 工作区干净性
108+
if [[ "${ALLOW_DIRTY}" -eq 0 ]]; then
109+
if ! git diff-index --quiet HEAD --; then
110+
error "工作区存在未提交变更。提交/暂存或使用 --allow-dirty 跳过。"
111+
exit 1
112+
fi
113+
else
114+
warn "已启用 --allow-dirty,跳过工作区干净性检查。"
115+
fi
116+
117+
# 远程一致性
118+
if [[ "${SKIP_REMOTE_CHECK}" -eq 0 ]]; then
119+
log "同步远程分支与标签(git fetch origin --prune --tags)"
120+
if [[ "${DRY_RUN}" -eq 1 ]]; then
121+
echo "DRY-RUN: git fetch origin --prune --tags"
122+
else
123+
git fetch origin --prune --tags
124+
fi
125+
DIFF="$(git rev-list --left-right --count "origin/${BRANCH}...${BRANCH}")"
126+
AHEAD="$(echo "${DIFF}" | awk '{print $1}')"
127+
BEHIND="$(echo "${DIFF}" | awk '{print $2}')"
128+
if [[ "${AHEAD}" != "0" || "${BEHIND}" != "0" ]]; then
129+
error "本地与 origin/${BRANCH} 不一致(ahead=${AHEAD}, behind=${BEHIND})。请先同步。"
130+
exit 1
131+
fi
132+
else
133+
warn "已启用 --skip-remote-check,跳过与远程差异检查。"
134+
fi
135+
136+
# 检查 tag 是否已存在
137+
TAG="v${VERSION}"
138+
if git show-ref --tags "refs/tags/${TAG}" >/dev/null 2>&1; then
139+
error "Tag ${TAG} 已存在。"
140+
exit 1
141+
fi
142+
143+
# 读取项目版本以校验
144+
PROJECT_VERSION="$(mvn -q help:evaluate -Dexpression=project.version -DforceStdout 2>/dev/null || true)"
145+
if [[ -z "${PROJECT_VERSION}" ]]; then
146+
warn "无法读取 Maven project.version,跳过一致性校验。"
147+
else
148+
if [[ "${PROJECT_VERSION}" != "${VERSION}" ]]; then
149+
error "pom.xml 中的 project.version=${PROJECT_VERSION} 与指定版本 ${VERSION} 不一致。"
150+
exit 1
151+
fi
152+
fi
153+
154+
# 发行说明
155+
if [[ -n "${NOTES_FILE}" ]]; then
156+
if [[ ! -f "${NOTES_FILE}" ]]; then
157+
error "发行说明文件不存在:${NOTES_FILE}"
158+
exit 1
159+
fi
160+
NOTES="$(cat "${NOTES_FILE}")"
161+
fi
162+
if [[ -z "${NOTES}" ]]; then
163+
NOTES="Release ${TAG}"
164+
fi
165+
166+
echo
167+
log "即将发布:${TAG}"
168+
echo "Tag message:"
169+
echo "----------------"
170+
echo -e "${NOTES}"
171+
echo "----------------"
172+
173+
if [[ "${YES}" -ne 1 ]]; then
174+
read -r -p "确认继续?(y/N) " ans
175+
if [[ "${ans}" != "y" && "${ans}" != "Y" ]]; then
176+
error "已取消。"
177+
exit 1
178+
fi
179+
fi
180+
181+
# 创建 tag
182+
log "创建注释 tag:${TAG}"
183+
if [[ "${DRY_RUN}" -eq 1 ]]; then
184+
echo -e "DRY-RUN: git tag -a ${TAG} -m \$'${TAG}\n\n${NOTES//\'/\'\"\'\"\'}'"
185+
else
186+
git tag -a "${TAG}" -m $''"${TAG}"$'\n\n'"${NOTES}"''
187+
fi
188+
189+
# 推送 tag
190+
if [[ "${DO_PUSH}" -eq 1 ]]; then
191+
log "推送 tag 到 origin:${TAG}"
192+
if [[ "${DRY_RUN}" -eq 1 ]]; then
193+
echo "DRY-RUN: git push origin ${TAG}"
194+
else
195+
git push origin "${TAG}"
196+
fi
197+
else
198+
warn "已禁用 push(--no-push)。"
199+
fi
200+
201+
# 发布到 Maven Central
202+
if [[ "${DO_PUBLISH}" -eq 1 ]]; then
203+
log "执行 Maven 发布:clean package -DskipTests gpg:sign central-publishing:publish"
204+
if [[ "${DRY_RUN}" -eq 1 ]]; then
205+
echo "DRY-RUN: mvn -B -ntp clean package -DskipTests gpg:sign central-publishing:publish"
206+
else
207+
mvn -B -ntp clean package -DskipTests gpg:sign central-publishing:publish
208+
fi
209+
else
210+
warn "已禁用发布(--no-publish)。"
211+
fi
212+
213+
log "完成:${TAG}"
214+
215+

src/main/java/io/github/imfangs/dify/client/DifyChatClient.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ public interface DifyChatClient extends DifyBaseClient {
165165
*/
166166
AppMetaResponse getAppMeta() throws IOException, DifyApiException;
167167

168+
/**
169+
* 获取应用反馈列表
170+
*
171+
* @return 反馈列表
172+
* @throws IOException IO异常
173+
* @throws DifyApiException API异常
174+
*/
175+
io.github.imfangs.dify.client.model.chat.AppFeedbacksResponse getAppFeedbacks() throws IOException, DifyApiException;
176+
168177
/**
169178
* 获取标注列表
170179
*
@@ -244,4 +253,17 @@ public interface DifyChatClient extends DifyBaseClient {
244253
* @return
245254
*/
246255
VariableResponse getConversationVariables(String conversationId, String user, String lastId, Integer limit, String variableName) throws DifyApiException, IOException;
256+
257+
/**
258+
* 更新对话变量的值
259+
*
260+
* @param conversationId 会话 ID
261+
* @param variableId 变量 ID
262+
* @param value 新的变量值(任意 JSON 类型)
263+
* @param user 用户标识
264+
* @return 更新后的变量
265+
* @throws IOException IO异常
266+
* @throws DifyApiException API异常
267+
*/
268+
VariableResponse.VariableData updateConversationVariable(String conversationId, String variableId, Object value, String user) throws IOException, DifyApiException;
247269
}

src/main/java/io/github/imfangs/dify/client/DifyDatasetsClient.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ public interface DifyDatasetsClient {
370370
* @throws IOException IO异常
371371
* @throws DifyApiException API异常
372372
*/
373+
@Deprecated
373374
UploadFileResponse getUploadFile(String datasetId, String documentId) throws IOException, DifyApiException;
374375

375376
/**

src/main/java/io/github/imfangs/dify/client/DifyWorkflowClient.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ public interface DifyWorkflowClient extends DifyBaseClient {
2222
*/
2323
WorkflowRunResponse runWorkflow(WorkflowRunRequest request) throws IOException, DifyApiException;
2424

25+
/**
26+
* 通过 workflow_id 执行工作流(阻塞模式)
27+
*
28+
* @param workflowId 工作流 ID
29+
* @param request 请求
30+
* @return 响应
31+
* @throws IOException IO异常
32+
* @throws DifyApiException API异常
33+
*/
34+
WorkflowRunResponse runWorkflowById(String workflowId, WorkflowRunRequest request) throws IOException, DifyApiException;
35+
2536
/**
2637
* 执行工作流(流式模式)
2738
*
@@ -65,4 +76,28 @@ public interface DifyWorkflowClient extends DifyBaseClient {
6576
* @throws DifyApiException API异常
6677
*/
6778
WorkflowLogsResponse getWorkflowLogs(String keyword, String status, Integer page, Integer limit) throws IOException, DifyApiException;
79+
80+
/**
81+
* 获取工作流日志(含扩展过滤项)
82+
*
83+
* @param keyword 关键字
84+
* @param status 状态
85+
* @param createdAtBefore 创建时间上界(ISO 字符串)
86+
* @param createdAtAfter 创建时间下界(ISO 字符串)
87+
* @param createdByEndUserSessionId 按终端用户会话 ID 过滤
88+
* @param createdByAccount 按账号过滤
89+
* @param page 页码
90+
* @param limit 每页条数
91+
* @return 日志列表
92+
* @throws IOException IO异常
93+
* @throws DifyApiException API异常
94+
*/
95+
WorkflowLogsResponse getWorkflowLogs(String keyword,
96+
String status,
97+
String createdAtBefore,
98+
String createdAtAfter,
99+
String createdByEndUserSessionId,
100+
String createdByAccount,
101+
Integer page,
102+
Integer limit) throws IOException, DifyApiException;
68103
}

src/main/java/io/github/imfangs/dify/client/impl/DefaultDifyClient.java

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public class DefaultDifyClient extends DifyBaseClientImpl implements DifyClient
4444
private static final String META_PATH = "/meta";
4545
private static final String STOP_PATH = "/stop";
4646
private static final String FEEDBACKS_PATH = "/feedbacks";
47+
private static final String APP_FEEDBACKS_PATH = "/app/feedbacks";
4748
private static final String SUGGESTED_QUESTIONS_PATH = "/suggested";
4849
private static final String NAME_PATH = "/name";
4950

@@ -265,6 +266,12 @@ public AppMetaResponse getAppMeta() throws IOException, DifyApiException {
265266
return executeGet(META_PATH, AppMetaResponse.class);
266267
}
267268

269+
@Override
270+
public io.github.imfangs.dify.client.model.chat.AppFeedbacksResponse getAppFeedbacks() throws IOException, DifyApiException {
271+
log.debug("获取应用反馈列表");
272+
return executeGet(APP_FEEDBACKS_PATH, io.github.imfangs.dify.client.model.chat.AppFeedbacksResponse.class);
273+
}
274+
268275
// ==================== 文本生成型应用相关方法 ====================
269276

270277
@Override
@@ -302,6 +309,13 @@ public WorkflowRunResponse runWorkflow(WorkflowRunRequest request) throws IOExce
302309
return executePost(WORKFLOWS_RUN_PATH, request, WorkflowRunResponse.class);
303310
}
304311

312+
@Override
313+
public WorkflowRunResponse runWorkflowById(String workflowId, WorkflowRunRequest request) throws IOException, DifyApiException {
314+
log.debug("按 ID 执行工作流: workflowId={}, request={}", workflowId, request);
315+
String path = WORKFLOWS_PATH + "/" + workflowId + "/run";
316+
return executePost(path, request, WorkflowRunResponse.class);
317+
}
318+
305319
@Override
306320
public void runWorkflowStream(WorkflowRunRequest request, WorkflowStreamCallback callback) throws IOException, DifyApiException {
307321
log.debug("执行流式工作流: {}", request);
@@ -339,10 +353,27 @@ public WorkflowRunStatusResponse getWorkflowRun(String workflowRunId) throws IOE
339353

340354
@Override
341355
public WorkflowLogsResponse getWorkflowLogs(String keyword, String status, Integer page, Integer limit) throws IOException, DifyApiException {
342-
log.debug("获取工作流日志: keyword={}, status={}, page={}, limit={}", keyword, status, page, limit);
356+
return getWorkflowLogs(keyword, status, null, null, null, null, page, limit);
357+
}
358+
359+
@Override
360+
public WorkflowLogsResponse getWorkflowLogs(String keyword,
361+
String status,
362+
String createdAtBefore,
363+
String createdAtAfter,
364+
String createdByEndUserSessionId,
365+
String createdByAccount,
366+
Integer page,
367+
Integer limit) throws IOException, DifyApiException {
368+
log.debug("获取工作流日志: keyword={}, status={}, createdAtBefore={}, createdAtAfter={}, createdByEndUserSessionId={}, createdByAccount={}, page={}, limit={}",
369+
keyword, status, createdAtBefore, createdAtAfter, createdByEndUserSessionId, createdByAccount, page, limit);
343370
Map<String, Object> params = new HashMap<>();
344371
params.put("keyword", keyword);
345372
params.put("status", status);
373+
params.put("created_at__before", createdAtBefore);
374+
params.put("created_at__after", createdAtAfter);
375+
params.put("created_by_end_user_session_id", createdByEndUserSessionId);
376+
params.put("created_by_account", createdByAccount);
346377
params.put("page", page);
347378
params.put("limit", limit);
348379

@@ -616,12 +647,18 @@ public VariableResponse getConversationVariables(String conversationId, String u
616647
Optional.ofNullable(lastId).ifPresent((lId) -> {
617648
path.append("&last_id=").append(lId);
618649
});
619-
Optional.ofNullable(variableName).ifPresent((vName) -> {
620-
path.append("&variable_name=").append(vName);
621-
});
622650
return executeGet(path.toString(), VariableResponse.class);
623651
}
624652

653+
@Override
654+
public VariableResponse.VariableData updateConversationVariable(String conversationId, String variableId, Object value, String user) throws IOException, DifyApiException {
655+
log.debug("更新对话变量: conversationId={}, variableId={}, user={}", conversationId, variableId, user);
656+
Map<String, Object> body = new HashMap<>(2);
657+
body.put("value", value);
658+
body.put("user", user);
659+
String path = CONVERSATIONS_PATH + "/" + conversationId + "/variables/" + variableId;
660+
return executePut(path, body, VariableResponse.VariableData.class);
661+
}
625662
private String getFileExtension(String fileName) {
626663
int lastDotIndex = fileName.lastIndexOf('.');
627664
return lastDotIndex > 0 ? fileName.substring(lastDotIndex + 1) : "";

0 commit comments

Comments
 (0)