diff --git a/README.md b/README.md index 3a7b601..6df8389 100644 --- a/README.md +++ b/README.md @@ -10,24 +10,28 @@ Compare prompts -## TODO +## TODO v0.1.0 - ~~model/provider selection dropdown~~ - ~~remove selected model button~~ - ~~initial results table skeleton~~ - ~~info button top left on eval page~~ - info button top left on compare page +- info button top left on transcription page - ~~header component with the nav dropdown and github icon link~~ - ? or just add the github link to the dropdown? - ~~parallel requests and streaming for compare page~~ - ~~results table outputs should be equal width per model~~ - results table resizable -- Github logo link to repo -- fix margin of header icons +- ~~Github logo link to repo~~ +- ~~fix margin of header icons~~ +- ~~add all model options to the dual llm comparison chat~~ + - about page - feedback / submit bug / feature request -- stt page with Groq whisper +- ~~stt page with Groq whisper~~ +- transcription optional arguments v0.2.0 - auth / sign up / login pages diff --git a/app/api/chat/anthropic/route.ts b/app/api/chat/anthropic/route.ts new file mode 100644 index 0000000..5fe5307 --- /dev/null +++ b/app/api/chat/anthropic/route.ts @@ -0,0 +1,16 @@ +import { anthropic } from '@ai-sdk/anthropic'; +import { streamText } from 'ai'; + +export const maxDuration = 30; + +export async function POST(req: Request) { + const { messages, model } = await req.json(); + + const result = await streamText({ + model: anthropic(model || 'claude-3-opus-20240229'), + messages, + }); + + return result.toAIStreamResponse(); +} + diff --git a/app/api/transcribe/route.ts b/app/api/transcribe/route.ts index 5aabfe8..858c146 100644 --- a/app/api/transcribe/route.ts +++ b/app/api/transcribe/route.ts @@ -22,10 +22,11 @@ export async function POST(request: NextRequest) { const responseFormat = formData.get('responseFormat') as string | null; const temperature = formData.get('temperature') as string | null; const language = formData.get('language') as string | null; + const model = formData.get('model') as string | null; const transcriptionOptions: any = { file: new File([buffer], file.name, { type: file.type }), - model: "distil-whisper-large-v3-en", + model: model === 'distil-whisper-large-v3-en' ? 'distil-whisper-large-v3-en' : 'whisper-large-v3', }; // Add optional parameters if provided @@ -46,6 +47,7 @@ export async function POST(request: NextRequest) { } else { // Default to JSON response with just the text return NextResponse.json({ text: transcription.text }); + //return NextResponse.json({ text: JSON.stringify(transcription) }); } } catch (error) { console.error('Transcription error:', error); diff --git a/app/chat-with-comparison/page.tsx b/app/chat-with-comparison/page.tsx index 88737fa..421509d 100644 --- a/app/chat-with-comparison/page.tsx +++ b/app/chat-with-comparison/page.tsx @@ -7,6 +7,7 @@ import { ProviderSelect } from '../../components/ProviderSelect'; import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" import { ScrollArea } from "@/components/ui/scroll-area" +import InfoButton from '@/components/InfoButton'; export default function DualChat() { const [provider1, setProvider1] = useState('openai'); @@ -32,7 +33,16 @@ export default function DualChat() { }; return ( -
+
+ +
    +
  1. Select two models you want to compare
  2. +
  3. Start chatting with the models like you normally would
  4. +
+

Dual Model Chat Comparison

