dsh-task-time-cost 0.2.8-beta.2 → 0.2.9-beta.1
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/CHANGELOG.md +9 -0
- package/COMPATIBILITY.md +2 -2
- package/README.en.md +7 -6
- package/README.md +7 -6
- package/docs/public/guide.en.md +4 -2
- package/docs/public/guide.zh.md +4 -2
- package/lib/client.js +23 -20
- package/lib/index.js +35 -11
- package/lib/index.js.map +2 -2
- package/package.json +1 -1
- package/src/client/details.tsx +1 -1
- package/src/client/format.ts +19 -0
- package/src/client/index.tsx +21 -37
- package/src/client/locale.ts +6 -0
- package/src/client/styles.ts +4 -1
- package/src/host/engine.ts +17 -4
- package/src/host/index.ts +2 -2
- package/src/shared/wire.ts +1 -1
- package/src/shared/workload.ts +19 -5
package/lib/index.js
CHANGED
|
@@ -19948,7 +19948,18 @@ function estimateWorkload(input2, route, options = {}) {
|
|
|
19948
19948
|
let output2 = { ...template.visibleOutputTokens };
|
|
19949
19949
|
let tool = { ...template.toolMs };
|
|
19950
19950
|
const reasons = [...classification.reasons, "serial-conservative-dependencies", "cache-ratio-unknown", "reasoning-budget-unknown"];
|
|
19951
|
-
|
|
19951
|
+
const observations = (options.observations ?? []).filter((sample) => Number.isSafeInteger(sample.requestCount) && sample.requestCount >= 1 && Number.isSafeInteger(sample.visibleOutputTokens) && sample.visibleOutputTokens >= 0 && Number.isFinite(sample.toolMs) && sample.toolMs >= 0).slice(-config2.maxSamplesPerBucket);
|
|
19952
|
+
const empirical = observations.length >= config2.minConditionalSamples;
|
|
19953
|
+
if (empirical) {
|
|
19954
|
+
const range = (key) => {
|
|
19955
|
+
const values = observations.map((sample) => sample[key]).sort((a, b) => a - b);
|
|
19956
|
+
return { low: values[Math.floor((values.length - 1) * 0.2)], high: values[Math.ceil((values.length - 1) * 0.8)] };
|
|
19957
|
+
};
|
|
19958
|
+
count3 = range("requestCount");
|
|
19959
|
+
output2 = range("visibleOutputTokens");
|
|
19960
|
+
tool = range("toolMs");
|
|
19961
|
+
reasons.push("local-workload-distribution");
|
|
19962
|
+
} else if (history) {
|
|
19952
19963
|
for (const key of ["requestCount", "visibleOutputTokens", "toolMs", "samples"]) nonnegative(history[key], key);
|
|
19953
19964
|
count3 = scaleRange(count3, history.requestCount);
|
|
19954
19965
|
count3 = { low: Math.max(1, Math.floor(count3.low)), high: Math.max(1, Math.ceil(count3.high)) };
|
|
@@ -19979,15 +19990,15 @@ function estimateWorkload(input2, route, options = {}) {
|
|
|
19979
19990
|
output2 = { low: Math.max(output2.low, length.low * artifacts), high: Math.max(output2.high, length.high * artifacts * overhead) };
|
|
19980
19991
|
reasons.push(input2.explicitOutputTokens ? "explicit-output-token-budget" : "requested-length-token-conversion:heuristic");
|
|
19981
19992
|
}
|
|
19982
|
-
const origin = history ? template.sourceKind === "local-measured" ? "local-measured" : "mixed" : template.sourceKind;
|
|
19993
|
+
const origin = empirical ? "local-measured" : history ? template.sourceKind === "local-measured" ? "local-measured" : "mixed" : template.sourceKind;
|
|
19983
19994
|
return {
|
|
19984
19995
|
kind: classification.kind,
|
|
19985
19996
|
low: buildScenario(count3.low, output2.low, tool.low, input2.currentInputTokens, route, classification.kind, template.sourceKind, config2, false, length ? length.low * artifacts : null),
|
|
19986
19997
|
high: buildScenario(count3.high, output2.high, tool.high, input2.currentInputTokens, route, classification.kind, template.sourceKind, config2, true, length ? length.high * artifacts : null),
|
|
19987
19998
|
origin,
|
|
19988
19999
|
reasons,
|
|
19989
|
-
workloadSamples: history?.samples ?? 0,
|
|
19990
|
-
sourceIds: [template.sourceId ?? `engineering-${template.kind}-v1`, ...history ? [`local-workload:${history.kind}:${history.samples}`] : []]
|
|
20000
|
+
workloadSamples: empirical ? observations.length : history?.samples ?? 0,
|
|
20001
|
+
sourceIds: empirical ? [`local-workload-distribution:${classification.kind}:${observations.length}`] : [template.sourceId ?? `engineering-${template.kind}-v1`, ...history ? [`local-workload:${history.kind}:${history.samples}`] : []]
|
|
19991
20002
|
};
|
|
19992
20003
|
}
|
|
19993
20004
|
function outputLengthFromPrompt(prompt) {
|
|
@@ -20232,7 +20243,7 @@ function csvBaselines(text) {
|
|
|
20232
20243
|
}
|
|
20233
20244
|
|
|
20234
20245
|
// src/shared/wire.ts
|
|
20235
|
-
var ALGORITHM_VERSION = "p0-0.2.
|
|
20246
|
+
var ALGORITHM_VERSION = "p0-0.2.9";
|
|
20236
20247
|
var PACKAGE_NAME = "dsh-task-time-cost";
|
|
20237
20248
|
|
|
20238
20249
|
// src/shared/remote-contract.ts
|
|
@@ -22200,10 +22211,14 @@ var EstimatorEngine = class {
|
|
|
22200
22211
|
const firstCall = task.calls[0];
|
|
22201
22212
|
const input2 = this.initialInput.get(key) ?? (firstCall ? this.normalize(firstCall).promptTokens : null);
|
|
22202
22213
|
const requestMode = task.calls.findLast((call) => call.mode !== "unknown" && (!task.route || call.route.providerId === task.route.providerId && call.route.modelId === task.route.modelId && call.route.reasoningEffort === task.route.reasoningEffort))?.mode ?? "unknown";
|
|
22203
|
-
const
|
|
22214
|
+
const route = this.route(task.route, requestMode);
|
|
22215
|
+
const comparable = task.aiPlan ? [] : [...this.repository.tasks.values()].filter((record2) => {
|
|
22216
|
+
const past = record2.current, p = past.performance;
|
|
22217
|
+
return record2.id !== key && record2.learned?.eligible && past.state === "completed" && past.endedAt !== null && past.endedAt <= task.startedAt && record2.savedAt > this.repository.resetAt && task.startedAt - past.endedAt <= this.config.historyRetentionDays * 864e5 && past.taskType === task.taskType && p?.providerId === route.providerId && p.modelId === route.modelId && p.endpointFingerprint === route.endpointFingerprint && p.reasoningEffort === route.reasoningEffort && p.serviceTier === route.serviceTier;
|
|
22218
|
+
}).sort((a, b) => a.current.endedAt - b.current.endedAt).slice(-this.config.maxSamplesPerBucket);
|
|
22219
|
+
const signature2 = JSON.stringify([task.taskType, task.explicitOutputWords, task.attachmentCount, task.route, requestMode, input2, task.aiPlan, comparable.map((record2) => record2.id)]);
|
|
22204
22220
|
const previous = this.plans.get(key);
|
|
22205
22221
|
if (previous?.signature === signature2) return structuredClone(previous.plan);
|
|
22206
|
-
const route = this.route(task.route, requestMode);
|
|
22207
22222
|
const templates = this.templates(route);
|
|
22208
22223
|
let plan = estimateWorkload(
|
|
22209
22224
|
{
|
|
@@ -22213,7 +22228,13 @@ var EstimatorEngine = class {
|
|
|
22213
22228
|
...task.explicitOutputWords ? { explicitOutputTokens: { low: task.explicitOutputWords, high: task.explicitOutputWords * 2 } } : {}
|
|
22214
22229
|
},
|
|
22215
22230
|
route,
|
|
22216
|
-
{
|
|
22231
|
+
{
|
|
22232
|
+
config: this.engineConfig(),
|
|
22233
|
+
templates,
|
|
22234
|
+
history: [...this.workloadHistory.values()],
|
|
22235
|
+
observations: comparable.map((record2) => ({ requestCount: record2.learned.calls, visibleOutputTokens: record2.learned.visibleOutputTokens, toolMs: record2.learned.toolWorkMs })),
|
|
22236
|
+
rules: [{ id: "durable-classification", kind: task.taskType, patterns: [".*"] }]
|
|
22237
|
+
}
|
|
22217
22238
|
);
|
|
22218
22239
|
if (task.aiPlan) plan = applyAiPlan(plan, task.aiPlan, route, this.currentInput.get(key) ?? input2, this.engineConfig());
|
|
22219
22240
|
this.plans.set(key, { signature: signature2, plan });
|
|
@@ -22369,7 +22390,10 @@ var EstimatorEngine = class {
|
|
|
22369
22390
|
const low = sumKnown(lows), high = sumKnown(highs);
|
|
22370
22391
|
return low === null || high === null ? null : { low, high };
|
|
22371
22392
|
});
|
|
22372
|
-
const outputScenarios = [plan.low, plan.high].map((scenario) => scenario.requests.filter((request) => !completedIds.includes(request.id)).reduce((sum, request) =>
|
|
22393
|
+
const outputScenarios = [plan.low, plan.high].map((scenario) => scenario.requests.filter((request) => !completedIds.includes(request.id)).reduce((sum, request) => {
|
|
22394
|
+
const generated = progress[request.id]?.generatedTextTokens ?? 0;
|
|
22395
|
+
return { low: sum.low + Math.max(0, request.visibleOutputTokens.low - generated), high: sum.high + Math.max(0, request.visibleOutputTokens.high - generated) };
|
|
22396
|
+
}, { low: 0, high: 0 }));
|
|
22373
22397
|
const estimatedRemainingOutputTokens = task.endedAt !== null ? { low: 0, high: 0 } : errors || planExhausted ? null : { low: Math.min(...outputScenarios.map((value) => value.low)), high: Math.max(...outputScenarios.map((value) => value.high)) };
|
|
22374
22398
|
const estimatedTokens = tokenScenarios.every((span3) => span3 !== null) ? { low: Math.max(totalTokens ?? 0, Math.min(...tokenScenarios.map((span3) => span3.low))), high: Math.max(totalTokens ?? 0, ...tokenScenarios.map((span3) => span3.high)) } : null;
|
|
22375
22399
|
const initial = saved?.current.initial ?? (task.endedAt === null && task.promptChars > 0 && this.startedHere.has(key) ? { estimatedTotalMs: remaining ? { low: elapsedActive + remaining.low, high: elapsedActive + remaining.high } : null, estimatedTotalCost: totalCost } : null);
|
|
@@ -22910,7 +22934,7 @@ async function apply(ctx, rawConfig) {
|
|
|
22910
22934
|
name: "task_estimate_plan",
|
|
22911
22935
|
description: "Update the current task ETA from your assessment of the work still needed. Submit after understanding a multi-step task, and revise when its scope or remaining work materially changes. The Host combines the plan with this route\u2019s observed response times. These are reference estimates, not completion promises.",
|
|
22912
22936
|
parameters: { type: "object", properties: {
|
|
22913
|
-
remainingCalls: { type: "object", description: "Future model
|
|
22937
|
+
remainingCalls: { type: "object", description: "Future model response rounds after this reporting call, including final answer and verification; 1\u2013500. Count one response containing many tool calls as ONE model request, not one per tool or file. Reuse the observed work pattern to bound remaining rounds.", properties: { low: { type: "integer", minimum: 1, maximum: 500 }, high: { type: "integer", minimum: 1, maximum: 500 } }, required: ["low", "high"], additionalProperties: false },
|
|
22914
22938
|
remainingOutputTokens: { type: "object", description: "Total future visible text/tool-argument tokens, excluding hidden reasoning; ordered bounds, 0\u20132000000.", properties: { low: { type: "integer", minimum: 0, maximum: 2e6 }, high: { type: "integer", minimum: 0, maximum: 2e6 } }, required: ["low", "high"], additionalProperties: false },
|
|
22915
22939
|
remainingToolSeconds: { type: "object", description: "Future tool execution time, excluding model requests and human waits; ordered bounds, 0\u201386400 seconds.", properties: { low: { type: "integer", minimum: 0, maximum: 86400 }, high: { type: "integer", minimum: 0, maximum: 86400 } }, required: ["low", "high"], additionalProperties: false },
|
|
22916
22940
|
stage: { type: "string", enum: ["planning", "implementation", "verification", "finishing"] }
|
|
@@ -22926,7 +22950,7 @@ async function apply(ctx, rawConfig) {
|
|
|
22926
22950
|
ctx.systemPrompt.section({
|
|
22927
22951
|
name: "task-estimate-planning",
|
|
22928
22952
|
order: 1e3,
|
|
22929
|
-
text: "For a multi-step user task, submit a task_estimate_plan after understanding the scope, preferably alongside your initial work tools. Include implementation, verification and delivery. Revise it only when scope or remaining work materially changes or the budget is exceeded. Count work remaining after the reporting call. Use task_estimator snapshot route timings when useful; never invent measured performance or treat call count as percentage complete. Skip planning for trivial answers. This uses the executing agent; do not start another model request just to estimate or poll."
|
|
22953
|
+
text: "For a multi-step user task, submit a task_estimate_plan after understanding the scope, preferably alongside your initial work tools. Include implementation, verification and delivery. Revise it only when scope or remaining work materially changes or the budget is exceeded. Count work remaining after the reporting call. Count model response rounds, not individual tools, files, commands or parallel calls; one model response can contain many tools. Estimate tool wall time on the critical path, not the sum of parallel execution times. Prefer concrete bounds for the known scope and revise when evidence changes; do not add speculative optional work. Use task_estimator snapshot route timings when useful; never invent measured performance or treat call count as percentage complete. Skip planning for trivial answers. This uses the executing agent; do not start another model request just to estimate or poll."
|
|
22930
22954
|
});
|
|
22931
22955
|
for (const agent of ctx.agents.list()) safe(agent, () => {
|
|
22932
22956
|
projection.stateOf(agent.session);
|