mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-22 08:15:53 +00:00
style: 格式化 packages/@ant/ 下所有文件以通过 biome ci
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -131,7 +131,13 @@ describe('anthropicMessagesToOpenAI', () => {
|
||||
],
|
||||
[] as any,
|
||||
)
|
||||
expect(result).toEqual([{ role: 'assistant', content: 'visible response', reasoning_content: 'internal thoughts...' }] as any)
|
||||
expect(result).toEqual([
|
||||
{
|
||||
role: 'assistant',
|
||||
content: 'visible response',
|
||||
reasoning_content: 'internal thoughts...',
|
||||
},
|
||||
] as any)
|
||||
})
|
||||
|
||||
test('handles full conversation with tools', () => {
|
||||
@@ -487,10 +493,19 @@ describe('DeepSeek thinking mode (enableThinking)', () => {
|
||||
[
|
||||
makeUserMsg('run ls'),
|
||||
makeAssistantMsg([
|
||||
{ type: 'tool_use' as const, id: 'toolu_1', name: 'bash', input: { command: 'ls' } },
|
||||
{
|
||||
type: 'tool_use' as const,
|
||||
id: 'toolu_1',
|
||||
name: 'bash',
|
||||
input: { command: 'ls' },
|
||||
},
|
||||
]),
|
||||
makeUserMsg([
|
||||
{ type: 'tool_result' as const, tool_use_id: 'toolu_1', content: 'file.txt' },
|
||||
{
|
||||
type: 'tool_result' as const,
|
||||
tool_use_id: 'toolu_1',
|
||||
content: 'file.txt',
|
||||
},
|
||||
{ type: 'text' as const, text: 'looks good' },
|
||||
]),
|
||||
],
|
||||
@@ -499,7 +514,10 @@ describe('DeepSeek thinking mode (enableThinking)', () => {
|
||||
// Find the tool message and the user text message
|
||||
const toolIdx = result.findIndex(m => m.role === 'tool')
|
||||
const userTextIdx = result.findIndex(
|
||||
m => m.role === 'user' && typeof m.content === 'string' && m.content.includes('looks good'),
|
||||
m =>
|
||||
m.role === 'user' &&
|
||||
typeof m.content === 'string' &&
|
||||
m.content.includes('looks good'),
|
||||
)
|
||||
expect(toolIdx).toBeGreaterThanOrEqual(0)
|
||||
expect(userTextIdx).toBeGreaterThanOrEqual(0)
|
||||
@@ -512,15 +530,26 @@ describe('DeepSeek thinking mode (enableThinking)', () => {
|
||||
[
|
||||
makeUserMsg('do something'),
|
||||
makeAssistantMsg([
|
||||
{ type: 'tool_use' as const, id: 'toolu_2', name: 'bash', input: { command: 'pwd' } },
|
||||
{
|
||||
type: 'tool_use' as const,
|
||||
id: 'toolu_2',
|
||||
name: 'bash',
|
||||
input: { command: 'pwd' },
|
||||
},
|
||||
]),
|
||||
makeUserMsg([
|
||||
{ type: 'tool_result' as const, tool_use_id: 'toolu_2', content: '/home/user' },
|
||||
{
|
||||
type: 'tool_result' as const,
|
||||
tool_use_id: 'toolu_2',
|
||||
content: '/home/user',
|
||||
},
|
||||
]),
|
||||
],
|
||||
[] as any,
|
||||
)
|
||||
const assistantIdx = result.findIndex(m => m.role === 'assistant' && (m as any).tool_calls)
|
||||
const assistantIdx = result.findIndex(
|
||||
m => m.role === 'assistant' && (m as any).tool_calls,
|
||||
)
|
||||
const toolIdx = result.findIndex(m => m.role === 'tool')
|
||||
expect(assistantIdx).toBeGreaterThanOrEqual(0)
|
||||
expect(toolIdx).toBe(assistantIdx + 1)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { describe, expect, test } from 'bun:test'
|
||||
import { anthropicToolsToOpenAI, anthropicToolChoiceToOpenAI } from '../openaiConvertTools.js'
|
||||
import {
|
||||
anthropicToolsToOpenAI,
|
||||
anthropicToolChoiceToOpenAI,
|
||||
} from '../openaiConvertTools.js'
|
||||
|
||||
describe('anthropicToolsToOpenAI', () => {
|
||||
test('converts basic tool', () => {
|
||||
|
||||
@@ -3,7 +3,9 @@ import type { ChatCompletionChunk } from 'openai/resources/chat/completions/comp
|
||||
import { adaptOpenAIStreamToAnthropic } from '../openaiStreamAdapter.js'
|
||||
|
||||
/** Helper to create a mock async iterable from chunk array */
|
||||
function mockStream(chunks: ChatCompletionChunk[]): AsyncIterable<ChatCompletionChunk> {
|
||||
function mockStream(
|
||||
chunks: ChatCompletionChunk[],
|
||||
): AsyncIterable<ChatCompletionChunk> {
|
||||
return {
|
||||
[Symbol.asyncIterator]() {
|
||||
let i = 0
|
||||
@@ -18,7 +20,9 @@ function mockStream(chunks: ChatCompletionChunk[]): AsyncIterable<ChatCompletion
|
||||
}
|
||||
|
||||
/** Create a minimal ChatCompletionChunk */
|
||||
function makeChunk(overrides: Partial<ChatCompletionChunk> & any = {}): ChatCompletionChunk {
|
||||
function makeChunk(
|
||||
overrides: Partial<ChatCompletionChunk> & any = {},
|
||||
): ChatCompletionChunk {
|
||||
return {
|
||||
id: 'chatcmpl-test',
|
||||
object: 'chat.completion.chunk',
|
||||
@@ -32,7 +36,10 @@ function makeChunk(overrides: Partial<ChatCompletionChunk> & any = {}): ChatComp
|
||||
/** Collect all emitted Anthropic events from the stream adapter for assertion */
|
||||
async function collectEvents(chunks: ChatCompletionChunk[]) {
|
||||
const events: any[] = []
|
||||
for await (const event of adaptOpenAIStreamToAnthropic(mockStream(chunks), 'gpt-4o')) {
|
||||
for await (const event of adaptOpenAIStreamToAnthropic(
|
||||
mockStream(chunks),
|
||||
'gpt-4o',
|
||||
)) {
|
||||
events.push(event)
|
||||
}
|
||||
return events
|
||||
@@ -42,25 +49,31 @@ describe('adaptOpenAIStreamToAnthropic', () => {
|
||||
test('emits message_start on first chunk', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { role: 'assistant', content: '' },
|
||||
finish_reason: null,
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { role: 'assistant', content: '' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { content: 'hello' },
|
||||
finish_reason: null,
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { content: 'hello' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {},
|
||||
finish_reason: 'stop',
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {},
|
||||
finish_reason: 'stop',
|
||||
},
|
||||
],
|
||||
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
|
||||
}),
|
||||
])
|
||||
@@ -73,10 +86,14 @@ describe('adaptOpenAIStreamToAnthropic', () => {
|
||||
test('converts text content stream', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: { content: 'Hello' }, finish_reason: null }],
|
||||
choices: [
|
||||
{ index: 0, delta: { content: 'Hello' }, finish_reason: null },
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: { content: ' world' }, finish_reason: null }],
|
||||
choices: [
|
||||
{ index: 0, delta: { content: ' world' }, finish_reason: null },
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
||||
@@ -91,7 +108,9 @@ describe('adaptOpenAIStreamToAnthropic', () => {
|
||||
expect(types).toContain('message_delta')
|
||||
expect(types).toContain('message_stop')
|
||||
|
||||
const textDeltas = events.filter(e => e.type === 'content_block_delta') as any[]
|
||||
const textDeltas = events.filter(
|
||||
e => e.type === 'content_block_delta',
|
||||
) as any[]
|
||||
expect(textDeltas[0].delta.text).toBe('Hello')
|
||||
expect(textDeltas[1].delta.text).toBe(' world')
|
||||
})
|
||||
@@ -99,42 +118,54 @@ describe('adaptOpenAIStreamToAnthropic', () => {
|
||||
test('converts tool_calls stream', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [{
|
||||
index: 0,
|
||||
id: 'call_abc',
|
||||
type: 'function',
|
||||
function: { name: 'bash', arguments: '' },
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_abc',
|
||||
type: 'function',
|
||||
function: { name: 'bash', arguments: '' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
finish_reason: null,
|
||||
}],
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [{
|
||||
index: 0,
|
||||
function: { arguments: '{"comm' },
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
function: { arguments: '{"comm' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
finish_reason: null,
|
||||
}],
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [{
|
||||
index: 0,
|
||||
function: { arguments: 'and":"ls"}' },
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
function: { arguments: 'and":"ls"}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
finish_reason: null,
|
||||
}],
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'tool_calls' }],
|
||||
@@ -146,7 +177,8 @@ describe('adaptOpenAIStreamToAnthropic', () => {
|
||||
expect(blockStart.content_block.name).toBe('bash')
|
||||
|
||||
const jsonDeltas = events.filter(
|
||||
e => e.type === 'content_block_delta' && e.delta.type === 'input_json_delta',
|
||||
e =>
|
||||
e.type === 'content_block_delta' && e.delta.type === 'input_json_delta',
|
||||
) as any[]
|
||||
const fullArgs = jsonDeltas.map(d => d.delta.partial_json).join('')
|
||||
expect(fullArgs).toBe('{"command":"ls"}')
|
||||
@@ -171,13 +203,21 @@ describe('adaptOpenAIStreamToAnthropic', () => {
|
||||
// return finish_reason "stop" when they actually made tool calls.
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [{ index: 0, id: 'call_1', function: { name: 'bash', arguments: '{"cmd":"ls"}' } }],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_1',
|
||||
function: { name: 'bash', arguments: '{"cmd":"ls"}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
finish_reason: null,
|
||||
}],
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
||||
@@ -191,13 +231,21 @@ describe('adaptOpenAIStreamToAnthropic', () => {
|
||||
test('maps finish_reason tool_calls to tool_use', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [{ index: 0, id: 'call_1', function: { name: 'bash', arguments: '{}' } }],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_1',
|
||||
function: { name: 'bash', arguments: '{}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
finish_reason: null,
|
||||
}],
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'tool_calls' }],
|
||||
@@ -211,7 +259,9 @@ describe('adaptOpenAIStreamToAnthropic', () => {
|
||||
test('maps finish_reason length to max_tokens', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: { content: 'truncated' }, finish_reason: null }],
|
||||
choices: [
|
||||
{ index: 0, delta: { content: 'truncated' }, finish_reason: null },
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'length' }],
|
||||
@@ -225,23 +275,35 @@ describe('adaptOpenAIStreamToAnthropic', () => {
|
||||
test('handles mixed text and tool_calls', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: { content: 'Thinking...' }, finish_reason: null }],
|
||||
choices: [
|
||||
{ index: 0, delta: { content: 'Thinking...' }, finish_reason: null },
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [{ index: 0, id: 'call_1', function: { name: 'grep', arguments: '{"p":"test"}' } }],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_1',
|
||||
function: { name: 'grep', arguments: '{"p":"test"}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
finish_reason: null,
|
||||
}],
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'tool_calls' }],
|
||||
}),
|
||||
])
|
||||
|
||||
const blockStarts = events.filter(e => e.type === 'content_block_start') as any[]
|
||||
const blockStarts = events.filter(
|
||||
e => e.type === 'content_block_start',
|
||||
) as any[]
|
||||
expect(blockStarts.length).toBe(2)
|
||||
expect(blockStarts[0].content_block.type).toBe('text')
|
||||
expect(blockStarts[1].content_block.type).toBe('tool_use')
|
||||
@@ -252,18 +314,22 @@ describe('thinking support (reasoning_content)', () => {
|
||||
test('converts reasoning_content to thinking block', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { reasoning_content: 'Let me analyze this...' },
|
||||
finish_reason: null,
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { reasoning_content: 'Let me analyze this...' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { reasoning_content: ' step by step.' },
|
||||
finish_reason: null,
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { reasoning_content: ' step by step.' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
||||
@@ -277,7 +343,8 @@ describe('thinking support (reasoning_content)', () => {
|
||||
|
||||
// Should have thinking_delta events
|
||||
const thinkingDeltas = events.filter(
|
||||
e => e.type === 'content_block_delta' && e.delta.type === 'thinking_delta',
|
||||
e =>
|
||||
e.type === 'content_block_delta' && e.delta.type === 'thinking_delta',
|
||||
) as any[]
|
||||
expect(thinkingDeltas.length).toBe(2)
|
||||
expect(thinkingDeltas[0].delta.thinking).toBe('Let me analyze this...')
|
||||
@@ -287,18 +354,22 @@ describe('thinking support (reasoning_content)', () => {
|
||||
test('converts reasoning then content (DeepSeek-style)', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { reasoning_content: 'Thinking about the answer...' },
|
||||
finish_reason: null,
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { reasoning_content: 'Thinking about the answer...' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { content: 'Here is my answer.' },
|
||||
finish_reason: null,
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { content: 'Here is my answer.' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
||||
@@ -306,13 +377,17 @@ describe('thinking support (reasoning_content)', () => {
|
||||
])
|
||||
|
||||
// Should have two content blocks: thinking + text
|
||||
const blockStarts = events.filter(e => e.type === 'content_block_start') as any[]
|
||||
const blockStarts = events.filter(
|
||||
e => e.type === 'content_block_start',
|
||||
) as any[]
|
||||
expect(blockStarts.length).toBe(2)
|
||||
expect(blockStarts[0].content_block.type).toBe('thinking')
|
||||
expect(blockStarts[1].content_block.type).toBe('text')
|
||||
|
||||
// Thinking block should be closed before text block starts
|
||||
const blockStops = events.filter(e => e.type === 'content_block_stop') as any[]
|
||||
const blockStops = events.filter(
|
||||
e => e.type === 'content_block_stop',
|
||||
) as any[]
|
||||
expect(blockStops[0].index).toBe(0) // thinking block closed at index 0
|
||||
expect(blockStarts[1].index).toBe(1) // text block starts at index 1
|
||||
|
||||
@@ -326,27 +401,39 @@ describe('thinking support (reasoning_content)', () => {
|
||||
test('handles reasoning then tool_calls', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { reasoning_content: 'I need to run a command.' },
|
||||
finish_reason: null,
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { reasoning_content: 'I need to run a command.' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [{ index: 0, id: 'call_1', function: { name: 'bash', arguments: '{"c":"ls"}' } }],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_1',
|
||||
function: { name: 'bash', arguments: '{"c":"ls"}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
finish_reason: null,
|
||||
}],
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'tool_calls' }],
|
||||
}),
|
||||
])
|
||||
|
||||
const blockStarts = events.filter(e => e.type === 'content_block_start') as any[]
|
||||
const blockStarts = events.filter(
|
||||
e => e.type === 'content_block_start',
|
||||
) as any[]
|
||||
expect(blockStarts.length).toBe(2)
|
||||
expect(blockStarts[0].content_block.type).toBe('thinking')
|
||||
expect(blockStarts[1].content_block.type).toBe('tool_use')
|
||||
@@ -355,25 +442,31 @@ describe('thinking support (reasoning_content)', () => {
|
||||
test('thinking block index is 0, text block index is 1', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { reasoning_content: 'reason' },
|
||||
finish_reason: null,
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { reasoning_content: 'reason' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { content: 'answer' },
|
||||
finish_reason: null,
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { content: 'answer' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
||||
}),
|
||||
])
|
||||
|
||||
const blockStarts = events.filter(e => e.type === 'content_block_start') as any[]
|
||||
const blockStarts = events.filter(
|
||||
e => e.type === 'content_block_start',
|
||||
) as any[]
|
||||
expect(blockStarts[0].index).toBe(0)
|
||||
expect(blockStarts[1].index).toBe(1)
|
||||
})
|
||||
@@ -383,11 +476,13 @@ describe('prompt caching support', () => {
|
||||
test('maps cached_tokens to cache_read_input_tokens', async () => {
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: { content: 'hi' },
|
||||
finish_reason: null,
|
||||
}],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: { content: 'hi' },
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: 1000,
|
||||
completion_tokens: 0,
|
||||
@@ -463,7 +558,9 @@ describe('prompt caching support', () => {
|
||||
// emitted before the trailing chunk and always has input_tokens=0.
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: { content: 'hello' }, finish_reason: null }],
|
||||
choices: [
|
||||
{ index: 0, delta: { content: 'hello' }, finish_reason: null },
|
||||
],
|
||||
}),
|
||||
// finish_reason chunk — usage not yet available
|
||||
makeChunk({
|
||||
@@ -493,14 +590,20 @@ describe('prompt caching support', () => {
|
||||
// the autocompact threshold (~33k), so compaction never fires.
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: { content: 'answer' }, finish_reason: null }],
|
||||
choices: [
|
||||
{ index: 0, delta: { content: 'answer' }, finish_reason: null },
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [],
|
||||
usage: { prompt_tokens: 800, completion_tokens: 200, total_tokens: 1000 },
|
||||
usage: {
|
||||
prompt_tokens: 800,
|
||||
completion_tokens: 200,
|
||||
total_tokens: 1000,
|
||||
},
|
||||
}),
|
||||
])
|
||||
|
||||
@@ -514,13 +617,21 @@ describe('prompt caching support', () => {
|
||||
// when the model made tool calls and usage arrives in a trailing chunk.
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [{ index: 0, id: 'call_x', function: { name: 'bash', arguments: '{"cmd":"ls"}' } }],
|
||||
choices: [
|
||||
{
|
||||
index: 0,
|
||||
delta: {
|
||||
tool_calls: [
|
||||
{
|
||||
index: 0,
|
||||
id: 'call_x',
|
||||
function: { name: 'bash', arguments: '{"cmd":"ls"}' },
|
||||
},
|
||||
],
|
||||
},
|
||||
finish_reason: null,
|
||||
},
|
||||
finish_reason: null,
|
||||
}],
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'tool_calls' }],
|
||||
@@ -540,9 +651,14 @@ describe('prompt caching support', () => {
|
||||
test('message_delta always comes before message_stop', async () => {
|
||||
// Verifies event ordering is preserved after deferring to post-loop emission.
|
||||
const events = await collectEvents([
|
||||
makeChunk({ choices: [{ index: 0, delta: { content: 'x' }, finish_reason: null }] }),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: { content: 'x' }, finish_reason: null }],
|
||||
}),
|
||||
makeChunk({ choices: [{ index: 0, delta: {}, finish_reason: 'stop' }] }),
|
||||
makeChunk({ choices: [], usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 } }),
|
||||
makeChunk({
|
||||
choices: [],
|
||||
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
|
||||
}),
|
||||
])
|
||||
|
||||
const types = events.map(e => e.type)
|
||||
@@ -561,7 +677,9 @@ describe('prompt caching support', () => {
|
||||
// queryModelOpenAI's spread — even though cachedTokens was captured internally.
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: { content: 'answer' }, finish_reason: null }],
|
||||
choices: [
|
||||
{ index: 0, delta: { content: 'answer' }, finish_reason: null },
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
||||
@@ -638,7 +756,9 @@ describe('prompt caching support', () => {
|
||||
// Some endpoints send usage in the finish_reason chunk instead of a trailing chunk.
|
||||
const events = await collectEvents([
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: { content: 'result' }, finish_reason: null }],
|
||||
choices: [
|
||||
{ index: 0, delta: { content: 'result' }, finish_reason: null },
|
||||
],
|
||||
}),
|
||||
makeChunk({
|
||||
choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
|
||||
|
||||
Reference in New Issue
Block a user