import type { HttpRequestBody } from '../http/client' import type { SdkFormData } from '../http/form-data' import type { BinaryStream, DifyClientConfig, DifyResponse, JsonObject, MessageFeedbackRequest, QueryParams, RequestMethod, SuccessResponse, TextToAudioRequest, } from '../types/common' import { FileUploadError, ValidationError } from '../errors/dify-error' import { HttpClient } from '../http/client' import { isFormData } from '../http/form-data' import { ensureNonEmptyString, ensureRating } from './validation' const toConfig = (init: string | DifyClientConfig, baseUrl?: string): DifyClientConfig => { if (typeof init === 'string') { return { apiKey: init, baseUrl, } } return init } const appendUserToFormData = (form: SdkFormData, user: string): void => { form.append('user', user) } export class DifyClient { protected http: HttpClient constructor(config: string | DifyClientConfig | HttpClient, baseUrl?: string) { if (config instanceof HttpClient) { this.http = config } else { this.http = new HttpClient(toConfig(config, baseUrl)) } } updateApiKey(apiKey: string): void { ensureNonEmptyString(apiKey, 'apiKey') this.http.updateApiKey(apiKey) } getHttpClient(): HttpClient { return this.http } sendRequest( method: RequestMethod, endpoint: string, data: HttpRequestBody = null, params: QueryParams | null = null, stream = false, headerParams: Record = {}, ): ReturnType { return this.http.requestRaw({ method, path: endpoint, data, query: params ?? undefined, headers: headerParams, responseType: stream ? 'stream' : 'json', }) } getRoot(): Promise> { return this.http.request({ method: 'GET', path: '/', }) } getApplicationParameters(user?: string): Promise> { if (user) { ensureNonEmptyString(user, 'user') } return this.http.request({ method: 'GET', path: '/parameters', query: user ? { user } : undefined, }) } async getParameters(user?: string): Promise> { return this.getApplicationParameters(user) } getMeta(user?: string): Promise> { if (user) { ensureNonEmptyString(user, 'user') } return this.http.request({ method: 'GET', path: '/meta', query: user ? { user } : undefined, }) } messageFeedback(request: MessageFeedbackRequest): Promise> messageFeedback( messageId: string, rating: 'like' | 'dislike' | null, user: string, content?: string, ): Promise> messageFeedback( messageIdOrRequest: string | MessageFeedbackRequest, rating?: 'like' | 'dislike' | null, user?: string, content?: string, ): Promise> { let messageId: string const payload: JsonObject = {} if (typeof messageIdOrRequest === 'string') { messageId = messageIdOrRequest ensureNonEmptyString(messageId, 'messageId') ensureNonEmptyString(user, 'user') payload.user = user if (rating !== undefined && rating !== null) { ensureRating(rating) payload.rating = rating } if (content !== undefined) { payload.content = content } } else { const request = messageIdOrRequest messageId = request.messageId ensureNonEmptyString(messageId, 'messageId') ensureNonEmptyString(request.user, 'user') payload.user = request.user if (request.rating !== undefined && request.rating !== null) { ensureRating(request.rating) payload.rating = request.rating } if (request.content !== undefined) { payload.content = request.content } } return this.http.request({ method: 'POST', path: `/messages/${messageId}/feedbacks`, data: payload, }) } getInfo(user?: string): Promise> { if (user) { ensureNonEmptyString(user, 'user') } return this.http.request({ method: 'GET', path: '/info', query: user ? { user } : undefined, }) } getSite(user?: string): Promise> { if (user) { ensureNonEmptyString(user, 'user') } return this.http.request({ method: 'GET', path: '/site', query: user ? { user } : undefined, }) } fileUpload(form: unknown, user: string): Promise> { if (!isFormData(form)) { throw new FileUploadError('FormData is required for file uploads') } ensureNonEmptyString(user, 'user') appendUserToFormData(form, user) return this.http.request({ method: 'POST', path: '/files/upload', data: form, }) } filePreview(fileId: string, user: string, asAttachment?: boolean): Promise> { ensureNonEmptyString(fileId, 'fileId') ensureNonEmptyString(user, 'user') return this.http.request({ method: 'GET', path: `/files/${fileId}/preview`, query: { user, as_attachment: asAttachment ? 'true' : undefined, }, responseType: 'bytes', }) } audioToText(form: unknown, user: string): Promise> { if (!isFormData(form)) { throw new FileUploadError('FormData is required for audio uploads') } ensureNonEmptyString(user, 'user') appendUserToFormData(form, user) return this.http.request({ method: 'POST', path: '/audio-to-text', data: form, }) } textToAudio(request: TextToAudioRequest): Promise | BinaryStream> textToAudio( text: string, user: string, streaming?: boolean, voice?: string, ): Promise | BinaryStream> textToAudio( textOrRequest: string | TextToAudioRequest, user?: string, streaming = false, voice?: string, ): Promise | BinaryStream> { let payload: TextToAudioRequest if (typeof textOrRequest === 'string') { ensureNonEmptyString(textOrRequest, 'text') ensureNonEmptyString(user, 'user') payload = { text: textOrRequest, user, streaming, } if (voice) { payload.voice = voice } } else { payload = { ...textOrRequest } ensureNonEmptyString(payload.user, 'user') if (payload.text !== undefined && payload.text !== null) { ensureNonEmptyString(payload.text, 'text') } if (payload.message_id !== undefined && payload.message_id !== null) { ensureNonEmptyString(payload.message_id, 'messageId') } if (!payload.text && !payload.message_id) { throw new ValidationError('text or message_id is required') } payload.streaming = payload.streaming ?? false } if (payload.streaming) { return this.http.requestBinaryStream({ method: 'POST', path: '/text-to-audio', data: payload, }) } return this.http.request({ method: 'POST', path: '/text-to-audio', data: payload, responseType: 'bytes', }) } }