Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/before group stage #57

Merged
merged 2 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,10 +14,9 @@
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 ChatHistory from './ChatHistory.svelte';

let { session }: { session: Readable<Session> } = $props();
let code = $state('Code generate error');
Expand All @@ -41,6 +40,7 @@
avatar?: string;
}>;
} | null>(null);
let selectedConversation = $state<{ data: Conversation; id: string } | null>(null);

onMount(() => {
const unsubscribes: (() => void)[] = [];
Expand Down Expand Up @@ -109,7 +109,7 @@

async function handleStartSession() {
try {
// 為每個群組的每個參與者創建對話
// 為每個群組的個參與者創建對話
for (const group of $groups) {
for (const participant of group.participants) {
const response = await fetch(
Expand Down Expand Up @@ -255,6 +255,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 @@ -265,6 +269,7 @@
}))
};
showChatHistory = true;
selectedConversation = conversation;
}
} catch (error) {
console.error('無法加載對話歷史:', error);
Expand Down Expand Up @@ -424,16 +429,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
Loading