@kienmatu/inkos-studio 0.1.0 → 0.1.2
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/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +134 -122
- package/dist/api/server.js.map +1 -1
- package/dist/assets/FilmWizard-CuCB-1xV.js +1 -0
- package/dist/assets/{FlowView-CZ7fxX3n.js → FlowView-rMLtzFZK.js} +3 -3
- package/dist/assets/{_baseUniq-DhsR81PW.js → _baseUniq-xbTfIKo7.js} +1 -1
- package/dist/assets/{arc-Ca3YI7OI.js → arc-BJ0Jn4qy.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-DDwYWwqm.js → architectureDiagram-Q4EWVU46-B54hBS8X.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-kqPOxZ2q.js → blockDiagram-DXYQGD6D-BrQpuNBu.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-DBL27B73.js → c4Diagram-AHTNJAMY-nN4kJiPC.js} +1 -1
- package/dist/assets/channel-BMnRcRpv.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-BBF1yv__.js → chunk-4BX2VUAB-DnzQoX6p.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-DEDtFRAF.js → chunk-4TB4RGXK-PeEeEWP2.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-DMKAeQw_.js → chunk-55IACEB6-BZHFzH6I.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-DMqU-4EI.js → chunk-EDXVE4YY-B06PJNq-.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-CZTsFDcK.js → chunk-FMBD7UC4-Cny1qCNu.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-CenEcrGy.js → chunk-OYMX7WX6-MqHFCczv.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-DXpv8Mti.js → chunk-QZHKN3VN-Bu8oSEuV.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-DLM5M52k.js → chunk-YZCP3GAM-DW9WLjg7.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BpMdbRYm.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-BpMdbRYm.js +1 -0
- package/dist/assets/clone-Bfx4_ecn.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-DUMnmCfq.js → cose-bilkent-S5V4N54A-BFF3VF0W.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-DCU2j_Xn.js → dagre-KV5264BT-7wO_SFt1.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-CNmXjIem.js → diagram-5BDNPKRD-DpdLNbTp.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-BJB7dwH0.js → diagram-G4DWMVQ6-B2Xd9yJj.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-5OEVcoMB.js → diagram-MMDJMWI5-BcJABwZb.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-BH2d24yw.js → diagram-TYMM5635-ByagqgDJ.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-DhzfgmWY.js → erDiagram-SMLLAGMA-zfdClUWU.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-Dq699o3O.js → flowDiagram-DWJPFMVM-Cign1ovE.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-D2bwmYj5.js → ganttDiagram-T4ZO3ILL-zHFblbgr.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-BkH_ILeo.js → gitGraphDiagram-UUTBAWPF-DgUNvxqj.js} +1 -1
- package/dist/assets/{graph-BiqUEdJM.js → graph-CT_N4MvP.js} +1 -1
- package/dist/assets/{highlighted-body-OFNGDK62-BD0SXLUv.js → highlighted-body-OFNGDK62-CbTQmRda.js} +1 -1
- package/dist/assets/{index-CRZhDmRu.js → index-C5AvWGUU.js} +253 -253
- package/dist/assets/index-DaFEDFsq.css +1 -0
- package/dist/assets/{infoDiagram-42DDH7IO-BVDONA_E.js → infoDiagram-42DDH7IO-B1j6wnrX.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-CBELCuuM.js → ishikawaDiagram-UXIWVN3A-CmTERA2Q.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-CpXgU8Ln.js → journeyDiagram-VCZTEJTY-lXMvtopT.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-DmjKOkdI.js → kanban-definition-6JOO6SKY-D2nd3IRf.js} +1 -1
- package/dist/assets/{layout-DXBAgkSC.js → layout-Dwdg17w2.js} +1 -1
- package/dist/assets/{linear-D5tXMFlB.js → linear-BGLXP-7J.js} +1 -1
- package/dist/assets/{min-Cd4glxaN.js → min-CWmMAm2D.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-DTq5iIBu.js → mindmap-definition-QFDTVHPH-Bc_KwUJk.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-Diz1Rvfr.js → pieDiagram-DEJITSTG-Dk4TEXCU.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-58cr5yzB.js → quadrantDiagram-34T5L4WZ-DjpGwBeJ.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-DDbIc9eT.js → requirementDiagram-MS252O5E-D-KTxT1S.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-Cdx6EKcx.js → sankeyDiagram-XADWPNL6-CAAThfhf.js} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-KfheY5t3.js → sequenceDiagram-FGHM5R23-EhYXipit.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-C5Ob61Ab.js → stateDiagram-FHFEXIEX-DlwRa19l.js} +1 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-DWOlnZT2.js +1 -0
- package/dist/assets/{timeline-definition-GMOUNBTQ-FSj6jaSw.js → timeline-definition-GMOUNBTQ-CnNt36mp.js} +1 -1
- package/dist/assets/{value-1MrA2_VI.js → value-BUuNnoEe.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-BQW3LO_W.js → vennDiagram-DHZGUBPP-CONWdidS.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-D1hNSINM.js → wardley-RL74JXVD-Dx3T9_uF.js} +1 -1
- package/dist/assets/{wardleyDiagram-NUSXRM2D-SCZLQyge.js → wardleyDiagram-NUSXRM2D-Brxifhti.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-DhvOA02s.js → xychartDiagram-5P7HB3ND-DkX9QW6S.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +2 -2
- package/dist/assets/FilmWizard-DW5wLwAY.js +0 -1
- package/dist/assets/channel-yY1GNn4w.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BPxDssBj.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-BPxDssBj.js +0 -1
- package/dist/assets/clone-C9djMHZG.js +0 -1
- package/dist/assets/index-CbBLpAjo.css +0 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-CcmSvc6O.js +0 -1
package/dist/api/server.js
CHANGED
|
@@ -28,28 +28,28 @@ export function pick(lang, zh, en, vi) {
|
|
|
28
28
|
}
|
|
29
29
|
const PIPELINE_STAGES = {
|
|
30
30
|
writer: [
|
|
31
|
-
{ zh: "准备章节输入", en: "Prepare chapter input" },
|
|
32
|
-
{ zh: "撰写章节草稿", en: "Write chapter draft" },
|
|
33
|
-
{ zh: "落盘最终章节", en: "Save final chapter" },
|
|
34
|
-
{ zh: "生成最终真相文件", en: "Generate final truth files" },
|
|
35
|
-
{ zh: "校验真相文件变更", en: "Validate truth file changes" },
|
|
36
|
-
{ zh: "同步记忆索引", en: "Sync memory index" },
|
|
37
|
-
{ zh: "更新章节索引与快照", en: "Update chapter index and snapshot" },
|
|
31
|
+
{ zh: "准备章节输入", en: "Prepare chapter input", vi: "Chuẩn bị dữ liệu chương" },
|
|
32
|
+
{ zh: "撰写章节草稿", en: "Write chapter draft", vi: "Viết bản nháp chương" },
|
|
33
|
+
{ zh: "落盘最终章节", en: "Save final chapter", vi: "Lưu chương hoàn chỉnh" },
|
|
34
|
+
{ zh: "生成最终真相文件", en: "Generate final truth files", vi: "Tạo tệp truth cuối cùng" },
|
|
35
|
+
{ zh: "校验真相文件变更", en: "Validate truth file changes", vi: "Kiểm tra thay đổi tệp truth" },
|
|
36
|
+
{ zh: "同步记忆索引", en: "Sync memory index", vi: "Đồng bộ chỉ mục bộ nhớ" },
|
|
37
|
+
{ zh: "更新章节索引与快照", en: "Update chapter index and snapshot", vi: "Cập nhật chỉ mục và snapshot chương" },
|
|
38
38
|
],
|
|
39
39
|
architect: [
|
|
40
|
-
{ zh: "生成基础设定", en: "Generate foundation" },
|
|
41
|
-
{ zh: "保存书籍配置", en: "Save book config" },
|
|
42
|
-
{ zh: "写入基础设定文件", en: "Write foundation files" },
|
|
43
|
-
{ zh: "初始化控制文档", en: "Initialize control documents" },
|
|
44
|
-
{ zh: "创建初始快照", en: "Create initial snapshot" },
|
|
40
|
+
{ zh: "生成基础设定", en: "Generate foundation", vi: "Tạo thiết lập nền tảng" },
|
|
41
|
+
{ zh: "保存书籍配置", en: "Save book config", vi: "Lưu cấu hình sách" },
|
|
42
|
+
{ zh: "写入基础设定文件", en: "Write foundation files", vi: "Ghi tệp thiết lập nền tảng" },
|
|
43
|
+
{ zh: "初始化控制文档", en: "Initialize control documents", vi: "Khởi tạo tài liệu kiểm soát" },
|
|
44
|
+
{ zh: "创建初始快照", en: "Create initial snapshot", vi: "Tạo snapshot ban đầu" },
|
|
45
45
|
],
|
|
46
46
|
reviser: [
|
|
47
|
-
{ zh: "加载修订上下文", en: "Load revision context" },
|
|
48
|
-
{ zh: "修订章节", en: "Revise chapter" },
|
|
49
|
-
{ zh: "落盘修订结果", en: "Save revision result" },
|
|
50
|
-
{ zh: "更新索引与快照", en: "Update index and snapshot" },
|
|
47
|
+
{ zh: "加载修订上下文", en: "Load revision context", vi: "Tải ngữ cảnh chỉnh sửa" },
|
|
48
|
+
{ zh: "修订章节", en: "Revise chapter", vi: "Chỉnh sửa chương" },
|
|
49
|
+
{ zh: "落盘修订结果", en: "Save revision result", vi: "Lưu kết quả chỉnh sửa" },
|
|
50
|
+
{ zh: "更新索引与快照", en: "Update index and snapshot", vi: "Cập nhật chỉ mục và snapshot" },
|
|
51
51
|
],
|
|
52
|
-
auditor: [{ zh: "审计章节", en: "Audit chapter" }],
|
|
52
|
+
auditor: [{ zh: "审计章节", en: "Audit chapter", vi: "Kiểm duyệt chương" }],
|
|
53
53
|
};
|
|
54
54
|
function pipelineStages(agent, lang = "vi") {
|
|
55
55
|
return PIPELINE_STAGES[agent]?.map((stage) => pick(lang, stage.zh, stage.en, stage.vi));
|
|
@@ -59,35 +59,35 @@ function attachmentDisposition(fileName) {
|
|
|
59
59
|
return `attachment; filename="${safeAscii}"; filename*=UTF-8''${encodeURIComponent(fileName)}`;
|
|
60
60
|
}
|
|
61
61
|
const AGENT_LABELS = {
|
|
62
|
-
architect: { zh: "建书", en: "Book setup" },
|
|
63
|
-
writer: { zh: "写作", en: "Writing" },
|
|
64
|
-
auditor: { zh: "审计", en: "Audit" },
|
|
65
|
-
reviser: { zh: "修订", en: "Revision" },
|
|
66
|
-
exporter: { zh: "导出", en: "Export" },
|
|
62
|
+
architect: { zh: "建书", en: "Book setup", vi: "Khởi tạo sách" },
|
|
63
|
+
writer: { zh: "写作", en: "Writing", vi: "Viết" },
|
|
64
|
+
auditor: { zh: "审计", en: "Audit", vi: "Kiểm duyệt" },
|
|
65
|
+
reviser: { zh: "修订", en: "Revision", vi: "Chỉnh sửa" },
|
|
66
|
+
exporter: { zh: "导出", en: "Export", vi: "Xuất" },
|
|
67
67
|
};
|
|
68
68
|
const TOOL_LABELS = {
|
|
69
|
-
read: { zh: "读取文件", en: "Read file" },
|
|
70
|
-
edit: { zh: "编辑文件", en: "Edit file" },
|
|
71
|
-
grep: { zh: "搜索", en: "Search" },
|
|
72
|
-
ls: { zh: "列目录", en: "List directory" },
|
|
73
|
-
propose_action: { zh: "确认动作", en: "Confirm action" },
|
|
74
|
-
short_fiction_run: { zh: "短篇生产", en: "Short fiction" },
|
|
75
|
-
script_create: { zh: "剧本创作", en: "Script creation" },
|
|
76
|
-
storyboard_create: { zh: "分镜创作", en: "Storyboard creation" },
|
|
77
|
-
interactive_film_create: { zh: "互动影游", en: "Interactive film" },
|
|
78
|
-
translation_create: { zh: "翻译项目", en: "Translation" },
|
|
79
|
-
fanfic_create: { zh: "同人创作", en: "Fanfiction" },
|
|
80
|
-
continuation_import: { zh: "导入续写", en: "Continuation import" },
|
|
81
|
-
spinoff_create: { zh: "番外创作", en: "Side story" },
|
|
82
|
-
imitation_create: { zh: "仿写创作", en: "Style imitation" },
|
|
83
|
-
generate_cover: { zh: "生成封面", en: "Cover generation" },
|
|
84
|
-
play_edit: { zh: "编辑互动世界", en: "Edit interactive world" },
|
|
85
|
-
play_start: { zh: "启动互动世界", en: "Start interactive world" },
|
|
86
|
-
play_revise: { zh: "重做互动回合", en: "Redo interactive turn" },
|
|
87
|
-
play_step: { zh: "推进互动世界", en: "Advance interactive world" },
|
|
88
|
-
create_narrative_forecast: { zh: "剧情多线推演", en: "Narrative forecast" },
|
|
89
|
-
get_narrative_forecast: { zh: "核验剧情推演", en: "Recheck forecast" },
|
|
90
|
-
select_narrative_branch: { zh: "采用候选分支", en: "Select candidate branch" },
|
|
69
|
+
read: { zh: "读取文件", en: "Read file", vi: "Đọc tệp" },
|
|
70
|
+
edit: { zh: "编辑文件", en: "Edit file", vi: "Chỉnh sửa tệp" },
|
|
71
|
+
grep: { zh: "搜索", en: "Search", vi: "Tìm kiếm" },
|
|
72
|
+
ls: { zh: "列目录", en: "List directory", vi: "Liệt kê thư mục" },
|
|
73
|
+
propose_action: { zh: "确认动作", en: "Confirm action", vi: "Xác nhận hành động" },
|
|
74
|
+
short_fiction_run: { zh: "短篇生产", en: "Short fiction", vi: "Truyện ngắn" },
|
|
75
|
+
script_create: { zh: "剧本创作", en: "Script creation", vi: "Sáng tác kịch bản" },
|
|
76
|
+
storyboard_create: { zh: "分镜创作", en: "Storyboard creation", vi: "Sáng tác phân cảnh" },
|
|
77
|
+
interactive_film_create: { zh: "互动影游", en: "Interactive film", vi: "Phim tương tác" },
|
|
78
|
+
translation_create: { zh: "翻译项目", en: "Translation", vi: "Dự án dịch thuật" },
|
|
79
|
+
fanfic_create: { zh: "同人创作", en: "Fanfiction", vi: "Sáng tác đồng nhân" },
|
|
80
|
+
continuation_import: { zh: "导入续写", en: "Continuation import", vi: "Nhập truyện viết tiếp" },
|
|
81
|
+
spinoff_create: { zh: "番外创作", en: "Side story", vi: "Sáng tác ngoại truyện" },
|
|
82
|
+
imitation_create: { zh: "仿写创作", en: "Style imitation", vi: "Sáng tác phỏng bút" },
|
|
83
|
+
generate_cover: { zh: "生成封面", en: "Cover generation", vi: "Tạo bìa sách" },
|
|
84
|
+
play_edit: { zh: "编辑互动世界", en: "Edit interactive world", vi: "Chỉnh sửa thế giới tương tác" },
|
|
85
|
+
play_start: { zh: "启动互动世界", en: "Start interactive world", vi: "Khởi động thế giới tương tác" },
|
|
86
|
+
play_revise: { zh: "重做互动回合", en: "Redo interactive turn", vi: "Làm lại lượt tương tác" },
|
|
87
|
+
play_step: { zh: "推进互动世界", en: "Advance interactive world", vi: "Tiếp tục thế giới tương tác" },
|
|
88
|
+
create_narrative_forecast: { zh: "剧情多线推演", en: "Narrative forecast", vi: "Dự đoán cốt truyện" },
|
|
89
|
+
get_narrative_forecast: { zh: "核验剧情推演", en: "Recheck forecast", vi: "Kiểm tra lại dự đoán" },
|
|
90
|
+
select_narrative_branch: { zh: "采用候选分支", en: "Select candidate branch", vi: "Chọn nhánh đề xuất" },
|
|
91
91
|
};
|
|
92
92
|
function resolveToolLabel(tool, agent, lang = "vi") {
|
|
93
93
|
if (tool === "sub_agent" && agent) {
|
|
@@ -102,8 +102,8 @@ function formatTaskElapsed(ms, lang) {
|
|
|
102
102
|
const minutes = Math.floor(totalSeconds / 60);
|
|
103
103
|
const seconds = totalSeconds % 60;
|
|
104
104
|
if (minutes === 0)
|
|
105
|
-
return pick(lang, `${seconds} 秒`, `${seconds}s`);
|
|
106
|
-
return pick(lang, `${minutes} 分 ${seconds} 秒`, `${minutes}m ${seconds}s`);
|
|
105
|
+
return pick(lang, `${seconds} 秒`, `${seconds}s`, `${seconds} giây`);
|
|
106
|
+
return pick(lang, `${minutes} 分 ${seconds} 秒`, `${minutes}m ${seconds}s`, `${minutes} phút ${seconds} giây`);
|
|
107
107
|
}
|
|
108
108
|
/**
|
|
109
109
|
* 把正在后台运行的生产任务状态渲染成一段系统提示词附录,注入聊天 agent 的上下文。
|
|
@@ -113,11 +113,11 @@ function buildRunningTaskContextBlock(task, lang) {
|
|
|
113
113
|
const exec = task.execution;
|
|
114
114
|
const elapsed = formatTaskElapsed(Date.now() - exec.startedAt, lang);
|
|
115
115
|
const status = exec.status === "processing"
|
|
116
|
-
? pick(lang, "处理中", "processing")
|
|
117
|
-
: pick(lang, "运行中", "running");
|
|
116
|
+
? pick(lang, "处理中", "processing", "đang xử lý")
|
|
117
|
+
: pick(lang, "运行中", "running", "đang chạy");
|
|
118
118
|
const logsTail = (exec.logs ?? []).slice(-3);
|
|
119
119
|
const logsBlock = logsTail.length > 0
|
|
120
|
-
? `\n${pick(lang, "- 最近日志:", "- Recent logs:")}\n${logsTail.map((line) => ` - ${line}`).join("\n")}`
|
|
120
|
+
? `\n${pick(lang, "- 最近日志:", "- Recent logs:", "- Nhật ký gần đây:")}\n${logsTail.map((line) => ` - ${line}`).join("\n")}`
|
|
121
121
|
: "";
|
|
122
122
|
return pick(lang, [
|
|
123
123
|
"## 后台任务状态",
|
|
@@ -133,6 +133,13 @@ function buildRunningTaskContextBlock(task, lang) {
|
|
|
133
133
|
`- Status: ${status}`,
|
|
134
134
|
`- Elapsed: ${elapsed}${logsBlock}`,
|
|
135
135
|
"The task runs independently in the background; this chat turn does not interrupt it. When the user asks about its progress, answer truthfully from the information above. Do not start another production task of the same kind, and do not claim that no task is running. Production tools are temporarily unavailable and will be restored when the task finishes.",
|
|
136
|
+
].join("\n"), [
|
|
137
|
+
"## Trạng thái tác vụ nền",
|
|
138
|
+
"Phiên này có một tác vụ sản xuất đang chạy nền:",
|
|
139
|
+
`- Tác vụ: ${exec.label} (${exec.tool})`,
|
|
140
|
+
`- Trạng thái: ${status}`,
|
|
141
|
+
`- Thời gian đã chạy: ${elapsed}${logsBlock}`,
|
|
142
|
+
"Tác vụ chạy độc lập ở nền; lượt trò chuyện này không làm gián đoạn nó. Khi người dùng hỏi về tiến độ, hãy trả lời trung thực dựa trên thông tin trên. Không khởi chạy thêm tác vụ sản xuất cùng loại, và không nói rằng không có tác vụ nào đang chạy. Các công cụ sản xuất tạm thời không khả dụng và sẽ được khôi phục khi tác vụ kết thúc.",
|
|
136
143
|
].join("\n"));
|
|
137
144
|
}
|
|
138
145
|
function compareServiceListItems(left, right) {
|
|
@@ -261,7 +268,7 @@ function normalizeApiBookId(value, fieldName) {
|
|
|
261
268
|
return bookId;
|
|
262
269
|
}
|
|
263
270
|
function nonTextModelMessage(modelId, lang = "vi") {
|
|
264
|
-
return pick(lang, `模型 ${modelId} 不适合文本聊天/写作。请在模型选择器中改用文本模型,例如 gemini-2.5-flash、gemini-2.5-pro 或对应服务的 chat 模型。`, `Model ${modelId} is not suitable for text chat/writing. Pick a text model in the model selector, e.g. gemini-2.5-flash, gemini-2.5-pro, or the service's chat model.`);
|
|
271
|
+
return pick(lang, `模型 ${modelId} 不适合文本聊天/写作。请在模型选择器中改用文本模型,例如 gemini-2.5-flash、gemini-2.5-pro 或对应服务的 chat 模型。`, `Model ${modelId} is not suitable for text chat/writing. Pick a text model in the model selector, e.g. gemini-2.5-flash, gemini-2.5-pro, or the service's chat model.`, `Model ${modelId} không phù hợp để chat/viết văn bản. Hãy chọn model văn bản khác trong bộ chọn model, ví dụ gemini-2.5-flash, gemini-2.5-pro, hoặc model chat của dịch vụ tương ứng.`);
|
|
265
272
|
}
|
|
266
273
|
function extractToolError(result) {
|
|
267
274
|
return summarizeToolResult(result, 500);
|
|
@@ -748,27 +755,27 @@ function validateAgentActionExecution(args) {
|
|
|
748
755
|
const lang = args.language ?? "vi";
|
|
749
756
|
const failedExec = args.collectedToolExecs.find(isLikelyFailedToolResult);
|
|
750
757
|
if (failedExec) {
|
|
751
|
-
const detail = failedExec.error ?? failedExec.result ?? pick(lang, "未知错误", "unknown error");
|
|
752
|
-
return pick(lang, `${failedExec.label} 执行失败:${detail}`, `${failedExec.label} failed: ${detail}`);
|
|
758
|
+
const detail = failedExec.error ?? failedExec.result ?? pick(lang, "未知错误", "unknown error", "lỗi không xác định");
|
|
759
|
+
return pick(lang, `${failedExec.label} 执行失败:${detail}`, `${failedExec.label} failed: ${detail}`, `${failedExec.label} thất bại: ${detail}`);
|
|
753
760
|
}
|
|
754
761
|
if (args.agentBookId
|
|
755
762
|
&& args.requestedIntent === "write_next"
|
|
756
763
|
&& !hasSuccessfulSubAgentExec(args.collectedToolExecs, "writer")) {
|
|
757
|
-
return pick(lang, "模型声称已完成下一章,但没有实际调用写作工具。请重试;如果仍失败,请检查模型是否支持工具调用。", "The model claimed the next chapter is done, but it never called the writing tool. Retry; if it keeps failing, check whether the model supports tool calls.");
|
|
764
|
+
return pick(lang, "模型声称已完成下一章,但没有实际调用写作工具。请重试;如果仍失败,请检查模型是否支持工具调用。", "The model claimed the next chapter is done, but it never called the writing tool. Retry; if it keeps failing, check whether the model supports tool calls.", "Model báo đã hoàn thành chương tiếp theo, nhưng thực tế không gọi tool viết. Hãy thử lại; nếu vẫn thất bại, kiểm tra xem model có hỗ trợ gọi tool không.");
|
|
758
765
|
}
|
|
759
766
|
if (!args.agentBookId
|
|
760
767
|
&& args.requestedIntent === "create_book"
|
|
761
768
|
&& !hasSuccessfulSubAgentExec(args.collectedToolExecs, "architect")) {
|
|
762
|
-
return pick(lang, "已确认建书,但模型没有实际调用建书工具。请重试;如果仍失败,请检查模型是否支持工具调用。", "Book creation was confirmed, but the model never called the book setup tool. Retry; if it keeps failing, check whether the model supports tool calls.");
|
|
769
|
+
return pick(lang, "已确认建书,但模型没有实际调用建书工具。请重试;如果仍失败,请检查模型是否支持工具调用。", "Book creation was confirmed, but the model never called the book setup tool. Retry; if it keeps failing, check whether the model supports tool calls.", "Đã xác nhận khởi tạo sách, nhưng model không gọi tool khởi tạo sách. Hãy thử lại; nếu vẫn thất bại, kiểm tra xem model có hỗ trợ gọi tool không.");
|
|
763
770
|
}
|
|
764
771
|
if (args.requestedIntent === "short_run" && !hasSuccessfulToolExec(args.collectedToolExecs, "short_fiction_run")) {
|
|
765
|
-
return pick(lang, "已确认生成短篇,但模型没有实际调用短篇生产工具。请重试;如果仍失败,请检查模型是否支持工具调用。", "Short fiction was confirmed, but the model never called the short fiction tool. Retry; if it keeps failing, check whether the model supports tool calls.");
|
|
772
|
+
return pick(lang, "已确认生成短篇,但模型没有实际调用短篇生产工具。请重试;如果仍失败,请检查模型是否支持工具调用。", "Short fiction was confirmed, but the model never called the short fiction tool. Retry; if it keeps failing, check whether the model supports tool calls.", "Đã xác nhận tạo truyện ngắn, nhưng model không gọi tool sản xuất truyện ngắn. Hãy thử lại; nếu vẫn thất bại, kiểm tra xem model có hỗ trợ gọi tool không.");
|
|
766
773
|
}
|
|
767
774
|
if (args.requestedIntent === "play_start" && !hasSuccessfulToolExec(args.collectedToolExecs, "play_start")) {
|
|
768
|
-
return pick(lang, "已确认启动互动世界,但模型没有实际调用互动世界工具。请重试;如果仍失败,请检查模型是否支持工具调用。", "Starting the interactive world was confirmed, but the model never called the interactive world tool. Retry; if it keeps failing, check whether the model supports tool calls.");
|
|
775
|
+
return pick(lang, "已确认启动互动世界,但模型没有实际调用互动世界工具。请重试;如果仍失败,请检查模型是否支持工具调用。", "Starting the interactive world was confirmed, but the model never called the interactive world tool. Retry; if it keeps failing, check whether the model supports tool calls.", "Đã xác nhận khởi động thế giới tương tác, nhưng model không gọi tool thế giới tương tác. Hãy thử lại; nếu vẫn thất bại, kiểm tra xem model có hỗ trợ gọi tool không.");
|
|
769
776
|
}
|
|
770
777
|
if (args.requestedIntent === "generate_cover" && !hasSuccessfulToolExec(args.collectedToolExecs, "generate_cover")) {
|
|
771
|
-
return pick(lang, "已确认生成封面,但模型没有实际调用封面工具。请重试;如果仍失败,请检查模型是否支持工具调用。", "Cover generation was confirmed, but the model never called the cover tool. Retry; if it keeps failing, check whether the model supports tool calls.");
|
|
778
|
+
return pick(lang, "已确认生成封面,但模型没有实际调用封面工具。请重试;如果仍失败,请检查模型是否支持工具调用。", "Cover generation was confirmed, but the model never called the cover tool. Retry; if it keeps failing, check whether the model supports tool calls.", "Đã xác nhận tạo bìa sách, nhưng model không gọi tool tạo bìa. Hãy thử lại; nếu vẫn thất bại, kiểm tra xem model có hỗ trợ gọi tool không.");
|
|
772
779
|
}
|
|
773
780
|
return undefined;
|
|
774
781
|
}
|
|
@@ -798,7 +805,7 @@ function formatAgentFailure(message, lang = "vi") {
|
|
|
798
805
|
if (kind === "internal") {
|
|
799
806
|
return {
|
|
800
807
|
code: "AGENT_INTERNAL_ERROR",
|
|
801
|
-
message: pick(lang, `InkOS 内部流程错误:${message}`, `InkOS internal pipeline error: ${message}`),
|
|
808
|
+
message: pick(lang, `InkOS 内部流程错误:${message}`, `InkOS internal pipeline error: ${message}`, `Lỗi quy trình nội bộ InkOS: ${message}`),
|
|
802
809
|
status: 500,
|
|
803
810
|
};
|
|
804
811
|
}
|
|
@@ -868,7 +875,7 @@ function requirePayloadText(value, message) {
|
|
|
868
875
|
}
|
|
869
876
|
function toolResultText(result, lang = "vi") {
|
|
870
877
|
const text = extractToolError(result).trim();
|
|
871
|
-
return text || pick(lang, "已完成。", "Done.");
|
|
878
|
+
return text || pick(lang, "已完成。", "Done.", "Đã hoàn thành.");
|
|
872
879
|
}
|
|
873
880
|
async function executeConfirmedProductionAction(args) {
|
|
874
881
|
const lang = args.language ?? "vi";
|
|
@@ -886,7 +893,7 @@ async function executeConfirmedProductionAction(args) {
|
|
|
886
893
|
let agent;
|
|
887
894
|
if (args.requestedIntent === "create_book") {
|
|
888
895
|
const payload = actionPayload?.createBook;
|
|
889
|
-
const title = requirePayloadText(payload?.title, pick(lang, "确认建书缺少书名,请重新生成确认卡。", "The book creation confirmation is missing a title. Regenerate the confirmation card."));
|
|
896
|
+
const title = requirePayloadText(payload?.title, pick(lang, "确认建书缺少书名,请重新生成确认卡。", "The book creation confirmation is missing a title. Regenerate the confirmation card.", "Xác nhận khởi tạo sách thiếu tên sách. Vui lòng tạo lại thẻ xác nhận."));
|
|
890
897
|
tool = createSubAgentTool(args.pipeline, null, args.root, {
|
|
891
898
|
actionPayload,
|
|
892
899
|
language: toWritingLanguage(lang),
|
|
@@ -908,7 +915,7 @@ async function executeConfirmedProductionAction(args) {
|
|
|
908
915
|
const payload = actionPayload?.shortRun;
|
|
909
916
|
const direction = payload?.direction?.trim() || args.instruction.trim();
|
|
910
917
|
if (!direction)
|
|
911
|
-
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "确认短篇缺少方向,请重新生成确认卡。", "The short fiction confirmation is missing a direction. Regenerate the confirmation card."));
|
|
918
|
+
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "确认短篇缺少方向,请重新生成确认卡。", "The short fiction confirmation is missing a direction. Regenerate the confirmation card.", "Xác nhận truyện ngắn thiếu định hướng. Vui lòng tạo lại thẻ xác nhận."));
|
|
912
919
|
tool = createShortFictionRunTool(args.pipeline, args.root, {
|
|
913
920
|
actionPayload,
|
|
914
921
|
language: toWritingLanguage(lang),
|
|
@@ -925,7 +932,7 @@ async function executeConfirmedProductionAction(args) {
|
|
|
925
932
|
}
|
|
926
933
|
else if (args.requestedIntent === "write_next") {
|
|
927
934
|
if (!args.bookId) {
|
|
928
|
-
throw new ApiError(400, "BOOK_ID_REQUIRED", pick(lang, "写下一章需要先打开一本书。", "Writing the next chapter requires an active book."));
|
|
935
|
+
throw new ApiError(400, "BOOK_ID_REQUIRED", pick(lang, "写下一章需要先打开一本书。", "Writing the next chapter requires an active book.", "Viết chương tiếp theo cần mở một cuốn sách trước."));
|
|
929
936
|
}
|
|
930
937
|
const chapterCount = actionPayload?.writeNext?.chapterCount ?? 1;
|
|
931
938
|
tool = createSubAgentTool(args.pipeline, args.bookId, args.root, {
|
|
@@ -942,7 +949,7 @@ async function executeConfirmedProductionAction(args) {
|
|
|
942
949
|
}
|
|
943
950
|
else if (args.requestedIntent === "generate_cover") {
|
|
944
951
|
const payload = actionPayload?.generateCover;
|
|
945
|
-
const title = requirePayloadText(payload?.title, pick(lang, "确认生成封面缺少标题,请重新生成确认卡。", "The cover generation confirmation is missing a title. Regenerate the confirmation card."));
|
|
952
|
+
const title = requirePayloadText(payload?.title, pick(lang, "确认生成封面缺少标题,请重新生成确认卡。", "The cover generation confirmation is missing a title. Regenerate the confirmation card.", "Xác nhận tạo bìa sách thiếu tiêu đề. Vui lòng tạo lại thẻ xác nhận."));
|
|
946
953
|
tool = createGenerateCoverTool(args.root, { actionPayload });
|
|
947
954
|
params = {
|
|
948
955
|
title,
|
|
@@ -954,7 +961,7 @@ async function executeConfirmedProductionAction(args) {
|
|
|
954
961
|
}
|
|
955
962
|
else if (args.requestedIntent === "script_create") {
|
|
956
963
|
const payload = actionPayload?.scriptCreate;
|
|
957
|
-
const title = requirePayloadText(payload?.title, pick(lang, "确认创建剧本缺少标题,请重新生成确认卡。", "The script creation confirmation is missing a title. Regenerate the confirmation card."));
|
|
964
|
+
const title = requirePayloadText(payload?.title, pick(lang, "确认创建剧本缺少标题,请重新生成确认卡。", "The script creation confirmation is missing a title. Regenerate the confirmation card.", "Xác nhận tạo kịch bản thiếu tiêu đề. Vui lòng tạo lại thẻ xác nhận."));
|
|
958
965
|
tool = createScriptCreationTool(args.pipeline, args.root, {
|
|
959
966
|
actionPayload,
|
|
960
967
|
language: toWritingLanguage(lang),
|
|
@@ -976,7 +983,7 @@ async function executeConfirmedProductionAction(args) {
|
|
|
976
983
|
}
|
|
977
984
|
else if (args.requestedIntent === "storyboard_create") {
|
|
978
985
|
const payload = actionPayload?.storyboardCreate;
|
|
979
|
-
const title = requirePayloadText(payload?.title, pick(lang, "确认创建分镜缺少标题,请重新生成确认卡。", "The storyboard creation confirmation is missing a title. Regenerate the confirmation card."));
|
|
986
|
+
const title = requirePayloadText(payload?.title, pick(lang, "确认创建分镜缺少标题,请重新生成确认卡。", "The storyboard creation confirmation is missing a title. Regenerate the confirmation card.", "Xác nhận tạo phân cảnh thiếu tiêu đề. Vui lòng tạo lại thẻ xác nhận."));
|
|
980
987
|
tool = createStoryboardCreationTool(args.pipeline, args.root, {
|
|
981
988
|
actionPayload,
|
|
982
989
|
language: toWritingLanguage(lang),
|
|
@@ -999,7 +1006,7 @@ async function executeConfirmedProductionAction(args) {
|
|
|
999
1006
|
}
|
|
1000
1007
|
else if (args.requestedIntent === "interactive_film_create") {
|
|
1001
1008
|
const payload = actionPayload?.interactiveFilmCreate;
|
|
1002
|
-
const title = requirePayloadText(payload?.title, pick(lang, "确认创建互动影游缺少标题,请重新生成确认卡。", "The interactive film confirmation is missing a title. Regenerate the confirmation card."));
|
|
1009
|
+
const title = requirePayloadText(payload?.title, pick(lang, "确认创建互动影游缺少标题,请重新生成确认卡。", "The interactive film confirmation is missing a title. Regenerate the confirmation card.", "Xác nhận tạo phim tương tác thiếu tiêu đề. Vui lòng tạo lại thẻ xác nhận."));
|
|
1003
1010
|
tool = createInteractiveFilmCreationTool(args.pipeline, args.root, {
|
|
1004
1011
|
actionPayload,
|
|
1005
1012
|
language: toWritingLanguage(lang),
|
|
@@ -1023,9 +1030,9 @@ async function executeConfirmedProductionAction(args) {
|
|
|
1023
1030
|
}
|
|
1024
1031
|
else if (args.requestedIntent === "translation_create") {
|
|
1025
1032
|
const payload = actionPayload?.translationCreate;
|
|
1026
|
-
const filePath = requirePayloadText(payload?.filePath, pick(lang, "确认创建翻译项目缺少文件路径,请重新生成确认卡。", "The translation confirmation is missing a file path. Regenerate the confirmation card."));
|
|
1027
|
-
const sourceLanguage = requirePayloadText(payload?.sourceLanguage, pick(lang, "确认创建翻译项目缺少源语言,请重新生成确认卡。", "The translation confirmation is missing a source language. Regenerate the confirmation card."));
|
|
1028
|
-
const targetLanguage = requirePayloadText(payload?.targetLanguage, pick(lang, "确认创建翻译项目缺少目标语言,请重新生成确认卡。", "The translation confirmation is missing a target language. Regenerate the confirmation card."));
|
|
1033
|
+
const filePath = requirePayloadText(payload?.filePath, pick(lang, "确认创建翻译项目缺少文件路径,请重新生成确认卡。", "The translation confirmation is missing a file path. Regenerate the confirmation card.", "Xác nhận tạo dự án dịch thuật thiếu đường dẫn tệp. Vui lòng tạo lại thẻ xác nhận."));
|
|
1034
|
+
const sourceLanguage = requirePayloadText(payload?.sourceLanguage, pick(lang, "确认创建翻译项目缺少源语言,请重新生成确认卡。", "The translation confirmation is missing a source language. Regenerate the confirmation card.", "Xác nhận tạo dự án dịch thuật thiếu ngôn ngữ nguồn. Vui lòng tạo lại thẻ xác nhận."));
|
|
1035
|
+
const targetLanguage = requirePayloadText(payload?.targetLanguage, pick(lang, "确认创建翻译项目缺少目标语言,请重新生成确认卡。", "The translation confirmation is missing a target language. Regenerate the confirmation card.", "Xác nhận tạo dự án dịch thuật thiếu ngôn ngữ đích. Vui lòng tạo lại thẻ xác nhận."));
|
|
1029
1036
|
tool = createTranslationCreateTool(args.root, { actionPayload });
|
|
1030
1037
|
params = {
|
|
1031
1038
|
filePath,
|
|
@@ -1037,9 +1044,9 @@ async function executeConfirmedProductionAction(args) {
|
|
|
1037
1044
|
}
|
|
1038
1045
|
else if (args.requestedIntent === "fanfic_init") {
|
|
1039
1046
|
const payload = actionPayload?.fanficCreate;
|
|
1040
|
-
const title = requirePayloadText(payload?.title, pick(lang, "确认创建同人缺少书名,请补充后重新确认。", "The fanfiction confirmation is missing a title."));
|
|
1047
|
+
const title = requirePayloadText(payload?.title, pick(lang, "确认创建同人缺少书名,请补充后重新确认。", "The fanfiction confirmation is missing a title.", "Xác nhận tạo truyện đồng nhân thiếu tên sách. Vui lòng bổ sung rồi xác nhận lại."));
|
|
1041
1048
|
if (!payload?.sourceText?.trim() && !payload?.sourcePath?.trim()) {
|
|
1042
|
-
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "创建同人需要原作资料或上传文件。", "Fanfiction creation requires source material or an uploaded file."));
|
|
1049
|
+
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "创建同人需要原作资料或上传文件。", "Fanfiction creation requires source material or an uploaded file.", "Tạo truyện đồng nhân cần tài liệu gốc hoặc tệp đã tải lên."));
|
|
1043
1050
|
}
|
|
1044
1051
|
tool = createFanficBookTool(args.pipeline, args.root, {
|
|
1045
1052
|
defaultSkills: productionSkills("longWriting"),
|
|
@@ -1059,10 +1066,10 @@ async function executeConfirmedProductionAction(args) {
|
|
|
1059
1066
|
}
|
|
1060
1067
|
else if (args.requestedIntent === "continuation_import") {
|
|
1061
1068
|
const payload = actionPayload?.continuationImport;
|
|
1062
|
-
const sourcePath = requirePayloadText(payload?.sourcePath, pick(lang, "导入续写需要上传文件或章节目录。", "Continuation import requires an uploaded file or chapter directory."));
|
|
1069
|
+
const sourcePath = requirePayloadText(payload?.sourcePath, pick(lang, "导入续写需要上传文件或章节目录。", "Continuation import requires an uploaded file or chapter directory.", "Nhập truyện viết tiếp cần tệp đã tải lên hoặc thư mục chương."));
|
|
1063
1070
|
const targetBookId = payload?.bookId ?? args.bookId ?? undefined;
|
|
1064
1071
|
if (!targetBookId && !payload?.title?.trim()) {
|
|
1065
|
-
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "导入续写需要选择已有书籍或填写新书名。", "Continuation import requires an existing book or a new title."));
|
|
1072
|
+
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "导入续写需要选择已有书籍或填写新书名。", "Continuation import requires an existing book or a new title.", "Nhập truyện viết tiếp cần chọn sách đã có hoặc nhập tên sách mới."));
|
|
1066
1073
|
}
|
|
1067
1074
|
tool = createContinuationImportTool(args.pipeline, args.bookId, args.root, {
|
|
1068
1075
|
defaultSkills: productionSkills("longWriting"),
|
|
@@ -1082,8 +1089,8 @@ async function executeConfirmedProductionAction(args) {
|
|
|
1082
1089
|
}
|
|
1083
1090
|
else if (args.requestedIntent === "spinoff_create") {
|
|
1084
1091
|
const payload = actionPayload?.spinoffCreate;
|
|
1085
|
-
const title = requirePayloadText(payload?.title, pick(lang, "确认创建番外缺少书名。", "The side-story confirmation is missing a title."));
|
|
1086
|
-
const parentBookId = requirePayloadText(payload?.parentBookId ?? args.bookId ?? undefined, pick(lang, "创建番外需要指定正传书籍。", "Side-story creation requires a parent book."));
|
|
1092
|
+
const title = requirePayloadText(payload?.title, pick(lang, "确认创建番外缺少书名。", "The side-story confirmation is missing a title.", "Xác nhận tạo ngoại truyện thiếu tên sách."));
|
|
1093
|
+
const parentBookId = requirePayloadText(payload?.parentBookId ?? args.bookId ?? undefined, pick(lang, "创建番外需要指定正传书籍。", "Side-story creation requires a parent book.", "Tạo ngoại truyện cần chỉ định sách chính."));
|
|
1087
1094
|
tool = createSpinoffBookTool(args.pipeline, args.root, {
|
|
1088
1095
|
defaultSkills: productionSkills("longWriting"),
|
|
1089
1096
|
});
|
|
@@ -1100,10 +1107,10 @@ async function executeConfirmedProductionAction(args) {
|
|
|
1100
1107
|
}
|
|
1101
1108
|
else if (args.requestedIntent === "style_imitation") {
|
|
1102
1109
|
const payload = actionPayload?.imitationCreate;
|
|
1103
|
-
const title = requirePayloadText(payload?.title, pick(lang, "确认创建仿写缺少书名。", "The imitation confirmation is missing a title."));
|
|
1104
|
-
const storyIdea = requirePayloadText(payload?.storyIdea, pick(lang, "仿写需要一个原创故事方向。", "Style imitation requires an original story idea."));
|
|
1110
|
+
const title = requirePayloadText(payload?.title, pick(lang, "确认创建仿写缺少书名。", "The imitation confirmation is missing a title.", "Xác nhận tạo tác phẩm phỏng bút thiếu tên sách."));
|
|
1111
|
+
const storyIdea = requirePayloadText(payload?.storyIdea, pick(lang, "仿写需要一个原创故事方向。", "Style imitation requires an original story idea.", "Phỏng bút cần một định hướng câu chuyện gốc."));
|
|
1105
1112
|
if (!payload?.referenceText?.trim() && !payload?.referencePath?.trim()) {
|
|
1106
|
-
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "仿写需要参考文本或上传文件。", "Style imitation requires reference text or an uploaded file."));
|
|
1113
|
+
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "仿写需要参考文本或上传文件。", "Style imitation requires reference text or an uploaded file.", "Phỏng bút cần văn bản tham khảo hoặc tệp đã tải lên."));
|
|
1107
1114
|
}
|
|
1108
1115
|
tool = createImitationBookTool(args.pipeline, args.root, {
|
|
1109
1116
|
defaultSkills: productionSkills("longWriting"),
|
|
@@ -1123,7 +1130,7 @@ async function executeConfirmedProductionAction(args) {
|
|
|
1123
1130
|
}
|
|
1124
1131
|
else if (args.requestedIntent === "play_start") {
|
|
1125
1132
|
const payload = actionPayload?.playStart;
|
|
1126
|
-
const title = requirePayloadText(payload?.title, pick(lang, "确认启动互动世界缺少标题,请重新生成确认卡。", "The interactive world start confirmation is missing a title. Regenerate the confirmation card."));
|
|
1133
|
+
const title = requirePayloadText(payload?.title, pick(lang, "确认启动互动世界缺少标题,请重新生成确认卡。", "The interactive world start confirmation is missing a title. Regenerate the confirmation card.", "Xác nhận khởi động thế giới tương tác thiếu tiêu đề. Vui lòng tạo lại thẻ xác nhận."));
|
|
1127
1134
|
const fallbackScene = [payload?.premise, args.instruction].filter((part) => typeof part === "string" && part.trim().length > 0).join("\n\n");
|
|
1128
1135
|
const initialScene = payload?.initialScene?.trim() || fallbackScene.trim();
|
|
1129
1136
|
const confirmedActionPayload = actionPayload
|
|
@@ -1167,7 +1174,7 @@ async function executeConfirmedProductionAction(args) {
|
|
|
1167
1174
|
else if (args.requestedIntent === "connect_choice") {
|
|
1168
1175
|
const payload = actionPayload?.connectChoice;
|
|
1169
1176
|
if (!payload?.node) {
|
|
1170
|
-
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "确认连接选择缺少节点数据,请重新生成确认卡。", "The connect-choice confirmation is missing node data. Regenerate the confirmation card."));
|
|
1177
|
+
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "确认连接选择缺少节点数据,请重新生成确认卡。", "The connect-choice confirmation is missing node data. Regenerate the confirmation card.", "Xác nhận nối lựa chọn thiếu dữ liệu node. Vui lòng tạo lại thẻ xác nhận."));
|
|
1171
1178
|
}
|
|
1172
1179
|
const projectId = payload?.projectId ?? args.bookId;
|
|
1173
1180
|
if (!projectId)
|
|
@@ -1180,7 +1187,7 @@ async function executeConfirmedProductionAction(args) {
|
|
|
1180
1187
|
else if (args.requestedIntent === "remove_node") {
|
|
1181
1188
|
const payload = actionPayload?.removeNode;
|
|
1182
1189
|
if (!payload?.nodeId) {
|
|
1183
|
-
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "确认删除节点缺少 nodeId,请重新生成确认卡。", "The remove-node confirmation is missing a nodeId. Regenerate the confirmation card."));
|
|
1190
|
+
throw new ApiError(400, "CONFIRMED_ACTION_PAYLOAD_INCOMPLETE", pick(lang, "确认删除节点缺少 nodeId,请重新生成确认卡。", "The remove-node confirmation is missing a nodeId. Regenerate the confirmation card.", "Xác nhận xóa node thiếu nodeId. Vui lòng tạo lại thẻ xác nhận."));
|
|
1184
1191
|
}
|
|
1185
1192
|
const projectId = payload?.projectId ?? args.bookId;
|
|
1186
1193
|
if (!projectId)
|
|
@@ -1750,7 +1757,7 @@ async function withTimeout(promise, timeoutMs, label, lang = "vi") {
|
|
|
1750
1757
|
return await Promise.race([
|
|
1751
1758
|
promise,
|
|
1752
1759
|
new Promise((_, reject) => {
|
|
1753
|
-
timeout = setTimeout(() => reject(new Error(pick(lang, `${label} 超时(${timeoutMs}ms)`, `${label} timed out (${timeoutMs}ms)`))), timeoutMs);
|
|
1760
|
+
timeout = setTimeout(() => reject(new Error(pick(lang, `${label} 超时(${timeoutMs}ms)`, `${label} timed out (${timeoutMs}ms)`, `${label} đã hết thời gian chờ (${timeoutMs}ms)`))), timeoutMs);
|
|
1754
1761
|
}),
|
|
1755
1762
|
]);
|
|
1756
1763
|
}
|
|
@@ -1769,42 +1776,42 @@ function formatServiceProbeError(args) {
|
|
|
1769
1776
|
: "";
|
|
1770
1777
|
const protocol = args.apiFormat === "responses" ? "Responses" : "Chat / Completions";
|
|
1771
1778
|
const streamSuffix = typeof args.stream === "boolean"
|
|
1772
|
-
? pick(lang, `,${args.stream ? "流式" : "非流式"}`, `, ${args.stream ? "streaming" : "non-streaming"}`)
|
|
1779
|
+
? pick(lang, `,${args.stream ? "流式" : "非流式"}`, `, ${args.stream ? "streaming" : "non-streaming"}`, `, ${args.stream ? "streaming" : "không streaming"}`)
|
|
1773
1780
|
: "";
|
|
1774
1781
|
const context = [
|
|
1775
|
-
pick(lang, `服务商:${args.label ?? args.service}`, `Service: ${args.label ?? args.service}`),
|
|
1776
|
-
pick(lang, `测试模型:${args.model ?? "未确定"}`, `Test model: ${args.model ?? "undetermined"}`),
|
|
1777
|
-
pick(lang, `协议:${protocol}${streamSuffix}`, `Protocol: ${protocol}${streamSuffix}`),
|
|
1778
|
-
pick(lang, `Base URL:${args.baseUrl}`, `Base URL: ${args.baseUrl}`),
|
|
1782
|
+
pick(lang, `服务商:${args.label ?? args.service}`, `Service: ${args.label ?? args.service}`, `Dịch vụ: ${args.label ?? args.service}`),
|
|
1783
|
+
pick(lang, `测试模型:${args.model ?? "未确定"}`, `Test model: ${args.model ?? "undetermined"}`, `Model kiểm tra: ${args.model ?? "chưa xác định"}`),
|
|
1784
|
+
pick(lang, `协议:${protocol}${streamSuffix}`, `Protocol: ${protocol}${streamSuffix}`, `Giao thức: ${protocol}${streamSuffix}`),
|
|
1785
|
+
pick(lang, `Base URL:${args.baseUrl}`, `Base URL: ${args.baseUrl}`, `Base URL: ${args.baseUrl}`),
|
|
1779
1786
|
].join("\n");
|
|
1780
|
-
const upstreamPrefix = (detail) => pick(lang, `\n上游返回:${detail}`, `\nUpstream response: ${detail}`);
|
|
1787
|
+
const upstreamPrefix = (detail) => pick(lang, `\n上游返回:${detail}`, `\nUpstream response: ${detail}`, `\nPhản hồi upstream: ${detail}`);
|
|
1781
1788
|
if (args.service === "google") {
|
|
1782
1789
|
return [
|
|
1783
|
-
pick(lang, "Google Gemini 测试连接失败。", "Google Gemini connection test failed."),
|
|
1790
|
+
pick(lang, "Google Gemini 测试连接失败。", "Google Gemini connection test failed.", "Kiểm tra kết nối Google Gemini thất bại."),
|
|
1784
1791
|
context,
|
|
1785
1792
|
"",
|
|
1786
|
-
pick(lang, "请优先检查:", "Check these first:"),
|
|
1787
|
-
pick(lang, "1. API Key 是否来自 Google AI Studio 的 Gemini API key,而不是 OAuth、Vertex AI 或其它 Google 服务凭据。", "1. The API Key is a Gemini API key from Google AI Studio, not an OAuth, Vertex AI, or other Google service credential."),
|
|
1788
|
-
pick(lang, "2. 该 key 所属项目是否已启用 Gemini API,并且没有被限制到其它 API、来源或服务。", "2. The key's project has the Gemini API enabled and is not restricted to other APIs, origins, or services."),
|
|
1789
|
-
pick(lang, "3. 当前地区/账号是否允许访问 Gemini API。", "3. Your region/account is allowed to access the Gemini API."),
|
|
1790
|
-
pick(lang, "4. 如果 key 曾经泄露,请在 AI Studio 重新生成后再保存。", "4. If the key was ever leaked, regenerate it in AI Studio before saving."),
|
|
1793
|
+
pick(lang, "请优先检查:", "Check these first:", "Hãy kiểm tra trước:"),
|
|
1794
|
+
pick(lang, "1. API Key 是否来自 Google AI Studio 的 Gemini API key,而不是 OAuth、Vertex AI 或其它 Google 服务凭据。", "1. The API Key is a Gemini API key from Google AI Studio, not an OAuth, Vertex AI, or other Google service credential.", "1. API Key có phải là Gemini API key từ Google AI Studio không, không phải OAuth, Vertex AI, hay thông tin xác thực dịch vụ Google khác."),
|
|
1795
|
+
pick(lang, "2. 该 key 所属项目是否已启用 Gemini API,并且没有被限制到其它 API、来源或服务。", "2. The key's project has the Gemini API enabled and is not restricted to other APIs, origins, or services.", "2. Dự án chứa key này đã bật Gemini API chưa, và không bị giới hạn cho API, nguồn hoặc dịch vụ khác."),
|
|
1796
|
+
pick(lang, "3. 当前地区/账号是否允许访问 Gemini API。", "3. Your region/account is allowed to access the Gemini API.", "3. Khu vực/tài khoản hiện tại có được phép truy cập Gemini API không."),
|
|
1797
|
+
pick(lang, "4. 如果 key 曾经泄露,请在 AI Studio 重新生成后再保存。", "4. If the key was ever leaked, regenerate it in AI Studio before saving.", "4. Nếu key từng bị lộ, hãy tạo lại trong AI Studio rồi mới lưu."),
|
|
1791
1798
|
upstreamDetail ? upstreamPrefix(upstreamDetail) : "",
|
|
1792
1799
|
].filter(Boolean).join("\n");
|
|
1793
1800
|
}
|
|
1794
1801
|
if (args.service === "moonshot" || args.service === "kimiCodingPlan" || args.service === "kimicode") {
|
|
1795
1802
|
return [
|
|
1796
|
-
pick(lang, `${args.label ?? args.service} 测试连接失败。`, `${args.label ?? args.service} connection test failed.`),
|
|
1803
|
+
pick(lang, `${args.label ?? args.service} 测试连接失败。`, `${args.label ?? args.service} connection test failed.`, `Kiểm tra kết nối ${args.label ?? args.service} thất bại.`),
|
|
1797
1804
|
context,
|
|
1798
1805
|
"",
|
|
1799
|
-
pick(lang, "请优先检查模型是否可用,以及 kimi-k2.x 这类模型是否需要 temperature=1。", "Check first whether the model is available, and whether models like kimi-k2.x require temperature=1."),
|
|
1806
|
+
pick(lang, "请优先检查模型是否可用,以及 kimi-k2.x 这类模型是否需要 temperature=1。", "Check first whether the model is available, and whether models like kimi-k2.x require temperature=1.", "Hãy kiểm tra trước xem model có khả dụng không, và các model như kimi-k2.x có cần temperature=1 không."),
|
|
1800
1807
|
rawDetail ? upstreamPrefix(rawDetail) : "",
|
|
1801
1808
|
].filter(Boolean).join("\n");
|
|
1802
1809
|
}
|
|
1803
1810
|
return [
|
|
1804
|
-
pick(lang, `${args.label ?? args.service} 测试连接失败。`, `${args.label ?? args.service} connection test failed.`),
|
|
1811
|
+
pick(lang, `${args.label ?? args.service} 测试连接失败。`, `${args.label ?? args.service} connection test failed.`, `Kiểm tra kết nối ${args.label ?? args.service} thất bại.`),
|
|
1805
1812
|
context,
|
|
1806
1813
|
"",
|
|
1807
|
-
pick(lang, "请检查 API Key、模型可用性、账号额度,以及协议类型是否匹配该服务商。", "Check the API Key, model availability, account quota, and whether the protocol type matches this service."),
|
|
1814
|
+
pick(lang, "请检查 API Key、模型可用性、账号额度,以及协议类型是否匹配该服务商。", "Check the API Key, model availability, account quota, and whether the protocol type matches this service.", "Hãy kiểm tra API Key, tính khả dụng của model, hạn mức tài khoản, và giao thức có khớp với dịch vụ này không."),
|
|
1808
1815
|
rawDetail ? upstreamPrefix(rawDetail) : "",
|
|
1809
1816
|
].filter(Boolean).join("\n");
|
|
1810
1817
|
}
|
|
@@ -1825,7 +1832,7 @@ async function fetchModelsFromServiceBaseUrl(serviceId, baseUrl, apiKey, proxyUr
|
|
|
1825
1832
|
const body = await res.text().catch(() => "");
|
|
1826
1833
|
return {
|
|
1827
1834
|
models: [],
|
|
1828
|
-
error: pick(lang, `服务商返回 ${res.status}: ${body.slice(0, 200)}`, `Service returned ${res.status}: ${body.slice(0, 200)}`),
|
|
1835
|
+
error: pick(lang, `服务商返回 ${res.status}: ${body.slice(0, 200)}`, `Service returned ${res.status}: ${body.slice(0, 200)}`, `Dịch vụ trả về ${res.status}: ${body.slice(0, 200)}`),
|
|
1829
1836
|
authFailed: res.status === 401 || res.status === 403,
|
|
1830
1837
|
};
|
|
1831
1838
|
}
|
|
@@ -1846,7 +1853,7 @@ function buildBearerAuthHeaders(apiKey, lang = "vi") {
|
|
|
1846
1853
|
if (!trimmed)
|
|
1847
1854
|
return {};
|
|
1848
1855
|
if (!/^[\x20-\x7e]+$/.test(trimmed)) {
|
|
1849
|
-
throw new Error(pick(lang, "API Key 只能包含英文、数字和常见 ASCII 符号,请检查是否误粘贴了中文说明。", "API Key may only contain ASCII letters, digits, and common symbols. Check whether you pasted explanatory text by mistake."));
|
|
1856
|
+
throw new Error(pick(lang, "API Key 只能包含英文、数字和常见 ASCII 符号,请检查是否误粘贴了中文说明。", "API Key may only contain ASCII letters, digits, and common symbols. Check whether you pasted explanatory text by mistake.", "API Key chỉ được chứa chữ cái, số và ký hiệu ASCII thông thường. Hãy kiểm tra xem có dán nhầm văn bản giải thích không."));
|
|
1850
1857
|
}
|
|
1851
1858
|
return { Authorization: `Bearer ${trimmed}` };
|
|
1852
1859
|
}
|
|
@@ -1866,7 +1873,7 @@ async function probeServiceCapabilities(args) {
|
|
|
1866
1873
|
return {
|
|
1867
1874
|
ok: false,
|
|
1868
1875
|
models: [],
|
|
1869
|
-
error: modelsResponse.error ?? pick(lang, "API Key 无效或无权访问模型列表。", "API Key is invalid or has no access to the model list."),
|
|
1876
|
+
error: modelsResponse.error ?? pick(lang, "API Key 无效或无权访问模型列表。", "API Key is invalid or has no access to the model list.", "API Key không hợp lệ hoặc không có quyền truy cập danh sách model."),
|
|
1870
1877
|
};
|
|
1871
1878
|
}
|
|
1872
1879
|
const discoveredModels = modelsResponse.models;
|
|
@@ -1879,7 +1886,7 @@ async function probeServiceCapabilities(args) {
|
|
|
1879
1886
|
return {
|
|
1880
1887
|
ok: false,
|
|
1881
1888
|
models: discoveredModels,
|
|
1882
|
-
error: pick(lang, "模型列表可访问,但没有发现可用于文本对话的模型。", "The model list is reachable, but no model usable for text chat was found."),
|
|
1889
|
+
error: pick(lang, "模型列表可访问,但没有发现可用于文本对话的模型。", "The model list is reachable, but no model usable for text chat was found.", "Danh sách model truy cập được, nhưng không tìm thấy model nào dùng được cho chat văn bản."),
|
|
1883
1890
|
};
|
|
1884
1891
|
}
|
|
1885
1892
|
return {
|
|
@@ -1938,10 +1945,10 @@ async function probeServiceCapabilities(args) {
|
|
|
1938
1945
|
return {
|
|
1939
1946
|
ok: false,
|
|
1940
1947
|
models: [],
|
|
1941
|
-
error: pick(lang, "无法自动确定模型,请先填写可用模型或提供支持 /models 的服务端点。", "Could not determine a model automatically. Fill in an available model first, or provide a service endpoint that supports /models."),
|
|
1948
|
+
error: pick(lang, "无法自动确定模型,请先填写可用模型或提供支持 /models 的服务端点。", "Could not determine a model automatically. Fill in an available model first, or provide a service endpoint that supports /models.", "Không thể tự động xác định model. Hãy điền model khả dụng trước, hoặc cung cấp endpoint dịch vụ hỗ trợ /models."),
|
|
1942
1949
|
};
|
|
1943
1950
|
}
|
|
1944
|
-
let lastError = modelsResponse.error ?? pick(lang, "自动探测失败", "Automatic probing failed");
|
|
1951
|
+
let lastError = modelsResponse.error ?? pick(lang, "自动探测失败", "Automatic probing failed", "Dò tự động thất bại");
|
|
1945
1952
|
for (const model of modelCandidates) {
|
|
1946
1953
|
for (const plan of buildProbePlans(args.preferredApiFormat, args.preferredStream)) {
|
|
1947
1954
|
const client = createLLMClient({
|
|
@@ -2059,7 +2066,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
2059
2066
|
execution: {
|
|
2060
2067
|
...task.execution,
|
|
2061
2068
|
status: "error",
|
|
2062
|
-
error: pick(lang, "任务已中断:Studio 服务在任务运行期间重启,任务未能继续。请重新发起。", "Task interrupted: the Studio server restarted while this task was running. Please start it again."),
|
|
2069
|
+
error: pick(lang, "任务已中断:Studio 服务在任务运行期间重启,任务未能继续。请重新发起。", "Task interrupted: the Studio server restarted while this task was running. Please start it again.", "Tác vụ bị gián đoạn: máy chủ Studio đã khởi động lại trong khi tác vụ đang chạy nên không thể tiếp tục. Vui lòng khởi chạy lại."),
|
|
2063
2070
|
completedAt,
|
|
2064
2071
|
},
|
|
2065
2072
|
};
|
|
@@ -2871,7 +2878,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
2871
2878
|
const env = await readEffectiveEnvConfigValues(root);
|
|
2872
2879
|
if (!env || !env.values.apiKey) {
|
|
2873
2880
|
return c.json({
|
|
2874
|
-
error: pick(await currentProjectLanguage(), "未检测到可导入的 LLM 环境变量配置,或缺少 INKOS_LLM_API_KEY。", "No importable LLM environment variable configuration was detected, or INKOS_LLM_API_KEY is missing."),
|
|
2881
|
+
error: pick(await currentProjectLanguage(), "未检测到可导入的 LLM 环境变量配置,或缺少 INKOS_LLM_API_KEY。", "No importable LLM environment variable configuration was detected, or INKOS_LLM_API_KEY is missing.", "Không phát hiện cấu hình biến môi trường LLM để nhập, hoặc thiếu INKOS_LLM_API_KEY."),
|
|
2875
2882
|
}, 400);
|
|
2876
2883
|
}
|
|
2877
2884
|
const config = await loadRawConfig(root);
|
|
@@ -2921,7 +2928,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
2921
2928
|
}
|
|
2922
2929
|
if (body.configSource === "env") {
|
|
2923
2930
|
return c.json({
|
|
2924
|
-
error: pick(await currentProjectLanguage(), "Studio 运行时不支持切换到 env;env 只在 CLI/daemon/部署运行时作为覆盖层使用。", "The Studio runtime does not support switching to env; env only acts as an override layer in the CLI/daemon/deployment runtimes."),
|
|
2931
|
+
error: pick(await currentProjectLanguage(), "Studio 运行时不支持切换到 env;env 只在 CLI/daemon/部署运行时作为覆盖层使用。", "The Studio runtime does not support switching to env; env only acts as an override layer in the CLI/daemon/deployment runtimes.", "Runtime Studio không hỗ trợ chuyển sang env; env chỉ hoạt động như lớp ghi đè trong runtime CLI/daemon/deployment."),
|
|
2925
2932
|
}, 400);
|
|
2926
2933
|
}
|
|
2927
2934
|
if (body.configSource !== undefined) {
|
|
@@ -2974,7 +2981,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
2974
2981
|
const baseUrl = normalizeCoverBaseUrl(requestedBaseUrl);
|
|
2975
2982
|
if (requestedBaseUrl && !baseUrl) {
|
|
2976
2983
|
return c.json({
|
|
2977
|
-
error: pick(await currentProjectLanguage(), "封面 Base URL 必须是有效的 HTTP(S) 地址,且不能包含账号、查询参数或锚点。", "Cover Base URL must be a valid HTTP(S) URL without credentials, query parameters, or fragments."),
|
|
2984
|
+
error: pick(await currentProjectLanguage(), "封面 Base URL 必须是有效的 HTTP(S) 地址,且不能包含账号、查询参数或锚点。", "Cover Base URL must be a valid HTTP(S) URL without credentials, query parameters, or fragments.", "Base URL bìa sách phải là địa chỉ HTTP(S) hợp lệ, không chứa thông tin đăng nhập, tham số truy vấn hoặc fragment."),
|
|
2978
2985
|
}, 400);
|
|
2979
2986
|
}
|
|
2980
2987
|
const config = await loadRawConfig(root);
|
|
@@ -3005,7 +3012,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
3005
3012
|
const trimmedKey = body.apiKey?.trim() ?? "";
|
|
3006
3013
|
if (trimmedKey && !isHeaderSafeApiKey(trimmedKey)) {
|
|
3007
3014
|
return c.json({
|
|
3008
|
-
error: pick(await currentProjectLanguage(), "API Key 包含不能放入 HTTP Authorization header 的字符,请只粘贴原始密钥。", "API Key contains characters that cannot go into an HTTP Authorization header. Paste only the raw key."),
|
|
3015
|
+
error: pick(await currentProjectLanguage(), "API Key 包含不能放入 HTTP Authorization header 的字符,请只粘贴原始密钥。", "API Key contains characters that cannot go into an HTTP Authorization header. Paste only the raw key.", "API Key chứa ký tự không thể đặt vào HTTP Authorization header. Vui lòng chỉ dán key gốc."),
|
|
3009
3016
|
}, 400);
|
|
3010
3017
|
}
|
|
3011
3018
|
const secrets = await loadSecrets(root);
|
|
@@ -3048,7 +3055,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
3048
3055
|
if (!resolvedBaseUrl) {
|
|
3049
3056
|
return c.json({
|
|
3050
3057
|
ok: false,
|
|
3051
|
-
error: pick(language, `未知服务商: ${service}`, `Unknown service: ${service}`),
|
|
3058
|
+
error: pick(language, `未知服务商: ${service}`, `Unknown service: ${service}`, `Dịch vụ không xác định: ${service}`),
|
|
3052
3059
|
}, 400);
|
|
3053
3060
|
}
|
|
3054
3061
|
const baseService = isCustomServiceId(service) ? "custom" : service;
|
|
@@ -3059,7 +3066,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
3059
3066
|
if (!apiKey?.trim() && !apiKeyOptional) {
|
|
3060
3067
|
return c.json({
|
|
3061
3068
|
ok: false,
|
|
3062
|
-
error: pick(language, "API Key 不能为空", "API Key must not be empty"),
|
|
3069
|
+
error: pick(language, "API Key 不能为空", "API Key must not be empty", "API Key không được để trống"),
|
|
3063
3070
|
}, 400);
|
|
3064
3071
|
}
|
|
3065
3072
|
const rawConfig = await loadRawConfig(root).catch(() => ({}));
|
|
@@ -3075,7 +3082,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
3075
3082
|
language,
|
|
3076
3083
|
});
|
|
3077
3084
|
// B12: 升级响应 shape 为 { probe, chat, ... },同时保留老字段供 UI 过渡期兼容
|
|
3078
|
-
const connectionFailed = pick(language, "连接失败", "Connection failed");
|
|
3085
|
+
const connectionFailed = pick(language, "连接失败", "Connection failed", "Kết nối thất bại");
|
|
3079
3086
|
const probeStatus = {
|
|
3080
3087
|
ok: probe.ok,
|
|
3081
3088
|
models: probe.models?.length ?? 0,
|
|
@@ -3114,7 +3121,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
3114
3121
|
if (!isHeaderSafeApiKey(trimmedKey)) {
|
|
3115
3122
|
return c.json({
|
|
3116
3123
|
ok: false,
|
|
3117
|
-
error: pick(await currentProjectLanguage(), "API Key 只能包含可放进 HTTP Authorization header 的非空白 ASCII 字符;请不要粘贴连接失败提示或诊断文本。", "API Key may only contain non-whitespace ASCII characters that fit in an HTTP Authorization header; do not paste connection failure hints or diagnostic text."),
|
|
3124
|
+
error: pick(await currentProjectLanguage(), "API Key 只能包含可放进 HTTP Authorization header 的非空白 ASCII 字符;请不要粘贴连接失败提示或诊断文本。", "API Key may only contain non-whitespace ASCII characters that fit in an HTTP Authorization header; do not paste connection failure hints or diagnostic text.", "API Key chỉ được chứa ký tự ASCII không khoảng trắng phù hợp với HTTP Authorization header; không dán gợi ý lỗi kết nối hay văn bản chẩn đoán."),
|
|
3118
3125
|
}, 400);
|
|
3119
3126
|
}
|
|
3120
3127
|
secrets.services[service] = { apiKey: trimmedKey };
|
|
@@ -3872,8 +3879,8 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
3872
3879
|
const msg = e?.message ?? String(e);
|
|
3873
3880
|
if (/API key/i.test(msg)) {
|
|
3874
3881
|
return c.json({
|
|
3875
|
-
error: pick(language, `请先为 ${reqService} 配置 API Key`, `Configure an API Key for ${reqService} first`),
|
|
3876
|
-
response: pick(language, `请先在模型配置中为 ${reqService} 填写 API Key,然后再试。`, `Fill in an API Key for ${reqService} in the model settings, then try again.`),
|
|
3882
|
+
error: pick(language, `请先为 ${reqService} 配置 API Key`, `Configure an API Key for ${reqService} first`, `Vui lòng cấu hình API Key cho ${reqService} trước`),
|
|
3883
|
+
response: pick(language, `请先在模型配置中为 ${reqService} 填写 API Key,然后再试。`, `Fill in an API Key for ${reqService} in the model settings, then try again.`, `Vui lòng điền API Key cho ${reqService} trong cấu hình model rồi thử lại.`),
|
|
3877
3884
|
}, 400);
|
|
3878
3885
|
}
|
|
3879
3886
|
throw e;
|
|
@@ -3958,7 +3965,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
3958
3965
|
}));
|
|
3959
3966
|
if (confirmedIntent && confirmedTaskId) {
|
|
3960
3967
|
const productionTaskBusyResponse = () => {
|
|
3961
|
-
const message = pick(surfaceLanguage, "当前会话已有一个生产任务在运行,请等它完成,或先用停止按钮结束它,再发起新任务。", "A production task is already running in this session. Wait for it to finish, or stop it first, then start a new task.");
|
|
3968
|
+
const message = pick(surfaceLanguage, "当前会话已有一个生产任务在运行,请等它完成,或先用停止按钮结束它,再发起新任务。", "A production task is already running in this session. Wait for it to finish, or stop it first, then start a new task.", "Phiên này đã có một tác vụ sản xuất đang chạy. Vui lòng đợi hoàn tất, hoặc dùng nút dừng để kết thúc trước khi bắt đầu tác vụ mới.");
|
|
3962
3969
|
return c.json({
|
|
3963
3970
|
error: { code: "PRODUCTION_TASK_ALREADY_RUNNING", message },
|
|
3964
3971
|
response: message,
|
|
@@ -4029,7 +4036,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
4029
4036
|
createdBookId = resolveCreatedBookIdFromToolExecs([exec]);
|
|
4030
4037
|
if (createdBookId) {
|
|
4031
4038
|
if (!await completeBookExists(join(root, "books", createdBookId))) {
|
|
4032
|
-
const message = pick(surfaceLanguage, "创作工具返回了建书结果,但磁盘上的书籍工件不完整。", "The creation tool returned a book result, but the on-disk book artifact is incomplete.");
|
|
4039
|
+
const message = pick(surfaceLanguage, "创作工具返回了建书结果,但磁盘上的书籍工件不完整。", "The creation tool returned a book result, but the on-disk book artifact is incomplete.", "Công cụ sáng tác đã trả về kết quả khởi tạo sách, nhưng tệp tin sách trên đĩa không đầy đủ.");
|
|
4033
4040
|
bookCreateStatus.set(createdBookId, { status: "error", error: message });
|
|
4034
4041
|
broadcast("book:error", { bookId: createdBookId, sessionId: bookSession.sessionId, error: message });
|
|
4035
4042
|
throw new ApiError(500, "BOOK_CREATION_INCOMPLETE", message);
|
|
@@ -4054,7 +4061,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
4054
4061
|
});
|
|
4055
4062
|
}
|
|
4056
4063
|
}
|
|
4057
|
-
const responseText = exec.result ?? pick(surfaceLanguage, "已完成。", "Done.");
|
|
4064
|
+
const responseText = exec.result ?? pick(surfaceLanguage, "已完成。", "Done.", "Đã hoàn thành.");
|
|
4058
4065
|
const responseForUser = suppressManualTextForTool(exec) ? "" : responseText;
|
|
4059
4066
|
// 指令已在任务开始时写入 transcript,这里只补助手工具消息。
|
|
4060
4067
|
await appendSessionMessagesUnlessDeleted(root, bookSession.sessionId, [
|
|
@@ -4331,7 +4338,7 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
4331
4338
|
details: { toolExecutions: collectedToolExecs },
|
|
4332
4339
|
});
|
|
4333
4340
|
}
|
|
4334
|
-
const emptyMessage = pick(language, "模型未返回文本内容。请检查协议类型(chat/responses)、流式开关或上游服务兼容性。", "The model returned no text content. Check the protocol type (chat/responses), the streaming switch, or upstream service compatibility.");
|
|
4341
|
+
const emptyMessage = pick(language, "模型未返回文本内容。请检查协议类型(chat/responses)、流式开关或上游服务兼容性。", "The model returned no text content. Check the protocol type (chat/responses), the streaming switch, or upstream service compatibility.", "Model không trả về nội dung văn bản. Hãy kiểm tra loại giao thức (chat/responses), chế độ streaming, hoặc khả năng tương thích của dịch vụ upstream.");
|
|
4335
4342
|
if (resolveCreatedBookIdFromToolExecs(collectedToolExecs)) {
|
|
4336
4343
|
await finalizeCreatedBook();
|
|
4337
4344
|
}
|
|
@@ -4368,9 +4375,9 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
4368
4375
|
return c.json({
|
|
4369
4376
|
error: {
|
|
4370
4377
|
code: "AGENT_BUSY",
|
|
4371
|
-
message: pick(language, "正在处理中,请等待当前操作完成", "Still processing. Wait for the current operation to finish"),
|
|
4378
|
+
message: pick(language, "正在处理中,请等待当前操作完成", "Still processing. Wait for the current operation to finish", "Đang xử lý, vui lòng chờ thao tác hiện tại hoàn tất"),
|
|
4372
4379
|
},
|
|
4373
|
-
response: pick(language, "正在处理中,请等待当前操作完成后再发送。", "Still processing. Wait for the current operation to finish before sending again."),
|
|
4380
|
+
response: pick(language, "正在处理中,请等待当前操作完成后再发送。", "Still processing. Wait for the current operation to finish before sending again.", "Đang xử lý, vui lòng chờ thao tác hiện tại hoàn tất rồi gửi lại."),
|
|
4374
4381
|
}, 429);
|
|
4375
4382
|
}
|
|
4376
4383
|
const failure = formatAgentFailure(msg, language);
|
|
@@ -4955,7 +4962,12 @@ export function createStudioServer(initialConfig, root, overrides = {}) {
|
|
|
4955
4962
|
return c.json({ error: "text is required" }, 400);
|
|
4956
4963
|
try {
|
|
4957
4964
|
const { analyzeStyle } = await import("@kienmatu/inkos-core");
|
|
4958
|
-
|
|
4965
|
+
// Infer the language from the sample itself. Without this the analyzer
|
|
4966
|
+
// defaults to "zh", which splits sentences on 。!?, counts characters
|
|
4967
|
+
// instead of words, takes the first TWO CHARACTERS as an opening pattern,
|
|
4968
|
+
// and labels counts with 次/处 — so an English sample came back as one
|
|
4969
|
+
// giant sentence with patterns like "Br...(15次)".
|
|
4970
|
+
const profile = analyzeStyle(text, sourceName ?? "unknown", inferLanguage(text));
|
|
4959
4971
|
return c.json(profile);
|
|
4960
4972
|
}
|
|
4961
4973
|
catch (e) {
|