Skip to content

Commit

Permalink
gemini bug fix with more models, UI enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
chandeldivyam committed Aug 29, 2024
1 parent dc2a738 commit 6b5c756
Show file tree
Hide file tree
Showing 14 changed files with 203 additions and 120 deletions.
Binary file modified desktop/bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "samwise",
"private": true,
"version": "0.0.0",
"version": "0.0.2",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
7 changes: 7 additions & 0 deletions desktop/src-tauri/src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,12 @@ pub fn get_migrations() -> Vec<Migration> {
);",
kind: MigrationKind::Up,
},
Migration {
version: 3,
description:
"ALTER TABLE recording_insights to add summary prompt as a recording property so that it doesn't get lost",
sql: "ALTER TABLE recording_insights ADD COLUMN summary_prompt TEXT;",
kind: MigrationKind::Up,
},
]
}
2 changes: 1 addition & 1 deletion desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "./gen/schemas/desktop-schema.json",
"productName": "samwise",
"version": "0.0.1",
"version": "0.0.2",
"identifier": "samwise.tech",
"app": {
"windows": [],
Expand Down
46 changes: 26 additions & 20 deletions desktop/src/components/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// src/components/Chat.tsx

import React, { useState, useEffect, useRef } from 'react';
import React, { useState, useEffect, useRef, useContext } from 'react';
import { useTranslation } from 'react-i18next';
import { invoke } from '@tauri-apps/api/core';
import { Segment, asSrt } from '~/lib/transcript'
import { usePreferenceProvider } from '~/providers/Preference';
import { ReactComponent as DeleteIcon } from '~/icons/cancel.svg';
import { ModifyState } from '~/lib/utils'
import { ErrorModalContext } from '~/providers/ErrorModal'
import ReactMarkdown from 'react-markdown';

interface ChatProps {
segments: Segment[] | null;
Expand All @@ -25,6 +27,7 @@ const Chat: React.FC<ChatProps> = ({ segments, messages, setMessages }) => {
const preference = usePreferenceProvider();
const [inputMessage, setInputMessage] = useState('');
const [isLoading, setIsLoading] = useState(false);
const { setState: setErrorModal } = useContext(ErrorModalContext)
const chatContainerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
Expand Down Expand Up @@ -68,8 +71,11 @@ const Chat: React.FC<ChatProps> = ({ segments, messages, setMessages }) => {
ollama_base_url: preference.chatModelOptions.ollama_base_url,
ollama_model: preference.chatModelOptions.ollama_model,
google_api_key: preference.chatModelOptions.gemini_api_key,
max_output_tokens: 1024,
gemini_model: preference.chatModelOptions.gemini_model,
max_output_tokens: 2048,
};

console.log(chatMessages)

const result = await invoke<string>('process_chat_message', {
options,
Expand All @@ -87,7 +93,7 @@ const Chat: React.FC<ChatProps> = ({ segments, messages, setMessages }) => {

} catch (error) {
console.error('Error in chat:', error);
alert(t('common.chat-error'));
setErrorModal({open: true, log: String(error)})
} finally {
setIsLoading(false);
}
Expand All @@ -102,23 +108,23 @@ const Chat: React.FC<ChatProps> = ({ segments, messages, setMessages }) => {
<h2 className="text-2xl font-bold mb-4 text-base-content">{t('common.chat')}</h2>

<div ref={chatContainerRef} className="flex-grow overflow-auto mb-4 space-y-4">
{messages.map((message) => (
<div key={message.id} className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}>
<div className={`max-w-3/4 p-3 rounded-lg relative group ${
message.role === 'user'
? 'bg-primary text-primary-content'
: 'bg-neutral text-neutral-content'
}`}>
{message.content}
<button
onClick={() => handleDeleteMessage(message.id)}
className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
>
<DeleteIcon className="w-4 h-4 text-base-content hover:text-error" />
</button>
</div>
</div>
))}
{messages.map((message) => (
<div key={message.id} className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}>
<div className={`max-w-3/4 p-3 rounded-lg relative group ${
message.role === 'user'
? 'bg-primary text-primary-content'
: 'bg-neutral text-neutral-content'
}`}>
<ReactMarkdown>{message.content}</ReactMarkdown>
<button
onClick={() => handleDeleteMessage(message.id)}
className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity duration-200"
>
<DeleteIcon className="w-4 h-4 text-base-content hover:text-error" />
</button>
</div>
</div>
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-neutral text-neutral-content p-3 rounded-lg">
Expand Down
2 changes: 1 addition & 1 deletion desktop/src/components/Params.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ export default function ModelOptions({ options, setOptions }: ParamsProps) {
</div>
<input
value={options.max_sentence_len}
onChange={(e) => setOptions({ ...options, max_sentence_len: parseInt(e.target.value) ?? 1 })}
onChange={(e) => setOptions({ ...options, max_sentence_len: parseInt(e.target.value) ?? 92 })}
className="input input-bordered"
type="number"
/>
Expand Down
82 changes: 35 additions & 47 deletions desktop/src/components/Summary.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from 'react'
import React, { useState, useRef, useEffect, useContext } from 'react'
import { useTranslation } from 'react-i18next'
import { ModifyState } from '~/lib/utils'
import { Segment, asSrt } from '~/lib/transcript'
Expand All @@ -7,23 +7,28 @@ import { usePreferenceProvider } from '~/providers/Preference'
import { useFilesContext } from '~/providers/FilesProvider'
import { getDbManager } from '~/lib/database'
import ReactMarkdown from 'react-markdown'
import { ErrorModalContext } from '~/providers/ErrorModal'

interface SummaryProps {
summary: string
loading: boolean
setSummary: ModifyState<string>
segments: Segment[] | null
summaryPrompt: string
setSummaryPrompt: ModifyState<string>
}

const Summary: React.FC<SummaryProps> = ({ summary, loading, setSummary, segments }) => {
const Summary: React.FC<SummaryProps> = ({ summary, loading, setSummary, segments, summaryPrompt, setSummaryPrompt }) => {
const { t } = useTranslation()
const preference = usePreferenceProvider()
const { files } = useFilesContext()
const [summaryPrompt, setSummaryPrompt] = useState(defaultSummaryPrompt)
const [generatingSummary, setGeneratingSummary] = useState(false)
const [isEditing, setIsEditing] = useState(false)
const [editableSummary, setEditableSummary] = useState(summary)
const editTextareaRef = useRef<HTMLTextAreaElement>(null)
const { setState: setErrorModal } = useContext(ErrorModalContext)

setSummaryPrompt(summaryPrompt || defaultSummaryPrompt)

useEffect(() => {
setEditableSummary(summary)
Expand All @@ -39,7 +44,7 @@ const Summary: React.FC<SummaryProps> = ({ summary, loading, setSummary, segment
if (files.length === 1) {
const dbManager = getDbManager();
await dbManager.update('recording_insights',
{ summary: editableSummary },
{ summary: editableSummary, summary_prompt: summaryPrompt },
'file_name = :fileName',
{ fileName: files[0].name }
);
Expand Down Expand Up @@ -85,7 +90,8 @@ const Summary: React.FC<SummaryProps> = ({ summary, loading, setSummary, segment
ollama_base_url: preference.chatModelOptions.ollama_base_url,
ollama_model: preference.chatModelOptions.ollama_model,
google_api_key: preference.chatModelOptions.gemini_api_key,
max_output_tokens: 1024,
gemini_model: preference.chatModelOptions.gemini_model,
max_output_tokens: 4096,
}

const result = await invoke<string>('process_chat_message', {
Expand All @@ -98,14 +104,13 @@ const Summary: React.FC<SummaryProps> = ({ summary, loading, setSummary, segment
if (files.length === 1) {
const dbManager = getDbManager();
await dbManager.update('recording_insights',
{ summary: result },
{ summary: result, summary_prompt: summaryPrompt },
'file_name = :fileName',
{ fileName: files[0].name }
);
}
} catch (error) {
console.error('Error generating summary:', error)
alert(t('common.summary-generation-error'))
setErrorModal({'open': true, 'log': String(error)})
} finally {
setGeneratingSummary(false)
}
Expand Down Expand Up @@ -175,50 +180,33 @@ const Summary: React.FC<SummaryProps> = ({ summary, loading, setSummary, segment
)
}

const defaultSummaryPrompt = `You are an expert executive assistant tasked with creating a detailed, informative summary of a meeting transcript. Your goal is to extract key insights and relevant information so that people don't need to listen to the recording. Please analyze the provided transcript and create a summary that includes:
- Meeting Overview:
- Main topic or purpose of the meeting
- Key participants (if identifiable)
- Meeting Timeline:
- Main topics discussed and oneliner about them
- Key Points and Decisions:
- List the most important points discussed
- Highlight any decisions made
- Note any significant agreements or disagreements
- Action Items and Next Steps:
- Extract any tasks, assignments, or follow-up actions
- Include responsible parties and deadlines if mentioned
const defaultSummaryPrompt = `Please analyze the meeting transcript and provide a structured summary with the following elements:
- Project Updates (if applicable):
- Summarize current status of any projects discussed
- Note any challenges, progress, or changes in direction
1. Overall meeting structure:
- Start and end times
- Total duration
- Number of main topics discussed
- Important Details:
- Include any critical numbers, dates, or facts mentioned
- Highlight any strategic insights or unique ideas presented
2. Detailed topic-wise breakdown:
- For each main topic:
a. Topic title or brief description
b. Start and end timestamps
c. Duration of discussion
d. Key points discussed (2-3 bullet points)
e. Participants who contributed significantly to this topic
- Questions and Open Issues:
- List any unanswered questions or topics requiring further discussion
3. Information clusters:
- Group related subtopics or recurring themes
- Provide a brief description for each cluster
- List the timestamps where these clusters were discussed
- Overall Summary:
- Provide a detailed, low-level summary of the meeting's outcome and significance
4. Action items and decisions:
- List any clear action items or decisions made
- Include associated timestamps and responsible parties (if mentioned)
Guidelines:
- The speaker diarization is not proper. So use your intelligence about who could be whom.
- If the transcript is short or generic, focus only on relevant points.
- If there's little of substance, state "This meeting contained minimal actionable or strategic content."
- Aim for clarity and conciseness.
- Use bullet points and clear headings for easy scanning.
- If technical terms are used, provide brief explanations if necessary for context.
- Maintain a professional, neutral tone throughout the summary.
- Use only MARKDOWN format for the response
- DO NOT MISS Factual information under any circumstance. This is very important, try to mention all the proper nouns and facts mentioned.
- Following the above mentioned strucutre is not necessary. Think for youself and figure out how can you add value to the user
5. Timeline overview:
- Create a brief timeline of the meeting, showing how topics flowed from one to another
Based on the transcript provided, generate a meeting summary following these guidelines.`
Please organize the summary in a clear, easy-to-read in as MARKDOWN format.`

export default Summary
1 change: 1 addition & 0 deletions desktop/src/lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export const databaseName = 'sqlite:samwise.db'
export const maxConnections = 5;

export const supportedChatStrategies = ["ollama", "gemini"]
export const supportedGeminiModels = ["gemini-1.5-flash", "gemini-1.5-pro"]
56 changes: 34 additions & 22 deletions desktop/src/pages/home/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import AudioInput from '~/pages/home/AudioInput'
import AudioPlayer from './AudioPlayer'
import ProgressPanel from './ProgressPanel'
import { viewModel } from './viewModel'
import { useEffect } from 'react'
import { useEffect, useRef } from 'react'
import * as webviewWindow from '@tauri-apps/api/webviewWindow'
import AudioDeviceInput from '~/components/AudioDeviceInput'
import { ReactComponent as FileIcon } from '~/icons/file.svg'
Expand All @@ -21,6 +21,9 @@ import Chat from '~/components/Chat'
export default function Home() {
const { t } = useTranslation()
const vm = viewModel()
const transcriptRef = useRef<HTMLDivElement>(null)
const summaryRef = useRef<HTMLDivElement>(null)
const chatRef = useRef<HTMLDivElement>(null)

async function showWindow() {
const currentWindow = webviewWindow.getCurrentWebviewWindow()
Expand All @@ -34,6 +37,18 @@ export default function Home() {
showWindow()
}, [])

useEffect(() => {
setTimeout(() => {
if (vm.activeTab === 'transcript' && transcriptRef.current) {
transcriptRef.current.scrollIntoView({ behavior: 'smooth' });
} else if (vm.activeTab === 'summary' && summaryRef.current) {
summaryRef.current.scrollIntoView({ behavior: 'smooth' });
} else if (vm.activeTab === 'chat' && chatRef.current) {
chatRef.current.scrollIntoView({ behavior: 'smooth' });
}
}, 100); // Add a small delay to ensure refs are set
}, [vm.activeTab, vm.summary]);

return (
<Layout>
<div role="tablist" className="tabs tabs-lifted flex m-auto mt-5">
Expand Down Expand Up @@ -79,7 +94,7 @@ export default function Home() {
<div className="h-20" />
{vm.loading && <ProgressPanel isAborting={vm.isAborting} onAbort={vm.onAbort} progress={vm.progress} />}
{(vm.segments || vm.loading) && (
<div className="flex flex-col mt-5 items-center w-[90%] max-w-[1000px] h-[84vh] m-auto">
<div ref={transcriptRef} className="flex flex-col mt-5 items-center w-[90%] max-w-[1000px] h-[84vh] m-auto">
<div className="tabs tabs-boxed mb-4">
<a
className={`tab ${vm.activeTab === 'transcript' ? 'tab-active' : ''}`}
Expand Down Expand Up @@ -110,19 +125,25 @@ export default function Home() {
/>
)}
{vm.activeTab === 'summary' && (
<Summary
summary={vm.summary}
loading={vm.loading}
setSummary={vm.setSummary}
segments={vm.segments}
/>
<div ref={summaryRef} className="flex flex-col mt-5 items-center w-[100%] max-w-[1000px] h-[84vh] m-auto">
<Summary
summary={vm.summary}
loading={vm.loading}
setSummary={vm.setSummary}
segments={vm.segments}
summaryPrompt={vm.summaryPrompt ? vm.summaryPrompt : ''}
setSummaryPrompt={vm.setSummaryPrompt}
/>
</div>
)}
{vm.activeTab === 'chat' && (
<Chat
segments={vm.segments}
messages={vm.messages}
setMessages={vm.setMessages}
/>
<div ref={chatRef} className="flex flex-col mt-5 items-center w-[100%] max-w-[1000px] h-[84vh] m-auto">
<Chat
segments={vm.segments}
messages={vm.messages}
setMessages={vm.setMessages}
/>
</div>
)}
</div>
)}
Expand All @@ -135,15 +156,6 @@ export default function Home() {
<div className="">
<AudioDeviceInput device={vm.inputDevice} setDevice={vm.setInputDevice} devices={vm.devices} type="input" />
<AudioDeviceInput device={vm.outputDevice} setDevice={vm.setOutputDevice} devices={vm.devices} type="output" />
{/* <label className="label cursor-pointer mt-2 mb-5">
<span className="label-text">{t('common.save-record-in-documents-folder')}</span>
<input
type="checkbox"
className="toggle toggle-primary"
onChange={(e) => vm.preference.setStoreRecordInDocuments(e.target.checked)}
checked={vm.preference.storeRecordInDocuments}
/>
</label> */}
</div>
{!vm.isRecording && (
<button onMouseDown={() => vm.startRecord()} className="btn btn-primary mt-3">
Expand Down
Loading

0 comments on commit 6b5c756

Please sign in to comment.