From 5db5d69cec9c623941cc4b49d160879d7d59ad22 Mon Sep 17 00:00:00 2001 From: icarus Date: Sun, 12 Oct 2025 00:52:09 +0800 Subject: [PATCH] feat(video): add retrieve video functionality for OpenAI Implement video retrieval endpoint and integrate it through the API client stack. This enables fetching existing video resources from OpenAI's API. --- src/renderer/src/aiCore/index_new.ts | 9 ++++++- .../clients/openai/OpenAIResponseAPIClient.ts | 7 ++++- src/renderer/src/aiCore/legacy/index.ts | 20 ++++++++++++-- src/renderer/src/services/ApiService.ts | 7 ++++- src/renderer/src/types/video.ts | 26 +++++++++++++++++++ 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/renderer/src/aiCore/index_new.ts b/src/renderer/src/aiCore/index_new.ts index d91b8bda24..b1f0819891 100644 --- a/src/renderer/src/aiCore/index_new.ts +++ b/src/renderer/src/aiCore/index_new.ts @@ -14,7 +14,7 @@ import { addSpan, endSpan } from '@renderer/services/SpanManagerService' import { StartSpanParams } from '@renderer/trace/types/ModelSpanEntity' import type { Assistant, GenerateImageParams, Model, Provider } from '@renderer/types' import type { AiSdkModel, StreamTextParams } from '@renderer/types/aiCoreTypes' -import { CreateVideoParams, CreateVideoResult } from '@renderer/types/video' +import { CreateVideoParams, CreateVideoResult, RetrieveVideoParams, RetrieveVideoResult } from '@renderer/types/video' import { buildClaudeCodeSystemModelMessage } from '@shared/anthropic' import { type ImageModel, type LanguageModel, type Provider as AiSdkProvider, wrapLanguageModel } from 'ai' @@ -508,6 +508,13 @@ export default class ModernAiProvider { return this.legacyProvider.createVideo(params) } + /** + * We manually implement this method before aisdk supports it well + */ + public async retrieveVideo(params: RetrieveVideoParams): Promise { + return this.legacyProvider.retrieveVideo(params) + } + public getBaseURL(): string { return this.legacyProvider.getBaseURL() } diff --git a/src/renderer/src/aiCore/legacy/clients/openai/OpenAIResponseAPIClient.ts b/src/renderer/src/aiCore/legacy/clients/openai/OpenAIResponseAPIClient.ts index 1a1a2a9ecb..b462e184ab 100644 --- a/src/renderer/src/aiCore/legacy/clients/openai/OpenAIResponseAPIClient.ts +++ b/src/renderer/src/aiCore/legacy/clients/openai/OpenAIResponseAPIClient.ts @@ -36,7 +36,7 @@ import { OpenAIResponseSdkTool, OpenAIResponseSdkToolCall } from '@renderer/types/sdk' -import { CreateVideoParams } from '@renderer/types/video' +import { CreateVideoParams, RetrieveVideoParams } from '@renderer/types/video' import { addImageFileToContents } from '@renderer/utils/formats' import { isSupportedToolUse, @@ -158,6 +158,11 @@ export class OpenAIResponseAPIClient extends OpenAIBaseClient< return sdk.videos.create(params.params, params.options) } + public async retrieveVideo(params: RetrieveVideoParams): Promise { + const sdk = await this.getSdkInstance() + return sdk.videos.retrieve(params.videoId, params.options) + } + private async handlePdfFile(file: FileMetadata): Promise { if (file.size > 32 * MB) return undefined try { diff --git a/src/renderer/src/aiCore/legacy/index.ts b/src/renderer/src/aiCore/legacy/index.ts index b897c904a1..aa08d5194b 100644 --- a/src/renderer/src/aiCore/legacy/index.ts +++ b/src/renderer/src/aiCore/legacy/index.ts @@ -7,7 +7,7 @@ import { withSpanResult } from '@renderer/services/SpanManagerService' import { StartSpanParams } from '@renderer/trace/types/ModelSpanEntity' import type { GenerateImageParams, Model, Provider } from '@renderer/types' import type { RequestOptions, SdkModel } from '@renderer/types/sdk' -import { CreateVideoParams, CreateVideoResult } from '@renderer/types/video' +import { CreateVideoParams, CreateVideoResult, RetrieveVideoParams, RetrieveVideoResult } from '@renderer/types/video' import { isSupportedToolUse } from '@renderer/utils/mcp-tools' import { AihubmixAPIClient } from './clients/aihubmix/AihubmixAPIClient' @@ -182,7 +182,23 @@ export default class AiProvider { public async createVideo(params: CreateVideoParams): Promise { if (this.apiClient instanceof OpenAIResponseAPIClient) { - return this.apiClient.createVideo(params) + const video = await this.apiClient.createVideo(params) + return { + type: 'openai', + video + } + } else { + throw new Error('Video generation is not supported by this provider') + } + } + + public async retrieveVideo(params: RetrieveVideoParams): Promise { + if (this.apiClient instanceof OpenAIResponseAPIClient && params.type === 'openai') { + const video = await this.apiClient.retrieveVideo(params) + return { + type: 'openai', + video + } } else { throw new Error('Video generation is not supported by this provider') } diff --git a/src/renderer/src/services/ApiService.ts b/src/renderer/src/services/ApiService.ts index 4a4c38f8f0..5c2b844f96 100644 --- a/src/renderer/src/services/ApiService.ts +++ b/src/renderer/src/services/ApiService.ts @@ -16,7 +16,7 @@ import type { StreamTextParams } from '@renderer/types/aiCoreTypes' import { type Chunk, ChunkType } from '@renderer/types/chunk' import { Message } from '@renderer/types/newMessage' import { SdkModel } from '@renderer/types/sdk' -import { CreateVideoParams, CreateVideoResult } from '@renderer/types/video' +import { CreateVideoParams, CreateVideoResult, RetrieveVideoParams, RetrieveVideoResult } from '@renderer/types/video' import { removeSpecialCharactersForTopicName, uuid } from '@renderer/utils' import { abortCompletion, readyToAbort } from '@renderer/utils/abortController' import { isAbortError } from '@renderer/utils/error' @@ -403,6 +403,11 @@ export async function createVideo(params: CreateVideoParams): Promise { + const ai = new AiProviderNew(params.provider) + return ai.retrieveVideo(params) +} + export function hasApiKey(provider: Provider) { if (!provider) return false if (['ollama', 'lmstudio', 'vertexai', 'cherryai'].includes(provider.id)) return true diff --git a/src/renderer/src/types/video.ts b/src/renderer/src/types/video.ts index 538023e547..d2b6d4ec19 100644 --- a/src/renderer/src/types/video.ts +++ b/src/renderer/src/types/video.ts @@ -5,6 +5,7 @@ import { Provider } from './provider' // Only OpenAI (Responses) is supported for now. export type VideoEndpointType = 'openai' +// Create Video interface CreateVideoBaseParams { type: VideoEndpointType provider: Provider @@ -28,3 +29,28 @@ export interface OpenAICreateVideoResult extends CreateVideoBaseResult { } export type CreateVideoResult = OpenAICreateVideoResult + +// Retrieve Video +interface RetrieveVideoBaseParams { + type: VideoEndpointType + provider: Provider +} + +export interface OpenAIRetrieveVideoParams extends RetrieveVideoBaseParams { + type: 'openai' + videoId: string + options?: OpenAI.RequestOptions +} + +export type RetrieveVideoParams = OpenAIRetrieveVideoParams + +interface RetrieveVideoBaseResult { + type: VideoEndpointType +} + +export interface OpenAIRetrieveVideoResult extends RetrieveVideoBaseResult { + type: 'openai' + video: OpenAI.Videos.Video +} + +export type RetrieveVideoResult = OpenAIRetrieveVideoResult