修正外部任务摘要状态读取边界
移除从轻量任务摘要读取主任务结果载荷的错误实现。 画布 Agent 继续轮询摘要状态,并通过画布完成回写刷新工程结果。
This commit is contained in:
@@ -77,7 +77,6 @@
|
|||||||
- 工具消息不再保存独立 `status`。OSS 文档只保存可选 `externalJobId` 与确认前取消时间 `cancelledAt`:两者都为空表示待确认,只有 `cancelledAt` 表示已取消;存在 `externalJobId` 时,排队、执行、完成和失败状态统一读取 SpacetimeDB `external_generation_job`,不得在 OSS 中复制第二套执行状态。
|
- 工具消息不再保存独立 `status`。OSS 文档只保存可选 `externalJobId` 与确认前取消时间 `cancelledAt`:两者都为空表示待确认,只有 `cancelledAt` 表示已取消;存在 `externalJobId` 时,排队、执行、完成和失败状态统一读取 SpacetimeDB `external_generation_job`,不得在 OSS 中复制第二套执行状态。
|
||||||
- 确认接口必须先把工具参数转换为既有编辑器 worker payload,再使用 `editor-agent:{conversationId}:{messageId}:{toolName}` 稳定 dedupe key 入队;同一确认的请求重试只能得到同一个 external job。入队成功后把返回的 job id 写回同一条 OSS 工具消息,不新增 Agent 工具执行关联表。
|
- 确认接口必须先把工具参数转换为既有编辑器 worker payload,再使用 `editor-agent:{conversationId}:{messageId}:{toolName}` 稳定 dedupe key 入队;同一确认的请求重试只能得到同一个 external job。入队成功后把返回的 job id 写回同一条 OSS 工具消息,不新增 Agent 工具执行关联表。
|
||||||
- 前端根据 `externalJobId` 查询通用 external-generation job 状态;worker 继续通过 `canvasCompletion` 把生成结果写回工程与素材库。浏览器断线、刷新或 api-server 重启不得导致确认接口重新扣费或重新提交 provider。
|
- 前端根据 `externalJobId` 查询通用 external-generation job 状态;worker 继续通过 `canvasCompletion` 把生成结果写回工程与素材库。浏览器断线、刷新或 api-server 重启不得导致确认接口重新扣费或重新提交 provider。
|
||||||
- worker 成功时把既有编辑器生成回包写入该 job 的 `result_payload_json`;通用 job 状态接口只读返回其中的 `result` 投影,Agent 卡据此恢复图片、视频或音频预览。该投影属于 SpacetimeDB job,不回写 OSS 对话文档。
|
|
||||||
- `EditorAgentToolCall.args` 保留为工具返回的原始 JSON,是确认接口重新反序列化并执行工具的唯一参数真相。图片参数继续只保存由真实 data key 计算出的 opaque SHA-256 `imageId`;不得为了前端预览把 `args` 中的图片 ID 改写成 `objectKey`、URL 或展示对象,也不得由前端重组或回传一份新的执行参数。
|
- `EditorAgentToolCall.args` 保留为工具返回的原始 JSON,是确认接口重新反序列化并执行工具的唯一参数真相。图片参数继续只保存由真实 data key 计算出的 opaque SHA-256 `imageId`;不得为了前端预览把 `args` 中的图片 ID 改写成 `objectKey`、URL 或展示对象,也不得由前端重组或回传一份新的执行参数。
|
||||||
- `EditorAgentToolCall.displayArgs` 是必填、只读的用户确认展示投影,与 `args` 分离:
|
- `EditorAgentToolCall.displayArgs` 是必填、只读的用户确认展示投影,与 `args` 分离:
|
||||||
- `stringArgs` 保存提示词、比例、清晰度、模型、时长等可展示参数的稳定名称、用户可见标题和值;
|
- `stringArgs` 保存提示词、比例、清晰度、模型、时长等可展示参数的稳定名称、用户可见标题和值;
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ export interface ExternalGenerationJobStatusRecord {
|
|||||||
progress: number;
|
progress: number;
|
||||||
error?: string | null;
|
error?: string | null;
|
||||||
updatedAtMicros: number;
|
updatedAtMicros: number;
|
||||||
result?: unknown;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExternalGenerationJobStatusResponse {
|
export interface ExternalGenerationJobStatusResponse {
|
||||||
|
|||||||
@@ -172,7 +172,6 @@ pub(crate) fn editor_generation_queue_state(
|
|||||||
progress: 8,
|
progress: 8,
|
||||||
error: job.last_error_message,
|
error: job.last_error_message,
|
||||||
updated_at_micros: job.updated_at_micros,
|
updated_at_micros: job.updated_at_micros,
|
||||||
result: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -180,11 +180,6 @@ fn map_external_generation_job_status(
|
|||||||
progress,
|
progress,
|
||||||
error: job.last_error_message.clone(),
|
error: job.last_error_message.clone(),
|
||||||
updated_at_micros: job.updated_at_micros,
|
updated_at_micros: job.updated_at_micros,
|
||||||
result: job
|
|
||||||
.result_payload_json
|
|
||||||
.as_deref()
|
|
||||||
.and_then(|payload| serde_json::from_str::<Value>(payload).ok())
|
|
||||||
.and_then(|payload| payload.get("response").cloned()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -671,9 +671,7 @@ async fn process_external_generation_job_once(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await,
|
||||||
complete_editor_generation_job(&state, &worker_id, &job, result.0).await
|
|
||||||
}
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let message = error.body_text();
|
let message = error.body_text();
|
||||||
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
||||||
@@ -701,9 +699,7 @@ async fn process_external_generation_job_once(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await,
|
||||||
complete_editor_generation_job(&state, &worker_id, &job, result.0).await
|
|
||||||
}
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let message = error.body_text();
|
let message = error.body_text();
|
||||||
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
||||||
@@ -732,9 +728,7 @@ async fn process_external_generation_job_once(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await,
|
||||||
complete_editor_generation_job(&state, &worker_id, &job, result.0).await
|
|
||||||
}
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let message = error.body_text();
|
let message = error.body_text();
|
||||||
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
||||||
@@ -762,9 +756,7 @@ async fn process_external_generation_job_once(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await,
|
||||||
complete_editor_generation_job(&state, &worker_id, &job, result.0).await
|
|
||||||
}
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let message = error.body_text();
|
let message = error.body_text();
|
||||||
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
||||||
@@ -792,9 +784,7 @@ async fn process_external_generation_job_once(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await,
|
||||||
complete_editor_generation_job(&state, &worker_id, &job, result.0).await
|
|
||||||
}
|
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let message = error.body_text();
|
let message = error.body_text();
|
||||||
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
||||||
@@ -823,9 +813,7 @@ async fn process_external_generation_job_once(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await,
|
||||||
complete_editor_generation_job(&state, &worker_id, &job, result.0).await
|
|
||||||
}
|
|
||||||
Err(response) => {
|
Err(response) => {
|
||||||
let message = response_error_message(response).await;
|
let message = response_error_message(response).await;
|
||||||
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
||||||
@@ -854,9 +842,7 @@ async fn process_external_generation_job_once(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await,
|
||||||
complete_editor_generation_job(&state, &worker_id, &job, result.0).await
|
|
||||||
}
|
|
||||||
Err(response) => {
|
Err(response) => {
|
||||||
let message = response_error_message(response).await;
|
let message = response_error_message(response).await;
|
||||||
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
||||||
@@ -885,9 +871,7 @@ async fn process_external_generation_job_once(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await,
|
||||||
complete_editor_generation_job(&state, &worker_id, &job, result.0).await
|
|
||||||
}
|
|
||||||
Err(response) => {
|
Err(response) => {
|
||||||
let message = response_error_message(response).await;
|
let message = response_error_message(response).await;
|
||||||
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
||||||
@@ -916,9 +900,7 @@ async fn process_external_generation_job_once(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(result) => {
|
Ok(_) => complete_editor_generation_job(&state, &worker_id, &job).await,
|
||||||
complete_editor_generation_job(&state, &worker_id, &job, result.0).await
|
|
||||||
}
|
|
||||||
Err(response) => {
|
Err(response) => {
|
||||||
let message = response_error_message(response).await;
|
let message = response_error_message(response).await;
|
||||||
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
fail_job(&state, &worker_id, &job, message.clone()).await?;
|
||||||
@@ -1023,7 +1005,6 @@ async fn complete_editor_generation_job(
|
|||||||
state: &AppState,
|
state: &AppState,
|
||||||
worker_id: &str,
|
worker_id: &str,
|
||||||
job: &ExternalGenerationJobRecord,
|
job: &ExternalGenerationJobRecord,
|
||||||
result: serde_json::Value,
|
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
complete_job(
|
complete_job(
|
||||||
state,
|
state,
|
||||||
@@ -1033,7 +1014,6 @@ async fn complete_editor_generation_job(
|
|||||||
json!({
|
json!({
|
||||||
"sourceModule": job.source_module.clone(),
|
"sourceModule": job.source_module.clone(),
|
||||||
"sourceEntityId": job.source_entity_id.clone(),
|
"sourceEntityId": job.source_entity_id.clone(),
|
||||||
"response": result,
|
|
||||||
})
|
})
|
||||||
.to_string(),
|
.to_string(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -354,7 +354,6 @@ fn map_jump_hop_queue_job_status(
|
|||||||
progress: 8,
|
progress: 8,
|
||||||
error: job.last_error_message,
|
error: job.last_error_message,
|
||||||
updated_at_micros: job.updated_at_micros,
|
updated_at_micros: job.updated_at_micros,
|
||||||
result: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -262,7 +262,6 @@ fn map_puzzle_queue_job_status(
|
|||||||
.unwrap_or(100),
|
.unwrap_or(100),
|
||||||
error: job.last_error_message.clone(),
|
error: job.last_error_message.clone(),
|
||||||
updated_at_micros: job.updated_at_micros,
|
updated_at_micros: job.updated_at_micros,
|
||||||
result: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -331,7 +331,6 @@ fn map_puzzle_clear_queue_job_status(
|
|||||||
progress: 8,
|
progress: 8,
|
||||||
error: job.last_error_message,
|
error: job.last_error_message,
|
||||||
updated_at_micros: job.updated_at_micros,
|
updated_at_micros: job.updated_at_micros,
|
||||||
result: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -340,7 +340,6 @@ fn map_wooden_fish_queue_job_status(
|
|||||||
progress: 8,
|
progress: 8,
|
||||||
error: job.last_error_message,
|
error: job.last_error_message,
|
||||||
updated_at_micros: job.updated_at_micros,
|
updated_at_micros: job.updated_at_micros,
|
||||||
result: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,6 @@ pub struct ExternalGenerationJobStatusRecord {
|
|||||||
pub progress: u8,
|
pub progress: u8,
|
||||||
pub error: Option<String>,
|
pub error: Option<String>,
|
||||||
pub updated_at_micros: i64,
|
pub updated_at_micros: i64,
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub result: Option<serde_json::Value>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
|||||||
@@ -111,26 +111,6 @@ function ToolCallView({
|
|||||||
};
|
};
|
||||||
}, [onJobCompleted, toolCall.externalJobId]);
|
}, [onJobCompleted, toolCall.externalJobId]);
|
||||||
const effectiveJobStatus = jobStatus ?? resolvedJob?.status ?? null;
|
const effectiveJobStatus = jobStatus ?? resolvedJob?.status ?? null;
|
||||||
const generationResult =
|
|
||||||
resolvedJob?.result && typeof resolvedJob.result === 'object'
|
|
||||||
? (resolvedJob.result as Record<string, unknown>)
|
|
||||||
: null;
|
|
||||||
const resultImageSrc =
|
|
||||||
typeof generationResult?.imageSrc === 'string'
|
|
||||||
? generationResult.imageSrc
|
|
||||||
: null;
|
|
||||||
const resultVideoSrc =
|
|
||||||
typeof generationResult?.videoSrc === 'string'
|
|
||||||
? generationResult.videoSrc
|
|
||||||
: null;
|
|
||||||
const resultAudioSrc =
|
|
||||||
typeof generationResult?.audioSrc === 'string'
|
|
||||||
? generationResult.audioSrc
|
|
||||||
: null;
|
|
||||||
const resultObjectKey =
|
|
||||||
typeof generationResult?.objectKey === 'string'
|
|
||||||
? generationResult.objectKey
|
|
||||||
: null;
|
|
||||||
const isCancelled = Boolean(toolCall.cancelledAt);
|
const isCancelled = Boolean(toolCall.cancelledAt);
|
||||||
const isExecuting =
|
const isExecuting =
|
||||||
effectiveJobStatus === 'queued' || effectiveJobStatus === 'running';
|
effectiveJobStatus === 'queued' || effectiveJobStatus === 'running';
|
||||||
@@ -181,16 +161,6 @@ function ToolCallView({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : resultImageSrc ? (
|
|
||||||
<div className="mt-2 overflow-hidden rounded-xl border border-slate-200 bg-slate-100">
|
|
||||||
<ResolvedAssetImage
|
|
||||||
src={resultImageSrc}
|
|
||||||
objectKey={resultObjectKey}
|
|
||||||
refreshKey={toolCall.externalJobId ?? toolCall.toolName}
|
|
||||||
alt=""
|
|
||||||
className="h-20 w-full object-cover"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
) : null}
|
||||||
{videos.length ? (
|
{videos.length ? (
|
||||||
<div className="mt-2 grid grid-cols-1 gap-2">
|
<div className="mt-2 grid grid-cols-1 gap-2">
|
||||||
@@ -214,16 +184,6 @@ function ToolCallView({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : resultVideoSrc ? (
|
|
||||||
<ResolvedAssetVideo
|
|
||||||
src={resultVideoSrc}
|
|
||||||
objectKey={resultObjectKey}
|
|
||||||
refreshKey={toolCall.externalJobId ?? toolCall.toolName}
|
|
||||||
controls
|
|
||||||
playsInline
|
|
||||||
preload="metadata"
|
|
||||||
className="mt-2 max-h-56 w-full rounded-xl bg-black object-contain"
|
|
||||||
/>
|
|
||||||
) : null}
|
) : null}
|
||||||
{audios.length ? (
|
{audios.length ? (
|
||||||
<div className="mt-2 space-y-2">
|
<div className="mt-2 space-y-2">
|
||||||
@@ -249,15 +209,6 @@ function ToolCallView({
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : resultAudioSrc ? (
|
|
||||||
<ResolvedAssetAudio
|
|
||||||
src={resultAudioSrc}
|
|
||||||
objectKey={resultObjectKey}
|
|
||||||
refreshKey={toolCall.externalJobId ?? toolCall.toolName}
|
|
||||||
controls
|
|
||||||
preload="metadata"
|
|
||||||
className="mt-2 w-full"
|
|
||||||
/>
|
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user