{[ @@ -72,6 +82,6 @@ export default function DualChat() {
-
+ ); } \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index fe6088e..0d6cf4f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -33,6 +33,11 @@ const AVAILABLE_MODELS: Model[] = [ { id: 'claude-3-sonnet-20240229', name: 'Claude 3 Sonnet', provider: 'anthropic' }, ]; + + + + + interface EvaluationResult { prompt: string; results: { @@ -116,7 +121,16 @@ export default function Home() { return (
- + +
    +
  1. Select the models you want to compare
  2. +
  3. Enter prompts you want to evaluate for
  4. +
  5. Click Evaluate and wait for the generated responses
  6. +
+

LLM Evaluations

{error && ( diff --git a/app/transcribe/page.tsx b/app/transcribe/page.tsx index b7a0bee..760cc91 100644 --- a/app/transcribe/page.tsx +++ b/app/transcribe/page.tsx @@ -1,6 +1,6 @@ 'use client' -import React, { useState } from 'react'; +import React, { useState, useRef } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; @@ -19,10 +19,31 @@ export default function TranscribePage() { const [language, setLanguage] = useState(''); const [prompt, setPrompt] = useState(''); const [temperature, setTemperature] = useState(''); + const [fileDetails, setFileDetails] = useState<{ name: string; duration: string; transcriptionTime: string } | null>(null); + const audioRef = useRef(null); + const [model, setModel] = useState('whisper-large-v3'); const handleFileChange = (event: React.ChangeEvent) => { if (event.target.files && event.target.files[0]) { - setFile(event.target.files[0]); + const selectedFile = event.target.files[0]; + setFile(selectedFile); + setFileDetails(null); // Reset file details when a new file is selected + + // Create a URL for the audio file + const audioUrl = URL.createObjectURL(selectedFile); + if (audioRef.current) { + audioRef.current.src = audioUrl; + audioRef.current.onloadedmetadata = () => { + const duration = audioRef.current?.duration || 0; + const minutes = Math.floor(duration / 60); + const seconds = Math.floor(duration % 60); + setFileDetails({ + name: selectedFile.name, + duration: `${minutes}:${seconds.toString().padStart(2, '0')}`, + transcriptionTime: '', + }); + }; + } } }; @@ -34,6 +55,7 @@ export default function TranscribePage() { setIsLoading(true); setError(''); + const startTime = Date.now(); try { const formData = new FormData(); @@ -42,6 +64,7 @@ export default function TranscribePage() { if (language) formData.append('language', language); if (prompt) formData.append('prompt', prompt); if (temperature) formData.append('temperature', temperature); + formData.append('model', model); const response = await fetch('/api/transcribe', { method: 'POST', @@ -54,6 +77,13 @@ export default function TranscribePage() { const data = await response.json(); setTranscription(verboseJson ? JSON.stringify(data, null, 2) : data.text); + + const endTime = Date.now(); + const transcriptionTime = ((endTime - startTime) / 1000).toFixed(2); + setFileDetails(prevDetails => ({ + ...prevDetails!, + transcriptionTime: `${transcriptionTime} seconds`, + })); } catch (err) { setError('An error occurred during transcription. Please try again.'); } finally { @@ -63,7 +93,17 @@ export default function TranscribePage() { return (
- + +
    +
  1. Select an audio file
  2. +
  3. Select any optional settings if you want
  4. +
  5. Click Transcribe Audio
  6. +
  7. Wait for the transcription to finish
  8. +
+

Audio Transcription

{error && ( @@ -78,6 +118,25 @@ export default function TranscribePage() { onChange={handleFileChange} className="mb-4" /> + {fileDetails && ( +
+

File Details:

+

Name: {fileDetails.name}

+

Duration: {fileDetails.duration}

+ {fileDetails.transcriptionTime && ( +

Time to transcribe: {fileDetails.transcriptionTime}

+ )} +
+ )} +
+
); } diff --git a/components/InfoButton.tsx b/components/InfoButton.tsx index e2bdc2b..39fa571 100644 --- a/components/InfoButton.tsx +++ b/components/InfoButton.tsx @@ -10,10 +10,17 @@ import { } from '@/components/ui/alert-dialog'; import { Button } from '@/components/ui/button'; import { QuestionMarkCircledIcon } from '@radix-ui/react-icons'; +import { ReactNode } from 'react'; -export default function InfoButton() { +interface InfoButtonProps { + title: string; + children: ReactNode; + className?: string; +} + +export default function InfoButton({ title, children, className = 'absolute top-4 left-4 z-10' }: InfoButtonProps) { return ( -
+