@downcity/agent 1.1.92 → 1.1.97

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.
@@ -1,9 +1,10 @@
1
1
  /**
2
- * @file 验证 ImagePlugin 的可观察图片任务协议。
2
+ * @file 验证 ImagePlugin 对 Agent 保持直接生图体验。
3
3
  *
4
4
  * 关键点(中文)
5
- * - `create` 应快速返回 job_id,而不是同步等待图片完成。
6
- * - `status/result` 可查询同一个任务,成功后 result 返回 UIMessage。
5
+ * - Agent 只调用 `generate` action。
6
+ * - 插件内部负责 image_create/image_result 轮询。
7
+ * - 成功后返回 UIMessage,后续由 plugin bridge 落盘 file parts。
7
8
  */
8
9
 
9
10
  import test from "node:test";
@@ -26,130 +27,75 @@ function create_image_message() {
26
27
  };
27
28
  }
28
29
 
29
- test("ImagePlugin create/status/result exposes async image jobs", async () => {
30
- let finish_image;
31
- const image_promise = new Promise((resolve) => {
32
- finish_image = () => resolve(create_image_message());
33
- });
30
+ test("ImagePlugin generate polls create/result until the image succeeds", async () => {
31
+ const calls = [];
34
32
  const plugin = new ImagePlugin({
35
- image: async () => image_promise,
33
+ create: (input) => {
34
+ calls.push(["create", input.prompt]);
35
+ return {
36
+ job_id: "img_custom",
37
+ status: "running",
38
+ poll_after_ms: 1,
39
+ };
40
+ },
41
+ result: () => {
42
+ calls.push(["result"]);
43
+ return calls.filter(([name]) => name === "result").length === 1
44
+ ? {
45
+ job_id: "img_custom",
46
+ status: "running",
47
+ poll_after_ms: 1,
48
+ }
49
+ : {
50
+ job_id: "img_custom",
51
+ status: "succeeded",
52
+ result: create_image_message(),
53
+ };
54
+ },
36
55
  poll_interval_ms: 1,
37
56
  wait_timeout_ms: 100,
38
57
  });
39
58
 
40
- const created = await plugin.actions.create.execute({
59
+ const result = await plugin.actions.generate.execute({
41
60
  context: {},
42
61
  payload: { prompt: "draw" },
43
62
  pluginName: "image",
44
- actionName: "create",
45
- });
46
-
47
- assert.equal(created.success, true);
48
- assert.equal(created.data.status, "running");
49
- assert.match(created.data.job_id, /^img_/);
50
-
51
- const before = await plugin.actions.status.execute({
52
- context: {},
53
- payload: { job_id: created.data.job_id },
54
- pluginName: "image",
55
- actionName: "status",
56
- });
57
-
58
- assert.equal(before.success, true);
59
- assert.equal(before.data.status, "running");
60
-
61
- finish_image();
62
- await new Promise((resolve) => setTimeout(resolve, 0));
63
-
64
- const after = await plugin.actions.status.execute({
65
- context: {},
66
- payload: { job_id: created.data.job_id },
67
- pluginName: "image",
68
- actionName: "status",
69
- });
70
-
71
- assert.equal(after.success, true);
72
- assert.equal(after.data.status, "succeeded");
73
- assert.equal("result" in after.data, false);
74
-
75
- const result = await plugin.actions.result.execute({
76
- context: {},
77
- payload: { job_id: created.data.job_id },
78
- pluginName: "image",
79
- actionName: "result",
63
+ actionName: "generate",
80
64
  });
81
65
 
82
66
  assert.equal(result.success, true);
83
67
  assert.equal(result.data.role, "assistant");
84
68
  assert.equal(result.data.parts[0].type, "file");
69
+ assert.deepEqual(calls, [
70
+ ["create", "draw"],
71
+ ["result"],
72
+ ["result"],
73
+ ]);
85
74
  });
86
75
 
87
- test("ImagePlugin accepts custom job API without synchronous image function", async () => {
88
- const message = create_image_message();
76
+ test("ImagePlugin generate reports job failure", async () => {
89
77
  const plugin = new ImagePlugin({
90
78
  create: () => ({
91
- job_id: "img_custom",
79
+ job_id: "img_failed",
92
80
  status: "running",
93
81
  poll_after_ms: 1,
94
82
  }),
95
- status: () => ({
96
- job_id: "img_custom",
97
- status: "succeeded",
98
- }),
99
83
  result: () => ({
100
- job_id: "img_custom",
101
- status: "succeeded",
102
- result: message,
84
+ job_id: "img_failed",
85
+ status: "failed",
86
+ error: "provider failed",
103
87
  }),
88
+ poll_interval_ms: 1,
89
+ wait_timeout_ms: 100,
104
90
  });
105
91
 
106
- const created = await plugin.actions.create.execute({
92
+ const result = await plugin.actions.generate.execute({
107
93
  context: {},
108
94
  payload: { prompt: "draw" },
109
95
  pluginName: "image",
110
- actionName: "create",
111
- });
112
-
113
- assert.equal(created.success, true);
114
- assert.equal(created.data.job_id, "img_custom");
115
-
116
- const result = await plugin.actions.result.execute({
117
- context: {},
118
- payload: { job_id: "img_custom" },
119
- pluginName: "image",
120
- actionName: "result",
121
- });
122
-
123
- assert.equal(result.success, true);
124
- assert.equal(result.data.parts[0].type, "file");
125
- });
126
-
127
- test("ImagePlugin strips accidental result data from custom status responses", async () => {
128
- const plugin = new ImagePlugin({
129
- create: () => ({
130
- job_id: "img_status_result",
131
- status: "succeeded",
132
- }),
133
- status: () => ({
134
- job_id: "img_status_result",
135
- status: "succeeded",
136
- result: create_image_message(),
137
- }),
138
- result: () => ({
139
- job_id: "img_status_result",
140
- status: "succeeded",
141
- result: create_image_message(),
142
- }),
143
- });
144
-
145
- const status = await plugin.actions.status.execute({
146
- context: {},
147
- payload: { job_id: "img_status_result" },
148
- pluginName: "image",
149
- actionName: "status",
96
+ actionName: "generate",
150
97
  });
151
98
 
152
- assert.equal(status.success, true);
153
- assert.equal(status.data.status, "succeeded");
154
- assert.equal("result" in status.data, false);
99
+ assert.equal(result.success, false);
100
+ assert.match(result.error, /provider failed/);
155
101
  });
package/src/index.ts CHANGED
@@ -232,10 +232,8 @@ export type {
232
232
  ImagePluginJobCreateResult,
233
233
  ImagePluginJobResult,
234
234
  ImagePluginJobStatus,
235
- ImagePluginJobStatusResult,
236
235
  ImagePluginMessage,
237
236
  ImagePluginOptions,
238
- ImagePluginResult,
239
237
  ImagePluginTextContent,
240
238
  } from "./types/plugin/ImagePlugin.js";
241
239
 
@@ -2,19 +2,15 @@
2
2
  * ImagePlugin:Agent 内置图片生成插件。
3
3
  *
4
4
  * 关键点(中文)
5
- * - 插件只负责把外部 image 函数暴露为 agent 可调用 action。
6
- * - 具体模型、Provider、鉴权与上游协议由调用方传入的 image 函数处理。
5
+ * - Agent 只暴露同步体验的 `generate` action。
6
+ * - City / provider 的异步任务细节由插件内部 create + result 轮询封装。
7
7
  * - action 返回 AI SDK UIMessage,后续由 plugin tool bridge 抽取 file parts 写回 assistant 消息。
8
8
  */
9
9
 
10
- import crypto from "node:crypto";
11
10
  import type { AgentContext } from "@/types/runtime/agent/AgentContext.js";
12
11
  import type { JsonObject, JsonValue } from "@/types/common/Json.js";
13
12
  import type {
14
13
  ImagePluginInput,
15
- ImagePluginJobCreateResult,
16
- ImagePluginJobResult,
17
- ImagePluginJobStatusResult,
18
14
  ImagePluginOptions,
19
15
  ImagePluginResult,
20
16
  } from "@/types/plugin/ImagePlugin.js";
@@ -27,37 +23,6 @@ const DEFAULT_IMAGE_PLUGIN_DESCRIPTION =
27
23
  const DEFAULT_WAIT_TIMEOUT_MS = 60_000;
28
24
  const DEFAULT_POLL_INTERVAL_MS = 3_000;
29
25
 
30
- type LocalImageJobRecord = {
31
- /**
32
- * 图片任务唯一 ID。
33
- */
34
- job_id: string;
35
- /**
36
- * 当前任务状态。
37
- */
38
- status: "queued" | "running" | "succeeded" | "failed";
39
- /**
40
- * 成功时的图片结果。
41
- */
42
- result?: ImagePluginResult;
43
- /**
44
- * 失败时的错误信息。
45
- */
46
- error?: string;
47
- /**
48
- * 人类可读状态说明。
49
- */
50
- message?: string;
51
- /**
52
- * 任务创建时间。
53
- */
54
- created_at: string;
55
- /**
56
- * 任务更新时间。
57
- */
58
- updated_at: string;
59
- };
60
-
61
26
  /**
62
27
  * 判断值是否为普通对象。
63
28
  */
@@ -77,43 +42,22 @@ function normalize_image_payload(payload: JsonValue | undefined): ImagePluginInp
77
42
  return { ...record } as ImagePluginInput;
78
43
  }
79
44
 
80
- function normalize_job_id_payload(payload: JsonValue | undefined): { job_id: string } {
81
- const record = to_record(payload ?? {});
82
- const job_id = String(record?.job_id || "").trim();
83
- if (!job_id) {
84
- throw new TypeError("ImagePlugin job action requires job_id");
85
- }
86
- return { job_id };
87
- }
88
-
89
45
  /**
90
46
  * 校验 image 函数返回的 UIMessage。
91
47
  */
92
48
  function normalize_image_result(result: ImagePluginResult): ImagePluginResult {
93
49
  const record = to_record(result);
94
50
  if (!record || !Array.isArray(record.parts)) {
95
- throw new TypeError("ImagePlugin image function must return an AI SDK UIMessage");
51
+ throw new TypeError("ImagePlugin image provider must return an AI SDK UIMessage");
96
52
  }
97
53
  return result;
98
54
  }
99
55
 
100
56
  /**
101
- * 归一化任务状态查询结果,确保 status action 不携带图片结果。
57
+ * 等待指定毫秒数。
102
58
  */
103
- function normalize_job_status_result(
104
- result: ImagePluginJobStatusResult,
105
- ): ImagePluginJobStatusResult {
106
- return {
107
- job_id: result.job_id,
108
- status: result.status,
109
- ...(result.message ? { message: result.message } : {}),
110
- ...(result.error ? { error: result.error } : {}),
111
- ...(typeof result.poll_after_ms === "number"
112
- ? { poll_after_ms: result.poll_after_ms }
113
- : {}),
114
- ...(result.created_at ? { created_at: result.created_at } : {}),
115
- ...(result.updated_at ? { updated_at: result.updated_at } : {}),
116
- };
59
+ function sleep(ms: number): Promise<void> {
60
+ return new Promise((resolve) => setTimeout(resolve, ms));
117
61
  }
118
62
 
119
63
  /**
@@ -135,13 +79,10 @@ export class ImagePlugin extends BasePlugin {
135
79
  */
136
80
  readonly description: string;
137
81
 
138
- private readonly image: ImagePluginOptions["image"];
139
- private readonly create_job?: ImagePluginOptions["create"];
140
- private readonly read_job_status?: ImagePluginOptions["status"];
141
- private readonly read_job_result?: ImagePluginOptions["result"];
82
+ private readonly create_job: NonNullable<ImagePluginOptions["create"]>;
83
+ private readonly read_job_result: NonNullable<ImagePluginOptions["result"]>;
142
84
  private readonly wait_timeout_ms: number;
143
85
  private readonly poll_interval_ms: number;
144
- private readonly local_jobs = new Map<string, LocalImageJobRecord>();
145
86
 
146
87
  constructor(options: ImagePluginOptions) {
147
88
  super();
@@ -149,32 +90,15 @@ export class ImagePlugin extends BasePlugin {
149
90
  if (!name) {
150
91
  throw new Error("ImagePlugin requires a non-empty name");
151
92
  }
152
- const has_custom_job_api = Boolean(
153
- options.create || options.status || options.result,
154
- );
155
- if (
156
- has_custom_job_api &&
157
- (typeof options.create !== "function" ||
158
- typeof options.status !== "function" ||
159
- typeof options.result !== "function")
160
- ) {
161
- throw new Error(
162
- "ImagePlugin custom job API requires create, status, and result functions",
163
- );
164
- }
165
- if (!has_custom_job_api && typeof options.image !== "function") {
166
- throw new Error(
167
- "ImagePlugin requires either image(input) or create/status/result functions",
168
- );
93
+ if (typeof options.create !== "function" || typeof options.result !== "function") {
94
+ throw new Error("ImagePlugin requires create and result functions");
169
95
  }
170
96
  this.name = name;
171
97
  this.title = String(options.title || DEFAULT_IMAGE_PLUGIN_TITLE).trim();
172
98
  this.description = String(
173
99
  options.description || DEFAULT_IMAGE_PLUGIN_DESCRIPTION,
174
100
  ).trim();
175
- this.image = options.image;
176
101
  this.create_job = options.create;
177
- this.read_job_status = options.status;
178
102
  this.read_job_result = options.result;
179
103
  this.wait_timeout_ms =
180
104
  typeof options.wait_timeout_ms === "number" && options.wait_timeout_ms > 0
@@ -191,108 +115,38 @@ export class ImagePlugin extends BasePlugin {
191
115
  */
192
116
  system(_context: AgentContext): string {
193
117
  return [
194
- "Image generation is available through the plugin_call tool as an observable job workflow.",
195
- `Call plugin "${this.name}" action "create" when the user asks to create, render, draw, or edit an image.`,
196
- `Then call plugin "${this.name}" action "status" with { job_id } to inspect progress.`,
197
- `When status is succeeded, call plugin "${this.name}" action "result" with { job_id } to attach the generated files.`,
198
- "Use action \"generate\" only as a compatibility shortcut when you explicitly need to wait for completion.",
199
- "Pass a JSON payload with prompt, optional size/aspect_ratio/quality/n, and optional provider_options to create/generate.",
118
+ "Image generation is available through the plugin_call tool.",
119
+ `Call plugin "${this.name}" action "generate" when the user asks to create, render, draw, or edit an image.`,
120
+ "Pass a JSON payload with prompt, optional size/aspect_ratio/quality/n, and optional provider_options.",
121
+ "The generated image files will be attached to the final assistant message automatically.",
200
122
  ].join("\n");
201
123
  }
202
124
 
203
- private create_local_job(input: ImagePluginInput): ImagePluginJobCreateResult {
204
- if (typeof this.image !== "function") {
205
- throw new Error("ImagePlugin local image job requires image(input)");
206
- }
207
- const now = new Date().toISOString();
208
- const job_id = `img_${crypto.randomUUID()}`;
209
- const record: LocalImageJobRecord = {
210
- job_id,
211
- status: "running",
212
- message: "image job is running",
213
- created_at: now,
214
- updated_at: now,
215
- };
216
- this.local_jobs.set(job_id, record);
217
-
218
- void this.run_local_job(record, input, this.image);
219
-
220
- return {
221
- job_id,
222
- status: record.status,
223
- message: record.message,
224
- poll_after_ms: this.poll_interval_ms,
225
- created_at: record.created_at,
226
- updated_at: record.updated_at,
227
- };
228
- }
229
-
230
- private async run_local_job(
231
- record: LocalImageJobRecord,
232
- input: ImagePluginInput,
233
- image: NonNullable<ImagePluginOptions["image"]>,
234
- ): Promise<void> {
235
- try {
236
- const message = normalize_image_result(await image(input));
237
- record.status = "succeeded";
238
- record.result = message;
239
- record.message = "image job succeeded";
240
- record.updated_at = new Date().toISOString();
241
- } catch (error) {
242
- record.status = "failed";
243
- record.error = String(error);
244
- record.message = "image job failed";
245
- record.updated_at = new Date().toISOString();
125
+ private async generate_image(input: ImagePluginInput): Promise<ImagePluginResult> {
126
+ const job = await this.create_job(input);
127
+ const job_id = String(job.job_id || "").trim();
128
+ if (!job_id) {
129
+ throw new Error("ImagePlugin image_create result requires job_id");
246
130
  }
247
- }
248
-
249
- private read_local_job(job_id: string): LocalImageJobRecord {
250
- const record = this.local_jobs.get(job_id);
251
- if (!record) {
252
- throw new Error(`Unknown image job: ${job_id}`);
253
- }
254
- return record;
255
- }
256
131
 
257
- private serialize_local_status(record: LocalImageJobRecord): ImagePluginJobStatusResult {
258
- return {
259
- job_id: record.job_id,
260
- status: record.status,
261
- ...(record.message ? { message: record.message } : {}),
262
- ...(record.error ? { error: record.error } : {}),
263
- ...(record.status === "running" || record.status === "queued"
264
- ? { poll_after_ms: this.poll_interval_ms }
265
- : {}),
266
- created_at: record.created_at,
267
- updated_at: record.updated_at,
268
- };
269
- }
270
-
271
- private serialize_local_result(record: LocalImageJobRecord): ImagePluginJobResult {
272
- return {
273
- job_id: record.job_id,
274
- status: record.status,
275
- ...(record.result ? { result: record.result } : {}),
276
- ...(record.error ? { error: record.error } : {}),
277
- ...(record.message ? { message: record.message } : {}),
278
- created_at: record.created_at,
279
- updated_at: record.updated_at,
280
- };
281
- }
282
-
283
- private async wait_for_job(job_id: string): Promise<ImagePluginResult> {
284
132
  const deadline = Date.now() + this.wait_timeout_ms;
133
+ let poll_after_ms =
134
+ typeof job.poll_after_ms === "number" && job.poll_after_ms > 0
135
+ ? job.poll_after_ms
136
+ : this.poll_interval_ms;
285
137
  while (Date.now() <= deadline) {
286
- const result = this.read_job_result
287
- ? await this.read_job_result({ job_id })
288
- : this.serialize_local_result(this.read_local_job(job_id));
138
+ const result = await this.read_job_result({ job_id });
289
139
  if (result.status === "succeeded" && result.result) {
290
140
  return normalize_image_result(result.result);
291
141
  }
292
142
  if (result.status === "failed") {
293
143
  throw new Error(result.error || result.message || "image job failed");
294
144
  }
295
- await new Promise((resolve) => setTimeout(resolve, this.poll_interval_ms));
145
+ poll_after_ms =
146
+ typeof result.poll_after_ms === "number" && result.poll_after_ms > 0
147
+ ? result.poll_after_ms
148
+ : this.poll_interval_ms;
149
+ await sleep(poll_after_ms);
296
150
  }
297
151
  throw new Error(`image job timed out: ${job_id}`);
298
152
  }
@@ -301,92 +155,11 @@ export class ImagePlugin extends BasePlugin {
301
155
  * 显式 action 集合。
302
156
  */
303
157
  readonly actions = {
304
- create: {
305
- execute: async ({ payload }: { payload: JsonValue }) => {
306
- try {
307
- const input = normalize_image_payload(payload);
308
- const result = this.create_job
309
- ? await this.create_job(input)
310
- : this.create_local_job(input);
311
- return {
312
- success: true,
313
- data: result as unknown as JsonObject,
314
- message: result.message || "image job created",
315
- };
316
- } catch (error) {
317
- return {
318
- success: false,
319
- error: String(error),
320
- message: String(error),
321
- };
322
- }
323
- },
324
- },
325
- status: {
326
- execute: async ({ payload }: { payload: JsonValue }) => {
327
- try {
328
- const input = normalize_job_id_payload(payload);
329
- const result = this.read_job_status
330
- ? normalize_job_status_result(await this.read_job_status(input))
331
- : this.serialize_local_status(this.read_local_job(input.job_id));
332
- return {
333
- success: true,
334
- data: result as unknown as JsonObject,
335
- message: result.message || `image job ${result.status}`,
336
- };
337
- } catch (error) {
338
- return {
339
- success: false,
340
- error: String(error),
341
- message: String(error),
342
- };
343
- }
344
- },
345
- },
346
- result: {
347
- execute: async ({ payload }: { payload: JsonValue }) => {
348
- try {
349
- const input = normalize_job_id_payload(payload);
350
- const result = this.read_job_result
351
- ? await this.read_job_result(input)
352
- : this.serialize_local_result(this.read_local_job(input.job_id));
353
- if (result.status === "succeeded" && result.result) {
354
- return {
355
- success: true,
356
- data: result.result as unknown as JsonObject,
357
- message: result.message || "image job succeeded",
358
- };
359
- }
360
- if (result.status === "failed") {
361
- return {
362
- success: false,
363
- data: result as unknown as JsonObject,
364
- error: result.error || result.message || "image job failed",
365
- message: result.message || "image job failed",
366
- };
367
- }
368
- return {
369
- success: true,
370
- data: result as unknown as JsonObject,
371
- message: result.message || `image job ${result.status}`,
372
- };
373
- } catch (error) {
374
- return {
375
- success: false,
376
- error: String(error),
377
- message: String(error),
378
- };
379
- }
380
- },
381
- },
382
158
  generate: {
383
159
  execute: async ({ payload }: { payload: JsonValue }) => {
384
160
  try {
385
161
  const input = normalize_image_payload(payload);
386
- const job = this.create_job
387
- ? await this.create_job(input)
388
- : this.create_local_job(input);
389
- const message = await this.wait_for_job(job.job_id);
162
+ const message = await this.generate_image(input);
390
163
  return {
391
164
  success: true,
392
165
  data: message as unknown as JsonObject,
@@ -84,14 +84,14 @@ export interface ImagePluginInput {
84
84
  }
85
85
 
86
86
  /**
87
- * ImagePlugin 生成结果。
87
+ * ImagePlugin 图片任务状态。
88
88
  */
89
- export type ImagePluginResult = UIMessage;
89
+ export type ImagePluginJobStatus = "queued" | "running" | "succeeded" | "failed";
90
90
 
91
91
  /**
92
- * ImagePlugin 图片任务状态。
92
+ * ImagePlugin 生成结果。
93
93
  */
94
- export type ImagePluginJobStatus = "queued" | "running" | "succeeded" | "failed";
94
+ export type ImagePluginResult = UIMessage;
95
95
 
96
96
  /**
97
97
  * ImagePlugin 图片任务创建结果。
@@ -101,8 +101,6 @@ export interface ImagePluginJobCreateResult {
101
101
  job_id: string;
102
102
  /** 当前任务状态。 */
103
103
  status: ImagePluginJobStatus;
104
- /** 查询任务状态的路径或 URL。 */
105
- status_path?: string;
106
104
  /** 读取任务结果的路径或 URL。 */
107
105
  result_path?: string;
108
106
  /** 人类可读状态说明。 */
@@ -115,26 +113,6 @@ export interface ImagePluginJobCreateResult {
115
113
  updated_at?: string;
116
114
  }
117
115
 
118
- /**
119
- * ImagePlugin 图片任务状态查询结果。
120
- */
121
- export interface ImagePluginJobStatusResult {
122
- /** 图片任务唯一 ID。 */
123
- job_id: string;
124
- /** 当前任务状态。 */
125
- status: ImagePluginJobStatus;
126
- /** 人类可读状态说明。 */
127
- message?: string;
128
- /** 失败时的错误信息。 */
129
- error?: string;
130
- /** 建议下次轮询前等待的毫秒数。 */
131
- poll_after_ms?: number;
132
- /** 任务创建时间。 */
133
- created_at?: string;
134
- /** 任务更新时间。 */
135
- updated_at?: string;
136
- }
137
-
138
116
  /**
139
117
  * ImagePlugin 图片任务结果查询结果。
140
118
  */
@@ -149,6 +127,8 @@ export interface ImagePluginJobResult {
149
127
  error?: string;
150
128
  /** 人类可读状态说明。 */
151
129
  message?: string;
130
+ /** 任务未完成时建议下次轮询前等待的毫秒数。 */
131
+ poll_after_ms?: number;
152
132
  /** 任务创建时间。 */
153
133
  created_at?: string;
154
134
  /** 任务更新时间。 */
@@ -165,13 +145,9 @@ export interface ImagePluginOptions {
165
145
  title?: string;
166
146
  /** Plugin 用途说明。 */
167
147
  description?: string;
168
- /** 可选:图片生成函数;未传 `create/status/result` 时用于本地后台任务兼容。 */
169
- image?: (input: ImagePluginInput) => Promise<ImagePluginResult> | ImagePluginResult;
170
- /** 可选:创建图片生成任务,通常传入 `(input) => city.ai.imageJobCreate(input)`。 */
148
+ /** 可选:创建图片生成任务,通常传入 `(input) => city.ai.image_create(input)`。 */
171
149
  create?: (input: ImagePluginInput) => Promise<ImagePluginJobCreateResult> | ImagePluginJobCreateResult;
172
- /** 可选:查询图片生成任务状态,通常传入 `(input) => city.ai.imageJobStatus(input)`。 */
173
- status?: (input: { job_id: string }) => Promise<ImagePluginJobStatusResult> | ImagePluginJobStatusResult;
174
- /** 可选:读取图片生成任务结果,通常传入 `(input) => city.ai.imageJobResult(input)`。 */
150
+ /** 可选:读取图片生成任务结果,通常传入 `(input) => city.ai.image_result(input)`。 */
175
151
  result?: (input: { job_id: string }) => Promise<ImagePluginJobResult> | ImagePluginJobResult;
176
152
  /** 兼容 `generate` 动作等待任务完成的最长毫秒数。 */
177
153
  wait_timeout_ms?: number;