feat: support course-specific Qwen endpoints
This commit is contained in:
@@ -118,6 +118,7 @@ export class InstructorService {
|
||||
enabled: setting?.enabled ?? false,
|
||||
provider: setting?.provider ?? 'OPENAI',
|
||||
model: setting?.model ?? 'gpt-4.1-mini',
|
||||
endpointUrl: setting?.provider === 'QWEN' ? setting.endpointUrl : null,
|
||||
hasApiKey: Boolean(setting?.encryptedApiKey),
|
||||
updatedAt: setting?.updatedAt ?? null,
|
||||
};
|
||||
@@ -138,6 +139,9 @@ export class InstructorService {
|
||||
if (input.enabled && !apiKey && (!existing?.encryptedApiKey || existing.provider !== input.provider)) {
|
||||
throw new BadRequestException('AI 에이전트를 사용하거나 공급자를 변경하려면 해당 공급자의 API 키를 입력하세요.');
|
||||
}
|
||||
const endpointUrl = input.provider === 'QWEN'
|
||||
? this.normalizeQwenEndpoint(input.endpointUrl)
|
||||
: null;
|
||||
const setting = await this.prisma.courseAiSetting.upsert({
|
||||
where: { courseId: course.id },
|
||||
create: {
|
||||
@@ -145,12 +149,14 @@ export class InstructorService {
|
||||
enabled: input.enabled,
|
||||
provider: input.provider,
|
||||
model: input.model.trim(),
|
||||
endpointUrl,
|
||||
encryptedApiKey: apiKey ? this.aiCredentialService.encrypt(apiKey) : null,
|
||||
},
|
||||
update: {
|
||||
enabled: input.enabled,
|
||||
provider: input.provider,
|
||||
model: input.model.trim(),
|
||||
...(input.provider !== 'QWEN' || input.endpointUrl !== undefined ? { endpointUrl } : {}),
|
||||
...(apiKey ? { encryptedApiKey: this.aiCredentialService.encrypt(apiKey) } : {}),
|
||||
},
|
||||
});
|
||||
@@ -159,11 +165,27 @@ export class InstructorService {
|
||||
enabled: setting.enabled,
|
||||
provider: setting.provider,
|
||||
model: setting.model,
|
||||
endpointUrl: setting.provider === 'QWEN' ? setting.endpointUrl : null,
|
||||
hasApiKey: Boolean(setting.encryptedApiKey),
|
||||
updatedAt: setting.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
private normalizeQwenEndpoint(value: string | undefined): string | null {
|
||||
const endpoint = value?.trim();
|
||||
if (!endpoint) return null;
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(endpoint);
|
||||
} catch {
|
||||
throw new BadRequestException('Qwen 엔드포인트 주소가 올바르지 않습니다.');
|
||||
}
|
||||
if (url.protocol !== 'https:' || url.username || url.password) {
|
||||
throw new BadRequestException('Qwen 엔드포인트는 인증정보가 없는 HTTPS 주소여야 합니다.');
|
||||
}
|
||||
return endpoint.replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
async listCourses(accessToken: string | undefined) {
|
||||
const { authUser, instructor } = await this.getInstructor(accessToken);
|
||||
const organizationName = authUser.primaryAffiliation?.organizationName?.trim();
|
||||
|
||||
Reference in New Issue
Block a user