gaoding-cli 1.0.0-alpha.16 → 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 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/CreativeParametersPart"
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/CreativeParametersPart"
148
+ "$ref": "#/$defs/GenerationConstraintsPart"
149
149
  },
150
150
  {
151
151
  "$ref": "#/$defs/QuestionAnswerPart"
@@ -211,7 +211,7 @@
211
211
  }
212
212
  }
213
213
  },
214
- "CreativeParametersPart": {
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
- "ratio": {
227
+ "model": {
228
228
  "type": "string",
229
229
  "minLength": 1,
230
230
  "pattern": ".*\\S.*",
231
- "description": "输出画面比例,例如 16:9。"
231
+ "description": "可选创作模型机器标识;指定时由 CLI 根据实时 Model Catalog 校验。"
232
232
  },
233
- "resolution": {
234
- "type": "string",
235
- "minLength": 1,
236
- "pattern": ".*\\S.*",
237
- "description": "输出分辨率。"
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: createMnsCatalogAdapter({ transport: mnsTransport }),
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 === "parameters") {
38
- if (part.data.ratio !== undefined)
39
- parameters.ratio = part.data.ratio;
40
- if (part.data.resolution !== undefined)
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 hasParameters = Object.keys(parameters).length > 0;
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
- ...(hasParameters ? { parameters } : {}),
57
+ ...generationFields,
63
58
  answer: { answers: answerValues }
64
59
  };
65
60
  }
66
- else if (prompt.length === 1 && prompt[0]?.type === "text" && !hasParameters) {
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
- ...(hasParameters ? { parameters } : {})
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: "parameters",
124
+ kind: "generation",
96
125
  data: {
97
- ...(part.data.ratio === undefined ? {} : { ratio: part.data.ratio }),
98
- ...(part.data.resolution === undefined ? {} : { resolution: part.data.resolution }),
99
- ...(part.data.durationSeconds === undefined
126
+ ...(part.data.model === undefined ? {} : { model: part.data.model }),
127
+ ...(part.data.arguments === undefined
100
128
  ? {}
101
- : { durationSeconds: part.data.durationSeconds })
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
+ }
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gaoding-cli",
3
- "version": "1.0.0-alpha.16",
3
+ "version": "1.0.0-alpha.17",
4
4
  "description": "Gaoding command-line interface for agents and people.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -7,7 +7,7 @@ description: Use when creating images, videos or text with GD CLI, operating the
7
7
 
8
8
  按用户目标只读取对应参考:
9
9
 
10
- - 开放式创作、工具发现与模型调用:[创作](references/creation.md)
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
- - 目标开放、需要稿定 Agent 完成创作时,使用 `gd-cli agent send`。
6
- - 已知要调用具体图片、视频或文本能力时,依次发现 Tool、选择 Model、读取 Model detail,再调用 Tool。
7
- - 不猜 Tool 名、Model 名或参数;以当前命令输出为准。
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
- 也可用 `--input -` stdin 读取 JSON。成功结果包含 `message` 和本轮实际稿豆消耗 `usage`;将文本、资源与实际消耗交付给用户。
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 在输入中提交本地媒体引用。CLI 会在内部通过 DAM 完成临时上传并把公网 URL 传给创作服务;不要自行伪造 URL,也不要读取用户未明确授权的路径。
96
+ Agent 将本地媒体写成独立 `file://` URL Part;Tool 按所选 Model detail 写入对应媒体参数。CLI 会在内部通过 DAM 完成临时上传并把公网 URL 传给创作服务;不要自行伪造 URL,也不要读取用户未明确授权的路径。