dsh-fast 0.1.2 → 0.2.0
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 +12 -0
- package/README.es.md +6 -4
- package/README.hi.md +6 -4
- package/README.md +7 -4
- package/README.pt.md +6 -4
- package/README.zh.md +6 -4
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/lib/index.js +225 -16
- package/lib/types/analyze.d.ts.map +1 -1
- package/lib/types/analyze.js +6 -0
- package/lib/types/analyze.js.map +1 -1
- package/lib/types/collector.d.ts +5 -1
- package/lib/types/collector.d.ts.map +1 -1
- package/lib/types/collector.js +14 -2
- package/lib/types/collector.js.map +1 -1
- package/lib/types/estimate.d.ts +19 -2
- package/lib/types/estimate.d.ts.map +1 -1
- package/lib/types/estimate.js +49 -2
- package/lib/types/estimate.js.map +1 -1
- package/lib/types/index.d.ts +2 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +34 -8
- package/lib/types/index.js.map +1 -1
- package/lib/types/model.d.ts +23 -1
- package/lib/types/model.d.ts.map +1 -1
- package/lib/types/store.d.ts +23 -1
- package/lib/types/store.d.ts.map +1 -1
- package/lib/types/store.js +15 -1
- package/lib/types/store.js.map +1 -1
- package/lib/types/version.d.ts +1 -1
- package/lib/types/version.js +1 -1
- package/package.json +13 -13
- package/src/analyze.ts +6 -0
- package/src/collector.ts +16 -2
- package/src/estimate.ts +54 -2
- package/src/index.ts +41 -8
- package/src/model.ts +25 -1
- package/src/store.ts +17 -1
- package/src/version.ts +1 -1
package/src/estimate.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type { EpochHeader } from '@deepseek-ai/dsh-session'
|
|
12
|
+
import type { SystemPromptBreakdown } from './model.ts'
|
|
12
13
|
|
|
13
14
|
/** Fixed text-density estimate (chars per token). */
|
|
14
15
|
const CHARS_PER_TOKEN = 4
|
|
@@ -19,9 +20,60 @@ const ROLE_OVERHEAD = 4
|
|
|
19
20
|
/** Per-block structural overhead for JSON framing and type tags. */
|
|
20
21
|
const BLOCK_OVERHEAD = 4
|
|
21
22
|
|
|
23
|
+
/** One named system-prompt section (name + resolved text). */
|
|
24
|
+
export interface SystemSection {
|
|
25
|
+
name: string
|
|
26
|
+
text: string
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Which tracked bucket a section name belongs to. */
|
|
30
|
+
function bucketOf(name: string): 'agentsMd' | 'skills' | 'persona' | 'other' {
|
|
31
|
+
if (name === 'deployment:persona') return 'persona'
|
|
32
|
+
if (name.toLowerCase().includes('skill')) return 'skills'
|
|
33
|
+
if (/agent|instructions?/iu.test(name)) return 'agentsMd'
|
|
34
|
+
return 'other'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** An empty breakdown (all buckets zeroed). */
|
|
38
|
+
function emptyBreakdown(): SystemPromptBreakdown {
|
|
39
|
+
return {
|
|
40
|
+
agentsMd: { tokens: 0, chars: 0, share: 0 },
|
|
41
|
+
skills: { tokens: 0, chars: 0, share: 0 },
|
|
42
|
+
persona: { tokens: 0, chars: 0, share: 0 },
|
|
43
|
+
other: { tokens: 0, chars: 0, share: 0 },
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Classify the named system-prompt sections into the AGENTS.md / skills /
|
|
49
|
+
* persona / other buckets, with heuristic tokens and per-bucket shares. The
|
|
50
|
+
* caller supplies either the real assembled sections (via the optional
|
|
51
|
+
* `systemPrompt` service) or a single synthetic `{ name: 'other' }` section
|
|
52
|
+
* carrying the rendered prompt, so the breakdown always sums to the system
|
|
53
|
+
* prompt.
|
|
54
|
+
* @param sections - named sections (never empty in practice).
|
|
55
|
+
* @returns the per-bucket breakdown.
|
|
56
|
+
*/
|
|
57
|
+
export function classifySystemSections(sections: readonly SystemSection[]): SystemPromptBreakdown {
|
|
58
|
+
const breakdown = emptyBreakdown()
|
|
59
|
+
for (const section of sections) {
|
|
60
|
+
const chars = section.text.length
|
|
61
|
+
if (chars === 0) continue
|
|
62
|
+
const bucket = breakdown[bucketOf(section.name)]
|
|
63
|
+
bucket.chars += chars
|
|
64
|
+
bucket.tokens += Math.ceil(chars / CHARS_PER_TOKEN)
|
|
65
|
+
}
|
|
66
|
+
const total = breakdown.agentsMd.tokens + breakdown.skills.tokens + breakdown.persona.tokens + breakdown.other.tokens
|
|
67
|
+
if (total <= 0) return breakdown
|
|
68
|
+
for (const bucket of [breakdown.agentsMd, breakdown.skills, breakdown.persona, breakdown.other] as const) {
|
|
69
|
+
bucket.share = bucket.tokens / total
|
|
70
|
+
}
|
|
71
|
+
return breakdown
|
|
72
|
+
}
|
|
73
|
+
|
|
22
74
|
/**
|
|
23
|
-
* Price the assembled system prompt (
|
|
24
|
-
*
|
|
75
|
+
* Price the assembled system prompt (harness identity + persona + tool
|
|
76
|
+
* guidance + plugin sections).
|
|
25
77
|
* @param header - canonical request envelope, or undefined before any request.
|
|
26
78
|
* @returns heuristic system-prompt tokens; 0 when absent.
|
|
27
79
|
*/
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ import type {} from '@deepseek-ai/dsh-commands'
|
|
|
21
21
|
import { Config, resolveConfig } from './config.ts'
|
|
22
22
|
import { FastCollector } from './collector.ts'
|
|
23
23
|
import type { MeasureFn, TokenMeasurement } from './collector.ts'
|
|
24
|
+
import type { SystemSection } from './estimate.ts'
|
|
24
25
|
import { buildReport, renderFastText } from './analyze.ts'
|
|
25
26
|
import type { FastReport } from './model.ts'
|
|
26
27
|
import { fastDomainSpec, appendSample } from './store.ts'
|
|
@@ -43,8 +44,11 @@ export type {
|
|
|
43
44
|
ContextStats,
|
|
44
45
|
CacheStats,
|
|
45
46
|
StoredSample,
|
|
47
|
+
PromptBucket,
|
|
48
|
+
SystemPromptBreakdown,
|
|
46
49
|
} from './model.ts'
|
|
47
50
|
export { FastCollector, detectSpilledResult, flattenToolResultText, sharesOf, hitRateOf } from './collector.ts'
|
|
51
|
+
export { classifySystemSections, type SystemSection } from './estimate.ts'
|
|
48
52
|
export { buildReport, buildSuggestions, renderFastText } from './analyze.ts'
|
|
49
53
|
export { fastDomainSpec, appendSample, historySchema } from './store.ts'
|
|
50
54
|
|
|
@@ -53,6 +57,11 @@ interface TokenMeterService {
|
|
|
53
57
|
measure(session: Session): TokenMeasurement
|
|
54
58
|
}
|
|
55
59
|
|
|
60
|
+
/** The structural surface of the optional `ctx.systemPrompt` service (section assembly). */
|
|
61
|
+
interface SystemPromptService {
|
|
62
|
+
assemble(): Promise<{ sections: readonly { name: string; text: string }[] }>
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
/**
|
|
57
66
|
* Mount the diagnostics. The resolved config is validated first (fail loud);
|
|
58
67
|
* with `enabled: false` the plugin registers nothing and stays inert.
|
|
@@ -84,9 +93,22 @@ export async function apply(ctx: Context, config: Config = {}): Promise<void> {
|
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
|
|
96
|
+
/** Assemble the named system-prompt sections for the per-section breakdown. */
|
|
97
|
+
const assembleSections = async (): Promise<readonly SystemSection[] | undefined> => {
|
|
98
|
+
const systemPrompt = ctx.get('systemPrompt') as unknown as SystemPromptService | undefined
|
|
99
|
+
if (systemPrompt === undefined) return undefined
|
|
100
|
+
try {
|
|
101
|
+
const assembly = await systemPrompt.assemble()
|
|
102
|
+
return assembly.sections.map(section => ({ name: section.name, text: section.text }))
|
|
103
|
+
} catch (error) {
|
|
104
|
+
logger.warn(`system-prompt section assembly failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
105
|
+
return undefined
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
87
109
|
/** Build the complete report for one session. */
|
|
88
|
-
const reportFor = (session: Session): FastReport => {
|
|
89
|
-
const snapshot = collector.snapshot(session, measure)
|
|
110
|
+
const reportFor = async (session: Session): Promise<FastReport> => {
|
|
111
|
+
const snapshot = collector.snapshot(session, measure, await assembleSections())
|
|
90
112
|
return buildReport(
|
|
91
113
|
snapshot,
|
|
92
114
|
{
|
|
@@ -100,8 +122,8 @@ export async function apply(ctx: Context, config: Config = {}): Promise<void> {
|
|
|
100
122
|
}
|
|
101
123
|
|
|
102
124
|
/** Append one snapshot to the session's durable history (fire-and-forget). */
|
|
103
|
-
const persist = (session: Session): void => {
|
|
104
|
-
const snapshot = collector.snapshot(session, measure)
|
|
125
|
+
const persist = async (session: Session): Promise<void> => {
|
|
126
|
+
const snapshot = collector.snapshot(session, measure, await assembleSections())
|
|
105
127
|
const next = appendSample(sessions.get(session.id), { at: Date.now(), snapshot }, resolved.maxHistorySamples)
|
|
106
128
|
void sessions.put(session.id, next).catch((error: unknown) => {
|
|
107
129
|
logger.warn(`session "${session.id}": persist failed: ${error instanceof Error ? error.message : String(error)}`)
|
|
@@ -112,8 +134,8 @@ export async function apply(ctx: Context, config: Config = {}): Promise<void> {
|
|
|
112
134
|
ctx.commands.register({
|
|
113
135
|
name: 'fast',
|
|
114
136
|
description: 'Print the dsh-fast performance report for the active session.',
|
|
115
|
-
handler
|
|
116
|
-
const report = reportFor(invocation.agent.session)
|
|
137
|
+
async handler(invocation) {
|
|
138
|
+
const report = await reportFor(invocation.agent.session)
|
|
117
139
|
return { kind: 'success', text: renderFastText(report) }
|
|
118
140
|
},
|
|
119
141
|
})
|
|
@@ -171,6 +193,17 @@ export async function apply(ctx: Context, config: Config = {}): Promise<void> {
|
|
|
171
193
|
systemShare: { type: 'number', required: true },
|
|
172
194
|
toolsShare: { type: 'number', required: true },
|
|
173
195
|
surfaceShare: { type: 'number', required: true },
|
|
196
|
+
systemBreakdown: {
|
|
197
|
+
type: 'object',
|
|
198
|
+
properties: {
|
|
199
|
+
agentsMd: { type: 'object', properties: { tokens: { type: 'number', required: true }, chars: { type: 'number', required: true }, share: { type: 'number', required: true } }, additionalProperties: false, required: true },
|
|
200
|
+
skills: { type: 'object', properties: { tokens: { type: 'number', required: true }, chars: { type: 'number', required: true }, share: { type: 'number', required: true } }, additionalProperties: false, required: true },
|
|
201
|
+
persona: { type: 'object', properties: { tokens: { type: 'number', required: true }, chars: { type: 'number', required: true }, share: { type: 'number', required: true } }, additionalProperties: false, required: true },
|
|
202
|
+
other: { type: 'object', properties: { tokens: { type: 'number', required: true }, chars: { type: 'number', required: true }, share: { type: 'number', required: true } }, additionalProperties: false, required: true },
|
|
203
|
+
},
|
|
204
|
+
additionalProperties: false,
|
|
205
|
+
required: true,
|
|
206
|
+
},
|
|
174
207
|
},
|
|
175
208
|
additionalProperties: false,
|
|
176
209
|
required: true,
|
|
@@ -199,7 +232,7 @@ export async function apply(ctx: Context, config: Config = {}): Promise<void> {
|
|
|
199
232
|
if (session === undefined) {
|
|
200
233
|
throw new Error('fast_report requires an agent-owned session')
|
|
201
234
|
}
|
|
202
|
-
return reportFor(session)
|
|
235
|
+
return await reportFor(session)
|
|
203
236
|
},
|
|
204
237
|
}))
|
|
205
238
|
|
|
@@ -224,7 +257,7 @@ export async function apply(ctx: Context, config: Config = {}): Promise<void> {
|
|
|
224
257
|
for (const session of collector.liveSessions()) {
|
|
225
258
|
if (!collector.isDirty(session)) continue
|
|
226
259
|
collector.markClean(session)
|
|
227
|
-
persist(session)
|
|
260
|
+
void persist(session)
|
|
228
261
|
}
|
|
229
262
|
}, resolved.snapshotIntervalMs)
|
|
230
263
|
return async () => {
|
package/src/model.ts
CHANGED
|
@@ -44,11 +44,33 @@ export interface CompactionStats {
|
|
|
44
44
|
shadowedTokens: number
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/** One named bucket of the injected system prompt. */
|
|
48
|
+
export interface PromptBucket {
|
|
49
|
+
/** Heuristic token estimate for this bucket (`ceil(chars / 4)`). */
|
|
50
|
+
tokens: number
|
|
51
|
+
/** Raw character count of the bucket's sections. */
|
|
52
|
+
chars: number
|
|
53
|
+
/** Fraction of the total system-prompt bucket tokens (0 when the total is 0). */
|
|
54
|
+
share: number
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** The system-prompt breakdown: AGENTS.md / skills / persona / other named sections. */
|
|
58
|
+
export interface SystemPromptBreakdown {
|
|
59
|
+
/** Sections that carry agent instructions (AGENTS.md-style). */
|
|
60
|
+
agentsMd: PromptBucket
|
|
61
|
+
/** Sections that carry skill content. */
|
|
62
|
+
skills: PromptBucket
|
|
63
|
+
/** The `deployment:persona` section. */
|
|
64
|
+
persona: PromptBucket
|
|
65
|
+
/** Harness identity, tool guidance, and any unclassified sections. */
|
|
66
|
+
other: PromptBucket
|
|
67
|
+
}
|
|
68
|
+
|
|
47
69
|
/** Context-injection volume: where the request context's tokens live. */
|
|
48
70
|
export interface ContextStats {
|
|
49
71
|
/** Canonical current pressure (token-meter total), or the heuristic header+surface sum. */
|
|
50
72
|
totalTokens: number
|
|
51
|
-
/** Assembled system-prompt tokens —
|
|
73
|
+
/** Assembled system-prompt tokens — harness identity, persona, tool guidance, and plugin sections. */
|
|
52
74
|
systemTokens: number
|
|
53
75
|
/** Tool-schema tokens. */
|
|
54
76
|
toolSchemaTokens: number
|
|
@@ -60,6 +82,8 @@ export interface ContextStats {
|
|
|
60
82
|
toolsShare: number
|
|
61
83
|
/** `surfaceTokens / totalTokens` (0 when the total is 0). */
|
|
62
84
|
surfaceShare: number
|
|
85
|
+
/** Per-section system-prompt breakdown (AGENTS.md / skills / persona / other). */
|
|
86
|
+
systemBreakdown: SystemPromptBreakdown
|
|
63
87
|
}
|
|
64
88
|
|
|
65
89
|
/** LLM cache accounting aggregated from `assistant/message` usage. */
|
package/src/store.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Durable metric storage over the harness storage domain. The `dsh-fast`
|
|
3
3
|
* domain keeps one bounded history per session, so `/fast` and `fast_report`
|
|
4
4
|
* metrics survive a restart and the trend stays queryable without touching the
|
|
5
|
-
* session log (the rc.
|
|
5
|
+
* session log (the rc.2 `Session.append` offers no `ignorable` marker and no
|
|
6
6
|
* external event-registration surface, so a custom session event would make
|
|
7
7
|
* the persistence coordinator refuse the log on restore).
|
|
8
8
|
* @module dsh-fast/store
|
|
@@ -33,6 +33,21 @@ const compactionSchema = z.object({
|
|
|
33
33
|
shadowedTokens: z.number().int().nonnegative(),
|
|
34
34
|
})
|
|
35
35
|
|
|
36
|
+
/** Zod schema for one prompt bucket. */
|
|
37
|
+
const bucketSchema = z.object({
|
|
38
|
+
tokens: z.number().nonnegative(),
|
|
39
|
+
chars: z.number().nonnegative(),
|
|
40
|
+
share: z.number(),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
/** Zod schema for the system-prompt breakdown. */
|
|
44
|
+
const breakdownSchema = z.object({
|
|
45
|
+
agentsMd: bucketSchema,
|
|
46
|
+
skills: bucketSchema,
|
|
47
|
+
persona: bucketSchema,
|
|
48
|
+
other: bucketSchema,
|
|
49
|
+
})
|
|
50
|
+
|
|
36
51
|
/** Zod schema for the context section. */
|
|
37
52
|
const contextSchema = z.object({
|
|
38
53
|
totalTokens: z.number().nonnegative(),
|
|
@@ -42,6 +57,7 @@ const contextSchema = z.object({
|
|
|
42
57
|
systemShare: z.number(),
|
|
43
58
|
toolsShare: z.number(),
|
|
44
59
|
surfaceShare: z.number(),
|
|
60
|
+
systemBreakdown: breakdownSchema,
|
|
45
61
|
})
|
|
46
62
|
|
|
47
63
|
/** Zod schema for the cache section. */
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Single-source plugin version, bumped by `scripts/release.mjs`. @module dsh-fast/version */
|
|
2
|
-
export const VERSION = '0.
|
|
2
|
+
export const VERSION = '0.2.0'
|