feat: configure Qwen endpoint per course
This commit is contained in:
@@ -117,6 +117,7 @@ export type CourseAiSetting = {
|
||||
enabled: boolean;
|
||||
provider: 'OPENAI' | 'ANTHROPIC' | 'GEMINI' | 'QWEN';
|
||||
model: string;
|
||||
endpointUrl?: string | null;
|
||||
hasApiKey: boolean;
|
||||
updatedAt?: string | null;
|
||||
};
|
||||
@@ -154,7 +155,7 @@ export async function getCourseAiSetting(accessToken: string, courseId: string):
|
||||
export async function updateCourseAiSetting(
|
||||
accessToken: string,
|
||||
courseId: string,
|
||||
input: Pick<CourseAiSetting, 'enabled' | 'provider' | 'model'> & { apiKey?: string },
|
||||
input: Pick<CourseAiSetting, 'enabled' | 'provider' | 'model'> & { apiKey?: string; endpointUrl?: string },
|
||||
): Promise<CourseAiSetting> {
|
||||
const response = await fetch(`${LMS_API_BASE_URL}/instructor/courses/${encodeURIComponent(courseId)}/ai-setting`, {
|
||||
method: 'PUT',
|
||||
|
||||
@@ -17,6 +17,8 @@ const AI_MODEL_DEFAULTS: Record<CourseAiSetting['provider'], string> = {
|
||||
QWEN: 'qwen3.7-plus',
|
||||
};
|
||||
|
||||
const DEFAULT_QWEN_ENDPOINT = 'https://dashscope-intl.aliyuncs.com/compatible-mode/v1';
|
||||
|
||||
const AI_PROVIDER_LABELS: Record<CourseAiSetting['provider'], string> = {
|
||||
OPENAI: 'OpenAI',
|
||||
ANTHROPIC: 'Claude',
|
||||
@@ -187,6 +189,7 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
||||
const [aiEnabled, setAiEnabled] = useState(false);
|
||||
const [aiProvider, setAiProvider] = useState<CourseAiSetting['provider']>('OPENAI');
|
||||
const [aiModel, setAiModel] = useState(AI_MODEL_DEFAULTS.OPENAI);
|
||||
const [aiEndpointUrl, setAiEndpointUrl] = useState(DEFAULT_QWEN_ENDPOINT);
|
||||
const [aiApiKey, setAiApiKey] = useState('');
|
||||
const [aiHasApiKey, setAiHasApiKey] = useState(false);
|
||||
const [aiConfiguredProvider, setAiConfiguredProvider] = useState<CourseAiSetting['provider'] | null>(null);
|
||||
@@ -326,6 +329,7 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
||||
setAiEnabled(setting.enabled);
|
||||
setAiProvider(setting.provider);
|
||||
setAiModel(setting.model);
|
||||
setAiEndpointUrl(setting.endpointUrl || DEFAULT_QWEN_ENDPOINT);
|
||||
setAiHasApiKey(setting.hasApiKey);
|
||||
setAiConfiguredProvider(setting.hasApiKey ? setting.provider : null);
|
||||
setAiApiKey('');
|
||||
@@ -348,11 +352,13 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
||||
enabled: aiEnabled,
|
||||
provider: aiProvider,
|
||||
model: aiModel.trim(),
|
||||
...(aiProvider === 'QWEN' ? { endpointUrl: aiEndpointUrl.trim() } : {}),
|
||||
...(aiApiKey.trim() ? { apiKey: aiApiKey.trim() } : {}),
|
||||
});
|
||||
setAiEnabled(setting.enabled);
|
||||
setAiProvider(setting.provider);
|
||||
setAiModel(setting.model);
|
||||
setAiEndpointUrl(setting.endpointUrl || DEFAULT_QWEN_ENDPOINT);
|
||||
setAiHasApiKey(setting.hasApiKey);
|
||||
setAiConfiguredProvider(setting.hasApiKey ? setting.provider : null);
|
||||
setAiApiKey('');
|
||||
@@ -1317,6 +1323,7 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
||||
onClick={() => {
|
||||
setAiProvider(provider);
|
||||
setAiModel(AI_MODEL_DEFAULTS[provider]);
|
||||
if (provider === 'QWEN' && !aiEndpointUrl.trim()) setAiEndpointUrl(DEFAULT_QWEN_ENDPOINT);
|
||||
setAiApiKey('');
|
||||
}}
|
||||
className={`h-10 rounded-md border text-[13px] font-black transition disabled:cursor-not-allowed disabled:opacity-60 ${
|
||||
@@ -1343,6 +1350,19 @@ export function CurriculumPage({ session, selectedCourseId }: { session: AuthSes
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
{aiProvider === 'QWEN' ? (
|
||||
<label className="mt-4 block">
|
||||
<span className="text-[12px] font-black text-[#475569]">엔드포인트 주소</span>
|
||||
<input
|
||||
type="url"
|
||||
value={aiEndpointUrl}
|
||||
onChange={(event) => setAiEndpointUrl(event.target.value)}
|
||||
placeholder={DEFAULT_QWEN_ENDPOINT}
|
||||
className="mt-1.5 h-10 w-full rounded-md border border-[#d1d9e6] bg-white px-3 font-mono text-[12px] font-bold text-[#111827] outline-none focus:border-[#2563eb]"
|
||||
/>
|
||||
<span className="mt-1.5 block text-[11px] font-semibold text-[#64748b]">OpenAI 호환 API의 기본 주소를 입력하세요. HTTPS 주소만 사용할 수 있습니다.</span>
|
||||
</label>
|
||||
) : null}
|
||||
<label className="mt-4 block">
|
||||
<span className="flex items-center gap-1.5 text-[12px] font-black text-[#475569]"><KeyRound size={13} aria-hidden="true" />API 키</span>
|
||||
<input
|
||||
|
||||
Reference in New Issue
Block a user