diff --git a/plugins/wasm-go/extensions/ai-proxy/main.go b/plugins/wasm-go/extensions/ai-proxy/main.go index abacce31d..c7e045140 100644 --- a/plugins/wasm-go/extensions/ai-proxy/main.go +++ b/plugins/wasm-go/extensions/ai-proxy/main.go @@ -379,6 +379,33 @@ func getApiName(path string) provider.ApiName { if strings.HasSuffix(path, "/v1/models") { return provider.ApiNameModels } + if strings.HasSuffix(path, "/v1/fine_tuning/jobs") { + return provider.ApiNameFineTuningJobs + } + if util.RegRetrieveFineTuningJobPath.MatchString(path) { + return provider.ApiNameFineTuningRetrieveJob + } + if util.RegRetrieveFineTuningJobEventsPath.MatchString(path) { + return provider.PathOpenAIFineTuningJobEvents + } + if util.RegRetrieveFineTuningJobCheckpointsPath.MatchString(path) { + return provider.PathOpenAIFineTuningJobCheckpoints + } + if util.RegCancelFineTuningJobPath.MatchString(path) { + return provider.ApiNameFineTuningCancelJob + } + if util.RegResumeFineTuningJobPath.MatchString(path) { + return provider.ApiNameFineTuningResumeJob + } + if util.RegPauseFineTuningJobPath.MatchString(path) { + return provider.ApiNameFineTuningPauseJob + } + if util.RegFineTuningCheckpointPermissionPath.MatchString(path) { + return provider.ApiNameFineTuningCheckpointPermissions + } + if util.RegDeleteFineTuningCheckpointPermissionPath.MatchString(path) { + return provider.PathOpenAIFineDeleteTuningCheckpointPermission + } // cohere style if strings.HasSuffix(path, "/v1/rerank") { return provider.ApiNameCohereV1Rerank diff --git a/plugins/wasm-go/extensions/ai-proxy/provider/openai.go b/plugins/wasm-go/extensions/ai-proxy/provider/openai.go index f5e0d487b..2a9a81e56 100644 --- a/plugins/wasm-go/extensions/ai-proxy/provider/openai.go +++ b/plugins/wasm-go/extensions/ai-proxy/provider/openai.go @@ -26,24 +26,34 @@ func (m *openaiProviderInitializer) ValidateConfig(config *ProviderConfig) error func (m *openaiProviderInitializer) DefaultCapabilities() map[string]string { return map[string]string{ - string(ApiNameCompletion): PathOpenAICompletions, - string(ApiNameChatCompletion): PathOpenAIChatCompletions, - string(ApiNameEmbeddings): PathOpenAIEmbeddings, - string(ApiNameImageGeneration): PathOpenAIImageGeneration, - string(ApiNameImageEdit): PathOpenAIImageEdit, - string(ApiNameImageVariation): PathOpenAIImageVariation, - string(ApiNameAudioSpeech): PathOpenAIAudioSpeech, - string(ApiNameModels): PathOpenAIModels, - string(ApiNameFiles): PathOpenAIFiles, - string(ApiNameRetrieveFile): PathOpenAIRetrieveFile, - string(ApiNameRetrieveFileContent): PathOpenAIRetrieveFileContent, - string(ApiNameBatches): PathOpenAIBatches, - string(ApiNameRetrieveBatch): PathOpenAIRetrieveBatch, - string(ApiNameCancelBatch): PathOpenAICancelBatch, - string(ApiNameResponses): PathOpenAIResponses, + string(ApiNameCompletion): PathOpenAICompletions, + string(ApiNameChatCompletion): PathOpenAIChatCompletions, + string(ApiNameEmbeddings): PathOpenAIEmbeddings, + string(ApiNameImageGeneration): PathOpenAIImageGeneration, + string(ApiNameImageEdit): PathOpenAIImageEdit, + string(ApiNameImageVariation): PathOpenAIImageVariation, + string(ApiNameAudioSpeech): PathOpenAIAudioSpeech, + string(ApiNameModels): PathOpenAIModels, + string(ApiNameFiles): PathOpenAIFiles, + string(ApiNameRetrieveFile): PathOpenAIRetrieveFile, + string(ApiNameRetrieveFileContent): PathOpenAIRetrieveFileContent, + string(ApiNameBatches): PathOpenAIBatches, + string(ApiNameRetrieveBatch): PathOpenAIRetrieveBatch, + string(ApiNameCancelBatch): PathOpenAICancelBatch, + string(ApiNameResponses): PathOpenAIResponses, + string(ApiNameFineTuningJobs): PathOpenAIFineTuningJobs, + string(ApiNameFineTuningRetrieveJob): PathOpenAIFineTuningRetrieveJob, + string(ApiNameFineTuningJobEvents): PathOpenAIFineTuningJobEvents, + string(ApiNameFineTuningJobCheckpoints): PathOpenAIFineTuningJobCheckpoints, + string(ApiNameFineTuningCancelJob): PathOpenAIFineTuningCancelJob, + string(ApiNameFineTuningResumeJob): PathOpenAIFineTuningResumeJob, + string(ApiNameFineTuningPauseJob): PathOpenAIFineTuningPauseJob, + string(ApiNameFineTuningCheckpointPermissions): PathOpenAIFineTuningCheckpointPermissions, + string(ApiNameDeleteFineTuningCheckpointPermission): PathOpenAIFineDeleteTuningCheckpointPermission, } } +// isDirectPath checks if the path is a known standard OpenAI interface path. func isDirectPath(path string) bool { return strings.HasSuffix(path, "/completions") || strings.HasSuffix(path, "/embeddings") || @@ -52,7 +62,9 @@ func isDirectPath(path string) bool { strings.HasSuffix(path, "/images/variations") || strings.HasSuffix(path, "/images/edits") || strings.HasSuffix(path, "/models") || - strings.HasSuffix(path, "/responses") + strings.HasSuffix(path, "/responses") || + strings.HasSuffix(path, "/fine_tuning/jobs") || + strings.HasSuffix(path, "/fine_tuning/checkpoints") } func (m *openaiProviderInitializer) CreateProvider(config ProviderConfig) (Provider, error) { diff --git a/plugins/wasm-go/extensions/ai-proxy/provider/provider.go b/plugins/wasm-go/extensions/ai-proxy/provider/provider.go index 69575df6b..42f780d56 100644 --- a/plugins/wasm-go/extensions/ai-proxy/provider/provider.go +++ b/plugins/wasm-go/extensions/ai-proxy/provider/provider.go @@ -27,42 +27,60 @@ const ( // ApiName 格式 {vendor}/{version}/{apitype} // 表示遵循 厂商/版本/接口类型 的格式 // 目前openai是事实意义上的标准,但是也有其他厂商存在其他任务的一些可能的标准,比如cohere的rerank - ApiNameCompletion ApiName = "openai/v1/completions" - ApiNameChatCompletion ApiName = "openai/v1/chatcompletions" - ApiNameEmbeddings ApiName = "openai/v1/embeddings" - ApiNameImageGeneration ApiName = "openai/v1/imagegeneration" - ApiNameImageEdit ApiName = "openai/v1/imageedit" - ApiNameImageVariation ApiName = "openai/v1/imagevariation" - ApiNameAudioSpeech ApiName = "openai/v1/audiospeech" - ApiNameFiles ApiName = "openai/v1/files" - ApiNameRetrieveFile ApiName = "openai/v1/retrievefile" - ApiNameRetrieveFileContent ApiName = "openai/v1/retrievefilecontent" - ApiNameBatches ApiName = "openai/v1/batches" - ApiNameRetrieveBatch ApiName = "openai/v1/retrievebatch" - ApiNameCancelBatch ApiName = "openai/v1/cancelbatch" - ApiNameModels ApiName = "openai/v1/models" - ApiNameResponses ApiName = "openai/v1/responses" + ApiNameCompletion ApiName = "openai/v1/completions" + ApiNameChatCompletion ApiName = "openai/v1/chatcompletions" + ApiNameEmbeddings ApiName = "openai/v1/embeddings" + ApiNameImageGeneration ApiName = "openai/v1/imagegeneration" + ApiNameImageEdit ApiName = "openai/v1/imageedit" + ApiNameImageVariation ApiName = "openai/v1/imagevariation" + ApiNameAudioSpeech ApiName = "openai/v1/audiospeech" + ApiNameFiles ApiName = "openai/v1/files" + ApiNameRetrieveFile ApiName = "openai/v1/retrievefile" + ApiNameRetrieveFileContent ApiName = "openai/v1/retrievefilecontent" + ApiNameBatches ApiName = "openai/v1/batches" + ApiNameRetrieveBatch ApiName = "openai/v1/retrievebatch" + ApiNameCancelBatch ApiName = "openai/v1/cancelbatch" + ApiNameModels ApiName = "openai/v1/models" + ApiNameResponses ApiName = "openai/v1/responses" + ApiNameFineTuningJobs ApiName = "openai/v1/fine-tuningjobs" + ApiNameFineTuningRetrieveJob ApiName = "openai/v1/retrievefine-tuningjob" + ApiNameFineTuningJobEvents ApiName = "openai/v1/fine-tuningjobsevents" + ApiNameFineTuningJobCheckpoints ApiName = "openai/v1/fine-tuningjobcheckpoints" + ApiNameFineTuningCancelJob ApiName = "openai/v1/cancelfine-tuningjob" + ApiNameFineTuningResumeJob ApiName = "openai/v1/resumefine-tuningjob" + ApiNameFineTuningPauseJob ApiName = "openai/v1/pausefine-tuningjob" + ApiNameFineTuningCheckpointPermissions ApiName = "openai/v1/fine-tuningjobcheckpointpermissions" + ApiNameDeleteFineTuningCheckpointPermission ApiName = "openai/v1/deletefine-tuningjobcheckpointpermission" - PathOpenAICompletions = "/v1/completions" - PathOpenAIChatCompletions = "/v1/chat/completions" - PathOpenAIEmbeddings = "/v1/embeddings" - PathOpenAIFiles = "/v1/files" - PathOpenAIRetrieveFile = "/v1/files/{file_id}" - PathOpenAIRetrieveFileContent = "/v1/files/{file_id}/content" - PathOpenAIBatches = "/v1/batches" - PathOpenAIRetrieveBatch = "/v1/batches/{batch_id}" - PathOpenAICancelBatch = "/v1/batches/{batch_id}/cancel" - PathOpenAIModels = "/v1/models" - PathOpenAIImageGeneration = "/v1/images/generations" - PathOpenAIImageEdit = "/v1/images/edits" - PathOpenAIImageVariation = "/v1/images/variations" - PathOpenAIAudioSpeech = "/v1/audio/speech" - PathOpenAIResponses = "/v1/responses" + PathOpenAICompletions = "/v1/completions" + PathOpenAIChatCompletions = "/v1/chat/completions" + PathOpenAIEmbeddings = "/v1/embeddings" + PathOpenAIFiles = "/v1/files" + PathOpenAIRetrieveFile = "/v1/files/{file_id}" + PathOpenAIRetrieveFileContent = "/v1/files/{file_id}/content" + PathOpenAIBatches = "/v1/batches" + PathOpenAIRetrieveBatch = "/v1/batches/{batch_id}" + PathOpenAICancelBatch = "/v1/batches/{batch_id}/cancel" + PathOpenAIModels = "/v1/models" + PathOpenAIImageGeneration = "/v1/images/generations" + PathOpenAIImageEdit = "/v1/images/edits" + PathOpenAIImageVariation = "/v1/images/variations" + PathOpenAIAudioSpeech = "/v1/audio/speech" + PathOpenAIResponses = "/v1/responses" + PathOpenAIFineTuningJobs = "/v1/fine_tuning/jobs" + PathOpenAIFineTuningRetrieveJob = "/v1/fine_tuning/jobs/{fine_tuning_job_id}" + PathOpenAIFineTuningJobEvents = "/v1/fine_tuning/jobs/{fine_tuning_job_id}/events" + PathOpenAIFineTuningJobCheckpoints = "/v1/fine_tuning/jobs/{fine_tuning_job_id}/checkpoints" + PathOpenAIFineTuningCancelJob = "/v1/fine_tuning/jobs/{fine_tuning_job_id}/cancel" + PathOpenAIFineTuningResumeJob = "/v1/fine_tuning/jobs/{fine_tuning_job_id}/resume" + PathOpenAIFineTuningPauseJob = "/v1/fine_tuning/jobs/{fine_tuning_job_id}/pause" + PathOpenAIFineTuningCheckpointPermissions = "/v1/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions" + PathOpenAIFineDeleteTuningCheckpointPermission = "/v1/fine_tuning/checkpoints/{fine_tuned_model_checkpoint}/permissions/{permission_id}" // TODO: 以下是一些非标准的API名称,需要进一步确认是否支持 ApiNameCohereV1Rerank ApiName = "cohere/v1/rerank" - ApiNameQwenAsyncAIGC ApiName = "api/v1/services/aigc" - ApiNameQwenAsyncTask ApiName = "api/v1/tasks/" + ApiNameQwenAsyncAIGC ApiName = "api/v1/services/aigc" + ApiNameQwenAsyncTask ApiName = "api/v1/tasks/" providerTypeMoonshot = "moonshot" providerTypeAzure = "azure" @@ -842,11 +860,14 @@ func (c *ProviderConfig) DefaultTransformResponseHeaders(ctx wrapper.HttpContext func (c *ProviderConfig) needToProcessRequestBody(apiName ApiName) bool { switch apiName { case ApiNameChatCompletion, + ApiNameCompletion, ApiNameEmbeddings, ApiNameImageGeneration, ApiNameImageEdit, ApiNameImageVariation, - ApiNameAudioSpeech: + ApiNameAudioSpeech, + ApiNameFineTuningJobs, + ApiNameResponses: return true } return false diff --git a/plugins/wasm-go/extensions/ai-proxy/util/http.go b/plugins/wasm-go/extensions/ai-proxy/util/http.go index 336a1ce7e..90baa3f99 100644 --- a/plugins/wasm-go/extensions/ai-proxy/util/http.go +++ b/plugins/wasm-go/extensions/ai-proxy/util/http.go @@ -17,10 +17,18 @@ const ( ) var ( - RegRetrieveBatchPath = regexp.MustCompile(`^.*/v1/batches/(?P[^/]+)$`) - RegCancelBatchPath = regexp.MustCompile(`^.*/v1/batches/(?P[^/]+)/cancel$`) - RegRetrieveFilePath = regexp.MustCompile(`^.*/v1/files/(?P[^/]+)$`) - RegRetrieveFileContentPath = regexp.MustCompile(`^.*/v1/files/(?P[^/]+)/content$`) + RegRetrieveBatchPath = regexp.MustCompile(`^.*/v1/batches/(?P[^/]+)$`) + RegCancelBatchPath = regexp.MustCompile(`^.*/v1/batches/(?P[^/]+)/cancel$`) + RegRetrieveFilePath = regexp.MustCompile(`^.*/v1/files/(?P[^/]+)$`) + RegRetrieveFileContentPath = regexp.MustCompile(`^.*/v1/files/(?P[^/]+)/content$`) + RegRetrieveFineTuningJobPath = regexp.MustCompile(`^.*/v1/fine_tuning/jobs/(?P[^/]+)$`) + RegRetrieveFineTuningJobEventsPath = regexp.MustCompile(`^.*/v1/fine_tuning/jobs/(?P[^/]+)/events$`) + RegRetrieveFineTuningJobCheckpointsPath = regexp.MustCompile(`^.*/v1/fine_tuning/jobs/(?P[^/]+)/checkpoints$`) + RegCancelFineTuningJobPath = regexp.MustCompile(`^.*/v1/fine_tuning/jobs/(?P[^/]+)/cancel$`) + RegResumeFineTuningJobPath = regexp.MustCompile(`^.*/v1/fine_tuning/jobs/(?P[^/]+)/resume$`) + RegPauseFineTuningJobPath = regexp.MustCompile(`^.*/v1/fine_tuning/jobs/(?P[^/]+)/pause$`) + RegFineTuningCheckpointPermissionPath = regexp.MustCompile(`^.*/v1/fine_tuning/checkpoints/(?P[^/]+)/permissions$`) + RegDeleteFineTuningCheckpointPermissionPath = regexp.MustCompile(`^.*/v1/fine_tuning/checkpoints/(?P[^/]+)/permissions/(?P[^/]+)$`) ) type ErrorHandlerFunc func(statusCodeDetails string, err error) error