mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-23 08:45:50 +00:00
style: 格式化 packages/@ant/ 下所有文件以通过 biome ci
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,5 @@
|
||||
import { describe, expect, test } from 'bun:test'
|
||||
import type {
|
||||
AssistantMessage,
|
||||
UserMessage,
|
||||
} from '../../../types/message.js'
|
||||
import type { AssistantMessage, UserMessage } from '../../../types/message.js'
|
||||
import { anthropicMessagesToGemini } from '../convertMessages.js'
|
||||
|
||||
function makeUserMsg(content: string | any[]): UserMessage {
|
||||
@@ -23,10 +20,9 @@ function makeAssistantMsg(content: string | any[]): AssistantMessage {
|
||||
|
||||
describe('anthropicMessagesToGemini', () => {
|
||||
test('converts system prompt to systemInstruction', () => {
|
||||
const result = anthropicMessagesToGemini(
|
||||
[makeUserMsg('hello')],
|
||||
['You are helpful.'] as any,
|
||||
)
|
||||
const result = anthropicMessagesToGemini([makeUserMsg('hello')], [
|
||||
'You are helpful.',
|
||||
] as any)
|
||||
|
||||
expect(result.systemInstruction).toEqual({
|
||||
parts: [{ text: 'You are helpful.' }],
|
||||
@@ -202,17 +198,19 @@ describe('anthropicMessagesToGemini', () => {
|
||||
|
||||
test('converts base64 image to inlineData', () => {
|
||||
const result = anthropicMessagesToGemini(
|
||||
[makeUserMsg([
|
||||
{ type: 'text', text: 'describe this' },
|
||||
{
|
||||
type: 'image',
|
||||
source: {
|
||||
type: 'base64',
|
||||
media_type: 'image/png',
|
||||
data: 'iVBORw0KGgo=',
|
||||
[
|
||||
makeUserMsg([
|
||||
{ type: 'text', text: 'describe this' },
|
||||
{
|
||||
type: 'image',
|
||||
source: {
|
||||
type: 'base64',
|
||||
media_type: 'image/png',
|
||||
data: 'iVBORw0KGgo=',
|
||||
},
|
||||
},
|
||||
},
|
||||
])],
|
||||
]),
|
||||
],
|
||||
[] as any,
|
||||
)
|
||||
expect(result.contents).toEqual([
|
||||
@@ -228,15 +226,17 @@ describe('anthropicMessagesToGemini', () => {
|
||||
|
||||
test('converts url image to text fallback', () => {
|
||||
const result = anthropicMessagesToGemini(
|
||||
[makeUserMsg([
|
||||
{
|
||||
type: 'image',
|
||||
source: {
|
||||
type: 'url',
|
||||
url: 'https://example.com/img.png',
|
||||
[
|
||||
makeUserMsg([
|
||||
{
|
||||
type: 'image',
|
||||
source: {
|
||||
type: 'url',
|
||||
url: 'https://example.com/img.png',
|
||||
},
|
||||
},
|
||||
},
|
||||
])],
|
||||
]),
|
||||
],
|
||||
[] as any,
|
||||
)
|
||||
expect(result.contents).toEqual([
|
||||
@@ -249,15 +249,17 @@ describe('anthropicMessagesToGemini', () => {
|
||||
|
||||
test('defaults to image/png when media_type is missing', () => {
|
||||
const result = anthropicMessagesToGemini(
|
||||
[makeUserMsg([
|
||||
{
|
||||
type: 'image',
|
||||
source: {
|
||||
type: 'base64',
|
||||
data: 'ABC123',
|
||||
[
|
||||
makeUserMsg([
|
||||
{
|
||||
type: 'image',
|
||||
source: {
|
||||
type: 'base64',
|
||||
data: 'ABC123',
|
||||
},
|
||||
},
|
||||
},
|
||||
])],
|
||||
]),
|
||||
],
|
||||
[] as any,
|
||||
)
|
||||
expect(result.contents[0].parts[0]).toEqual({
|
||||
|
||||
@@ -120,11 +120,11 @@ describe('anthropicToolChoiceToGemini', () => {
|
||||
})
|
||||
|
||||
test('maps explicit tool choice', () => {
|
||||
expect(
|
||||
anthropicToolChoiceToGemini({ type: 'tool', name: 'bash' }),
|
||||
).toEqual({
|
||||
mode: 'ANY',
|
||||
allowedFunctionNames: ['bash'],
|
||||
})
|
||||
expect(anthropicToolChoiceToGemini({ type: 'tool', name: 'bash' })).toEqual(
|
||||
{
|
||||
mode: 'ANY',
|
||||
allowedFunctionNames: ['bash'],
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -57,7 +57,8 @@ describe('adaptGeminiStreamToAnthropic', () => {
|
||||
|
||||
const textDeltas = events.filter(
|
||||
event =>
|
||||
event.type === 'content_block_delta' && event.delta.type === 'text_delta',
|
||||
event.type === 'content_block_delta' &&
|
||||
event.delta.type === 'text_delta',
|
||||
)
|
||||
|
||||
expect(events[0].type).toBe('message_start')
|
||||
@@ -92,7 +93,9 @@ describe('adaptGeminiStreamToAnthropic', () => {
|
||||
},
|
||||
])
|
||||
|
||||
const blockStart = events.find(event => event.type === 'content_block_start')
|
||||
const blockStart = events.find(
|
||||
event => event.type === 'content_block_start',
|
||||
)
|
||||
expect(blockStart.content_block.type).toBe('thinking')
|
||||
|
||||
const signatureDelta = events.find(
|
||||
@@ -125,7 +128,9 @@ describe('adaptGeminiStreamToAnthropic', () => {
|
||||
},
|
||||
])
|
||||
|
||||
const blockStart = events.find(event => event.type === 'content_block_start')
|
||||
const blockStart = events.find(
|
||||
event => event.type === 'content_block_start',
|
||||
)
|
||||
expect(blockStart.content_block.type).toBe('tool_use')
|
||||
expect(blockStart.content_block.name).toBe('bash')
|
||||
|
||||
|
||||
@@ -93,7 +93,10 @@ function convertInternalUserMessage(
|
||||
return {
|
||||
role: 'user',
|
||||
parts: content.flatMap(block =>
|
||||
convertUserContentBlockToGeminiParts(block as unknown as string | Record<string, unknown>, toolNamesById),
|
||||
convertUserContentBlockToGeminiParts(
|
||||
block as unknown as string | Record<string, unknown>,
|
||||
toolNamesById,
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -115,7 +118,8 @@ function convertUserContentBlockToGeminiParts(
|
||||
return [
|
||||
{
|
||||
functionResponse: {
|
||||
name: toolNamesById.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
|
||||
name:
|
||||
toolNamesById.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
|
||||
response: toolResultToResponseObject(toolResult),
|
||||
},
|
||||
},
|
||||
@@ -170,7 +174,9 @@ function convertInternalAssistantMessage(msg: AssistantMessage): GeminiContent {
|
||||
parts.push(
|
||||
...createTextGeminiParts(
|
||||
block.text,
|
||||
getGeminiThoughtSignature(block as unknown as Record<string, unknown>),
|
||||
getGeminiThoughtSignature(
|
||||
block as unknown as Record<string, unknown>,
|
||||
),
|
||||
),
|
||||
)
|
||||
continue
|
||||
@@ -194,8 +200,12 @@ function convertInternalAssistantMessage(msg: AssistantMessage): GeminiContent {
|
||||
name: toolUse.name,
|
||||
args: normalizeToolUseInput(toolUse.input),
|
||||
},
|
||||
...(getGeminiThoughtSignature(block as unknown as Record<string, unknown>) && {
|
||||
thoughtSignature: getGeminiThoughtSignature(block as unknown as Record<string, unknown>),
|
||||
...(getGeminiThoughtSignature(
|
||||
block as unknown as Record<string, unknown>,
|
||||
) && {
|
||||
thoughtSignature: getGeminiThoughtSignature(
|
||||
block as unknown as Record<string, unknown>,
|
||||
),
|
||||
}),
|
||||
})
|
||||
}
|
||||
@@ -255,12 +265,10 @@ function toolResultToResponseObject(
|
||||
block: BetaToolResultBlockParam,
|
||||
): Record<string, unknown> {
|
||||
const result = normalizeToolResultContent(block.content)
|
||||
if (
|
||||
result &&
|
||||
typeof result === 'object' &&
|
||||
!Array.isArray(result)
|
||||
) {
|
||||
return block.is_error ? { ...(result as Record<string, unknown>), is_error: true } : result as Record<string, unknown>
|
||||
if (result && typeof result === 'object' && !Array.isArray(result)) {
|
||||
return block.is_error
|
||||
? { ...(result as Record<string, unknown>), is_error: true }
|
||||
: (result as Record<string, unknown>)
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -299,7 +307,9 @@ function normalizeToolResultContent(content: unknown): unknown {
|
||||
return content ?? ''
|
||||
}
|
||||
|
||||
function getGeminiThoughtSignature(block: Record<string, unknown>): string | undefined {
|
||||
function getGeminiThoughtSignature(
|
||||
block: Record<string, unknown>,
|
||||
): string | undefined {
|
||||
const signature = block[GEMINI_THOUGHT_SIGNATURE_FIELD]
|
||||
return typeof signature === 'string' && signature.length > 0
|
||||
? signature
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import type { BetaToolUnion } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
|
||||
import type {
|
||||
GeminiFunctionCallingConfig,
|
||||
GeminiTool,
|
||||
} from './types.js'
|
||||
import type { GeminiFunctionCallingConfig, GeminiTool } from './types.js'
|
||||
|
||||
const GEMINI_JSON_SCHEMA_TYPES = new Set([
|
||||
'string',
|
||||
@@ -34,7 +31,9 @@ function normalizeGeminiJsonSchemaType(
|
||||
return undefined
|
||||
}
|
||||
|
||||
function inferGeminiJsonSchemaTypeFromValue(value: unknown): string | undefined {
|
||||
function inferGeminiJsonSchemaTypeFromValue(
|
||||
value: unknown,
|
||||
): string | undefined {
|
||||
if (value === null) return 'null'
|
||||
if (Array.isArray(value)) return 'array'
|
||||
if (typeof value === 'string') return 'string'
|
||||
@@ -97,9 +96,7 @@ function sanitizeGeminiJsonSchemaArray(
|
||||
return sanitized.length > 0 ? sanitized : undefined
|
||||
}
|
||||
|
||||
function sanitizeGeminiJsonSchema(
|
||||
schema: unknown,
|
||||
): Record<string, unknown> {
|
||||
function sanitizeGeminiJsonSchema(schema: unknown): Record<string, unknown> {
|
||||
if (!schema || typeof schema !== 'object' || Array.isArray(schema)) {
|
||||
return {}
|
||||
}
|
||||
@@ -236,17 +233,20 @@ export function anthropicToolsToGemini(tools: BetaToolUnion[]): GeminiTool[] {
|
||||
const functionDeclarations = tools
|
||||
.filter(tool => {
|
||||
const toolType = (tool as unknown as { type?: string }).type
|
||||
return tool.type === 'custom' || !('type' in tool) || toolType !== 'server'
|
||||
return (
|
||||
tool.type === 'custom' || !('type' in tool) || toolType !== 'server'
|
||||
)
|
||||
})
|
||||
.map(tool => {
|
||||
const anyTool = tool as unknown as Record<string, unknown>
|
||||
const name = (anyTool.name as string) || ''
|
||||
const description = (anyTool.description as string) || ''
|
||||
const inputSchema =
|
||||
(anyTool.input_schema as Record<string, unknown> | undefined) ?? {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
}
|
||||
const inputSchema = (anyTool.input_schema as
|
||||
| Record<string, unknown>
|
||||
| undefined) ?? {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
@@ -255,9 +255,7 @@ export function anthropicToolsToGemini(tools: BetaToolUnion[]): GeminiTool[] {
|
||||
}
|
||||
})
|
||||
|
||||
return functionDeclarations.length > 0
|
||||
? [{ functionDeclarations }]
|
||||
: []
|
||||
return functionDeclarations.length > 0 ? [{ functionDeclarations }] : []
|
||||
}
|
||||
|
||||
export function anthropicToolChoiceToGemini(
|
||||
|
||||
@@ -10,9 +10,8 @@ export async function* adaptGeminiStreamToAnthropic(
|
||||
let started = false
|
||||
let stopped = false
|
||||
let nextContentIndex = 0
|
||||
let openTextLikeBlock:
|
||||
| { index: number; type: 'text' | 'thinking' }
|
||||
| null = null
|
||||
let openTextLikeBlock: { index: number; type: 'text' | 'thinking' } | null =
|
||||
null
|
||||
let sawToolUse = false
|
||||
let finishReason: string | undefined
|
||||
let inputTokens = 0
|
||||
@@ -85,7 +84,10 @@ export async function* adaptGeminiStreamToAnthropic(
|
||||
} as BetaRawMessageStreamEvent
|
||||
}
|
||||
|
||||
if (part.functionCall.args && Object.keys(part.functionCall.args).length > 0) {
|
||||
if (
|
||||
part.functionCall.args &&
|
||||
Object.keys(part.functionCall.args).length > 0
|
||||
) {
|
||||
yield {
|
||||
type: 'content_block_delta',
|
||||
index: toolIndex,
|
||||
@@ -213,9 +215,7 @@ export async function* adaptGeminiStreamToAnthropic(
|
||||
}
|
||||
}
|
||||
|
||||
function getTextLikeBlockType(
|
||||
part: GeminiPart,
|
||||
): 'text' | 'thinking' | null {
|
||||
function getTextLikeBlockType(part: GeminiPart): 'text' | 'thinking' | null {
|
||||
if (typeof part.text !== 'string') {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -33,11 +33,14 @@ describe('resolveGrokModel', () => {
|
||||
})
|
||||
|
||||
test('maps haiku models to grok-3-mini-fast', () => {
|
||||
expect(resolveGrokModel('claude-haiku-4-5-20251001')).toBe('grok-3-mini-fast')
|
||||
expect(resolveGrokModel('claude-haiku-4-5-20251001')).toBe(
|
||||
'grok-3-mini-fast',
|
||||
)
|
||||
})
|
||||
|
||||
test('GROK_MODEL_MAP overrides family mapping', () => {
|
||||
process.env.GROK_MODEL_MAP = '{"opus":"grok-4","sonnet":"grok-3","haiku":"grok-mini"}'
|
||||
process.env.GROK_MODEL_MAP =
|
||||
'{"opus":"grok-4","sonnet":"grok-3","haiku":"grok-mini"}'
|
||||
expect(resolveGrokModel('claude-opus-4-6')).toBe('grok-4')
|
||||
expect(resolveGrokModel('claude-sonnet-4-6')).toBe('grok-3')
|
||||
expect(resolveGrokModel('claude-haiku-4-5-20251001')).toBe('grok-mini')
|
||||
@@ -62,6 +65,8 @@ describe('resolveGrokModel', () => {
|
||||
})
|
||||
|
||||
test('falls back to family default for unlisted model', () => {
|
||||
expect(resolveGrokModel('claude-opus-99-20300101')).toBe('grok-4.20-reasoning')
|
||||
expect(resolveGrokModel('claude-opus-99-20300101')).toBe(
|
||||
'grok-4.20-reasoning',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user