refactor: apply repository naming contract
Apply the accepted pre-release package, service, type, directory, and role renames as one repository-wide change.
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/llm/token-meter/README.md
|
||||
README.md: 6cbbefa69fd4dd108d4c4ff717647e173922d7b2
|
||||
README.zh.md: 4f95b4fed4c6cd2b08fbf8b02f73cc5291b52a5d
|
||||
README.md: a2deab11a31285ba598b8864d3a734ecf7c56620
|
||||
README.zh.md: e0c3a8aa123ca0acc374277cd92b415e0b0b3cb5
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
English | [中文](README.zh.md)
|
||||
|
||||
Replay-aware token measurement through the singleton `ctx.tokenMeter` service. It advances one isolated fold per session from the durable log, so compaction and other pressure-sensitive plugins can share accounting without depending on `CompactService`.
|
||||
Replay-aware token measurement through the singleton `ctx.tokenMeter` service. It advances one isolated fold per session from the durable log, so compaction and other pressure-sensitive plugins can share accounting without depending on `CompactionEngine`.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -47,14 +47,14 @@ The [Agent Note](../../../.agents/notes/implemented/architecture/2026-07-29-proj
|
||||
|
||||
```yaml
|
||||
- name: '@deepseek-ai/dsh-token-meter'
|
||||
- name: '@deepseek-ai/dsh-compact-basic'
|
||||
- name: '@deepseek-ai/dsh-compaction-basic'
|
||||
```
|
||||
|
||||
Both plugins have usable defaults. The meter remains independent of model routing and optional compaction. A deployment configures capacity on its LLM adapter and compaction policy on `dsh-compact-basic`.
|
||||
Both plugins have usable defaults. The meter remains independent of model routing and optional compaction. A deployment configures capacity on its LLM adapter and compaction policy on `dsh-compaction-basic`.
|
||||
|
||||
## Model Experience
|
||||
|
||||
Indirectly, through consumers such as `dsh-compact-basic`; the service itself adds no prompt, message, schema, tool, or model call.
|
||||
Indirectly, through consumers such as `dsh-compaction-basic`; the service itself adds no prompt, message, schema, tool, or model call.
|
||||
|
||||
#### KV Cache effect
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
通过单例 `ctx.tokenMeter` 服务进行具备回放感知能力的 token 测量。它从持久日志为每个会话推进一个隔离 fold,因此压缩(compaction)与其他压力敏感插件可以共享计量,无需依赖 `CompactService`。
|
||||
通过单例 `ctx.tokenMeter` 服务进行具备回放感知能力的 token 测量。它从持久日志为每个会话推进一个隔离 fold,因此压缩(compaction)与其他压力敏感插件可以共享计量,无需依赖 `CompactionEngine`。
|
||||
|
||||
## 配置
|
||||
|
||||
@@ -47,14 +47,14 @@ fold 跟踪完整请求标头快照、步骤边界、表层追加与替换、成
|
||||
|
||||
```yaml
|
||||
- name: '@deepseek-ai/dsh-token-meter'
|
||||
- name: '@deepseek-ai/dsh-compact-basic'
|
||||
- name: '@deepseek-ai/dsh-compaction-basic'
|
||||
```
|
||||
|
||||
两个插件都有可用默认值。meter 保持与模型路由和可选压缩无关。部署会在 LLM(大语言模型)适配器上配置容量,并在 `dsh-compact-basic` 上配置压缩策略。
|
||||
两个插件都有可用默认值。meter 保持与模型路由和可选压缩无关。部署会在 LLM(大语言模型)适配器上配置容量,并在 `dsh-compaction-basic` 上配置压缩策略。
|
||||
|
||||
## 模型体验
|
||||
|
||||
通过 `dsh-compact-basic` 等消费方间接影响;该服务自身不添加提示词、消息、schema、工具或模型调用。
|
||||
通过 `dsh-compaction-basic` 等消费方间接影响;该服务自身不添加提示词、消息、schema、工具或模型调用。
|
||||
|
||||
#### KV Cache 影响
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"peerDependencies": {
|
||||
"@deepseek-ai/dsh-compact": "workspace:^",
|
||||
"@deepseek-ai/dsh-compaction": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-llm": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
@@ -49,7 +49,7 @@
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@deepseek-ai/dsh-compact": "workspace:^",
|
||||
"@deepseek-ai/dsh-compaction": "workspace:^",
|
||||
"@deepseek-ai/dsh-invariants": "workspace:^",
|
||||
"@deepseek-ai/dsh-llm": "workspace:^",
|
||||
"@deepseek-ai/dsh-session": "workspace:^",
|
||||
|
||||
@@ -66,12 +66,12 @@ function validateConfigKeys(config: TokenMeterConfig): void {
|
||||
|
||||
declare module '@deepseek-ai/cordis' {
|
||||
interface Context {
|
||||
tokenMeter: TokenMeterService
|
||||
tokenMeter: TokenMeter
|
||||
}
|
||||
}
|
||||
|
||||
/** Replay owner for one service-wide estimator and isolated per-session folds. */
|
||||
export class TokenMeterService extends Service {
|
||||
export class TokenMeter extends Service {
|
||||
// Schemastery preserves untrusted loader keys on an empty object schema;
|
||||
// the public type excludes settings while validateConfigKeys rejects them.
|
||||
static Config: z<TokenMeterConfig> = z.object({}) as unknown as z<TokenMeterConfig>
|
||||
@@ -310,4 +310,4 @@ export class TokenMeterService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
export default TokenMeterService
|
||||
export default TokenMeter
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* (one node per model-visible message) would grow a checkpoint without
|
||||
* bound over the session's life. Instead, replacements ride the compact
|
||||
* seam's shadow-price protocol: the metering event immediately before a
|
||||
* surface `replace` (`compact/summary` or `compact/prune`) states the
|
||||
* surface `replace` (`compaction/summary` or `compaction/prune`) states the
|
||||
* heuristic price of the exact replaced range, so the fold keeps a running
|
||||
* total plus at most one pending claim and never retains per-node prices.
|
||||
* The counts are exact by construction: producers derive them from the same
|
||||
@@ -19,8 +19,8 @@
|
||||
|
||||
import { deriveEventMessage, isSurfaceEvent } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
// Type-only: the `compact/*` SessionEventMap merges (shadow-price events).
|
||||
import type {} from '@deepseek-ai/dsh-compact'
|
||||
// Type-only: the `compaction/*` SessionEventMap merges (shadow-price events).
|
||||
import type {} from '@deepseek-ai/dsh-compaction'
|
||||
import { estimateMessage } from './estimate.ts'
|
||||
|
||||
/**
|
||||
@@ -67,7 +67,7 @@ export function foldSurfaceProjection(
|
||||
claim: ShadowPriceClaim | undefined,
|
||||
event: SessionEvent,
|
||||
): SurfaceTokensFold {
|
||||
if (event.type === 'compact/summary' || event.type === 'compact/prune') {
|
||||
if (event.type === 'compaction/summary' || event.type === 'compaction/prune') {
|
||||
const { shadowedRange, shadowedTokenCount } = event.data
|
||||
return {
|
||||
deltaTokens: 0,
|
||||
|
||||
@@ -8,9 +8,9 @@ import type { ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore from '@deepseek-ai/dsh-session'
|
||||
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import TokenMeterService from '@deepseek-ai/dsh-token-meter'
|
||||
import TokenMeter from '@deepseek-ai/dsh-token-meter'
|
||||
import type { ContextBreakdownProjection } from '@deepseek-ai/dsh-token-meter/client'
|
||||
import { CompactionId } from '@deepseek-ai/dsh-compact'
|
||||
import { CompactionId } from '@deepseek-ai/dsh-compaction'
|
||||
import { contextBreakdownProjectionDefinition } from '../src/breakdown-projection.ts'
|
||||
import {
|
||||
estimateContent,
|
||||
@@ -32,7 +32,7 @@ async function harness(): Promise<{ ctx: Context; session: Session }> {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
await ctx.plugin(TokenMeterService)
|
||||
await ctx.plugin(TokenMeter)
|
||||
return { ctx, session: ctx.sessions.create() }
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ function appendUser(session: Session, text: string): number {
|
||||
}
|
||||
|
||||
/**
|
||||
* Meter one upcoming replacement the way compact-basic does: price the
|
||||
* Meter one upcoming replacement the way compaction-basic does: price the
|
||||
* replaced span from the measurement service's own nodes and log the
|
||||
* shadow-price event directly before the replace.
|
||||
*/
|
||||
@@ -59,7 +59,7 @@ function appendSummaryMeter(ctx: Context, session: Session, start: number, end:
|
||||
const startIdx = nodes.findIndex(node => node.seq === start)
|
||||
const endIdx = nodes.findIndex(node => node.seq === end)
|
||||
const shadowed = nodes.slice(startIdx, endIdx + 1)
|
||||
session.append('compact/summary', {
|
||||
session.append('compaction/summary', {
|
||||
compactionId: CompactionId('context-breakdown-summary'),
|
||||
summary: [{ type: 'text', text: 'summary' }],
|
||||
shadowedRange: { start, end },
|
||||
@@ -200,7 +200,7 @@ describe('contextBreakdown session projection', () => {
|
||||
surfaceOp: 'append',
|
||||
} as unknown as SessionEvent)
|
||||
const meter = (start: number, end: number, seq: number): SessionEvent => ({
|
||||
type: 'compact/prune',
|
||||
type: 'compaction/prune',
|
||||
seq,
|
||||
time: 0,
|
||||
data: { shadowedRange: { start, end }, shadowedSeqs: [start, end], shadowedTokenCount: 5 },
|
||||
@@ -260,7 +260,7 @@ describe('contextBreakdown session projection', () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
const meterFiber = await ctx.plugin(TokenMeterService)
|
||||
const meterFiber = await ctx.plugin(TokenMeter)
|
||||
const session = ctx.sessions.create()
|
||||
session.append('request/header', {
|
||||
header: { config: CONFIG, system: 'You are terse.' },
|
||||
@@ -274,7 +274,7 @@ describe('contextBreakdown session projection', () => {
|
||||
await meterFiber.dispose()
|
||||
expect(ctx.sessionProjections.snapshot(session).values).not.toHaveProperty('contextBreakdown')
|
||||
|
||||
await ctx.plugin(TokenMeterService)
|
||||
await ctx.plugin(TokenMeter)
|
||||
expect(ctx.sessionProjections.viewCheckpoint(checkpoint).contextBreakdown).toEqual({
|
||||
systemTokens: estimateSystemTokens({ config: CONFIG, system: 'You are terse.' }),
|
||||
toolsTokens: 0,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { createUserMessage, CallId, createMessage } from '@deepseek-ai/dsh-llm'
|
||||
import type { ContentBlock, Message, TokenUsage } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore, { Session, SessionId, canonicalHeader } from '@deepseek-ai/dsh-session'
|
||||
import type { EpochHeader, SessionEvent } from '@deepseek-ai/dsh-session'
|
||||
import TokenMeterService from '@deepseek-ai/dsh-token-meter'
|
||||
import TokenMeter from '@deepseek-ai/dsh-token-meter'
|
||||
import type { TokenMeasurement, TokenMeterConfig } from '@deepseek-ai/dsh-token-meter'
|
||||
|
||||
function header(model: string, extras: Omit<EpochHeader, 'config'> = {}): EpochHeader {
|
||||
@@ -89,8 +89,8 @@ function appendSuccessfulCall(
|
||||
session.append('step/end', { turn, step })
|
||||
}
|
||||
|
||||
function meter(config: TokenMeterConfig = {}): TokenMeterService {
|
||||
return new TokenMeterService(new Context(), config)
|
||||
function meter(config: TokenMeterConfig = {}): TokenMeter {
|
||||
return new TokenMeter(new Context(), config)
|
||||
}
|
||||
|
||||
function expectSurfaceTotal(measurement: TokenMeasurement): void {
|
||||
@@ -98,7 +98,7 @@ function expectSurfaceTotal(measurement: TokenMeasurement): void {
|
||||
.toBe(measurement.surfaceTokens)
|
||||
}
|
||||
|
||||
describe('TokenMeterService configuration and registration', () => {
|
||||
describe('TokenMeter configuration and registration', () => {
|
||||
it('exposes an empty public configuration type', () => {
|
||||
expectTypeOf<{}>().toExtend<TokenMeterConfig>()
|
||||
expectTypeOf<{ contextWindow: number }>().not.toExtend<TokenMeterConfig>()
|
||||
@@ -115,14 +115,14 @@ describe('TokenMeterService configuration and registration', () => {
|
||||
it('registers and unregisters ctx.tokenMeter with its plugin fiber', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
const fiber = await ctx.plugin(TokenMeterService)
|
||||
expect(ctx.get('tokenMeter')).toBeInstanceOf(TokenMeterService)
|
||||
const fiber = await ctx.plugin(TokenMeter)
|
||||
expect(ctx.get('tokenMeter')).toBeInstanceOf(TokenMeter)
|
||||
await fiber.dispose()
|
||||
expect(ctx.get('tokenMeter')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('TokenMeterService pricing', () => {
|
||||
describe('TokenMeter pricing', () => {
|
||||
it('prices every built-in content shape and merge-extended blocks with one fixed heuristic', () => {
|
||||
const service = meter()
|
||||
const blocks: ContentBlock[] = [
|
||||
@@ -438,7 +438,7 @@ describe('replay anchors and surface folds', () => {
|
||||
})
|
||||
|
||||
describe('malformed replay and listener lifecycle', () => {
|
||||
function expectRepeatedFailure(service: TokenMeterService, session: Session, pattern: RegExp): void {
|
||||
function expectRepeatedFailure(service: TokenMeter, session: Session, pattern: RegExp): void {
|
||||
expect(() => service.measure(session)).toThrow(pattern)
|
||||
expect(() => service.measure(session)).toThrow(pattern)
|
||||
}
|
||||
@@ -660,12 +660,12 @@ describe('malformed replay and listener lifecycle', () => {
|
||||
it('handles earlier-reader catch-up, eager observation, and service reload', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
let activeMeter: TokenMeterService | undefined
|
||||
let activeMeter: TokenMeter | undefined
|
||||
const revisions: number[] = []
|
||||
ctx.on('session/event', (session) => {
|
||||
if (activeMeter !== undefined) revisions.push(activeMeter.measure(session).logRevision)
|
||||
})
|
||||
const firstFiber = await ctx.plugin(TokenMeterService)
|
||||
const firstFiber = await ctx.plugin(TokenMeter)
|
||||
activeMeter = ctx.tokenMeter
|
||||
const session = ctx.sessions.create(SessionId('listener-order'), { seed: [{
|
||||
type: 'turn/start',
|
||||
@@ -684,7 +684,7 @@ describe('malformed replay and listener lifecycle', () => {
|
||||
expect(activeMeter.measure(session).logRevision).toBe(3)
|
||||
|
||||
await firstFiber.dispose()
|
||||
const secondFiber = await ctx.plugin(TokenMeterService)
|
||||
const secondFiber = await ctx.plugin(TokenMeter)
|
||||
activeMeter = ctx.tokenMeter
|
||||
expect(activeMeter.measure(session).logRevision).toBe(3)
|
||||
await secondFiber.dispose()
|
||||
|
||||
@@ -5,9 +5,9 @@ import type { TokenUsage } from '@deepseek-ai/dsh-llm'
|
||||
import SessionStore from '@deepseek-ai/dsh-session'
|
||||
import type { Session } from '@deepseek-ai/dsh-session'
|
||||
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
|
||||
import TokenMeterService from '@deepseek-ai/dsh-token-meter'
|
||||
import TokenMeter from '@deepseek-ai/dsh-token-meter'
|
||||
import type { ContextPressureProjection, TokenUsageProjection } from '@deepseek-ai/dsh-token-meter/client'
|
||||
import { CompactionId } from '@deepseek-ai/dsh-compact'
|
||||
import { CompactionId } from '@deepseek-ai/dsh-compaction'
|
||||
|
||||
const ZERO: TokenUsageProjection = {
|
||||
uncachedInputTokens: 0,
|
||||
@@ -24,7 +24,7 @@ async function harness(): Promise<{
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(SessionProjectionRegistry)
|
||||
const meterFiber = await ctx.plugin(TokenMeterService)
|
||||
const meterFiber = await ctx.plugin(TokenMeter)
|
||||
return { ctx, session: ctx.sessions.create(), meterFiber }
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ const projected = (ctx: Context, session: Session): TokenUsageProjection => {
|
||||
}
|
||||
|
||||
/**
|
||||
* Meter one upcoming replacement the way compact-basic does: price the
|
||||
* Meter one upcoming replacement the way compaction-basic does: price the
|
||||
* replaced span from the measurement service's own nodes and log the
|
||||
* shadow-price event directly before the replace.
|
||||
*/
|
||||
@@ -81,7 +81,7 @@ function appendSummaryMeter(ctx: Context, session: Session, start: number, end:
|
||||
const startIdx = nodes.findIndex(node => node.seq === start)
|
||||
const endIdx = nodes.findIndex(node => node.seq === end)
|
||||
const shadowed = nodes.slice(startIdx, endIdx + 1)
|
||||
session.append('compact/summary', {
|
||||
session.append('compaction/summary', {
|
||||
compactionId: CompactionId('token-usage-summary'),
|
||||
summary: [{ type: 'text', text: 'summary' }],
|
||||
shadowedRange: { start, end },
|
||||
@@ -234,7 +234,7 @@ describe('tokenUsage session projection', () => {
|
||||
await meterFiber.dispose()
|
||||
expect(ctx.sessionProjections.snapshot(session).values).not.toHaveProperty('tokenUsage')
|
||||
|
||||
await ctx.plugin(TokenMeterService)
|
||||
await ctx.plugin(TokenMeter)
|
||||
expect(ctx.sessionProjections.viewCheckpoint(checkpoint).tokenUsage).toEqual({
|
||||
uncachedInputTokens: 8,
|
||||
outputTokens: 2,
|
||||
@@ -379,7 +379,7 @@ describe('contextPressure session projection', () => {
|
||||
await meterFiber.dispose()
|
||||
expect(ctx.sessionProjections.snapshot(session).values).not.toHaveProperty('contextPressure')
|
||||
|
||||
await ctx.plugin(TokenMeterService)
|
||||
await ctx.plugin(TokenMeter)
|
||||
expect(ctx.sessionProjections.viewCheckpoint(checkpoint).contextPressure).toEqual({
|
||||
pressureTokens: 42,
|
||||
projectedTokens: 42,
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
"path": "../../core/session"
|
||||
},
|
||||
{
|
||||
"path": "../../compact/compact"
|
||||
"path": "../../compaction/compaction"
|
||||
},
|
||||
{
|
||||
"path": "../../session/session-projection"
|
||||
},
|
||||
{
|
||||
"path": "../../support/invariants"
|
||||
"path": "../../runtime-diagnostics/invariants"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user