feat: integrate image generation capabilities and enhance testing framework

- Added support for image generation in the `RuntimeExecutor` with a new `generateImage` method.
- Updated `aiCore` package to include `vitest` for testing, with new test scripts added.
- Enhanced type definitions to accommodate image model handling in plugins.
- Introduced new methods for resolving and executing image generation with plugins.
- Updated package dependencies in `package.json` to include `vitest` and ensure compatibility with new features.
This commit is contained in:
suyao
2025-08-01 10:45:31 +08:00
parent 7216e9943c
commit ecc08bd3f7
12 changed files with 1873 additions and 17 deletions
+157 -2
View File
@@ -12,6 +12,7 @@ import {
AiCore,
AiPlugin,
createExecutor,
generateImage,
ProviderConfigFactory,
type ProviderId,
type ProviderSettingsMap,
@@ -22,6 +23,7 @@ import { isDedicatedImageGenerationModel } from '@renderer/config/models'
import { createVertexProvider, isVertexAIConfigured, isVertexProvider } from '@renderer/hooks/useVertexAI'
import { getProviderByModel } from '@renderer/services/AssistantService'
import type { GenerateImageParams, Model, Provider } from '@renderer/types'
import { ChunkType } from '@renderer/types/chunk'
import { formatApiHost } from '@renderer/utils/api'
import { cloneDeep } from 'lodash'
@@ -127,9 +129,11 @@ function isModernSdkSupported(provider: Provider, model?: Model): boolean {
return false
}
// 检查是否为图像生成模型(暂时不支持)
// 图像生成模型现在支持新的 AI SDK
// (但需要确保 provider 是支持的
if (model && isDedicatedImageGenerationModel(model)) {
return false
return true
}
return true
@@ -211,6 +215,12 @@ export default class ModernAiProvider {
middlewareConfig: AiSdkMiddlewareConfig
): Promise<CompletionsResult> {
console.log('completions', modelId, params, middlewareConfig)
// 检查是否为图像生成模型
if (middlewareConfig.model && isDedicatedImageGenerationModel(middlewareConfig.model)) {
return await this.modernImageGeneration(modelId, params, middlewareConfig)
}
return await this.modernCompletions(modelId, params, middlewareConfig)
}
@@ -271,6 +281,109 @@ export default class ModernAiProvider {
// }
}
/**
* 使用现代化 AI SDK 的图像生成实现,支持流式输出
*/
private async modernImageGeneration(
modelId: string,
params: StreamTextParams,
middlewareConfig: AiSdkMiddlewareConfig
): Promise<CompletionsResult> {
const { onChunk } = middlewareConfig
try {
// 检查 messages 是否存在
if (!params.messages || params.messages.length === 0) {
throw new Error('No messages provided for image generation.')
}
// 从最后一条用户消息中提取 prompt
const lastUserMessage = params.messages.findLast((m) => m.role === 'user')
if (!lastUserMessage) {
throw new Error('No user message found for image generation.')
}
// 直接使用消息内容,避免类型转换问题
const prompt =
typeof lastUserMessage.content === 'string'
? lastUserMessage.content
: lastUserMessage.content?.map((part) => ('text' in part ? part.text : '')).join('') || ''
if (!prompt) {
throw new Error('No prompt found in user message.')
}
// 发送图像生成开始事件
if (onChunk) {
onChunk({ type: ChunkType.IMAGE_CREATED })
}
const startTime = Date.now()
// 构建图像生成参数
const imageParams = {
prompt,
size: '1024x1024' as `${number}x${number}`, // 默认尺寸,使用正确的类型
n: 1,
...(params.abortSignal && { abortSignal: params.abortSignal })
}
// 调用新 AI SDK 的图像生成功能
const result = await generateImage(this.config.providerId, this.config.options, modelId, imageParams)
// 转换结果格式
const images: string[] = []
const imageType: 'url' | 'base64' = 'base64'
if (result.images) {
for (const image of result.images) {
if ('base64' in image && image.base64) {
images.push(`data:image/png;base64,${image.base64}`)
}
}
}
// 发送图像生成完成事件
if (onChunk && images.length > 0) {
onChunk({
type: ChunkType.IMAGE_COMPLETE,
image: { type: imageType, images }
})
}
// 发送响应完成事件
if (onChunk) {
const usage = {
prompt_tokens: 0,
completion_tokens: 0,
total_tokens: 0
}
onChunk({
type: ChunkType.LLM_RESPONSE_COMPLETE,
response: {
usage,
metrics: {
completion_tokens: usage.completion_tokens,
time_first_token_millsec: 0,
time_completion_millsec: Date.now() - startTime
}
}
})
}
return {
getText: () => '' // 图像生成不返回文本
}
} catch (error) {
// 发送错误事件
if (onChunk) {
onChunk({ type: ChunkType.ERROR, error: error as any })
}
throw error
}
}
// 代理其他方法到原有实现
public async models() {
return this.legacyProvider.models()
@@ -281,9 +394,51 @@ export default class ModernAiProvider {
}
public async generateImage(params: GenerateImageParams): Promise<string[]> {
// 如果支持新的 AI SDK,使用现代化实现
if (isModernSdkSupported(this.actualProvider)) {
try {
const result = await this.modernGenerateImage(params)
return result
} catch (error) {
console.warn('Modern AI SDK generateImage failed, falling back to legacy:', error)
// fallback 到传统实现
return this.legacyProvider.generateImage(params)
}
}
// 直接使用传统实现
return this.legacyProvider.generateImage(params)
}
/**
* 使用现代化 AI SDK 的图像生成实现
*/
private async modernGenerateImage(params: GenerateImageParams): Promise<string[]> {
const { model, prompt, imageSize, batchSize, signal } = params
// 转换参数格式
const aiSdkParams = {
prompt,
size: (imageSize || '1024x1024') as `${number}x${number}`,
n: batchSize || 1,
...(signal && { abortSignal: signal })
}
const result = await generateImage(this.config.providerId, this.config.options, model, aiSdkParams)
// 转换结果格式
const images: string[] = []
if (result.images) {
for (const image of result.images) {
if ('base64' in image && image.base64) {
images.push(`data:image/png;base64,${image.base64}`)
}
}
}
return images
}
public getBaseURL(): string {
return this.legacyProvider.getBaseURL()
}