mirror of
https://github.com/tiennm99/ccstatusline.git
synced 2026-10-14 06:17:28 +00:00
Fix: Dynamic context window calculation for Sonnet 4.5 (1M tokens) (#107)
* Fix: Dynamic context window calculation for Sonnet 4.5 (1M tokens) - Create model-context utility with dynamic context window mapping - Sonnet 4.5 models now use 1M context (800k usable) - Older models default to 200k context (160k usable) - Update ContextPercentage and ContextPercentageUsable widgets - Update renderer.ts flex mode calculations (2 locations) - Add comprehensive test coverage (14 new tests, 36 total passing) Fixes incorrect percentage display for Sonnet 4.5: - Before: 42k tokens showed 21.0% (using 200k denominator) - After: 42k tokens shows 4.2% (using 1M denominator) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Style: Apply ESLint auto-fixes for consistency - Standardize indentation to 4 spaces - Apply formatting rules from eslint.config.js - No logic changes, formatting only 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Refactor: Code review fixes for context window changes - Fix misleading test name (line 69 of context-percentage.test.ts) - Strengthen model ID pattern matching (claude-sonnet-4-5 vs sonnet-4-5) - Standardize export style (inline export for consistency) All tests passing (19/19 for affected files) TypeScript and ESLint checks passed * Enhance CLAUDE.md: Update piped input example for Sonnet 4.5 and add test commands - Changed piped input example to reflect the new model ID for Claude 4.5 - Added instructions for running tests and tests in watch mode - Updated context window metrics description to include dynamic model-based context windows Improves clarity and usability for developers working with the project. * Fix: Require [1m] suffix for Sonnet 4.5 1M context window detection Address PR feedback that Sonnet 4.5 only has 1M context on API usage when the model ID includes the [1m] suffix (long context beta access). Changes: - Update getContextConfig() to check for [1m] suffix (case-insensitive) - Models without suffix now safely default to 200K context - Add comprehensive test coverage for suffix detection - Update all test fixtures to use correct model ID format - Document suffix requirement in CLAUDE.md This ensures conservative behavior - only models with explicit [1m] suffix receive 1M context allocation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
8477dc5e6b
commit
da325930ad
11 files changed
+361
-14
No files matched your search
@@ -20,12 +20,18 @@ bun run start
|
||||
# Run directly (TUI mode)
|
||||
bun run statusline
|
||||
|
||||
# Test with piped input
|
||||
echo '{"model":{"display_name":"Claude 3.5 Sonnet"},"transcript_path":"test.jsonl"}' | bun run src/ccstatusline.ts
|
||||
# Test with piped input (use [1m] suffix for 1M context models)
|
||||
echo '{"model":{"id":"claude-sonnet-4-5-20250929[1m]"},"transcript_path":"test.jsonl"}' | bun run src/ccstatusline.ts
|
||||
|
||||
# Build for npm distribution
|
||||
bun run build # Creates dist/ccstatusline.js with Node.js 14+ compatibility
|
||||
|
||||
# Run tests
|
||||
bun test
|
||||
|
||||
# Run tests in watch mode
|
||||
bun test --watch
|
||||
|
||||
# Lint and type check
|
||||
bun run lint # Runs TypeScript type checking and ESLint with auto-fix
|
||||
```
|
||||
@@ -62,13 +68,18 @@ The project has dual runtime compatibility - works with both Bun and Node.js:
|
||||
- Detects installation status and manages settings.json updates
|
||||
- Validates config directory paths with proper error handling
|
||||
- **colors.ts**: Color definitions and ANSI code mapping
|
||||
- **model-context.ts**: Model-to-context-window mapping
|
||||
- Maps model IDs to their context window sizes based on [1m] suffix
|
||||
- Sonnet 4.5 WITH [1m] suffix: 1M tokens (800k usable at 80%) - requires long context beta access
|
||||
- Sonnet 4.5 WITHOUT [1m] suffix: 200k tokens (160k usable at 80%)
|
||||
- Legacy models: 200k tokens (160k usable at 80%)
|
||||
|
||||
### Widgets (src/widgets/)
|
||||
Custom widgets implementing the StatusItemWidget interface:
|
||||
- Model, Version, OutputStyle - Claude Code metadata display
|
||||
- GitBranch, GitChanges - Git repository status
|
||||
- TokensInput, TokensOutput, TokensCached, TokensTotal - Token usage metrics
|
||||
- ContextLength, ContextPercentage, ContextPercentageUsable - Context window metrics
|
||||
- ContextLength, ContextPercentage, ContextPercentageUsable - Context window metrics (uses dynamic model-based context windows: 1M for Sonnet 4.5 with [1m] suffix, 200k for all other models)
|
||||
- BlockTimer, SessionClock - Time tracking
|
||||
- CurrentWorkingDir, TerminalWidth - Environment info
|
||||
|
||||
@@ -100,4 +111,4 @@ Default to using Bun instead of Node.js:
|
||||
- **Dependencies**: All runtime dependencies are bundled using `--packages=external` for npm package
|
||||
- **Type checking and linting**: Only run via `bun run lint` command, never using `npx eslint` or `eslint` directly. Never run `tsx`, `bun tsc` or any other variation
|
||||
- **Lint rules**: Never disable a lint rule via a comment, no matter how benign the lint warning or error may seem
|
||||
- **Testing**: No test framework is currently configured. Manual testing is done via piped input and TUI interaction
|
||||
- **Testing**: Uses Bun's built-in test framework. Run tests with `bun test` (36 tests covering model context, widgets, and rendering). Manual testing also available via piped input and TUI interaction
|
||||
@@ -364,8 +364,8 @@ Once configured, ccstatusline automatically formats your Claude Code status line
|
||||
- **Tokens Cached** - Shows cached tokens used
|
||||
- **Tokens Total** - Shows total tokens used
|
||||
- **Context Length** - Shows current context length in tokens
|
||||
- **Context Percentage** - Shows percentage of context limit used (out of 200k)
|
||||
- **Context Percentage (usable)** - Shows percentage of usable context (out of 160k, accounting for auto-compact at 80%)
|
||||
- **Context Percentage** - Shows percentage of context limit used (dynamic: 1M for Sonnet 4.5 with `[1m]` suffix, 200k otherwise)
|
||||
- **Context Percentage (usable)** - Shows percentage of usable context (dynamic: 800k for Sonnet 4.5 with `[1m]` suffix, 160k otherwise, accounting for auto-compact at 80%)
|
||||
- **Terminal Width** - Shows detected terminal width (for debugging)
|
||||
- **Custom Text** - Add your own custom text to the status line
|
||||
- **Custom Command** - Execute shell commands and display their output (refreshes whenever the statusline is updated by Claude Code)
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import {
|
||||
describe,
|
||||
expect,
|
||||
it
|
||||
} from 'vitest';
|
||||
|
||||
import type { RenderContext } from '../../types';
|
||||
import { calculateContextPercentage } from '../context-percentage';
|
||||
|
||||
describe('calculateContextPercentage', () => {
|
||||
describe('Sonnet 4.5 with 1M context window', () => {
|
||||
it('should calculate percentage using 1M denominator with [1m] suffix', () => {
|
||||
const context: RenderContext = {
|
||||
data: { model: { id: 'claude-sonnet-4-5-20250929[1m]' } },
|
||||
tokenMetrics: {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cachedTokens: 0,
|
||||
totalTokens: 0,
|
||||
contextLength: 42000
|
||||
}
|
||||
};
|
||||
|
||||
const percentage = calculateContextPercentage(context);
|
||||
expect(percentage).toBe(4.2);
|
||||
});
|
||||
|
||||
it('should cap at 100% with [1m] suffix', () => {
|
||||
const context: RenderContext = {
|
||||
data: { model: { id: 'claude-sonnet-4-5-20250929[1m]' } },
|
||||
tokenMetrics: {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cachedTokens: 0,
|
||||
totalTokens: 0,
|
||||
contextLength: 2000000
|
||||
}
|
||||
};
|
||||
|
||||
const percentage = calculateContextPercentage(context);
|
||||
expect(percentage).toBe(100);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Older models with 200k context window', () => {
|
||||
it('should calculate percentage using 200k denominator', () => {
|
||||
const context: RenderContext = {
|
||||
data: { model: { id: 'claude-3-5-sonnet-20241022' } },
|
||||
tokenMetrics: {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cachedTokens: 0,
|
||||
totalTokens: 0,
|
||||
contextLength: 42000
|
||||
}
|
||||
};
|
||||
|
||||
const percentage = calculateContextPercentage(context);
|
||||
expect(percentage).toBe(21.0);
|
||||
});
|
||||
|
||||
it('should return 0 when no token metrics', () => {
|
||||
const context: RenderContext = { data: { model: { id: 'claude-3-5-sonnet-20241022' } } };
|
||||
|
||||
const percentage = calculateContextPercentage(context);
|
||||
expect(percentage).toBe(0);
|
||||
});
|
||||
|
||||
it('should use default 200k context when model ID is undefined', () => {
|
||||
const context: RenderContext = {
|
||||
tokenMetrics: {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cachedTokens: 0,
|
||||
totalTokens: 0,
|
||||
contextLength: 42000
|
||||
}
|
||||
};
|
||||
|
||||
const percentage = calculateContextPercentage(context);
|
||||
expect(percentage).toBe(21.0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { getContextConfig } from "../model-context";
|
||||
|
||||
describe("getContextConfig", () => {
|
||||
describe("Sonnet 4.5 models with [1m] suffix", () => {
|
||||
it("should return 1M context window for claude-sonnet-4-5 with [1m] suffix", () => {
|
||||
const config = getContextConfig("claude-sonnet-4-5-20250929[1m]");
|
||||
|
||||
expect(config.maxTokens).toBe(1000000);
|
||||
expect(config.usableTokens).toBe(800000);
|
||||
});
|
||||
|
||||
it("should return 1M context window for AWS Bedrock format with [1m] suffix", () => {
|
||||
const config = getContextConfig(
|
||||
"us.anthropic.claude-sonnet-4-5-20250929-v1:0[1m]"
|
||||
);
|
||||
|
||||
expect(config.maxTokens).toBe(1000000);
|
||||
expect(config.usableTokens).toBe(800000);
|
||||
});
|
||||
|
||||
it("should return 1M context window with uppercase [1M] suffix", () => {
|
||||
const config = getContextConfig("claude-sonnet-4-5-20250929[1M]");
|
||||
|
||||
expect(config.maxTokens).toBe(1000000);
|
||||
expect(config.usableTokens).toBe(800000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Sonnet 4.5 models without [1m] suffix", () => {
|
||||
it("should return 200k context window for claude-sonnet-4-5 without [1m] suffix", () => {
|
||||
const config = getContextConfig("claude-sonnet-4-5-20250929");
|
||||
|
||||
expect(config.maxTokens).toBe(200000);
|
||||
expect(config.usableTokens).toBe(160000);
|
||||
});
|
||||
|
||||
it("should return 200k context window for AWS Bedrock format without [1m] suffix", () => {
|
||||
const config = getContextConfig(
|
||||
"us.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||
);
|
||||
|
||||
expect(config.maxTokens).toBe(200000);
|
||||
expect(config.usableTokens).toBe(160000);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Older/default models", () => {
|
||||
it("should return 200k context window for older Sonnet 3.5 model", () => {
|
||||
const config = getContextConfig("claude-3-5-sonnet-20241022");
|
||||
|
||||
expect(config.maxTokens).toBe(200000);
|
||||
expect(config.usableTokens).toBe(160000);
|
||||
});
|
||||
|
||||
it("should return 200k context window when model ID is undefined", () => {
|
||||
const config = getContextConfig(undefined);
|
||||
|
||||
expect(config.maxTokens).toBe(200000);
|
||||
expect(config.usableTokens).toBe(160000);
|
||||
});
|
||||
|
||||
it("should return 200k context window for unknown model ID", () => {
|
||||
const config = getContextConfig("claude-unknown-model");
|
||||
|
||||
expect(config.maxTokens).toBe(200000);
|
||||
expect(config.usableTokens).toBe(160000);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { RenderContext } from '../types';
|
||||
|
||||
import { getContextConfig } from './model-context';
|
||||
|
||||
/**
|
||||
* Calculate context window usage percentage based on model's max tokens
|
||||
*/
|
||||
export function calculateContextPercentage(context: RenderContext): number {
|
||||
if (!context.tokenMetrics) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const modelId = context.data?.model?.id;
|
||||
const contextConfig = getContextConfig(modelId);
|
||||
|
||||
return Math.min(100, (context.tokenMetrics.contextLength / contextConfig.maxTokens) * 100);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
interface ModelContextConfig {
|
||||
maxTokens: number;
|
||||
usableTokens: number;
|
||||
}
|
||||
|
||||
export function getContextConfig(modelId?: string): ModelContextConfig {
|
||||
// Default to 200k for older models
|
||||
const defaultConfig = {
|
||||
maxTokens: 200000,
|
||||
usableTokens: 160000,
|
||||
};
|
||||
|
||||
if (!modelId) return defaultConfig;
|
||||
|
||||
// Sonnet 4.5 variants with 1M context (requires [1m] suffix for long context beta)
|
||||
if (
|
||||
modelId.includes("claude-sonnet-4-5") &&
|
||||
modelId.toLowerCase().includes("[1m]")
|
||||
) {
|
||||
return {
|
||||
maxTokens: 1000000,
|
||||
usableTokens: 800000, // 80% of 1M
|
||||
};
|
||||
}
|
||||
|
||||
// Add future models here as needed
|
||||
|
||||
return defaultConfig;
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
getColorAnsiCode,
|
||||
getPowerlineTheme
|
||||
} from './colors';
|
||||
import { calculateContextPercentage } from './context-percentage';
|
||||
import { getTerminalWidth } from './terminal';
|
||||
import { getWidget } from './widgets';
|
||||
|
||||
@@ -101,8 +102,7 @@ function renderPowerlineStatusLine(
|
||||
terminalWidth = detectedWidth - 40;
|
||||
} else if (flexMode === 'full-until-compact') {
|
||||
const threshold = settings.compactThreshold;
|
||||
const contextPercentage = context.tokenMetrics
|
||||
? Math.min(100, (context.tokenMetrics.contextLength / 200000) * 100) : 0;
|
||||
const contextPercentage = calculateContextPercentage(context);
|
||||
|
||||
if (contextPercentage >= threshold) {
|
||||
terminalWidth = detectedWidth - 40;
|
||||
@@ -680,8 +680,7 @@ export function renderStatusLine(
|
||||
} else if (flexMode === 'full-until-compact') {
|
||||
// Check context percentage to decide
|
||||
const threshold = settings.compactThreshold;
|
||||
const contextPercentage = context.tokenMetrics
|
||||
? Math.min(100, (context.tokenMetrics.contextLength / 200000) * 100) : 0;
|
||||
const contextPercentage = calculateContextPercentage(context);
|
||||
|
||||
if (contextPercentage >= threshold) {
|
||||
// Context is high, leave space for auto-compact
|
||||
|
||||
@@ -6,10 +6,11 @@ import type {
|
||||
WidgetEditorDisplay,
|
||||
WidgetItem
|
||||
} from '../types/Widget';
|
||||
import { getContextConfig } from '../utils/model-context';
|
||||
|
||||
export class ContextPercentageWidget implements Widget {
|
||||
getDefaultColor(): string { return 'blue'; }
|
||||
getDescription(): string { return 'Shows percentage of context window used or remaining (of 200k tokens)'; }
|
||||
getDescription(): string { return 'Shows percentage of context window used or remaining'; }
|
||||
getDisplayName(): string { return 'Context %'; }
|
||||
getEditorDisplay(item: WidgetItem): WidgetEditorDisplay {
|
||||
const isInverse = item.metadata?.inverse === 'true';
|
||||
@@ -46,7 +47,9 @@ export class ContextPercentageWidget implements Widget {
|
||||
const previewValue = isInverse ? '90.7%' : '9.3%';
|
||||
return item.rawValue ? previewValue : `Ctx: ${previewValue}`;
|
||||
} else if (context.tokenMetrics) {
|
||||
const usedPercentage = Math.min(100, (context.tokenMetrics.contextLength / 200000) * 100);
|
||||
const modelId = context.data?.model?.id;
|
||||
const contextConfig = getContextConfig(modelId);
|
||||
const usedPercentage = Math.min(100, (context.tokenMetrics.contextLength / contextConfig.maxTokens) * 100);
|
||||
const displayPercentage = isInverse ? (100 - usedPercentage) : usedPercentage;
|
||||
return item.rawValue ? `${displayPercentage.toFixed(1)}%` : `Ctx: ${displayPercentage.toFixed(1)}%`;
|
||||
}
|
||||
|
||||
@@ -6,10 +6,11 @@ import type {
|
||||
WidgetEditorDisplay,
|
||||
WidgetItem
|
||||
} from '../types/Widget';
|
||||
import { getContextConfig } from '../utils/model-context';
|
||||
|
||||
export class ContextPercentageUsableWidget implements Widget {
|
||||
getDefaultColor(): string { return 'green'; }
|
||||
getDescription(): string { return 'Shows percentage of usable context window used or remaining (of 160k tokens before auto-compact)'; }
|
||||
getDescription(): string { return 'Shows percentage of usable context window used or remaining (80% of max before auto-compact)'; }
|
||||
getDisplayName(): string { return 'Context % (usable)'; }
|
||||
getEditorDisplay(item: WidgetItem): WidgetEditorDisplay {
|
||||
const isInverse = item.metadata?.inverse === 'true';
|
||||
@@ -46,7 +47,9 @@ export class ContextPercentageUsableWidget implements Widget {
|
||||
const previewValue = isInverse ? '88.4%' : '11.6%';
|
||||
return item.rawValue ? previewValue : `Ctx(u): ${previewValue}`;
|
||||
} else if (context.tokenMetrics) {
|
||||
const usedPercentage = Math.min(100, (context.tokenMetrics.contextLength / 160000) * 100);
|
||||
const modelId = context.data?.model?.id;
|
||||
const contextConfig = getContextConfig(modelId);
|
||||
const usedPercentage = Math.min(100, (context.tokenMetrics.contextLength / contextConfig.usableTokens) * 100);
|
||||
const displayPercentage = isInverse ? (100 - usedPercentage) : usedPercentage;
|
||||
return item.rawValue ? `${displayPercentage.toFixed(1)}%` : `Ctx(u): ${displayPercentage.toFixed(1)}%`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
describe,
|
||||
expect,
|
||||
it
|
||||
} from 'vitest';
|
||||
|
||||
import type {
|
||||
RenderContext,
|
||||
WidgetItem
|
||||
} from '../../types';
|
||||
import { DEFAULT_SETTINGS } from '../../types/Settings';
|
||||
import { ContextPercentageWidget } from '../ContextPercentage';
|
||||
|
||||
function render(modelId: string | undefined, contextLength: number, rawValue = false, inverse = false) {
|
||||
const widget = new ContextPercentageWidget();
|
||||
const context: RenderContext = {
|
||||
data: modelId ? { model: { id: modelId } } : undefined,
|
||||
tokenMetrics: {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cachedTokens: 0,
|
||||
totalTokens: 0,
|
||||
contextLength
|
||||
}
|
||||
};
|
||||
const item: WidgetItem = {
|
||||
id: 'context-percentage',
|
||||
type: 'context-percentage',
|
||||
rawValue,
|
||||
metadata: inverse ? { inverse: 'true' } : undefined
|
||||
};
|
||||
|
||||
return widget.render(item, context, DEFAULT_SETTINGS);
|
||||
}
|
||||
|
||||
describe('ContextPercentageWidget', () => {
|
||||
describe('Sonnet 4.5 with 1M context window', () => {
|
||||
it('should calculate percentage using 1M denominator for Sonnet 4.5 with [1m] suffix', () => {
|
||||
const result = render('claude-sonnet-4-5-20250929[1m]', 42000);
|
||||
expect(result).toBe('Ctx: 4.2%');
|
||||
});
|
||||
|
||||
it('should calculate percentage using 1M denominator for Sonnet 4.5 (raw value) with [1m] suffix', () => {
|
||||
const result = render('claude-sonnet-4-5-20250929[1m]', 42000, true);
|
||||
expect(result).toBe('4.2%');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Older models with 200k context window', () => {
|
||||
it('should calculate percentage using 200k denominator for older Sonnet 3.5', () => {
|
||||
const result = render('claude-3-5-sonnet-20241022', 42000);
|
||||
expect(result).toBe('Ctx: 21.0%');
|
||||
});
|
||||
|
||||
it('should calculate percentage using 200k denominator when model ID is undefined', () => {
|
||||
const result = render(undefined, 42000);
|
||||
expect(result).toBe('Ctx: 21.0%');
|
||||
});
|
||||
|
||||
it('should calculate percentage using 200k denominator for unknown model', () => {
|
||||
const result = render('claude-unknown-model', 42000);
|
||||
expect(result).toBe('Ctx: 21.0%');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
describe,
|
||||
expect,
|
||||
it
|
||||
} from 'vitest';
|
||||
|
||||
import type {
|
||||
RenderContext,
|
||||
WidgetItem
|
||||
} from '../../types';
|
||||
import { DEFAULT_SETTINGS } from '../../types/Settings';
|
||||
import { ContextPercentageUsableWidget } from '../ContextPercentageUsable';
|
||||
|
||||
function render(modelId: string | undefined, contextLength: number, rawValue = false, inverse = false) {
|
||||
const widget = new ContextPercentageUsableWidget();
|
||||
const context: RenderContext = {
|
||||
data: modelId ? { model: { id: modelId } } : undefined,
|
||||
tokenMetrics: {
|
||||
inputTokens: 0,
|
||||
outputTokens: 0,
|
||||
cachedTokens: 0,
|
||||
totalTokens: 0,
|
||||
contextLength
|
||||
}
|
||||
};
|
||||
const item: WidgetItem = {
|
||||
id: 'context-percentage-usable',
|
||||
type: 'context-percentage-usable',
|
||||
rawValue,
|
||||
metadata: inverse ? { inverse: 'true' } : undefined
|
||||
};
|
||||
|
||||
return widget.render(item, context, DEFAULT_SETTINGS);
|
||||
}
|
||||
|
||||
describe('ContextPercentageUsableWidget', () => {
|
||||
describe('Sonnet 4.5 with 800k usable tokens', () => {
|
||||
it('should calculate percentage using 800k denominator for Sonnet 4.5 with [1m] suffix', () => {
|
||||
const result = render('claude-sonnet-4-5-20250929[1m]', 42000);
|
||||
expect(result).toBe('Ctx(u): 5.3%');
|
||||
});
|
||||
|
||||
it('should calculate percentage using 800k denominator for Sonnet 4.5 (raw value) with [1m] suffix', () => {
|
||||
const result = render('claude-sonnet-4-5-20250929[1m]', 42000, true);
|
||||
expect(result).toBe('5.3%');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Older models with 160k usable tokens', () => {
|
||||
it('should calculate percentage using 160k denominator for older Sonnet 3.5', () => {
|
||||
const result = render('claude-3-5-sonnet-20241022', 42000);
|
||||
expect(result).toBe('Ctx(u): 26.3%');
|
||||
});
|
||||
|
||||
it('should calculate percentage using 160k denominator when model ID is undefined', () => {
|
||||
const result = render(undefined, 42000);
|
||||
expect(result).toBe('Ctx(u): 26.3%');
|
||||
});
|
||||
|
||||
it('should calculate percentage using 160k denominator for unknown model', () => {
|
||||
const result = render('claude-unknown-model', 42000);
|
||||
expect(result).toBe('Ctx(u): 26.3%');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user