chore: ai, public course and other ui improvements
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { generateText } from 'ai';
|
||||
import { generateText, generateObject } from 'ai';
|
||||
import { z } from 'zod';
|
||||
import { AIProvider, type AIProviderConfig } from '@cio/ai-assistant';
|
||||
import { createOpenAI } from '@ai-sdk/openai';
|
||||
import { createAnthropic } from '@ai-sdk/anthropic';
|
||||
@@ -9,8 +10,9 @@ import { createGoogleGenerativeAI } from '@ai-sdk/google';
|
||||
*/
|
||||
const TITLE_MODELS: Record<AIProvider, string> = {
|
||||
[AIProvider.OPENAI]: 'gpt-4o-mini',
|
||||
[AIProvider.ANTHROPIC]: 'claude-haiku-4-20250414',
|
||||
[AIProvider.GOOGLE]: 'gemini-2.5-flash'
|
||||
[AIProvider.ANTHROPIC]: 'claude-haiku-4-5-20251001',
|
||||
[AIProvider.GOOGLE]: 'gemini-2.5-flash',
|
||||
[AIProvider.MOONSHOT]: 'kimi-k2.6'
|
||||
};
|
||||
|
||||
function createTitleModel(config: AIProviderConfig) {
|
||||
@@ -29,6 +31,10 @@ function createTitleModel(config: AIProviderConfig) {
|
||||
const google = createGoogleGenerativeAI({ apiKey: config.apiKey });
|
||||
return google(modelName);
|
||||
}
|
||||
case AIProvider.MOONSHOT: {
|
||||
const moonshot = createOpenAI({ apiKey: config.apiKey, baseURL: 'https://api.moonshot.cn/v1' });
|
||||
return moonshot(modelName);
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unsupported AI provider: ${config.provider}`);
|
||||
}
|
||||
@@ -44,13 +50,58 @@ export async function generateConversationTitle(
|
||||
): Promise<string> {
|
||||
const model = createTitleModel(providerConfig);
|
||||
|
||||
const { text } = await generateText({
|
||||
model,
|
||||
system:
|
||||
"Generate a short title (max 6 words) for a chat conversation based on the user's first message. Return ONLY the title, no quotes, no punctuation at the end. Be concise and descriptive.",
|
||||
prompt: firstMessageText,
|
||||
maxOutputTokens: 30
|
||||
});
|
||||
const systemPrompt =
|
||||
"Generate a short, descriptive title (2–5 words) for a chat conversation based on the user's first message. The title must be at least 2 words — never a single word. Return ONLY the title, no quotes, no punctuation at the end.";
|
||||
|
||||
const truncatedMessage = firstMessageText.slice(0, 1000);
|
||||
|
||||
// Gemini 2.5 counts thinking toward maxOutputTokens; disable thinking and allow headroom for the title text.
|
||||
const { text } = await generateText(
|
||||
providerConfig.provider === AIProvider.GOOGLE
|
||||
? {
|
||||
model,
|
||||
system: systemPrompt,
|
||||
prompt: truncatedMessage,
|
||||
maxOutputTokens: 256,
|
||||
maxRetries: 0,
|
||||
providerOptions: {
|
||||
google: { thinkingConfig: { thinkingBudget: 0 } }
|
||||
}
|
||||
}
|
||||
: {
|
||||
model,
|
||||
system: systemPrompt,
|
||||
prompt: truncatedMessage,
|
||||
maxOutputTokens: 96,
|
||||
maxRetries: 0
|
||||
}
|
||||
);
|
||||
|
||||
return text.trim().slice(0, 80);
|
||||
}
|
||||
|
||||
const CourseMeta = z.object({
|
||||
title: z.string().describe('3–6 word course title, no quotes, no trailing punctuation'),
|
||||
description: z.string().describe('20–30 word description of what students will learn')
|
||||
});
|
||||
|
||||
export async function generateCourseMeta(
|
||||
prompt: string,
|
||||
providerConfig: AIProviderConfig
|
||||
): Promise<{ title: string; description: string }> {
|
||||
const model = createTitleModel(providerConfig);
|
||||
|
||||
const { object } = await generateObject({
|
||||
model,
|
||||
schema: CourseMeta,
|
||||
system:
|
||||
"You are helping create an online course. Based on the user's course idea, generate a short title (3–6 words, no quotes, no trailing punctuation) and a concise description (20–30 words) explaining what students will learn.",
|
||||
prompt: prompt.slice(0, 1000),
|
||||
maxRetries: 0
|
||||
});
|
||||
|
||||
return {
|
||||
title: object.title.trim().slice(0, 100),
|
||||
description: object.description.trim().slice(0, 300)
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user