gaoding-cli 1.0.0-alpha.14 → 1.0.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/bin/gd-cli.js +34 -15
- package/dist/src/bootstrap/create-cli.js +14 -8
- package/dist/src/bootstrap/create-runtime.js +13 -7
- package/dist/src/cli/errors.js +16 -1
- package/dist/src/cli/model-commands.js +1 -17
- package/dist/src/cli/presenter.js +4 -4
- package/dist/src/features/tool/tool-api-adapter.js +207 -57
- package/dist/src/features/tool/use-cases.js +25 -6
- package/dist/src/platform/remote-error-evidence.js +80 -0
- package/dist/src/platform/remote-protocol.js +5 -1
- package/dist/src/platform/signed-http-transport.js +31 -5
- package/dist/src/telemetry/invocation.js +35 -7
- package/package.json +1 -1
- package/skills/gd-cli/references/creation.md +14 -5
package/README.md
CHANGED
|
@@ -29,10 +29,9 @@ gd-cli agent send --schema
|
|
|
29
29
|
gd-cli tool list
|
|
30
30
|
gd-cli model list --tool <tool>
|
|
31
31
|
gd-cli model get <model>
|
|
32
|
-
gd-cli model get <model> --schema
|
|
33
32
|
```
|
|
34
33
|
|
|
35
|
-
选定 Model
|
|
34
|
+
选定 Model 后,以 `model get` 返回的 `parameters` 与 `usageDescription` 构造实际输入;`tool call --schema` 只给出整个 Tool 的参数并集。运行 `gd-cli --help` 查看当前命令;运行任一命令的 `--help` 查看参数。`gd-cli update` 固定检查 npm `latest`,支持更新 npm、pnpm 全局安装并同步 Agent Skill。
|
|
36
35
|
|
|
37
36
|
## License
|
|
38
37
|
|
package/dist/bin/gd-cli.js
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { createCli, executeCli } from "../src/bootstrap/create-cli.js";
|
|
3
|
-
import { createProductionRuntime } from "../src/bootstrap/create-runtime.js";
|
|
4
|
-
import { mapCliError } from "../src/cli/errors.js";
|
|
5
|
-
import { redact } from "../src/platform/redact.js";
|
|
6
2
|
const controller = new AbortController();
|
|
7
3
|
const interrupt = () => controller.abort(new Error("SIGINT"));
|
|
8
4
|
process.once("SIGINT", interrupt);
|
|
9
5
|
let runtime;
|
|
6
|
+
let mapCliError;
|
|
7
|
+
let redact;
|
|
10
8
|
try {
|
|
9
|
+
// Register SIGINT before loading the CLI graph so a cold-start interrupt is
|
|
10
|
+
// converted into the same abort path as an interrupt during stdin reading.
|
|
11
|
+
const [cliModule, runtimeModule, errorsModule, redactModule] = await Promise.all([
|
|
12
|
+
import("../src/bootstrap/create-cli.js"),
|
|
13
|
+
import("../src/bootstrap/create-runtime.js"),
|
|
14
|
+
import("../src/cli/errors.js"),
|
|
15
|
+
import("../src/platform/redact.js")
|
|
16
|
+
]);
|
|
17
|
+
const { createCli, executeCli } = cliModule;
|
|
18
|
+
const { createProductionRuntime } = runtimeModule;
|
|
19
|
+
mapCliError = errorsModule.mapCliError;
|
|
20
|
+
redact = redactModule.redact;
|
|
11
21
|
runtime = createProductionRuntime();
|
|
12
22
|
const program = createCli(runtime, { signal: controller.signal });
|
|
13
23
|
process.exitCode = await executeCli({
|
|
@@ -20,21 +30,30 @@ try {
|
|
|
20
30
|
});
|
|
21
31
|
}
|
|
22
32
|
catch (error) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
process.stderr.write("已取消。\n");
|
|
29
|
-
}
|
|
30
|
-
else if (process.argv.includes("--json")) {
|
|
31
|
-
process.stderr.write(`${JSON.stringify({ error: outcome.failure })}\n`);
|
|
33
|
+
// Dynamic module loading can fail before the error mapper is available.
|
|
34
|
+
// Keep that rare startup failure bounded and avoid an unhandled rejection.
|
|
35
|
+
if (mapCliError === undefined || redact === undefined) {
|
|
36
|
+
process.stderr.write("INTERNAL_ERROR: CLI 启动失败。\n");
|
|
37
|
+
process.exitCode = 1;
|
|
32
38
|
}
|
|
33
39
|
else {
|
|
34
|
-
|
|
40
|
+
const outcome = redact(mapCliError(error, { aborted: controller.signal.aborted }));
|
|
41
|
+
if (runtime) {
|
|
42
|
+
runtime.presenter.error(outcome, { json: process.argv.includes("--json") });
|
|
43
|
+
}
|
|
44
|
+
else if (outcome.kind === "interrupted") {
|
|
45
|
+
process.stderr.write("已取消。\n");
|
|
46
|
+
}
|
|
47
|
+
else if (process.argv.includes("--json")) {
|
|
48
|
+
process.stderr.write(`${JSON.stringify({ error: outcome.failure })}\n`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
process.stderr.write(`${outcome.failure.code}: ${outcome.failure.message}\n`);
|
|
52
|
+
}
|
|
53
|
+
process.exitCode = outcome.exitCode;
|
|
35
54
|
}
|
|
36
|
-
process.exitCode = outcome.exitCode;
|
|
37
55
|
}
|
|
38
56
|
finally {
|
|
39
57
|
process.removeListener("SIGINT", interrupt);
|
|
40
58
|
}
|
|
59
|
+
export {};
|
|
@@ -2,7 +2,7 @@ import { Command, CommanderError } from "commander";
|
|
|
2
2
|
import { assertAllLeafCommandsBound, configureActionEnvironment } from "../cli/action-binding.js";
|
|
3
3
|
import { registerAgentCommands } from "../cli/agent-commands.js";
|
|
4
4
|
import { registerAuthCommands } from "../cli/auth-commands.js";
|
|
5
|
-
import { isHelpOutcome, mapCliError } from "../cli/errors.js";
|
|
5
|
+
import { classifyTelemetryError, isHelpOutcome, mapCliError } from "../cli/errors.js";
|
|
6
6
|
import { registerOrgCommands } from "../cli/org-commands.js";
|
|
7
7
|
import { registerToolCommands } from "../cli/tool-commands.js";
|
|
8
8
|
import { registerModelCommands } from "../cli/model-commands.js";
|
|
@@ -10,7 +10,6 @@ import { registerEditorCommands } from "../cli/editor-commands.js";
|
|
|
10
10
|
import { registerDamCommands } from "../cli/dam-commands.js";
|
|
11
11
|
import { registerUpdateCommand } from "../cli/update-command.js";
|
|
12
12
|
import { redact } from "../platform/redact.js";
|
|
13
|
-
import { RemoteRequestError } from "../platform/signed-http-transport.js";
|
|
14
13
|
export { assertAllLeafCommandsBound, bindAction } from "../cli/action-binding.js";
|
|
15
14
|
export function createCli(runtime, options) {
|
|
16
15
|
const program = new Command()
|
|
@@ -60,12 +59,13 @@ export async function executeCli(options) {
|
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
61
|
const outcome = redact(mapCliError(error, { aborted: options.signal.aborted }));
|
|
63
|
-
const
|
|
64
|
-
|
|
62
|
+
const telemetryError = classifyTelemetryError(error);
|
|
63
|
+
const traceId = options.telemetry.selected
|
|
64
|
+
? options.telemetry.traceId
|
|
65
65
|
: undefined;
|
|
66
66
|
options.presenter.error(outcome, {
|
|
67
67
|
json: options.argv.includes("--json"),
|
|
68
|
-
...(
|
|
68
|
+
...(traceId ? { traceId } : {})
|
|
69
69
|
});
|
|
70
70
|
await sendFinished(options, outcome.kind === "interrupted"
|
|
71
71
|
? { outcome: "interrupted", error }
|
|
@@ -74,9 +74,15 @@ export async function executeCli(options) {
|
|
|
74
74
|
error,
|
|
75
75
|
errorCode: outcome.failure.code,
|
|
76
76
|
errorMessage: outcome.failure.message,
|
|
77
|
-
...(
|
|
78
|
-
? {
|
|
79
|
-
: {})
|
|
77
|
+
...(telemetryError.httpStatus === undefined
|
|
78
|
+
? {}
|
|
79
|
+
: { httpStatus: telemetryError.httpStatus }),
|
|
80
|
+
...(telemetryError.remoteErrorCode === undefined
|
|
81
|
+
? {}
|
|
82
|
+
: { remoteErrorCode: telemetryError.remoteErrorCode }),
|
|
83
|
+
...(telemetryError.remoteErrorBody === undefined
|
|
84
|
+
? {}
|
|
85
|
+
: { remoteErrorBody: telemetryError.remoteErrorBody })
|
|
80
86
|
});
|
|
81
87
|
return outcome.exitCode;
|
|
82
88
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
1
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { setTimeout as delay } from "node:timers/promises";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
@@ -53,7 +53,8 @@ export function createProductionRuntime(options = {}) {
|
|
|
53
53
|
const telemetry = createTelemetryInvocation({
|
|
54
54
|
cliVersion: primarySkill.version,
|
|
55
55
|
now: () => now().getTime(),
|
|
56
|
-
|
|
56
|
+
traceIdFactory: () => randomBytes(16).toString("hex"),
|
|
57
|
+
spanIdFactory: () => randomBytes(8).toString("hex"),
|
|
57
58
|
runtime: {
|
|
58
59
|
os: process.platform,
|
|
59
60
|
arch: process.arch,
|
|
@@ -81,28 +82,33 @@ export function createProductionRuntime(options = {}) {
|
|
|
81
82
|
const orgTransport = createSignedHttpTransport({
|
|
82
83
|
baseUrl: endpoints.orgApi,
|
|
83
84
|
fetch,
|
|
84
|
-
now
|
|
85
|
+
now,
|
|
86
|
+
traceparent: () => telemetry.traceparent()
|
|
85
87
|
});
|
|
86
88
|
const agentTransport = createSignedHttpTransport({
|
|
87
89
|
baseUrl: endpoints.agentApi,
|
|
88
90
|
fetch,
|
|
89
|
-
now
|
|
91
|
+
now,
|
|
92
|
+
traceparent: () => telemetry.traceparent()
|
|
90
93
|
});
|
|
91
94
|
const mnsTransport = createSignedHttpTransport({
|
|
92
95
|
baseUrl: endpoints.mnsApi,
|
|
93
96
|
fetch,
|
|
94
|
-
now
|
|
97
|
+
now,
|
|
98
|
+
traceparent: () => telemetry.traceparent()
|
|
95
99
|
});
|
|
96
100
|
const toolTransport = createSignedHttpTransport({
|
|
97
101
|
baseUrl: endpoints.toolApi,
|
|
98
102
|
fetch,
|
|
99
|
-
now
|
|
103
|
+
now,
|
|
104
|
+
traceparent: () => telemetry.traceparent()
|
|
100
105
|
});
|
|
101
106
|
const damTransport = createSignedHttpTransport({
|
|
102
107
|
baseUrl: endpoints.damApi,
|
|
103
108
|
fetch,
|
|
104
109
|
now,
|
|
105
|
-
channelId: "32"
|
|
110
|
+
channelId: "32",
|
|
111
|
+
traceparent: () => telemetry.traceparent()
|
|
106
112
|
});
|
|
107
113
|
const sso = createSsoService({
|
|
108
114
|
ssoApi: endpoints.ssoApi,
|
package/dist/src/cli/errors.js
CHANGED
|
@@ -5,6 +5,7 @@ import { SsoRequestError } from "../features/auth/sso-service.js";
|
|
|
5
5
|
import { DeviceAuthorizationExpiredError } from "../features/auth/use-cases.js";
|
|
6
6
|
import { AgentInputError } from "../features/agent/use-cases.js";
|
|
7
7
|
import { ToolInputError } from "../features/tool/catalog.js";
|
|
8
|
+
import { ToolTaskFailedError } from "../features/tool/tool-api-adapter.js";
|
|
8
9
|
import { ObjectStorageUploadError } from "../features/dam/object-storage.js";
|
|
9
10
|
import { DamAssetNotFoundError } from "../features/dam/dam-api-adapter.js";
|
|
10
11
|
import { DamInputError } from "../features/dam/use-cases.js";
|
|
@@ -64,6 +65,9 @@ export function mapCliError(error, options) {
|
|
|
64
65
|
if (error instanceof RemoteProtocolError) {
|
|
65
66
|
return failure("REMOTE_PROTOCOL_INCOMPATIBLE", "稿定服务返回了当前 CLI 无法安全处理的结果;为避免重复创作,已停止处理。", 1);
|
|
66
67
|
}
|
|
68
|
+
if (error instanceof ToolTaskFailedError) {
|
|
69
|
+
return failure("TOOL_TASK_FAILED", "稿定工具任务执行失败。", 1);
|
|
70
|
+
}
|
|
67
71
|
if (error instanceof DamAssetNotFoundError) {
|
|
68
72
|
return failure("DAM_ASSET_NOT_FOUND", "未找到指定的 DAM 素材。", 1);
|
|
69
73
|
}
|
|
@@ -87,12 +91,23 @@ export function mapCliError(error, options) {
|
|
|
87
91
|
}
|
|
88
92
|
export function classifyTelemetryError(error) {
|
|
89
93
|
const outcome = mapCliError(error, { aborted: false });
|
|
94
|
+
const remoteError = error instanceof RemoteRequestError
|
|
95
|
+
|| error instanceof RemoteProtocolError
|
|
96
|
+
|| error instanceof ToolTaskFailedError
|
|
97
|
+
? error
|
|
98
|
+
: undefined;
|
|
90
99
|
return {
|
|
91
100
|
outcome: outcome.kind === "interrupted" ? "interrupted" : "failure",
|
|
92
101
|
...(outcome.kind === "failure" ? { errorCode: outcome.failure.code } : {}),
|
|
93
102
|
...(error instanceof RemoteRequestError && error.status !== undefined
|
|
94
103
|
? { httpStatus: error.status }
|
|
95
|
-
: {})
|
|
104
|
+
: {}),
|
|
105
|
+
...(remoteError?.remoteCode === undefined
|
|
106
|
+
? {}
|
|
107
|
+
: { remoteErrorCode: remoteError.remoteCode }),
|
|
108
|
+
...(remoteError?.remoteBody === undefined
|
|
109
|
+
? {}
|
|
110
|
+
: { remoteErrorBody: remoteError.remoteBody })
|
|
96
111
|
};
|
|
97
112
|
}
|
|
98
113
|
function failure(code, message, exitCode, nextSteps) {
|
|
@@ -32,7 +32,6 @@ export function registerModelCommands(program, runtime) {
|
|
|
32
32
|
const get = model.command("get")
|
|
33
33
|
.description("查看模型完整信息")
|
|
34
34
|
.argument("<model>", "模型机器标识")
|
|
35
|
-
.option("--schema", "输出所选模型的实际运行时输入 Schema")
|
|
36
35
|
.allowExcessArguments(false);
|
|
37
36
|
bindAction(get, "organization", async (context, signal) => {
|
|
38
37
|
const modelName = get.processedArgs[0];
|
|
@@ -46,18 +45,6 @@ export function registerModelCommands(program, runtime) {
|
|
|
46
45
|
credential: context.state.credential,
|
|
47
46
|
organizationId: context.state.organization.id
|
|
48
47
|
};
|
|
49
|
-
if (get.opts().schema === true) {
|
|
50
|
-
const schema = await runtime.tool.getModelArgumentsSchema({
|
|
51
|
-
model: modelName,
|
|
52
|
-
access,
|
|
53
|
-
signal
|
|
54
|
-
});
|
|
55
|
-
runtime.telemetry.annotate({ model_id: modelName });
|
|
56
|
-
await runtime.telemetry.stage("output", async () => {
|
|
57
|
-
runtime.io.writeOut(`${JSON.stringify(schema)}\n`);
|
|
58
|
-
});
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
48
|
const result = await runtime.tool.getModel({
|
|
62
49
|
input,
|
|
63
50
|
access,
|
|
@@ -69,9 +56,6 @@ export function registerModelCommands(program, runtime) {
|
|
|
69
56
|
runtime.io.writeOut(`${JSON.stringify(result)}\n`);
|
|
70
57
|
});
|
|
71
58
|
}, {
|
|
72
|
-
operation: "model.get"
|
|
73
|
-
mode: () => get.opts().schema === true
|
|
74
|
-
? "schema"
|
|
75
|
-
: "detail"
|
|
59
|
+
operation: "model.get"
|
|
76
60
|
});
|
|
77
61
|
}
|
|
@@ -30,8 +30,8 @@ export function createPresenter(options) {
|
|
|
30
30
|
options.writeError(`${JSON.stringify({
|
|
31
31
|
error: {
|
|
32
32
|
...outcome.failure,
|
|
33
|
-
...(presentation.
|
|
34
|
-
? {
|
|
33
|
+
...(presentation.traceId
|
|
34
|
+
? { trace_id: presentation.traceId }
|
|
35
35
|
: {})
|
|
36
36
|
}
|
|
37
37
|
})}\n`);
|
|
@@ -42,8 +42,8 @@ export function createPresenter(options) {
|
|
|
42
42
|
if (nextSteps.length > 0) {
|
|
43
43
|
options.writeError(`下一步: ${nextSteps.join(",")}\n`);
|
|
44
44
|
}
|
|
45
|
-
if (presentation.
|
|
46
|
-
options.writeError(`诊断 ID: ${presentation.
|
|
45
|
+
if (presentation.traceId) {
|
|
46
|
+
options.writeError(`诊断 ID: ${presentation.traceId}\n`);
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { randomInt } from "node:crypto";
|
|
2
2
|
import { setTimeout as delay } from "node:timers/promises";
|
|
3
3
|
import { RemoteRequestError } from "../../platform/signed-http-transport.js";
|
|
4
|
+
import { remoteErrorEvidence } from "../../platform/remote-error-evidence.js";
|
|
5
|
+
import { RemoteProtocolError } from "../../platform/remote-protocol.js";
|
|
4
6
|
const MAX_JAVA_LONG = 9223372036854775807n;
|
|
5
7
|
function isRecord(value) {
|
|
6
8
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -8,6 +10,9 @@ function isRecord(value) {
|
|
|
8
10
|
function fail() {
|
|
9
11
|
throw new RemoteRequestError();
|
|
10
12
|
}
|
|
13
|
+
function protocolFailure(value) {
|
|
14
|
+
throw new RemoteProtocolError(remoteErrorEvidence(value));
|
|
15
|
+
}
|
|
11
16
|
function nonBlank(value) {
|
|
12
17
|
if (typeof value !== "string" || value.trim() === "")
|
|
13
18
|
return fail();
|
|
@@ -42,10 +47,33 @@ function parseIntent(value) {
|
|
|
42
47
|
arguments: parseRecord(nonBlank(value.arguments))
|
|
43
48
|
};
|
|
44
49
|
}
|
|
50
|
+
function isEmptyOptionalMedia(value) {
|
|
51
|
+
return value === undefined
|
|
52
|
+
|| value === null
|
|
53
|
+
|| value === ""
|
|
54
|
+
|| (Array.isArray(value) && value.length === 0)
|
|
55
|
+
|| (typeof value === "string" && value.trim() === "[]");
|
|
56
|
+
}
|
|
57
|
+
function withoutEmptyOptionalMedia(value, optionalMediaParameters) {
|
|
58
|
+
const normalized = { ...value };
|
|
59
|
+
for (const name of optionalMediaParameters) {
|
|
60
|
+
if (isEmptyOptionalMedia(normalized[name]))
|
|
61
|
+
delete normalized[name];
|
|
62
|
+
}
|
|
63
|
+
return normalized;
|
|
64
|
+
}
|
|
45
65
|
function submittedTask(value) {
|
|
46
|
-
if (!isRecord(value) || !isRecord(value.result)
|
|
47
|
-
return
|
|
48
|
-
|
|
66
|
+
if (!isRecord(value) || !isRecord(value.result))
|
|
67
|
+
return protocolFailure(value);
|
|
68
|
+
if (value.result.isError === true) {
|
|
69
|
+
throw new ToolTaskFailedError(remoteErrorEvidence(value));
|
|
70
|
+
}
|
|
71
|
+
if (value.result.isError !== false)
|
|
72
|
+
return protocolFailure(value);
|
|
73
|
+
if (typeof value.task_id !== "string" || value.task_id.trim() === "") {
|
|
74
|
+
return protocolFailure(value);
|
|
75
|
+
}
|
|
76
|
+
return value.task_id;
|
|
49
77
|
}
|
|
50
78
|
function resourceName(uri) {
|
|
51
79
|
let url;
|
|
@@ -53,32 +81,42 @@ function resourceName(uri) {
|
|
|
53
81
|
url = new URL(uri);
|
|
54
82
|
}
|
|
55
83
|
catch {
|
|
56
|
-
return
|
|
84
|
+
return protocolFailure(uri);
|
|
57
85
|
}
|
|
58
86
|
const segment = url.pathname.split("/").filter(Boolean).at(-1);
|
|
59
87
|
if (segment === undefined)
|
|
60
|
-
return
|
|
88
|
+
return protocolFailure(uri);
|
|
61
89
|
try {
|
|
62
|
-
|
|
90
|
+
const decoded = decodeURIComponent(segment);
|
|
91
|
+
if (decoded.trim() === "")
|
|
92
|
+
return protocolFailure(uri);
|
|
93
|
+
return decoded;
|
|
63
94
|
}
|
|
64
95
|
catch {
|
|
65
|
-
return
|
|
96
|
+
return protocolFailure(uri);
|
|
66
97
|
}
|
|
67
98
|
}
|
|
68
99
|
function normalizeContent(value) {
|
|
69
100
|
if (!isRecord(value))
|
|
70
|
-
return
|
|
101
|
+
return protocolFailure(value);
|
|
71
102
|
if (value.type === "text") {
|
|
72
|
-
|
|
103
|
+
if (typeof value.text !== "string" || value.text.trim() === "") {
|
|
104
|
+
return protocolFailure(value);
|
|
105
|
+
}
|
|
106
|
+
return { type: "text", text: value.text };
|
|
107
|
+
}
|
|
108
|
+
if (value.type !== "resource" || !isRecord(value.resource)) {
|
|
109
|
+
return protocolFailure(value);
|
|
73
110
|
}
|
|
74
|
-
if (value.type !== "resource" || !isRecord(value.resource))
|
|
75
|
-
return fail();
|
|
76
111
|
if ("blob" in value.resource || "text" in value.resource)
|
|
77
|
-
return
|
|
78
|
-
|
|
112
|
+
return protocolFailure(value);
|
|
113
|
+
if (typeof value.resource.uri !== "string" || value.resource.uri.trim() === "") {
|
|
114
|
+
return protocolFailure(value);
|
|
115
|
+
}
|
|
116
|
+
const uri = value.resource.uri;
|
|
79
117
|
const mimeType = value.resource.mimeType;
|
|
80
118
|
if (mimeType !== undefined && (typeof mimeType !== "string" || mimeType.trim() === "")) {
|
|
81
|
-
return
|
|
119
|
+
return protocolFailure(value);
|
|
82
120
|
}
|
|
83
121
|
return {
|
|
84
122
|
type: "resource_link",
|
|
@@ -87,23 +125,118 @@ function normalizeContent(value) {
|
|
|
87
125
|
...(mimeType === undefined ? {} : { mimeType })
|
|
88
126
|
};
|
|
89
127
|
}
|
|
128
|
+
export class ToolTaskFailedError extends Error {
|
|
129
|
+
remoteCode;
|
|
130
|
+
remoteBody;
|
|
131
|
+
constructor(evidence = {}) {
|
|
132
|
+
super("稿定工具任务执行失败。");
|
|
133
|
+
this.name = "ToolTaskFailedError";
|
|
134
|
+
this.remoteCode = evidence.remoteCode;
|
|
135
|
+
this.remoteBody = evidence.remoteBody;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function failedPoll(error, difyTaskId) {
|
|
139
|
+
return {
|
|
140
|
+
state: "failed",
|
|
141
|
+
error,
|
|
142
|
+
...(difyTaskId === undefined ? {} : { difyTaskId })
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function optionalInnerDifyTaskId(value) {
|
|
146
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
147
|
+
return undefined;
|
|
148
|
+
try {
|
|
149
|
+
const parsed = JSON.parse(value);
|
|
150
|
+
if (!isRecord(parsed))
|
|
151
|
+
return undefined;
|
|
152
|
+
const difyTaskId = parsed.dify_task_id;
|
|
153
|
+
return typeof difyTaskId === "string" && difyTaskId.trim() !== ""
|
|
154
|
+
? difyTaskId
|
|
155
|
+
: undefined;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function isJsonRpcError(value) {
|
|
162
|
+
if (value.jsonrpc !== "2.0"
|
|
163
|
+
|| Object.prototype.hasOwnProperty.call(value, "result")
|
|
164
|
+
|| !isRecord(value.error)) {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
return typeof value.error.code === "number"
|
|
168
|
+
&& Number.isInteger(value.error.code)
|
|
169
|
+
&& typeof value.error.message === "string"
|
|
170
|
+
&& value.error.message.trim() !== "";
|
|
171
|
+
}
|
|
90
172
|
function pollResult(value, taskId) {
|
|
91
173
|
if (!Array.isArray(value))
|
|
92
|
-
return
|
|
174
|
+
return protocolFailure(value);
|
|
93
175
|
const item = value.find((candidate) => isRecord(candidate) && candidate.task_id === taskId);
|
|
94
|
-
if (!isRecord(item)
|
|
95
|
-
return
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
176
|
+
if (!isRecord(item))
|
|
177
|
+
return { state: "pending" };
|
|
178
|
+
if (typeof item.code !== "string" || item.code.trim() === "") {
|
|
179
|
+
return protocolFailure(item);
|
|
180
|
+
}
|
|
181
|
+
if (item.code !== "200") {
|
|
182
|
+
return failedPoll(new ToolTaskFailedError(remoteErrorEvidence(item)), optionalInnerDifyTaskId(item.result));
|
|
183
|
+
}
|
|
184
|
+
if (typeof item.result !== "string" || item.result.trim() === "") {
|
|
185
|
+
return protocolFailure(item);
|
|
186
|
+
}
|
|
187
|
+
let inner;
|
|
188
|
+
try {
|
|
189
|
+
const parsed = JSON.parse(item.result);
|
|
190
|
+
if (!isRecord(parsed))
|
|
191
|
+
return protocolFailure(item);
|
|
192
|
+
inner = parsed;
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
if (error instanceof RemoteProtocolError)
|
|
196
|
+
throw error;
|
|
197
|
+
return protocolFailure(item);
|
|
198
|
+
}
|
|
199
|
+
const difyTaskId = inner.dify_task_id;
|
|
200
|
+
if (difyTaskId !== undefined
|
|
201
|
+
&& (typeof difyTaskId !== "string" || difyTaskId.trim() === "")) {
|
|
202
|
+
return protocolFailure(inner);
|
|
203
|
+
}
|
|
204
|
+
if (Object.prototype.hasOwnProperty.call(inner, "error")) {
|
|
205
|
+
return isJsonRpcError(inner)
|
|
206
|
+
? failedPoll(new ToolTaskFailedError(remoteErrorEvidence(inner)), difyTaskId)
|
|
207
|
+
: failedPoll(new RemoteProtocolError(remoteErrorEvidence(inner)), difyTaskId);
|
|
208
|
+
}
|
|
209
|
+
if (!isRecord(inner.result)) {
|
|
210
|
+
return failedPoll(new RemoteProtocolError(remoteErrorEvidence(inner)), difyTaskId);
|
|
211
|
+
}
|
|
212
|
+
if (inner.result.isError === true) {
|
|
213
|
+
return failedPoll(new ToolTaskFailedError(remoteErrorEvidence(inner)), difyTaskId);
|
|
214
|
+
}
|
|
215
|
+
if (inner.result.isError !== false) {
|
|
216
|
+
return failedPoll(new RemoteProtocolError(remoteErrorEvidence(inner)), difyTaskId);
|
|
217
|
+
}
|
|
99
218
|
const content = inner.result.content;
|
|
100
|
-
if (content === undefined)
|
|
101
|
-
return
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
219
|
+
if (content === undefined || (Array.isArray(content) && content.length === 0)) {
|
|
220
|
+
return {
|
|
221
|
+
state: "pending",
|
|
222
|
+
...(difyTaskId === undefined ? {} : { difyTaskId })
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (!Array.isArray(content)) {
|
|
226
|
+
return failedPoll(new RemoteProtocolError(remoteErrorEvidence(inner)), difyTaskId);
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
return {
|
|
230
|
+
state: "complete",
|
|
231
|
+
content: content.map(normalizeContent),
|
|
232
|
+
...(difyTaskId === undefined ? {} : { difyTaskId })
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
if (error instanceof RemoteProtocolError)
|
|
237
|
+
return failedPoll(error, difyTaskId);
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
107
240
|
}
|
|
108
241
|
async function safePost(transport, request) {
|
|
109
242
|
try {
|
|
@@ -112,7 +245,7 @@ async function safePost(transport, request) {
|
|
|
112
245
|
catch (error) {
|
|
113
246
|
if (request.signal.aborted)
|
|
114
247
|
throw request.signal.reason;
|
|
115
|
-
if (error instanceof RemoteRequestError)
|
|
248
|
+
if (error instanceof RemoteRequestError || error instanceof RemoteProtocolError)
|
|
116
249
|
throw error;
|
|
117
250
|
throw new RemoteRequestError();
|
|
118
251
|
}
|
|
@@ -138,33 +271,45 @@ export function createToolApiAdapter(dependencies) {
|
|
|
138
271
|
async execute({ invocation, access, signal }) {
|
|
139
272
|
signal.throwIfAborted();
|
|
140
273
|
const managedContentId = contentId(createContentId());
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
274
|
+
const resolved = invocation.kind === "direct"
|
|
275
|
+
? { name: invocation.name, arguments: invocation.arguments }
|
|
276
|
+
: await dependencies.telemetry.stage("intent", async () => {
|
|
277
|
+
const intent = parseIntent(await safePost(dependencies.transport, {
|
|
278
|
+
path: "/ai-agent/v1/tool-intent",
|
|
279
|
+
body: {
|
|
280
|
+
prompt: invocation.prompt,
|
|
281
|
+
scene_code: invocation.model,
|
|
282
|
+
parameters: invocation.parameters
|
|
283
|
+
},
|
|
284
|
+
credential: access.credential,
|
|
285
|
+
organizationId: access.organizationId,
|
|
286
|
+
signal
|
|
287
|
+
}));
|
|
288
|
+
return {
|
|
289
|
+
...intent,
|
|
290
|
+
arguments: withoutEmptyOptionalMedia(intent.arguments, invocation.optionalMediaParameters)
|
|
291
|
+
};
|
|
292
|
+
});
|
|
152
293
|
signal.throwIfAborted();
|
|
153
|
-
const taskId = await dependencies.telemetry.stage("submit", async () =>
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
294
|
+
const taskId = await dependencies.telemetry.stage("submit", async () => {
|
|
295
|
+
dependencies.telemetry.annotate({ content_id: managedContentId });
|
|
296
|
+
return submittedTask(await safePost(dependencies.transport, {
|
|
297
|
+
path: "/gdesign/tool/v1/dify/call_async",
|
|
298
|
+
body: {
|
|
299
|
+
jsonrpc: "2.0",
|
|
300
|
+
id: 0,
|
|
301
|
+
method: "tools/call",
|
|
302
|
+
params: {
|
|
303
|
+
name: resolved.name,
|
|
304
|
+
arguments: { ...resolved.arguments, content_id: managedContentId }
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
credential: access.credential,
|
|
308
|
+
organizationId: access.organizationId,
|
|
309
|
+
signal
|
|
310
|
+
}));
|
|
311
|
+
});
|
|
312
|
+
dependencies.telemetry.annotate({ task_id: taskId });
|
|
168
313
|
return dependencies.telemetry.stage("wait", async () => {
|
|
169
314
|
let transientFailures = 0;
|
|
170
315
|
while (true) {
|
|
@@ -191,9 +336,14 @@ export function createToolApiAdapter(dependencies) {
|
|
|
191
336
|
await sleep(transientFailures * 1_000, signal);
|
|
192
337
|
continue;
|
|
193
338
|
}
|
|
194
|
-
const
|
|
195
|
-
if (
|
|
196
|
-
|
|
339
|
+
const poll = pollResult(response, taskId);
|
|
340
|
+
if (poll.difyTaskId !== undefined) {
|
|
341
|
+
dependencies.telemetry.annotate({ dify_task_id: poll.difyTaskId });
|
|
342
|
+
}
|
|
343
|
+
if (poll.state === "failed")
|
|
344
|
+
throw poll.error;
|
|
345
|
+
if (poll.state === "complete")
|
|
346
|
+
return poll.content;
|
|
197
347
|
await sleep(2_000, signal);
|
|
198
348
|
}
|
|
199
349
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseSafeRemoteAssetUrl, UrlSafetyError } from "../../platform/url-safety.js";
|
|
2
|
-
import {
|
|
3
|
-
import { assertModelArguments,
|
|
2
|
+
import { findToolModel, projectModelDetail, projectModelList, projectToolList, ToolInputError } from "./catalog.js";
|
|
3
|
+
import { assertModelArguments, buildToolArgumentsSchema } from "./dynamic-schema.js";
|
|
4
4
|
const mediaTypes = {
|
|
5
5
|
avif: "image/avif",
|
|
6
6
|
gif: "image/gif",
|
|
@@ -67,9 +67,6 @@ export function createToolUseCases(dependencies) {
|
|
|
67
67
|
async getArgumentsSchema({ name, access, signal }) {
|
|
68
68
|
return buildToolArgumentsSchema(await load(access, signal), name);
|
|
69
69
|
},
|
|
70
|
-
async getModelArgumentsSchema({ model, access, signal }) {
|
|
71
|
-
return buildModelArgumentsSchema(findModel(await load(access, signal), model));
|
|
72
|
-
},
|
|
73
70
|
async call({ input, access, signal }) {
|
|
74
71
|
const catalog = await load(access, signal);
|
|
75
72
|
const model = await dependencies.telemetry.stage("input", () => {
|
|
@@ -89,6 +86,7 @@ export function createToolUseCases(dependencies) {
|
|
|
89
86
|
});
|
|
90
87
|
const prompt = [];
|
|
91
88
|
const requestParameters = {};
|
|
89
|
+
const directArguments = {};
|
|
92
90
|
const prepare = async () => {
|
|
93
91
|
for (const parameter of model.parameters) {
|
|
94
92
|
signal.throwIfAborted();
|
|
@@ -98,14 +96,17 @@ export function createToolUseCases(dependencies) {
|
|
|
98
96
|
if (parameter.type === "string") {
|
|
99
97
|
if (parameter.name === "prompt") {
|
|
100
98
|
prompt.push({ type: "text", content: value });
|
|
99
|
+
directArguments[parameter.name] = value;
|
|
101
100
|
}
|
|
102
101
|
else {
|
|
103
102
|
requestParameters[parameter.name] = value;
|
|
103
|
+
directArguments[parameter.name] = value;
|
|
104
104
|
}
|
|
105
105
|
continue;
|
|
106
106
|
}
|
|
107
107
|
if (parameter.type === "number") {
|
|
108
108
|
requestParameters[parameter.name] = value;
|
|
109
|
+
directArguments[parameter.name] = value;
|
|
109
110
|
continue;
|
|
110
111
|
}
|
|
111
112
|
const values = parameter.type === "uri[]" ? value : [value];
|
|
@@ -141,8 +142,26 @@ export function createToolUseCases(dependencies) {
|
|
|
141
142
|
await prepare();
|
|
142
143
|
}
|
|
143
144
|
signal.throwIfAborted();
|
|
145
|
+
const requiresIntent = prompt.some((item) => item.type === "media")
|
|
146
|
+
|| Object.values(requestParameters).some((value) => value === "");
|
|
147
|
+
const invocation = requiresIntent
|
|
148
|
+
? {
|
|
149
|
+
kind: "intent",
|
|
150
|
+
model: model.model,
|
|
151
|
+
prompt,
|
|
152
|
+
parameters: requestParameters,
|
|
153
|
+
optionalMediaParameters: model.parameters
|
|
154
|
+
.filter((parameter) => ((parameter.type === "uri" || parameter.type === "uri[]")
|
|
155
|
+
&& !parameter.required))
|
|
156
|
+
.map((parameter) => parameter.name)
|
|
157
|
+
}
|
|
158
|
+
: {
|
|
159
|
+
kind: "direct",
|
|
160
|
+
name: model.model,
|
|
161
|
+
arguments: directArguments
|
|
162
|
+
};
|
|
144
163
|
const content = await dependencies.execution.execute({
|
|
145
|
-
invocation
|
|
164
|
+
invocation,
|
|
146
165
|
access,
|
|
147
166
|
signal
|
|
148
167
|
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { redact, redactSensitiveText } from "./redact.js";
|
|
2
|
+
const maximumLength = 8_192;
|
|
3
|
+
const truncationMarker = "[TRUNCATED]";
|
|
4
|
+
export function remoteErrorEvidence(value) {
|
|
5
|
+
const normalized = parseJson(value);
|
|
6
|
+
const remoteCode = topLevelCode(normalized) ?? jsonRpcErrorCode(normalized);
|
|
7
|
+
const sanitized = sanitize(normalized);
|
|
8
|
+
if (sanitized === undefined) {
|
|
9
|
+
return remoteCode === undefined ? {} : { remoteCode };
|
|
10
|
+
}
|
|
11
|
+
const remoteBody = sanitized.length <= maximumLength
|
|
12
|
+
? sanitized
|
|
13
|
+
: `${sanitized.slice(0, maximumLength - truncationMarker.length)}${truncationMarker}`;
|
|
14
|
+
return {
|
|
15
|
+
...(remoteCode === undefined ? {} : { remoteCode }),
|
|
16
|
+
remoteBody
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function parseJson(value) {
|
|
20
|
+
if (typeof value !== "string")
|
|
21
|
+
return value;
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(value);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function topLevelCode(value) {
|
|
30
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
31
|
+
return undefined;
|
|
32
|
+
if (!Object.prototype.hasOwnProperty.call(value, "code"))
|
|
33
|
+
return undefined;
|
|
34
|
+
return normalizeCode(value.code);
|
|
35
|
+
}
|
|
36
|
+
function jsonRpcErrorCode(value) {
|
|
37
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
38
|
+
return undefined;
|
|
39
|
+
const record = value;
|
|
40
|
+
if (record.jsonrpc !== "2.0"
|
|
41
|
+
|| record.error === null
|
|
42
|
+
|| typeof record.error !== "object"
|
|
43
|
+
|| Array.isArray(record.error)) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
return normalizeCode(record.error.code);
|
|
47
|
+
}
|
|
48
|
+
function normalizeCode(code) {
|
|
49
|
+
if (typeof code === "string")
|
|
50
|
+
return redactRemoteText(code);
|
|
51
|
+
return typeof code === "number" ? String(code) : undefined;
|
|
52
|
+
}
|
|
53
|
+
function sanitize(value) {
|
|
54
|
+
if (typeof value === "string")
|
|
55
|
+
return redactRemoteText(value);
|
|
56
|
+
try {
|
|
57
|
+
return JSON.stringify(redactCookieAssignments(redact(value)));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function redactRemoteText(value) {
|
|
64
|
+
return redactCookieAssignment(redactSensitiveText(value));
|
|
65
|
+
}
|
|
66
|
+
function redactCookieAssignments(value) {
|
|
67
|
+
if (typeof value === "string")
|
|
68
|
+
return redactCookieAssignment(value);
|
|
69
|
+
if (value === null || typeof value !== "object")
|
|
70
|
+
return value;
|
|
71
|
+
if (Array.isArray(value))
|
|
72
|
+
return value.map(redactCookieAssignments);
|
|
73
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
74
|
+
key,
|
|
75
|
+
redactCookieAssignments(item)
|
|
76
|
+
]));
|
|
77
|
+
}
|
|
78
|
+
function redactCookieAssignment(value) {
|
|
79
|
+
return value.replace(/\b(cookie)\s*=\s*[^\s,;}&]+/giu, "$1=[REDACTED]");
|
|
80
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export class RemoteProtocolError extends Error {
|
|
2
|
-
|
|
2
|
+
remoteCode;
|
|
3
|
+
remoteBody;
|
|
4
|
+
constructor(evidence = {}) {
|
|
3
5
|
super("稿定服务响应与当前 CLI 不兼容。");
|
|
4
6
|
this.name = "RemoteProtocolError";
|
|
7
|
+
this.remoteCode = evidence.remoteCode;
|
|
8
|
+
this.remoteBody = evidence.remoteBody;
|
|
5
9
|
}
|
|
6
10
|
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
import { remoteErrorEvidence } from "./remote-error-evidence.js";
|
|
2
|
+
import { RemoteProtocolError } from "./remote-protocol.js";
|
|
1
3
|
import { canonicalizeQuery, signRequest } from "./signature.js";
|
|
2
4
|
export class RemoteRequestError extends Error {
|
|
3
5
|
status;
|
|
4
6
|
transient;
|
|
5
|
-
|
|
7
|
+
remoteCode;
|
|
8
|
+
remoteBody;
|
|
9
|
+
constructor(status, transient = false, evidence = {}) {
|
|
6
10
|
super(status === undefined ? "无法连接稿定服务。" : `稿定服务请求失败(HTTP ${status})。`);
|
|
7
11
|
this.name = "RemoteRequestError";
|
|
8
12
|
this.status = status;
|
|
9
13
|
this.transient = transient;
|
|
14
|
+
this.remoteCode = evidence.remoteCode;
|
|
15
|
+
this.remoteBody = evidence.remoteBody;
|
|
10
16
|
}
|
|
11
17
|
}
|
|
12
18
|
const TRANSIENT_NETWORK_CODES = new Set([
|
|
@@ -56,6 +62,8 @@ export function createSignedHttpTransport(options) {
|
|
|
56
62
|
}),
|
|
57
63
|
"X-Timestamp": String(timestamp)
|
|
58
64
|
};
|
|
65
|
+
if (options.traceparent !== undefined)
|
|
66
|
+
headers.traceparent = options.traceparent();
|
|
59
67
|
if (options.channelId !== undefined)
|
|
60
68
|
headers["X-Channel-Id"] = options.channelId;
|
|
61
69
|
if (body !== undefined)
|
|
@@ -83,14 +91,22 @@ export function createSignedHttpTransport(options) {
|
|
|
83
91
|
}
|
|
84
92
|
}
|
|
85
93
|
async function json(response) {
|
|
86
|
-
|
|
87
|
-
throw new RemoteRequestError(response.status);
|
|
94
|
+
let body;
|
|
88
95
|
try {
|
|
89
|
-
|
|
96
|
+
body = await response.text();
|
|
90
97
|
}
|
|
91
98
|
catch {
|
|
92
99
|
throw new RemoteRequestError(response.status);
|
|
93
100
|
}
|
|
101
|
+
const evidence = remoteErrorEvidence(body);
|
|
102
|
+
if (!response.ok)
|
|
103
|
+
throw new RemoteRequestError(response.status, false, evidence);
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(body);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
throw new RemoteProtocolError(evidence);
|
|
109
|
+
}
|
|
94
110
|
}
|
|
95
111
|
return {
|
|
96
112
|
async getJson(request) {
|
|
@@ -101,7 +117,17 @@ export function createSignedHttpTransport(options) {
|
|
|
101
117
|
},
|
|
102
118
|
async postStream(request) {
|
|
103
119
|
const response = await send("POST", request, request.accept, JSON.stringify(request.body), false);
|
|
104
|
-
if (!response.ok
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
let body;
|
|
122
|
+
try {
|
|
123
|
+
body = await response.text();
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
throw new RemoteRequestError(response.status);
|
|
127
|
+
}
|
|
128
|
+
throw new RemoteRequestError(response.status, false, remoteErrorEvidence(body));
|
|
129
|
+
}
|
|
130
|
+
if (!response.body)
|
|
105
131
|
throw new RemoteRequestError(response.status);
|
|
106
132
|
return response.body;
|
|
107
133
|
}
|
|
@@ -7,16 +7,19 @@ const detailedStages = {
|
|
|
7
7
|
update: new Set(["registry", "install", "sync", "verify"])
|
|
8
8
|
};
|
|
9
9
|
export function createTelemetryInvocation(options) {
|
|
10
|
-
const
|
|
10
|
+
const traceId = options.traceIdFactory();
|
|
11
11
|
const startedAt = options.now();
|
|
12
12
|
let definition;
|
|
13
13
|
const context = {};
|
|
14
14
|
const stages = [];
|
|
15
15
|
return {
|
|
16
|
-
|
|
16
|
+
traceId,
|
|
17
17
|
get selected() {
|
|
18
18
|
return definition !== undefined;
|
|
19
19
|
},
|
|
20
|
+
traceparent() {
|
|
21
|
+
return `00-${traceId}-${options.spanIdFactory()}-${traceFlags(definition)}`;
|
|
22
|
+
},
|
|
20
23
|
select(nextDefinition) {
|
|
21
24
|
definition = nextDefinition;
|
|
22
25
|
},
|
|
@@ -30,6 +33,13 @@ export function createTelemetryInvocation(options) {
|
|
|
30
33
|
context.tool_name = annotation.tool_name;
|
|
31
34
|
if (annotation.model_id !== undefined)
|
|
32
35
|
context.model_id = annotation.model_id;
|
|
36
|
+
if (annotation.content_id !== undefined)
|
|
37
|
+
context.content_id = annotation.content_id;
|
|
38
|
+
if (annotation.task_id !== undefined)
|
|
39
|
+
context.task_id = annotation.task_id;
|
|
40
|
+
if (annotation.dify_task_id !== undefined) {
|
|
41
|
+
context.dify_task_id = annotation.dify_task_id;
|
|
42
|
+
}
|
|
33
43
|
if (annotation.parameters !== undefined) {
|
|
34
44
|
context.parameters = { ...annotation.parameters };
|
|
35
45
|
}
|
|
@@ -72,9 +82,9 @@ export function createTelemetryInvocation(options) {
|
|
|
72
82
|
? stages
|
|
73
83
|
.filter((stage) => allowedStages.has(stage.name))
|
|
74
84
|
.map((stage) => ({
|
|
75
|
-
schema_version: "
|
|
85
|
+
schema_version: "2",
|
|
76
86
|
event_name: "stage_finished",
|
|
77
|
-
|
|
87
|
+
trace_id: traceId,
|
|
78
88
|
operation: selectedDefinition.operation,
|
|
79
89
|
stage: stage.name,
|
|
80
90
|
outcome: stage.outcome,
|
|
@@ -84,9 +94,9 @@ export function createTelemetryInvocation(options) {
|
|
|
84
94
|
}))
|
|
85
95
|
: [];
|
|
86
96
|
const command = {
|
|
87
|
-
schema_version: "
|
|
97
|
+
schema_version: "2",
|
|
88
98
|
event_name: "command_finished",
|
|
89
|
-
|
|
99
|
+
trace_id: traceId,
|
|
90
100
|
operation: selectedDefinition.operation,
|
|
91
101
|
...(mode === undefined ? {} : { mode }),
|
|
92
102
|
outcome: completion.outcome,
|
|
@@ -101,6 +111,11 @@ export function createTelemetryInvocation(options) {
|
|
|
101
111
|
: { organization_id: context.organization_id }),
|
|
102
112
|
...(context.tool_name === undefined ? {} : { tool_name: context.tool_name }),
|
|
103
113
|
...(context.model_id === undefined ? {} : { model_id: context.model_id }),
|
|
114
|
+
...(context.content_id === undefined ? {} : { content_id: context.content_id }),
|
|
115
|
+
...(context.task_id === undefined ? {} : { task_id: context.task_id }),
|
|
116
|
+
...(context.dify_task_id === undefined
|
|
117
|
+
? {}
|
|
118
|
+
: { dify_task_id: context.dify_task_id }),
|
|
104
119
|
...(context.parameters === undefined
|
|
105
120
|
? {}
|
|
106
121
|
: { parameters: { ...context.parameters } }),
|
|
@@ -112,7 +127,13 @@ export function createTelemetryInvocation(options) {
|
|
|
112
127
|
?? "unknown",
|
|
113
128
|
...(completion.httpStatus === undefined
|
|
114
129
|
? {}
|
|
115
|
-
: { http_status: completion.httpStatus })
|
|
130
|
+
: { http_status: completion.httpStatus }),
|
|
131
|
+
...(completion.remoteErrorCode === undefined
|
|
132
|
+
? {}
|
|
133
|
+
: { remote_error_code: completion.remoteErrorCode }),
|
|
134
|
+
...(completion.remoteErrorBody === undefined
|
|
135
|
+
? {}
|
|
136
|
+
: { remote_error_body: completion.remoteErrorBody })
|
|
116
137
|
}
|
|
117
138
|
: {})
|
|
118
139
|
};
|
|
@@ -120,6 +141,13 @@ export function createTelemetryInvocation(options) {
|
|
|
120
141
|
}
|
|
121
142
|
};
|
|
122
143
|
}
|
|
144
|
+
function traceFlags(definition) {
|
|
145
|
+
if (definition === undefined)
|
|
146
|
+
return "00";
|
|
147
|
+
const paidExecution = (definition.operation === "agent.send"
|
|
148
|
+
|| definition.operation === "tool.call") && definition.mode?.() === "execute";
|
|
149
|
+
return paidExecution ? "01" : "00";
|
|
150
|
+
}
|
|
123
151
|
function stageKey(operation, mode) {
|
|
124
152
|
return mode === undefined ? operation : `${operation}:${mode}`;
|
|
125
153
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## 选择入口
|
|
4
4
|
|
|
5
5
|
- 目标开放、需要稿定 Agent 完成创作时,使用 `gd-cli agent send`。
|
|
6
|
-
- 已知要调用具体图片、视频或文本能力时,依次发现 Tool、选择 Model
|
|
6
|
+
- 已知要调用具体图片、视频或文本能力时,依次发现 Tool、选择 Model、读取 Model detail,再调用 Tool。
|
|
7
7
|
- 不猜 Tool 名、Model 名或参数;以当前命令输出为准。
|
|
8
8
|
|
|
9
9
|
## Agent 创作
|
|
@@ -25,16 +25,25 @@ gd-cli agent send --input request.json
|
|
|
25
25
|
gd-cli tool list
|
|
26
26
|
gd-cli model list --tool <tool>
|
|
27
27
|
gd-cli model get <model>
|
|
28
|
-
gd-cli model get <model> --schema
|
|
29
28
|
gd-cli tool call <tool> --input request.json
|
|
30
29
|
```
|
|
31
30
|
|
|
32
|
-
`tool list` 给出可用 Tool;`model list` 和 `model get` 给出当前可用 Model
|
|
31
|
+
`tool list` 给出可用 Tool;`model list` 和 `model get` 给出当前可用 Model、说明、费用、预估耗时和结构化参数。`model get` 的 `parameters` 是所选 Model 的实际调用元数据:遵循 `required`、`type`、`description`,SELECT 参数使用 `options[].value`,required 参数带 `default` 时在用户未指定时显式传入默认值。`usageDescription` 描述跨字段规则,例如视频 mode 对首尾帧或素材字段的要求;这类组合不能只从单个字段推断。不要把列表值固化在提示或脚本中。
|
|
33
32
|
|
|
34
|
-
按所选 Model
|
|
33
|
+
按所选 Model detail 构造 JSON:提供全部 required 字段;SELECT 参数使用 `options[].value`;required 字段带 default 且用户未指定时,在输入中显式使用该 default。存在 width、height、resolution 等专用字段时,将用户要求写入对应结构化参数,不要只写在 prompt 中。`tool call <tool> --schema` 返回整个 Tool 下所有 Model 的参数并集,只用于 Tool 级发现;选定 Model 后以 `model get` 的参数和 `usageDescription` 为准。
|
|
34
|
+
|
|
35
|
+
务必将 `model get <model>` 返回的 `model` 原样放在请求 JSON 的顶层 `model` 字段(即运行时的 `arguments.model`),与所选 Model 的参数字段并列;不要只复制 `parameters`。例如:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"model": "<model>",
|
|
40
|
+
"prompt": "<required prompt>",
|
|
41
|
+
"<parameter>": "<value>"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
35
44
|
|
|
36
45
|
成功结果包含 `content` 和 `usage`。`content` 是文本或资源链接;`usage` 包含所选模型及模型目录公布的稿豆价格区间,不表示实际扣费。
|
|
37
46
|
|
|
38
47
|
## 本地媒体
|
|
39
48
|
|
|
40
|
-
按所选
|
|
49
|
+
按所选 Model detail 在输入中提交本地媒体引用。CLI 会在内部通过 DAM 完成临时上传并把公网 URL 传给创作服务;不要自行伪造 URL,也不要读取用户未明确授权的路径。
|