添加全能创作功能
This commit is contained in:
@@ -29,6 +29,10 @@ import type {
|
||||
ImageConversation,
|
||||
ImageConversationTrash,
|
||||
ImageConversationTask,
|
||||
CreationConversation,
|
||||
CreationConversationDetail,
|
||||
CreationMessage,
|
||||
CreationRef,
|
||||
ModelConfig,
|
||||
ModelEntity,
|
||||
Notification,
|
||||
@@ -530,6 +534,129 @@ export const api = {
|
||||
}
|
||||
}
|
||||
},
|
||||
// ── 全能创作(契约见仓库根 `全能创作-契约-2026-09-02.md`)
|
||||
|
||||
/** @ 检索:输入框打 @ 或用户说了名字时用。返回的整条 Ref 要原样带进 send 的 refs。 */
|
||||
searchMentions(params: { q?: string; types?: CreationRef["type"][]; limit?: number } = {}) {
|
||||
const query = new URLSearchParams();
|
||||
if (params.q) query.set("q", params.q);
|
||||
if (params.types?.length) query.set("types", params.types.join(","));
|
||||
if (params.limit) query.set("limit", String(params.limit));
|
||||
const suffix = query.toString();
|
||||
return request<{ results: CreationRef[]; type_labels: Record<string, string> }>(
|
||||
`/api/ai/mentions/${suffix ? `?${suffix}` : ""}`
|
||||
);
|
||||
},
|
||||
listCreations(params: { mode?: "video" | "image"; status?: string } = {}) {
|
||||
const query = new URLSearchParams();
|
||||
if (params.mode) query.set("mode", params.mode);
|
||||
if (params.status) query.set("status", params.status);
|
||||
const suffix = query.toString();
|
||||
return request<Paginated<CreationConversation>>(`/api/ai/creations/${suffix ? `?${suffix}` : ""}`);
|
||||
},
|
||||
createCreation(payload: {
|
||||
title?: string;
|
||||
mode: "video" | "image";
|
||||
preset?: string;
|
||||
params?: Record<string, string>;
|
||||
}) {
|
||||
return request<CreationConversation>("/api/ai/creations/", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
},
|
||||
getCreation(id: string) {
|
||||
return request<CreationConversationDetail>(`/api/ai/creations/${id}/`);
|
||||
},
|
||||
renameCreation(id: string, title: string) {
|
||||
return request<CreationConversation>(`/api/ai/creations/${id}/`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify({ title })
|
||||
});
|
||||
},
|
||||
updateCreation(id: string, payload: { title?: string; params?: Record<string, string> }) {
|
||||
return request<CreationConversation>(`/api/ai/creations/${id}/`, {
|
||||
method: "PATCH",
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
},
|
||||
deleteCreation(id: string) {
|
||||
return request<void>(`/api/ai/creations/${id}/`, { method: "DELETE" });
|
||||
},
|
||||
/** 增量拉消息:轮询生成结果时只补 after_seq 之后的,不重拉整条会话。 */
|
||||
creationMessages(id: string, afterSeq?: number) {
|
||||
const suffix = afterSeq === undefined ? "" : `?after_seq=${afterSeq}`;
|
||||
return request<CreationMessage[]>(`/api/ai/creations/${id}/messages/${suffix}`);
|
||||
},
|
||||
/**
|
||||
* 点确认闸门 → 直接出片。**这个不是 SSE**:后端不跑模型,按方案卡存好的
|
||||
* video_prompt 直接提交,同步返回「生成中」消息,之后靠轮询转成结果。
|
||||
*/
|
||||
confirmCreationPlan(id: string, replyTo: string, params?: Record<string, string>) {
|
||||
return request<{ message: CreationMessage }>(`/api/ai/creations/${id}/send/`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ kind: "confirm", reply_to: replyTo, params })
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 发一条消息 → SSE 流。事件:tool / reasoning / delta / message / task / credits / done / error。
|
||||
* 和 agentScriptStream 同一套 fetch + ReadableStream(EventSource 只支持 GET,这里要 POST 带 body)。
|
||||
*/
|
||||
async creationSendStream(
|
||||
id: string,
|
||||
payload: {
|
||||
kind?: "text" | "elicit_answer";
|
||||
text?: string;
|
||||
refs?: CreationRef[];
|
||||
reply_to?: string;
|
||||
answers?: Record<string, string | string[]>;
|
||||
model_config_id?: string;
|
||||
params?: Record<string, string>;
|
||||
},
|
||||
onEvent: (evt: { type: string; [k: string]: unknown }) => void,
|
||||
signal?: AbortSignal
|
||||
): Promise<void> {
|
||||
const token = getToken();
|
||||
const headers = new Headers({ "Content-Type": "application/json", Accept: "text/event-stream" });
|
||||
if (token) headers.set("Authorization", `Token ${token}`);
|
||||
const response = await fetch(`${API_BASE}/api/ai/creations/${id}/send/`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(payload),
|
||||
signal
|
||||
});
|
||||
if (!response.ok || !response.body) {
|
||||
const text = await response.text().catch(() => "");
|
||||
let message = text || "发送失败";
|
||||
try {
|
||||
const data = JSON.parse(text) as Record<string, unknown>;
|
||||
if (typeof data.detail === "string") message = data.detail;
|
||||
} catch {
|
||||
/* 非 JSON 错误体,用原文 */
|
||||
}
|
||||
throw new ApiError(response.status, message);
|
||||
}
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder("utf-8");
|
||||
let buffer = "";
|
||||
for (;;) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
let sep: number;
|
||||
while ((sep = buffer.indexOf("\n\n")) !== -1) {
|
||||
const frame = buffer.slice(0, sep);
|
||||
buffer = buffer.slice(sep + 2);
|
||||
const dataLine = frame.split("\n").find((l) => l.startsWith("data:"));
|
||||
if (!dataLine) continue;
|
||||
try {
|
||||
onEvent(JSON.parse(dataLine.slice(5).trim()));
|
||||
} catch {
|
||||
/* 跳过解析失败的帧 */
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
adoptScript(projectId: string, script_version_id: string) {
|
||||
return request<ScriptVersion>(`/api/projects/${projectId}/adopt-script/`, {
|
||||
method: "POST",
|
||||
|
||||
Reference in New Issue
Block a user