Skip to content

Commit

Permalink
Merge branch 'main' into markdown-support
Browse files Browse the repository at this point in the history
  • Loading branch information
JacobLinCool authored Dec 23, 2024
2 parents 9119a01 + 26fcdb7 commit cb44fc3
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 23 deletions.
60 changes: 60 additions & 0 deletions src/lib/components/session/ChatHistory.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<script lang="ts">
import { Modal, TabItem, Tabs } from 'flowbite-svelte';
import Chatroom from '$lib/components/Chatroom.svelte';
import Summary from './Summary.svelte';
import type { Conversation } from '$lib/schema/conversation';
let {
open = $bindable(false),
participant = null,
conversation,
readonly = true
} = $props<{
open: boolean;
participant: {
displayName: string;
history: Array<{
name: string;
content: string;
self?: boolean;
audio?: string;
avatar?: string;
}>;
} | null;
conversation: {
data: Conversation;
id: string;
};
readonly?: boolean;
}>();
let loadingSummary = $state(false);
</script>

{#if open && participant}
<Modal bind:open size="xl" outsideclose class="w-full">
<div class="mb-4">
<h3 class="text-xl font-semibold">
{participant.displayName} 的對話記錄
</h3>
</div>

<Tabs style="underline">
<TabItem open title="對話歷史">
<div class="messages h-[400px] overflow-y-auto rounded-lg border border-gray-200 p-4">
<Chatroom readonly conversations={participant.history} />
</div>
</TabItem>
<TabItem title="對話總結">
<div class="h-[400px] overflow-y-auto rounded-lg border border-gray-200 p-4">
<Summary
{conversation}
loading={loadingSummary}
onRefresh={() => Promise.resolve()}
{readonly}
/>
</div>
</TabItem>
</Tabs>
</Modal>
{/if}
28 changes: 14 additions & 14 deletions src/lib/components/session/HostView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@
import { writable } from 'svelte/store';
import { getUser } from '$lib/utils/getUser';
import type { Conversation } from '$lib/schema/conversation';
import { Modal } from 'flowbite-svelte';
import Chatroom from '$lib/components/Chatroom.svelte';
import { X } from 'lucide-svelte';
import { SvelteMap } from 'svelte/reactivity';
import { renderMarkdown } from '$lib/utils/renderMarkdown';
import ChatHistory from './ChatHistory.svelte';
let { session }: { session: Readable<Session> } = $props();
let code = $state('Code generate error');
Expand All @@ -42,6 +41,7 @@
avatar?: string;
}>;
} | null>(null);
let selectedConversation = $state<{ data: Conversation; id: string } | null>(null);
onMount(() => {
const unsubscribes: (() => void)[] = [];
Expand Down Expand Up @@ -110,7 +110,7 @@
async function handleStartSession() {
try {
// 為每個群組的每個參與者創建對話
// 為每個群組的個參與者創建對話
for (const group of $groups) {
for (const participant of group.participants) {
const response = await fetch(
Expand Down Expand Up @@ -256,6 +256,10 @@
if (conversations.length > 0) {
const userData = await getUser(participant);
const conversation = {
data: conversations[0],
id: snapshot.docs[0].id
};
selectedParticipant = {
displayName: userData.displayName,
history: conversations[0].history.map((message) => ({
Expand All @@ -266,6 +270,7 @@
}))
};
showChatHistory = true;
selectedConversation = conversation;
}
} catch (error) {
console.error('無法加載對話歷史:', error);
Expand Down Expand Up @@ -432,16 +437,11 @@
</div>
</div>

{#if showChatHistory && selectedParticipant}
<Modal bind:open={showChatHistory} size="xl" autoclose outsideclose class="w-full">
<div class="mb-4">
<h3 class="text-xl font-semibold">
{selectedParticipant.displayName} 的對話歷史
</h3>
</div>
<div class="messages h-[400px] overflow-y-auto rounded-lg border border-gray-200 p-4">
<Chatroom readonly conversations={selectedParticipant.history} />
</div>
</Modal>
{#if showChatHistory && selectedParticipant && selectedConversation}
<ChatHistory
bind:open={showChatHistory}
participant={selectedParticipant}
conversation={selectedConversation}
/>
{/if}
</main>
48 changes: 45 additions & 3 deletions src/lib/components/session/ParticipantView.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import { getUser } from '$lib/utils/getUser';
import Chatroom from '$lib/components/Chatroom.svelte';
import { MicVAD, utils } from '@ricky0123/vad-web';
import Summary from '$lib/components/session/Summary.svelte';
interface ChatroomConversation {
name: string;
Expand Down Expand Up @@ -78,6 +79,10 @@
data: snapshot.docs[0].data() as Conversation,
id: snapshot.docs[0].id
};
if ($session?.status === 'before-group' && !conversationDoc.data.summary) {
fetchSummary();
}
});
}
Expand Down Expand Up @@ -250,6 +255,34 @@
notifications.error('Failed to send message');
}
}
let loadingSummary = $state(false);
async function fetchSummary() {
if (!groupDoc || !conversationDoc) {
notifications.error('無法獲取總結:找不到群組或對話');
return;
}
loadingSummary = true;
try {
const response = await fetch(
`/api/session/${$page.params.id}/group/${groupDoc.id}/conversations/${conversationDoc.id}/summary`
);
if (!response.ok) {
const data = await response.json();
throw new Error(data.error || '無法獲取總結');
}
notifications.success('成功獲取總結');
} catch (error) {
console.error('獲取總結時出錯:', error);
notifications.error('無法獲取總結');
} finally {
loadingSummary = false;
}
}
</script>

<main class="mx-auto max-w-7xl px-2 py-8">
Expand Down Expand Up @@ -278,6 +311,10 @@
</div>
{#if $session?.status === 'individual'}
<p class="text-gray-600">Work on your individual contributions.</p>
{:else if $session?.status === 'before-group'}
<p class="text-gray-600">Get ready to collaborate with your group.</p>
{:else if $session?.status === 'group'}
<p class="text-gray-600">Collaborate with your group members.</p>
{/if}
</div>
</div>
Expand Down Expand Up @@ -361,9 +398,14 @@
{:else if $session?.status === 'individual'}
<Chatroom record={handleRecord} send={handleSend} {conversations} />
{:else if $session?.status === 'before-group'}
<div class="mt-4">
<h3 class="mb-2 font-medium">Preparing for Group Discussion</h3>
<p class="text-gray-600">Get ready to collaborate with your group.</p>
<div class="space-y-6">
{#if groupDoc && conversationDoc}
<Summary
conversation={conversationDoc}
loading={loadingSummary}
onRefresh={fetchSummary}
/>
{/if}
</div>
{:else if $session?.status === 'group'}
<div class="mt-4">
Expand Down
65 changes: 65 additions & 0 deletions src/lib/components/session/Summary.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script lang="ts">
import type { Conversation } from '$lib/schema/conversation';
export let conversation: {
data: Conversation;
id: string;
};
export let loading: boolean;
export let onRefresh: () => Promise<void>;
export let readonly = false;
$: summaryData = conversation.data.summary
? {
summary: conversation.data.summary,
keyPoints: conversation.data.keyPoints || []
}
: null;
</script>

<div class="space-y-6 p-6">
<div class="flex items-center justify-between">
<h2 class="text-xl font-semibold">對話總結</h2>
{#if !readonly}
<button
class="rounded-lg bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 disabled:opacity-50"
on:click={onRefresh}
disabled={loading}
>
{#if loading}
更新中...
{:else}
更新總結
{/if}
</button>
{/if}
</div>

{#if summaryData}
<div class="space-y-4">
<div>
<h3 class="mb-2 font-medium">總結內容:</h3>
<p class="rounded-lg bg-gray-50 p-4 text-gray-700">{summaryData.summary}</p>
</div>

{#if summaryData.keyPoints.length > 0}
<div>
<h3 class="mb-2 font-medium">關鍵重點:</h3>
<ul class="list-inside list-disc space-y-2">
{#each summaryData.keyPoints as point}
<li class="text-gray-700">{point}</li>
{/each}
</ul>
</div>
{/if}
</div>
{:else if loading}
<div class="text-center text-gray-600">
<p>正在生成總結,請稍候...</p>
</div>
{:else}
<div class="text-center text-gray-600">
<p>點擊上方按鈕生成對話總結</p>
</div>
{/if}
</div>
25 changes: 24 additions & 1 deletion src/lib/server/llm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,11 +286,34 @@ export async function summarizeConcepts(
error?: string;
}> {
try {
if (!Array.isArray(student_opinion) || student_opinion.length === 0) {
return {
success: false,
similar_view_points: [],
different_view_points: [],
students_summary: '',
error: 'no student opinion'
};
}

const formatted_opinions = student_opinion
.map((opinion) => {
return `${opinion.summary}\nKey points: ${opinion.keyPoints.join(',')}`;
const keyPoints = Array.isArray(opinion.keyPoints) ? opinion.keyPoints : [];
return `${opinion.summary || ''}\nKey points: ${keyPoints.join(',')}`;
})
.filter(Boolean)
.join('\n{separator}\n');

if (!formatted_opinions) {
return {
success: false,
similar_view_points: [],
different_view_points: [],
students_summary: '',
error: 'format student opinion error'
};
}

const system_prompt = CONCEPT_SUMMARY_PROMPT.replace('{studentOpinions}', formatted_opinions);
const summary_student_concept_schema = z.object({
similar_view_points: z.array(z.string()),
Expand Down
13 changes: 9 additions & 4 deletions src/lib/utils/firestore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,19 @@ export async function createConversation(
subtasks: string[],
resources: string[]
) {
const conversationRef = adminDb
const conversationsRef = adminDb
.collection('sessions')
.doc(id)
.collection('groups')
.doc(group_number)
.collection('conversations')
.doc();
.collection('conversations');

const existingConversations = await conversationsRef.get();
if (!existingConversations.empty) {
return existingConversations.docs[0].id;
}

const conversationRef = conversationsRef.doc();
await conversationRef.set({
userId: userId,
task: task,
Expand Down Expand Up @@ -53,7 +58,7 @@ export async function getConversationData(

export function getConversationsRef(id: string, group_number: string) {
return adminDb
.collection('session')
.collection('sessions')
.doc(id)
.collection('groups')
.doc(group_number)
Expand Down
5 changes: 5 additions & 0 deletions src/lib/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ export interface StudentSpeak {
role: string;
content: string;
}

export interface SummaryData {
summary: string;
keyPoints: string[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ export const GET: RequestHandler = async ({ params, locals }) => {
const conversations_ref = getConversationsRef(id, group_number);
const conversations = await getConversationsData(conversations_ref);

if (!isAllSummarized(conversations)) {
if (!(await isAllSummarized(conversations))) {
return new Response(JSON.stringify({ success: true }), { status: 200 });
}
console.log('all summarized');

const { similar_view_points, different_view_points, students_summary } =
await summarizeConcepts(
Expand Down

0 comments on commit cb44fc3

Please sign in to comment.