feat(skills/settings): 技能复制 + 设置持久化等会话前改动
- 技能复制:新增 POST 复制接口(_rewrite_skill_frontmatter_name 重写 frontmatter name + 校验 + 去重),前端 skills api/hooks 与技能设置页配套 - 设置持久化:lastAgentId、线程 RAG 模式锁(settings/index、local) - i18n(en/zh/types)、agent-card/input-box/message-list 等页面文案与交互微调 - scheduler/element.py 小修 (本批为会话前已存在的工作区改动,按要求一并提交;构建产物 backend.zip 未提交) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
adcc5a358b
commit
133054e2dd
18 files changed
+526
-54
No files matched your search
@@ -41,6 +41,7 @@ export const enUS: Translations = {
|
||||
code: "Code",
|
||||
preview: "Preview",
|
||||
cancel: "Cancel",
|
||||
confirm: "Confirm",
|
||||
save: "Save",
|
||||
install: "Install",
|
||||
create: "Create",
|
||||
@@ -80,6 +81,7 @@ export const enUS: Translations = {
|
||||
// Input Box
|
||||
inputBox: {
|
||||
placeholder: "How can I assist you today?",
|
||||
editQuestion: "Edit question",
|
||||
createSkillPrompt:
|
||||
"We're going to build a new skill step by step with `skill-creator`. To start, what do you want this skill to do?",
|
||||
addAttachments: "Add attachments",
|
||||
|
||||
@@ -42,6 +42,7 @@ export interface Translations {
|
||||
code: string;
|
||||
preview: string;
|
||||
cancel: string;
|
||||
confirm: string;
|
||||
save: string;
|
||||
install: string;
|
||||
create: string;
|
||||
@@ -77,6 +78,7 @@ export interface Translations {
|
||||
// Input Box
|
||||
inputBox: {
|
||||
placeholder: string;
|
||||
editQuestion: string;
|
||||
createSkillPrompt: string;
|
||||
addAttachments: string;
|
||||
mode: string;
|
||||
|
||||
@@ -41,6 +41,7 @@ export const zhCN: Translations = {
|
||||
code: "代码",
|
||||
preview: "预览",
|
||||
cancel: "取消",
|
||||
confirm: "确定",
|
||||
save: "保存",
|
||||
install: "安装",
|
||||
create: "创建",
|
||||
@@ -80,6 +81,7 @@ export const zhCN: Translations = {
|
||||
// Input Box
|
||||
inputBox: {
|
||||
placeholder: "今天我能为你做些什么?",
|
||||
editQuestion: "编辑问题",
|
||||
createSkillPrompt:
|
||||
"我们一起用 skill-creator 技能来创建一个技能吧。先问问我希望这个技能能做什么。",
|
||||
addAttachments: "添加附件",
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
export { useLocalSettings, useThreadSettings } from "./hooks";
|
||||
export { isThreadRagModeLocked, lockThreadRagMode } from "./local";
|
||||
export {
|
||||
getLastAgentId,
|
||||
isThreadRagModeLocked,
|
||||
lockThreadRagMode,
|
||||
saveLastAgentId,
|
||||
} from "./local";
|
||||
export type { LocalSettings } from "./local";
|
||||
@@ -21,6 +21,7 @@ export const DEFAULT_LOCAL_SETTINGS: LocalSettings = {
|
||||
export const LOCAL_SETTINGS_KEY = "deerflow.local-settings";
|
||||
export const THREAD_MODEL_KEY_PREFIX = "deerflow.thread-model.";
|
||||
export const THREAD_RAG_MODE_KEY_PREFIX = "deerflow.thread-rag-mode.";
|
||||
export const LAST_AGENT_ID_KEY = "deerflow.last-agent-id";
|
||||
|
||||
function isBrowser(): boolean {
|
||||
return typeof window !== "undefined";
|
||||
@@ -148,6 +149,29 @@ export function applyThreadModelOverride(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The id of the agent the user last started a conversation with. New-chat
|
||||
* landing screens pre-select this agent so the user doesn't have to re-pick it
|
||||
* every time. Returns ``null`` when nothing was stored yet.
|
||||
*/
|
||||
export function getLastAgentId(): string | null {
|
||||
if (!isBrowser()) {
|
||||
return null;
|
||||
}
|
||||
return localStorage.getItem(LAST_AGENT_ID_KEY) || null;
|
||||
}
|
||||
|
||||
export function saveLastAgentId(agentId: string | null | undefined) {
|
||||
if (!isBrowser()) {
|
||||
return;
|
||||
}
|
||||
if (!agentId) {
|
||||
localStorage.removeItem(LAST_AGENT_ID_KEY);
|
||||
return;
|
||||
}
|
||||
localStorage.setItem(LAST_AGENT_ID_KEY, agentId);
|
||||
}
|
||||
|
||||
export function getLocalSettings(): LocalSettings {
|
||||
if (!isBrowser()) {
|
||||
return DEFAULT_LOCAL_SETTINGS;
|
||||
|
||||
@@ -202,6 +202,24 @@ export async function installSkillUpload(
|
||||
return response.json() as Promise<InstallSkillResponse>;
|
||||
}
|
||||
|
||||
export async function duplicateSkill(
|
||||
sourceName: string,
|
||||
newName: string,
|
||||
): Promise<InstallSkillResponse> {
|
||||
const response = await apiFetch(
|
||||
`${getBackendBaseURL()}/api/skills/custom/${encodeURIComponent(sourceName)}/duplicate`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ new_name: newName }),
|
||||
},
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw await parseDetailedError(response, "Failed to duplicate skill");
|
||||
}
|
||||
return response.json() as Promise<InstallSkillResponse>;
|
||||
}
|
||||
|
||||
export interface ReconcileCustomSkillsResponse {
|
||||
adopted: string[];
|
||||
already_registered: string[];
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
|
||||
import {
|
||||
deleteCustomSkill,
|
||||
duplicateSkill,
|
||||
enableSkill,
|
||||
getSkill,
|
||||
getSkillContent,
|
||||
@@ -192,6 +193,22 @@ export function useSetSkillPinned() {
|
||||
});
|
||||
}
|
||||
|
||||
export function useDuplicateSkill() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: ({
|
||||
sourceName,
|
||||
newName,
|
||||
}: {
|
||||
sourceName: string;
|
||||
newName: string;
|
||||
}) => duplicateSkill(sourceName, newName),
|
||||
onSuccess: () => {
|
||||
void queryClient.invalidateQueries({ queryKey: ["skills"] });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteCustomSkill() {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
|
||||
Reference in new issue
Block a user