Merge branch 'migrate/v6' into migrate/v6-2

This commit is contained in:
suyao 2026-01-01 23:46:45 +08:00
commit 9024eea727
No known key found for this signature in database
5 changed files with 11 additions and 94 deletions

View File

@ -21,16 +21,11 @@ vi.mock('../core/ProviderInstanceRegistry', () => ({
DEFAULT_SEPARATOR: '|'
}))
vi.mock('ai', async (importOriginal) => {
// biome-ignore lint/style/consistent-type-imports: Mock setup requires typeof import
const actual = await importOriginal<typeof AiModule>()
return {
jsonSchema: actual.jsonSchema, // Keep real jsonSchema for test utilities
customProvider: vi.fn((config) => config.fallbackProvider),
wrapProvider: vi.fn((config) => config.provider)
}
})
vi.mock('ai', () => ({
customProvider: vi.fn((config) => config.fallbackProvider),
wrapProvider: vi.fn((config) => config.provider),
jsonSchema: vi.fn((schema) => schema)
}))
describe('HubProvider', () => {
let mockOpenAIProvider: ProviderV3

View File

@ -10,6 +10,8 @@ import { RuntimeExecutor } from '../executor'
// Mock dependencies
vi.mock('ai', () => ({
experimental_generateImage: vi.fn(),
generateImage: vi.fn(),
jsonSchema: vi.fn((schema) => schema),
NoImageGeneratedError: class NoImageGeneratedError extends Error {
static isInstance = vi.fn()
constructor() {

View File

@ -1,80 +0,0 @@
/**
* Branded Types for type-safe IDs
*
* Branded types prevent accidental misuse of primitive types (like string)
* by adding compile-time type safety without runtime overhead.
*
* @example
* ```typescript
* const modelId = ModelId('gpt-4') // ModelId type
* const requestId = RequestId('req-123') // RequestId type
*
* function processModel(id: ModelId) { ... }
* processModel(requestId) // ❌ Compile error - type mismatch
* ```
*/
/**
* Brand helper type
*/
type Brand<K, T> = K & { readonly __brand: T }
/**
* Model ID branded type
* Represents a unique model identifier
*/
export type ModelId = Brand<string, 'ModelId'>
/**
* Request ID branded type
* Represents a unique request identifier for tracing
*/
export type RequestId = Brand<string, 'RequestId'>
/**
* Provider ID branded type
* Represents a provider identifier (e.g., 'openai', 'anthropic')
*/
export type ProviderId = Brand<string, 'ProviderId'>
/**
* Create a ModelId from a string
* @param id - The model identifier string
* @returns Branded ModelId
*/
export const ModelId = (id: string): ModelId => id as ModelId
/**
* Create a RequestId from a string
* @param id - The request identifier string
* @returns Branded RequestId
*/
export const RequestId = (id: string): RequestId => id as RequestId
/**
* Create a ProviderId from a string
* @param id - The provider identifier string
* @returns Branded ProviderId
*/
export const ProviderId = (id: string): ProviderId => id as ProviderId
/**
* Type guard to check if a string is a valid ModelId
*/
export const isModelId = (value: unknown): value is ModelId => {
return typeof value === 'string' && value.length > 0
}
/**
* Type guard to check if a string is a valid RequestId
*/
export const isRequestId = (value: unknown): value is RequestId => {
return typeof value === 'string' && value.length > 0
}
/**
* Type guard to check if a string is a valid ProviderId
*/
export const isProviderId = (value: unknown): value is ProviderId => {
return typeof value === 'string' && value.length > 0
}

View File

@ -181,7 +181,7 @@ describe('Claude → AiSDK transform', () => {
{ type: 'finish-step' }
>
expect(finishStep.finishReason).toBe('tool-calls')
expect(finishStep.usage).toEqual({ inputTokens: 1, outputTokens: 5, totalTokens: 6 })
expect(finishStep.usage).toMatchObject({ inputTokens: 1, outputTokens: 5, totalTokens: 6 })
const toolResult = parts.find((part) => part.type === 'tool-result') as Extract<
(typeof parts)[number],
@ -408,7 +408,7 @@ describe('Claude → AiSDK transform', () => {
{ type: 'finish-step' }
>
expect(finishStep.finishReason).toBe('stop')
expect(finishStep.usage).toEqual({ inputTokens: 2, outputTokens: 4, totalTokens: 6 })
expect(finishStep.usage).toMatchObject({ inputTokens: 2, outputTokens: 4, totalTokens: 6 })
})
it('emits fallback text when Claude sends a snapshot instead of deltas', () => {
@ -490,7 +490,7 @@ describe('Claude → AiSDK transform', () => {
(typeof parts)[number],
{ type: 'finish-step' }
>
expect(finish.usage).toEqual({ inputTokens: 3, outputTokens: 7, totalTokens: 10 })
expect(finish.usage).toMatchObject({ inputTokens: 3, outputTokens: 7, totalTokens: 10 })
expect(finish.finishReason).toBe('stop')
})
})

View File

@ -92,7 +92,7 @@ export function convertClaudeCodeUsage(usage: ClaudeCodeUsage): LanguageModelUsa
return {
inputTokens,
outputTokens,
totalTokens: inputTokens + cacheWrite + cacheRead,
totalTokens: inputTokens + outputTokens,
inputTokenDetails: {
noCacheTokens: inputTokens,
cacheReadTokens: cacheRead,