Files
orcs-code/src/utils/thinkingTokenExtractor.test.ts
ArkhAngelLifeJiggy 268c0398e4 feat: add thinking token extraction (#798)
* feat: add thinking token tracking and historical analytics

- extractThinkingTokens(): separate thinking from output tokens
- TokenUsageTracker class for historical analytics
- Track: cache hit rate, most used model, requests per hour/day
- Analytics: average tokens per request, totals
- Add tests (7 passing)

PR 4B: Features 1.10 + 1.11

* refactor: extract thinking and analytics to separate files

- Create thinkingTokenExtractor.ts with ThinkingTokenAnalyzer
- Create tokenAnalytics.ts with TokenUsageTracker
- Add production-grade methods and tests
- Update test imports
2026-04-21 23:25:12 +08:00

106 lines
2.9 KiB
TypeScript

import { describe, expect, it } from 'bun:test'
import { ThinkingTokenAnalyzer } from './thinkingTokenExtractor.js'
describe('ThinkingTokenAnalyzer', () => {
describe('extract', () => {
it('extracts thinking and output separately', () => {
const message = {
type: 'assistant',
message: {
content: [
{ type: 'thinking', thinking: 'Let me think about this...' },
{ type: 'text', text: 'Here is my answer.' },
],
},
} as any
const result = ThinkingTokenAnalyzer.extract(message)
expect(result.thinking).toBeGreaterThan(0)
expect(result.output).toBeGreaterThan(0)
expect(result.total).toBe(result.thinking + result.output)
})
it('handles no thinking', () => {
const message = {
type: 'assistant',
message: {
content: [{ type: 'text', text: 'Hello world' }],
},
} as any
const result = ThinkingTokenAnalyzer.extract(message)
expect(result.thinking).toBe(0)
expect(result.output).toBeGreaterThan(0)
})
it('handles redacted thinking', () => {
const message = {
type: 'assistant',
message: {
content: [
{ type: 'redacted_thinking', data: '[thinking hidden]' },
{ type: 'text', text: 'Answer here.' },
],
},
} as any
const result = ThinkingTokenAnalyzer.extract(message)
expect(result.thinking).toBeGreaterThan(0)
expect(result.output).toBeGreaterThan(0)
})
})
describe('analyze', () => {
it('calculates percentages', () => {
const message = {
type: 'assistant',
message: {
content: [
{ type: 'thinking', thinking: 'Thinking1 Thinking2 Thinking3' },
{ type: 'text', text: 'Output1 Output2' },
],
},
} as any
const analysis = ThinkingTokenAnalyzer.analyze(message)
expect(analysis.hasThinking).toBe(true)
expect(analysis.thinkingPercentage).toBeGreaterThan(0)
expect(analysis.outputPercentage).toBeGreaterThan(0)
expect(analysis.reasoningComplexity).toBeTruthy()
})
})
describe('hasSignificantThinking', () => {
it('detects significant thinking', () => {
const message = {
type: 'assistant',
message: {
content: [
{ type: 'thinking', thinking: 'x'.repeat(500) },
{ type: 'text', text: 'short' },
],
},
} as any
expect(ThinkingTokenAnalyzer.hasSignificantThinking(message, 20)).toBe(true)
})
it('rejects minimal thinking', () => {
const message = {
type: 'assistant',
message: {
content: [
{ type: 'thinking', thinking: 'a' },
{ type: 'text', text: 'much longer output text here with more content' },
],
},
} as any
expect(ThinkingTokenAnalyzer.hasSignificantThinking(message, 20)).toBe(false)
})
})
})