@musnows/scriverse 0.7.6 → 0.7.7
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/ai.js +41 -8
- package/dist/ai.js.map +1 -1
- package/dist/attachment-storage.js +4 -0
- package/dist/attachment-storage.js.map +1 -1
- package/dist/public/app.js +49 -12
- package/dist/public/index.html +4 -4
- package/dist/public/styles.css +2 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/ai.js
CHANGED
|
@@ -51,6 +51,8 @@ const AUTO_RUN_RETRY_DELAYS_MS = [5_000, 30_000];
|
|
|
51
51
|
const AI_INTERACTIVE_TIMEOUT_MS = 60_000;
|
|
52
52
|
const AI_LONG_RUNNING_TIMEOUT_MS = 300_000;
|
|
53
53
|
const FORCE_CONVERSATION_COMPACTION_USAGE_PERCENT = 95;
|
|
54
|
+
const MIN_OUTPUT_RESERVE_TOKENS = 1_024;
|
|
55
|
+
const MIN_CONTEXT_REMAINING_TOKENS = 5_000;
|
|
54
56
|
const analysisTaskTypes = new Set(ANALYSIS_TASK_TYPES);
|
|
55
57
|
const interactiveStreamErrorCodes = new Set([
|
|
56
58
|
"AI_STREAM_IDLE_TIMEOUT",
|
|
@@ -173,6 +175,7 @@ export function autoRunFailureDisposition(error, attemptCount) {
|
|
|
173
175
|
}
|
|
174
176
|
const allowedParameters = new Set(["temperature", "top_p", "max_tokens", "presence_penalty", "frequency_penalty", "seed"]);
|
|
175
177
|
const DEFAULT_MAX_TOKENS = 32_000;
|
|
178
|
+
const MAX_MODEL_OUTPUT_TOKENS = 2_000_000;
|
|
176
179
|
const DEFAULT_CONTEXT_WINDOW = 128_000;
|
|
177
180
|
const RELATIONSHIP_MAX_FUZZY_REFERENCES = 32;
|
|
178
181
|
const RELATIONSHIP_MAX_FUZZY_SOURCES = 200;
|
|
@@ -405,7 +408,7 @@ function sanitizeCompletionTraceResponse(value) {
|
|
|
405
408
|
}
|
|
406
409
|
const MAX_AGENT_TOOL_CALLS = 12;
|
|
407
410
|
const TOOL_CONTEXT_COMPACT_MAX_TOKENS = 1_024;
|
|
408
|
-
const TOOL_CONTEXT_RESPONSE_RESERVE_TOKENS =
|
|
411
|
+
const TOOL_CONTEXT_RESPONSE_RESERVE_TOKENS = MIN_OUTPUT_RESERVE_TOKENS;
|
|
409
412
|
const IMAGE_TOOL_MAX_BYTES = 30 * 1024 * 1024;
|
|
410
413
|
const IMAGE_TOOL_MAX_OUTPUT_TOKENS = 8_192;
|
|
411
414
|
const agentToolCursor = z.number().int().min(0).max(100_000).default(0);
|
|
@@ -738,7 +741,7 @@ function completionPayloadOutputText(payload) {
|
|
|
738
741
|
}
|
|
739
742
|
function normalizeModelPreset(input, modelId = "") {
|
|
740
743
|
const maxTokens = typeof input.max_tokens === "number" && Number.isFinite(input.max_tokens)
|
|
741
|
-
? Math.round(clamp(input.max_tokens, 1,
|
|
744
|
+
? Math.round(clamp(input.max_tokens, 1, MAX_MODEL_OUTPUT_TOKENS))
|
|
742
745
|
: DEFAULT_MAX_TOKENS;
|
|
743
746
|
const temperature = input.temperature;
|
|
744
747
|
const defaultTemperature = isKimiModelId(modelId) && !(typeof temperature === "number" && Number.isFinite(temperature))
|
|
@@ -3571,7 +3574,7 @@ export class AiManager {
|
|
|
3571
3574
|
const contextWindow = numberValue(model, "context_window") || DEFAULT_CONTEXT_WINDOW;
|
|
3572
3575
|
const preset = safeJsonObject(stringValue(model, "preset_json"));
|
|
3573
3576
|
const configuredOutputTokens = typeof preset.max_tokens === "number" ? preset.max_tokens : DEFAULT_MAX_TOKENS;
|
|
3574
|
-
const outputReserveTokens = Math.max(
|
|
3577
|
+
const outputReserveTokens = Math.max(MIN_OUTPUT_RESERVE_TOKENS, Math.min(configuredOutputTokens, Math.floor(contextWindow * 0.25), contextWindow - MIN_OUTPUT_RESERVE_TOKENS));
|
|
3575
3578
|
const availableInputTokens = Math.max(256, contextWindow - outputReserveTokens - 512);
|
|
3576
3579
|
const conversation = input.conversationId
|
|
3577
3580
|
? this.store.getAiConversationContext(input.conversationId, input.workId, input.excludeConversationMessageId)
|
|
@@ -3590,6 +3593,7 @@ export class AiManager {
|
|
|
3590
3593
|
- functionTokens);
|
|
3591
3594
|
return {
|
|
3592
3595
|
contextWindow,
|
|
3596
|
+
configuredOutputTokens,
|
|
3593
3597
|
outputReserveTokens,
|
|
3594
3598
|
availableInputTokens,
|
|
3595
3599
|
conversation,
|
|
@@ -3619,7 +3623,10 @@ export class AiManager {
|
|
|
3619
3623
|
const threshold = Math.min(90, Math.max(50, Number(this.store.getWorkAiSettings(input.workId).contextCompactThreshold) || 85));
|
|
3620
3624
|
const conversation = budget.conversation;
|
|
3621
3625
|
const conversationUsagePercent = Number(budget.conversationUsagePercent) || 0;
|
|
3626
|
+
const configuredOutputTokens = Number(budget.configuredOutputTokens) || DEFAULT_MAX_TOKENS;
|
|
3627
|
+
const maxOutputUsagePercent = Math.min(100, Math.round(configuredOutputTokens / contextWindow * 100));
|
|
3622
3628
|
const compactableMessageCount = Math.max(0, (conversation?.messages.length ?? 0) - 2);
|
|
3629
|
+
const contextFallbackReached = remainingTokens <= MIN_CONTEXT_REMAINING_TOKENS;
|
|
3623
3630
|
return {
|
|
3624
3631
|
modelId: stringValue(model, "id"),
|
|
3625
3632
|
contextWindow,
|
|
@@ -3628,8 +3635,12 @@ export class AiManager {
|
|
|
3628
3635
|
conversationTokens: Number(budget.conversationTokens),
|
|
3629
3636
|
conversationBudgetTokens: Number(budget.conversationBudgetTokens),
|
|
3630
3637
|
conversationUsagePercent,
|
|
3638
|
+
maxOutputTokens: configuredOutputTokens,
|
|
3639
|
+
maxOutputUsagePercent,
|
|
3640
|
+
maxOutputThresholdReached: maxOutputUsagePercent >= threshold,
|
|
3631
3641
|
outputReserveTokens: Number(budget.outputReserveTokens),
|
|
3632
3642
|
remainingTokens,
|
|
3643
|
+
contextFallbackReached,
|
|
3633
3644
|
usagePercent: Math.min(100, Math.round(inputTokens / contextWindow * 100)),
|
|
3634
3645
|
tokenDistribution: {
|
|
3635
3646
|
systemPromptTokens,
|
|
@@ -3640,7 +3651,7 @@ export class AiManager {
|
|
|
3640
3651
|
},
|
|
3641
3652
|
compactThreshold: threshold,
|
|
3642
3653
|
compactableMessageCount,
|
|
3643
|
-
compactRecommended: compactableMessageCount > 0 && conversationUsagePercent >= threshold,
|
|
3654
|
+
compactRecommended: compactableMessageCount > 0 && (conversationUsagePercent >= threshold || contextFallbackReached),
|
|
3644
3655
|
contextWarningPending: conversation?.warningPending ?? false,
|
|
3645
3656
|
compactedMessageCount: conversation?.compactedMessageCount ?? 0,
|
|
3646
3657
|
includedContextBlocks: contextPlan.includedBlockIds.length,
|
|
@@ -3665,6 +3676,7 @@ export class AiManager {
|
|
|
3665
3676
|
contextWindow,
|
|
3666
3677
|
inputTokens,
|
|
3667
3678
|
remainingTokens,
|
|
3679
|
+
contextFallbackReached: remainingTokens <= MIN_CONTEXT_REMAINING_TOKENS,
|
|
3668
3680
|
usagePercent: Math.min(100, Math.round(inputTokens / contextWindow * 100)),
|
|
3669
3681
|
tokenDistribution: {
|
|
3670
3682
|
systemPromptTokens,
|
|
@@ -3678,8 +3690,11 @@ export class AiManager {
|
|
|
3678
3690
|
inspectConversationContext(input) {
|
|
3679
3691
|
const usage = this.getContextUsage({ ...input, taskType: "chat" });
|
|
3680
3692
|
const usagePercent = Number(usage.usagePercent) || 0;
|
|
3693
|
+
const maxOutputThresholdReached = usage.maxOutputThresholdReached === true;
|
|
3694
|
+
const contextFallbackReached = usage.contextFallbackReached === true;
|
|
3681
3695
|
const compactableMessageCount = Number(usage.compactableMessageCount) || 0;
|
|
3682
|
-
|
|
3696
|
+
const outputThresholdNeedsCompaction = (maxOutputThresholdReached || contextFallbackReached) && compactableMessageCount > 0;
|
|
3697
|
+
if (usagePercent >= FORCE_CONVERSATION_COMPACTION_USAGE_PERCENT || outputThresholdNeedsCompaction) {
|
|
3683
3698
|
if (compactableMessageCount <= 0) {
|
|
3684
3699
|
throw new AppError(409, "AI_CONTEXT_COMPACTION_UNAVAILABLE", "当前请求已占满模型上下文,但没有可压缩的较早对话;请缩短问题、减少引用或新开对话");
|
|
3685
3700
|
}
|
|
@@ -3712,10 +3727,19 @@ export class AiManager {
|
|
|
3712
3727
|
}
|
|
3713
3728
|
const compaction = await this.compactConversation(input);
|
|
3714
3729
|
if (compaction.changed !== true) {
|
|
3730
|
+
const inputBelowForcedThreshold = (Number(inspection.usage.usagePercent) || 0) < FORCE_CONVERSATION_COMPACTION_USAGE_PERCENT;
|
|
3731
|
+
if ((inspection.usage.maxOutputThresholdReached === true || inspection.usage.contextFallbackReached === true) && inputBelowForcedThreshold) {
|
|
3732
|
+
return {
|
|
3733
|
+
action: "ready",
|
|
3734
|
+
reason: "output_budget_already_fits",
|
|
3735
|
+
usage: { ...inspection.usage, contextWarningPending: false }
|
|
3736
|
+
};
|
|
3737
|
+
}
|
|
3715
3738
|
throw new AppError(409, "AI_CONTEXT_COMPACTION_UNAVAILABLE", "当前请求已占满模型上下文,但没有可压缩的较早对话;请缩短问题、减少引用或新开对话");
|
|
3716
3739
|
}
|
|
3717
3740
|
const compactedUsage = this.getContextUsage({ ...input, taskType: "chat" });
|
|
3718
|
-
if ((Number(compactedUsage.usagePercent) || 0) >= FORCE_CONVERSATION_COMPACTION_USAGE_PERCENT
|
|
3741
|
+
if ((Number(compactedUsage.usagePercent) || 0) >= FORCE_CONVERSATION_COMPACTION_USAGE_PERCENT
|
|
3742
|
+
|| compactedUsage.contextFallbackReached === true) {
|
|
3719
3743
|
throw new AppError(413, "AI_CONTEXT_STILL_OVER_LIMIT", "自动压缩后当前请求仍占满模型上下文;请缩短问题、减少引用或新开对话");
|
|
3720
3744
|
}
|
|
3721
3745
|
return {
|
|
@@ -4751,6 +4775,8 @@ export class AiManager {
|
|
|
4751
4775
|
...this.sanitizeParameters({ ...preset, ...(input.parameters ?? {}) }, stringValue(model, "model_id")),
|
|
4752
4776
|
...thinkingParameters(provider, model)
|
|
4753
4777
|
};
|
|
4778
|
+
const configuredOutputTokens = Number(requestedParameters.max_tokens) || DEFAULT_MAX_TOKENS;
|
|
4779
|
+
const contextCompactThreshold = Math.min(90, Math.max(50, Number(this.store.getWorkAiSettings(input.workId).contextCompactThreshold) || 85));
|
|
4754
4780
|
let effectiveInput = input;
|
|
4755
4781
|
let context = this.buildContext(effectiveInput, model);
|
|
4756
4782
|
let messages = this.buildMessages(effectiveInput, context);
|
|
@@ -5167,7 +5193,14 @@ export class AiManager {
|
|
|
5167
5193
|
// 新工具结果可能附带 toolCallQuotaNotice,预估体积时一并计入,避免低估后触发上下文溢出。
|
|
5168
5194
|
const noticeBudgetChars = Math.max(agentToolCallQuotaNoticeBudgetChars(1, agentToolCallLimit), agentToolCallQuotaNoticeBudgetChars(agentToolCallSoftWarningThreshold(agentToolCallLimit), agentToolCallLimit));
|
|
5169
5195
|
const maximumNewToolTokens = Math.ceil((AGENT_TOOL_RESULT_MAX_CHARS + noticeBudgetChars) * 1.1) * Math.max(1, toolCallCount);
|
|
5170
|
-
|
|
5196
|
+
// 这里只按工具结果写入后的 context 剩余判断;输出 max_tokens 由下方独立判断。
|
|
5197
|
+
const projectedContextTokens = currentTokens + maximumNewToolTokens;
|
|
5198
|
+
const projectedUsagePercent = Math.round(projectedContextTokens / contextWindow * 100);
|
|
5199
|
+
const projectedContextRemainingTokens = Math.max(0, contextWindow - projectedContextTokens);
|
|
5200
|
+
const maxOutputThresholdReached = configuredOutputTokens >= contextWindow * contextCompactThreshold / 100;
|
|
5201
|
+
return projectedUsagePercent >= contextCompactThreshold
|
|
5202
|
+
|| maxOutputThresholdReached
|
|
5203
|
+
|| projectedContextRemainingTokens <= MIN_CONTEXT_REMAINING_TOKENS;
|
|
5171
5204
|
};
|
|
5172
5205
|
let payload = await requestCompletion("auto");
|
|
5173
5206
|
let choice = payload.choices?.[0];
|
|
@@ -9187,7 +9220,7 @@ export class AiManager {
|
|
|
9187
9220
|
if (typeof output.top_p === "number")
|
|
9188
9221
|
output.top_p = clamp(output.top_p, 0, 1);
|
|
9189
9222
|
output.max_tokens = typeof output.max_tokens === "number"
|
|
9190
|
-
? Math.round(clamp(output.max_tokens, 1,
|
|
9223
|
+
? Math.round(clamp(output.max_tokens, 1, MAX_MODEL_OUTPUT_TOKENS))
|
|
9191
9224
|
: DEFAULT_MAX_TOKENS;
|
|
9192
9225
|
return output;
|
|
9193
9226
|
}
|