gaoding-cli 1.0.0-alpha.15 → 1.0.0-alpha.17
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 +2 -0
- package/contracts/operations/agent.send/input.schema.json +11 -16
- package/dist/src/bootstrap/create-runtime.js +4 -1
- package/dist/src/features/agent/creative-protocol.js +14 -19
- package/dist/src/features/agent/use-cases.js +33 -5
- package/dist/src/features/tool/dynamic-schema.js +22 -0
- package/dist/src/features/tool/tool-api-adapter.js +52 -13
- package/dist/src/features/tool/use-cases.js +23 -1
- package/dist/src/platform/remote-error-evidence.js +16 -2
- package/dist/src/telemetry/invocation.js +1 -1
- package/package.json +1 -1
- package/skills/gd-cli/SKILL.md +1 -1
- package/skills/gd-cli/references/creation.md +54 -7
package/README.md
CHANGED
|
@@ -33,6 +33,8 @@ gd-cli model get <model>
|
|
|
33
33
|
|
|
34
34
|
选定 Model 后,以 `model get` 返回的 `parameters` 与 `usageDescription` 构造实际输入;`tool call --schema` 只给出整个 Tool 的参数并集。运行 `gd-cli --help` 查看当前命令;运行任一命令的 `--help` 查看参数。`gd-cli update` 固定检查 npm `latest`,支持更新 npm、pnpm 全局安装并同步 Agent Skill。
|
|
35
35
|
|
|
36
|
+
发布切换必须先完成精确版本的安装验收,再执行仓库中的 `scripts/promote-latest.mjs`;不要直接用未校验的 `npm dist-tag add` 移动 `latest`。
|
|
37
|
+
|
|
36
38
|
## License
|
|
37
39
|
|
|
38
40
|
MIT
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"parts": {
|
|
61
61
|
"type": "array",
|
|
62
62
|
"contains": {
|
|
63
|
-
"$ref": "#/$defs/
|
|
63
|
+
"$ref": "#/$defs/GenerationConstraintsPart"
|
|
64
64
|
},
|
|
65
65
|
"minContains": 0,
|
|
66
66
|
"maxContains": 1
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
"$ref": "#/$defs/InputAssetPart"
|
|
146
146
|
},
|
|
147
147
|
{
|
|
148
|
-
"$ref": "#/$defs/
|
|
148
|
+
"$ref": "#/$defs/GenerationConstraintsPart"
|
|
149
149
|
},
|
|
150
150
|
{
|
|
151
151
|
"$ref": "#/$defs/QuestionAnswerPart"
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
213
|
},
|
|
214
|
-
"
|
|
214
|
+
"GenerationConstraintsPart": {
|
|
215
215
|
"type": "object",
|
|
216
216
|
"additionalProperties": false,
|
|
217
217
|
"required": [
|
|
@@ -222,24 +222,19 @@
|
|
|
222
222
|
"type": "object",
|
|
223
223
|
"additionalProperties": false,
|
|
224
224
|
"minProperties": 1,
|
|
225
|
-
"description": "
|
|
225
|
+
"description": "本次创作指定的模型与动态参数;它们不决定 Agent 或 Tool 路由。",
|
|
226
226
|
"properties": {
|
|
227
|
-
"
|
|
227
|
+
"model": {
|
|
228
228
|
"type": "string",
|
|
229
229
|
"minLength": 1,
|
|
230
230
|
"pattern": ".*\\S.*",
|
|
231
|
-
"description": "
|
|
231
|
+
"description": "可选创作模型机器标识;指定时由 CLI 根据实时 Model Catalog 校验。"
|
|
232
232
|
},
|
|
233
|
-
"
|
|
234
|
-
"type": "
|
|
235
|
-
"
|
|
236
|
-
"
|
|
237
|
-
"
|
|
238
|
-
},
|
|
239
|
-
"durationSeconds": {
|
|
240
|
-
"type": "integer",
|
|
241
|
-
"minimum": 1,
|
|
242
|
-
"description": "视频或音频时长,单位为秒。"
|
|
233
|
+
"arguments": {
|
|
234
|
+
"type": "object",
|
|
235
|
+
"minProperties": 1,
|
|
236
|
+
"description": "按模型公开字段传递的动态控制参数;素材使用独立 InputAssetPart。",
|
|
237
|
+
"additionalProperties": true
|
|
243
238
|
}
|
|
244
239
|
}
|
|
245
240
|
}
|
|
@@ -144,6 +144,7 @@ export function createProductionRuntime(options = {}) {
|
|
|
144
144
|
});
|
|
145
145
|
const browser = createBrowserOpener();
|
|
146
146
|
const validators = createContractValidators();
|
|
147
|
+
const modelCatalog = createMnsCatalogAdapter({ transport: mnsTransport });
|
|
147
148
|
const damApi = createDamApiAdapter({ transport: damTransport });
|
|
148
149
|
const dam = createDamUseCases({
|
|
149
150
|
api: damApi,
|
|
@@ -165,16 +166,18 @@ export function createProductionRuntime(options = {}) {
|
|
|
165
166
|
telemetry
|
|
166
167
|
});
|
|
167
168
|
const agent = createAgentUseCases({
|
|
169
|
+
catalog: modelCatalog,
|
|
168
170
|
uploader: transientUploader,
|
|
169
171
|
completion: createCreativeAgentAdapter({
|
|
170
172
|
transport: agentTransport,
|
|
171
173
|
idFactory: randomUUID,
|
|
172
174
|
telemetry
|
|
173
175
|
}),
|
|
176
|
+
warn: (message) => { presenter.warning(message); },
|
|
174
177
|
telemetry
|
|
175
178
|
});
|
|
176
179
|
const tool = createToolUseCases({
|
|
177
|
-
catalog:
|
|
180
|
+
catalog: modelCatalog,
|
|
178
181
|
execution: createToolApiAdapter({ transport: toolTransport, telemetry }),
|
|
179
182
|
uploader: transientUploader,
|
|
180
183
|
warn: (message) => { presenter.warning(message); },
|
|
@@ -3,11 +3,13 @@ export function buildCreativeRequest(message, ids) {
|
|
|
3
3
|
const prompt = [];
|
|
4
4
|
const textParts = [];
|
|
5
5
|
const attachments = [];
|
|
6
|
-
const parameters =
|
|
6
|
+
const parameters = Object.create(null);
|
|
7
7
|
const answerValues = [];
|
|
8
8
|
const answeredQuestions = [];
|
|
9
9
|
let questionMessageId;
|
|
10
10
|
let hasAnswers = false;
|
|
11
|
+
let hasGeneration = false;
|
|
12
|
+
let model;
|
|
11
13
|
for (const part of message.parts) {
|
|
12
14
|
if (part.kind === "text") {
|
|
13
15
|
textParts.push(part.text);
|
|
@@ -15,12 +17,6 @@ export function buildCreativeRequest(message, ids) {
|
|
|
15
17
|
continue;
|
|
16
18
|
}
|
|
17
19
|
if (part.kind === "asset") {
|
|
18
|
-
if (part.role === "first_frame" || part.role === "last_frame") {
|
|
19
|
-
prompt.push({
|
|
20
|
-
type: "text",
|
|
21
|
-
content: part.role === "first_frame" ? "首帧" : "尾帧"
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
20
|
prompt.push({
|
|
25
21
|
type: "media",
|
|
26
22
|
url: part.asset.url,
|
|
@@ -34,14 +30,10 @@ export function buildCreativeRequest(message, ids) {
|
|
|
34
30
|
});
|
|
35
31
|
continue;
|
|
36
32
|
}
|
|
37
|
-
if (part.kind === "
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
parameters.resolution = part.data.resolution;
|
|
42
|
-
if (part.data.durationSeconds !== undefined) {
|
|
43
|
-
parameters.duration = String(part.data.durationSeconds);
|
|
44
|
-
}
|
|
33
|
+
if (part.kind === "generation") {
|
|
34
|
+
hasGeneration = true;
|
|
35
|
+
model = part.data.model ?? model;
|
|
36
|
+
Object.assign(parameters, part.data.arguments);
|
|
45
37
|
continue;
|
|
46
38
|
}
|
|
47
39
|
hasAnswers = true;
|
|
@@ -52,25 +44,28 @@ export function buildCreativeRequest(message, ids) {
|
|
|
52
44
|
answeredQuestions.push({ question: answer.question, answers: values });
|
|
53
45
|
}
|
|
54
46
|
}
|
|
55
|
-
const
|
|
47
|
+
const generationFields = {
|
|
48
|
+
...(model === undefined ? {} : { scene_code: model }),
|
|
49
|
+
...(hasGeneration ? { parameters } : {})
|
|
50
|
+
};
|
|
56
51
|
let content;
|
|
57
52
|
if (hasAnswers) {
|
|
58
53
|
content = {
|
|
59
54
|
type: "user",
|
|
60
55
|
text: textParts.join("\n"),
|
|
61
56
|
prompt: prompt.length > 0 ? prompt : [{ type: "text", content: "" }],
|
|
62
|
-
...
|
|
57
|
+
...generationFields,
|
|
63
58
|
answer: { answers: answerValues }
|
|
64
59
|
};
|
|
65
60
|
}
|
|
66
|
-
else if (prompt.length === 1 && prompt[0]?.type === "text" && !
|
|
61
|
+
else if (prompt.length === 1 && prompt[0]?.type === "text" && !hasGeneration) {
|
|
67
62
|
content = { type: "plain", text: prompt[0].content };
|
|
68
63
|
}
|
|
69
64
|
else {
|
|
70
65
|
content = {
|
|
71
66
|
type: "plain",
|
|
72
67
|
prompt,
|
|
73
|
-
...
|
|
68
|
+
...generationFields
|
|
74
69
|
};
|
|
75
70
|
}
|
|
76
71
|
const extra = {};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { findModel, ToolInputError } from "../tool/catalog.js";
|
|
2
|
+
import { modelControlArgumentsAreValid } from "../tool/dynamic-schema.js";
|
|
1
3
|
import { parseSafeRemoteAssetUrl, UrlSafetyError } from "../../platform/url-safety.js";
|
|
2
4
|
export class AgentInputError extends Error {
|
|
3
5
|
constructor() {
|
|
@@ -37,6 +39,33 @@ export function createAgentUseCases(dependencies) {
|
|
|
37
39
|
async send({ input, access, signal }) {
|
|
38
40
|
signal.throwIfAborted();
|
|
39
41
|
const prepared = [];
|
|
42
|
+
let generation;
|
|
43
|
+
for (const part of input.message.parts) {
|
|
44
|
+
if ("data" in part && !("questionMessageId" in part.data)) {
|
|
45
|
+
generation = part.data;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (generation?.model !== undefined) {
|
|
50
|
+
dependencies.telemetry.annotate({ model_id: generation.model });
|
|
51
|
+
const catalog = await dependencies.telemetry.stage("catalog", () => (dependencies.catalog.load({ access, signal })));
|
|
52
|
+
signal.throwIfAborted();
|
|
53
|
+
for (const warning of catalog.warnings)
|
|
54
|
+
dependencies.warn(warning);
|
|
55
|
+
await dependencies.telemetry.stage("input", async () => {
|
|
56
|
+
let model;
|
|
57
|
+
try {
|
|
58
|
+
model = findModel(catalog, generation.model);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (error instanceof ToolInputError)
|
|
62
|
+
throw new AgentInputError();
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
if (!modelControlArgumentsAreValid(model, generation.arguments ?? {}))
|
|
66
|
+
throw new AgentInputError();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
40
69
|
const prepare = async () => {
|
|
41
70
|
for (const part of input.message.parts) {
|
|
42
71
|
signal.throwIfAborted();
|
|
@@ -92,13 +121,12 @@ export function createAgentUseCases(dependencies) {
|
|
|
92
121
|
continue;
|
|
93
122
|
}
|
|
94
123
|
prepared.push({
|
|
95
|
-
kind: "
|
|
124
|
+
kind: "generation",
|
|
96
125
|
data: {
|
|
97
|
-
...(part.data.
|
|
98
|
-
...(part.data.
|
|
99
|
-
...(part.data.durationSeconds === undefined
|
|
126
|
+
...(part.data.model === undefined ? {} : { model: part.data.model }),
|
|
127
|
+
...(part.data.arguments === undefined
|
|
100
128
|
? {}
|
|
101
|
-
: {
|
|
129
|
+
: { arguments: { ...part.data.arguments } })
|
|
102
130
|
}
|
|
103
131
|
});
|
|
104
132
|
}
|
|
@@ -98,9 +98,31 @@ addFormats(ajv);
|
|
|
98
98
|
function validator(model) {
|
|
99
99
|
return ajv.compile(buildModelArgumentsSchema(model));
|
|
100
100
|
}
|
|
101
|
+
function controlArgumentsValidator(model) {
|
|
102
|
+
const properties = {};
|
|
103
|
+
for (const parameter of model.parameters) {
|
|
104
|
+
if (parameter.name === "prompt"
|
|
105
|
+
|| parameter.type === "uri"
|
|
106
|
+
|| parameter.type === "uri[]")
|
|
107
|
+
continue;
|
|
108
|
+
properties[parameter.name] = parameterSchema(parameter);
|
|
109
|
+
}
|
|
110
|
+
return ajv.compile({
|
|
111
|
+
type: "object",
|
|
112
|
+
properties,
|
|
113
|
+
required: [],
|
|
114
|
+
additionalProperties: false
|
|
115
|
+
});
|
|
116
|
+
}
|
|
101
117
|
export function assertModelArguments(model, value) {
|
|
102
118
|
if (typeof value !== "object" || value === null
|
|
103
119
|
|| !validator(model)(value)) {
|
|
104
120
|
throw new ToolInputError();
|
|
105
121
|
}
|
|
106
122
|
}
|
|
123
|
+
export function modelControlArgumentsAreValid(model, value) {
|
|
124
|
+
return typeof value === "object"
|
|
125
|
+
&& value !== null
|
|
126
|
+
&& !Array.isArray(value)
|
|
127
|
+
&& controlArgumentsValidator(model)(value);
|
|
128
|
+
}
|
|
@@ -47,6 +47,21 @@ function parseIntent(value) {
|
|
|
47
47
|
arguments: parseRecord(nonBlank(value.arguments))
|
|
48
48
|
};
|
|
49
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
|
+
}
|
|
50
65
|
function submittedTask(value) {
|
|
51
66
|
if (!isRecord(value) || !isRecord(value.result))
|
|
52
67
|
return protocolFailure(value);
|
|
@@ -143,6 +158,17 @@ function optionalInnerDifyTaskId(value) {
|
|
|
143
158
|
return undefined;
|
|
144
159
|
}
|
|
145
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
|
+
}
|
|
146
172
|
function pollResult(value, taskId) {
|
|
147
173
|
if (!Array.isArray(value))
|
|
148
174
|
return protocolFailure(value);
|
|
@@ -175,6 +201,11 @@ function pollResult(value, taskId) {
|
|
|
175
201
|
&& (typeof difyTaskId !== "string" || difyTaskId.trim() === "")) {
|
|
176
202
|
return protocolFailure(inner);
|
|
177
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
|
+
}
|
|
178
209
|
if (!isRecord(inner.result)) {
|
|
179
210
|
return failedPoll(new RemoteProtocolError(remoteErrorEvidence(inner)), difyTaskId);
|
|
180
211
|
}
|
|
@@ -240,17 +271,25 @@ export function createToolApiAdapter(dependencies) {
|
|
|
240
271
|
async execute({ invocation, access, signal }) {
|
|
241
272
|
signal.throwIfAborted();
|
|
242
273
|
const managedContentId = contentId(createContentId());
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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
|
+
});
|
|
254
293
|
signal.throwIfAborted();
|
|
255
294
|
const taskId = await dependencies.telemetry.stage("submit", async () => {
|
|
256
295
|
dependencies.telemetry.annotate({ content_id: managedContentId });
|
|
@@ -261,8 +300,8 @@ export function createToolApiAdapter(dependencies) {
|
|
|
261
300
|
id: 0,
|
|
262
301
|
method: "tools/call",
|
|
263
302
|
params: {
|
|
264
|
-
name:
|
|
265
|
-
arguments: { ...
|
|
303
|
+
name: resolved.name,
|
|
304
|
+
arguments: { ...resolved.arguments, content_id: managedContentId }
|
|
266
305
|
}
|
|
267
306
|
},
|
|
268
307
|
credential: access.credential,
|
|
@@ -86,6 +86,7 @@ export function createToolUseCases(dependencies) {
|
|
|
86
86
|
});
|
|
87
87
|
const prompt = [];
|
|
88
88
|
const requestParameters = {};
|
|
89
|
+
const directArguments = {};
|
|
89
90
|
const prepare = async () => {
|
|
90
91
|
for (const parameter of model.parameters) {
|
|
91
92
|
signal.throwIfAborted();
|
|
@@ -95,14 +96,17 @@ export function createToolUseCases(dependencies) {
|
|
|
95
96
|
if (parameter.type === "string") {
|
|
96
97
|
if (parameter.name === "prompt") {
|
|
97
98
|
prompt.push({ type: "text", content: value });
|
|
99
|
+
directArguments[parameter.name] = value;
|
|
98
100
|
}
|
|
99
101
|
else {
|
|
100
102
|
requestParameters[parameter.name] = value;
|
|
103
|
+
directArguments[parameter.name] = value;
|
|
101
104
|
}
|
|
102
105
|
continue;
|
|
103
106
|
}
|
|
104
107
|
if (parameter.type === "number") {
|
|
105
108
|
requestParameters[parameter.name] = value;
|
|
109
|
+
directArguments[parameter.name] = value;
|
|
106
110
|
continue;
|
|
107
111
|
}
|
|
108
112
|
const values = parameter.type === "uri[]" ? value : [value];
|
|
@@ -138,8 +142,26 @@ export function createToolUseCases(dependencies) {
|
|
|
138
142
|
await prepare();
|
|
139
143
|
}
|
|
140
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
|
+
};
|
|
141
163
|
const content = await dependencies.execution.execute({
|
|
142
|
-
invocation
|
|
164
|
+
invocation,
|
|
143
165
|
access,
|
|
144
166
|
signal
|
|
145
167
|
});
|
|
@@ -3,7 +3,7 @@ const maximumLength = 8_192;
|
|
|
3
3
|
const truncationMarker = "[TRUNCATED]";
|
|
4
4
|
export function remoteErrorEvidence(value) {
|
|
5
5
|
const normalized = parseJson(value);
|
|
6
|
-
const remoteCode = topLevelCode(normalized);
|
|
6
|
+
const remoteCode = topLevelCode(normalized) ?? jsonRpcErrorCode(normalized);
|
|
7
7
|
const sanitized = sanitize(normalized);
|
|
8
8
|
if (sanitized === undefined) {
|
|
9
9
|
return remoteCode === undefined ? {} : { remoteCode };
|
|
@@ -31,7 +31,21 @@ function topLevelCode(value) {
|
|
|
31
31
|
return undefined;
|
|
32
32
|
if (!Object.prototype.hasOwnProperty.call(value, "code"))
|
|
33
33
|
return undefined;
|
|
34
|
-
|
|
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) {
|
|
35
49
|
if (typeof code === "string")
|
|
36
50
|
return redactRemoteText(code);
|
|
37
51
|
return typeof code === "number" ? String(code) : undefined;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const detailedStages = {
|
|
2
2
|
"auth.login": new Set(["authorize", "wait", "identity", "bind"]),
|
|
3
3
|
"org.switch": new Set(["list", "bind"]),
|
|
4
|
-
"agent.send:execute": new Set(["upload", "execute", "result"]),
|
|
4
|
+
"agent.send:execute": new Set(["catalog", "upload", "execute", "result"]),
|
|
5
5
|
"tool.call:execute": new Set(["catalog", "upload", "intent", "submit", "wait"]),
|
|
6
6
|
"dam.upload": new Set(["inspect", "repository", "prepare", "upload", "persist", "wait"]),
|
|
7
7
|
update: new Set(["registry", "install", "sync", "verify"])
|
package/package.json
CHANGED
package/skills/gd-cli/SKILL.md
CHANGED
|
@@ -7,7 +7,7 @@ description: Use when creating images, videos or text with GD CLI, operating the
|
|
|
7
7
|
|
|
8
8
|
按用户目标只读取对应参考:
|
|
9
9
|
|
|
10
|
-
-
|
|
10
|
+
- 选择创作路由、发现 Model 并生成图片、视频或文本:[创作](references/creation.md)
|
|
11
11
|
- 操作当前 AI+ Editor 作品:[编辑器](references/editor.md)
|
|
12
12
|
- 查询、上传或删除 DAM 素材:[素材管理](references/dam.md)
|
|
13
13
|
- 登录或选择组织:[登录与组织](references/auth-org.md)
|
|
@@ -1,21 +1,68 @@
|
|
|
1
1
|
# 创作
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## Creation Route
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
5
|
+
先判断 Creation Route,再选择或校验 Model。Model 和参数不决定 Agent 或 Tool 路由;两条路由都支持指定 Model 及其参数,`model list` 和 `model get` 是共享的实时发现能力。
|
|
6
|
+
|
|
7
|
+
- Agent:用户要的是业务交付物,或过程需要澄清、规划、多步骤、多产物或继续已有创作。具体 Business Skill 由服务端 Agent 选择,CLI 不猜测或复制服务端匹配规则。
|
|
8
|
+
- Tool:任务是一个无需业务编排的原子单次图片、视频或文本调用,或用户明确要求直接调用 Tool。
|
|
9
|
+
|
|
10
|
+
典型判断:
|
|
11
|
+
|
|
12
|
+
- “指定 Model 的电商主图套图”仍使用 Agent,因为目标是业务交付物且可能需要多产物编排。
|
|
13
|
+
- “规划三种风格,再分别生成图片”使用 Agent,因为它包含规划和多步骤。
|
|
14
|
+
- “指定 Model 生成一只猫”使用 Tool,因为它只是原子单次调用,而不是因为指定了 Model。
|
|
15
|
+
- 用户明确要求直接调用某个 Tool 时,使用 Tool。
|
|
16
|
+
|
|
17
|
+
不猜 Tool 名、Model 名或参数;以当前命令输出为准。用户询问推荐 Model、Model 能力、参数或稿豆价格时,先用 `model list` 筛选,再用 `model get` 核对实时详情。
|
|
8
18
|
|
|
9
19
|
## Agent 创作
|
|
10
20
|
|
|
11
|
-
|
|
21
|
+
先读取输入规范。需要指定 Model 时,也先读取实时 Model detail:
|
|
12
22
|
|
|
13
23
|
```bash
|
|
14
24
|
gd-cli agent send --schema
|
|
25
|
+
gd-cli model list --tool <tool>
|
|
26
|
+
gd-cli model get <model>
|
|
15
27
|
gd-cli agent send --input request.json
|
|
16
28
|
```
|
|
17
29
|
|
|
18
|
-
|
|
30
|
+
将用户要求放在 Text Part。指定 Model 和动态参数时,只增加一个 Generation Constraints Part;`model` 使用 Model 机器标识,`arguments` 的字段和值取自 `model get`,并如实保留用户指定值:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"message": {
|
|
35
|
+
"parts": [
|
|
36
|
+
{ "text": "<用户要求>" },
|
|
37
|
+
{
|
|
38
|
+
"data": {
|
|
39
|
+
"model": "<model>",
|
|
40
|
+
"arguments": {
|
|
41
|
+
"resolution": "2K"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`arguments` 可以在不指定 `model` 时单独提交,由服务端 Agent 选择 Model。指定 `model` 时,CLI 会按实时 Model detail 校验已提供参数;不要为了 Agent 调用补齐 Tool 的全部 required 字段。Prompt 使用 Text Part,素材使用独立 URL Part,不放入 `arguments`。素材保持消息 Part 的输入顺序;`metadata.role` 可标记 `reference`、`first_frame` 或 `last_frame`,不注入“首帧”或“尾帧”文本。例如:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"message": {
|
|
55
|
+
"parts": [
|
|
56
|
+
{ "text": "生成一段转场视频" },
|
|
57
|
+
{ "url": "file:///absolute/first.png", "metadata": { "role": "first_frame" } },
|
|
58
|
+
{ "url": "file:///absolute/last.png", "metadata": { "role": "last_frame" } },
|
|
59
|
+
{ "data": { "model": "<model>", "arguments": { "mode": "<value>" } } }
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
也可用 `--input -` 从 stdin 读取 JSON。成功结果包含 `message` 和本轮实际稿豆消耗 `usage`;将最终文本、资源、服务端续作标识和实际消耗交付给用户。
|
|
19
66
|
|
|
20
67
|
## Tool 创作
|
|
21
68
|
|
|
@@ -46,4 +93,4 @@ gd-cli tool call <tool> --input request.json
|
|
|
46
93
|
|
|
47
94
|
## 本地媒体
|
|
48
95
|
|
|
49
|
-
按所选 Model detail
|
|
96
|
+
Agent 将本地媒体写成独立 `file://` URL Part;Tool 按所选 Model detail 写入对应媒体参数。CLI 会在内部通过 DAM 完成临时上传并把公网 URL 传给创作服务;不要自行伪造 URL,也不要读取用户未明确授权的路径。
|