| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- <script lang="ts">
- import { CircleCheckBig, Plus, X } from '@lucide/svelte';
- import * as modelsApi from '$lib/api/models';
- import { GlassPane } from '$lib/components/common/glass-pane';
- import { TestResult } from '$lib/components/common/test-result';
- import { Button } from '$lib/components/controls/button';
- import { Spinner } from '$lib/components/controls/spinner';
- import { Textarea } from '$lib/components/controls/textarea';
- import type { Model } from '$lib/types/entities';
- type TestDoc = { id: number; text: string; score?: number };
- let { workspaceId, model }: { workspaceId: string; model: Model } = $props();
- let query = $state('wireless noise cancelling headphones');
- let docId = $state(3);
- let docs = $state<TestDoc[]>([
- { id: 0, text: 'Fresh apples are available at the local farmers market.' },
- { id: 1, text: 'This speaker delivers powerful sound for outdoor parties.' },
- { id: 2, text: 'These headphones offer active noise cancellation and Bluetooth connectivity.' },
- ]);
- let response = $state<modelsApi.RerankResponse | undefined>(undefined);
- let loading = $state(false);
- let error = $state<{ type: string; message: string } | undefined>(undefined);
- const rerankResponse = $derived(response?.response);
- $effect(() => {
- if (model) {
- response = undefined;
- error = undefined;
- }
- });
- const runRerank = async () => {
- if (!model || !query.trim() || loading) return;
- if (!query.trim() || docs.filter((d) => d.text.trim()).length < 2) return;
- error = undefined;
- response = undefined;
- loading = true;
- try {
- response = await modelsApi.testRerank(workspaceId, model.id, {
- query: query.trim(),
- documents: docs.map((d) => d.text.trim()).filter((d) => d),
- });
- for (const item of response?.response?.results || []) {
- docs[item.index].score = item.relevance_score;
- }
- docs = [...docs].sort((a, b) => (b.score ?? 0) - (a.score ?? 0));
- } catch (err) {
- error =
- err instanceof Error
- ? { type: 'error', message: err.message }
- : { type: 'error', message: 'An error occurred' };
- } finally {
- loading = false;
- }
- };
- const addDoc = () => {
- docs = [...docs, { id: docId, text: '' }];
- docId += 1;
- };
- const removeDoc = (idx: number) => {
- if (docs.length <= 2) return;
- docs = docs.filter((_, i) => i !== idx);
- };
- </script>
- <GlassPane class="overflow-hidden bg-black/25">
- <div class="bg-white/5 p-4">
- <div class="flex items-center justify-between">
- <span class="text-lg font-thin">Rerank</span>
- <Button
- disabled={!query.trim() || docs.filter((d) => d.text.trim()).length < 2 || loading}
- title="Rerank"
- onclick={runRerank}
- >
- {#if loading}
- <Spinner />
- {/if}
- Rerank
- </Button>
- </div>
- </div>
- <textarea
- class="h-full w-full resize-none p-4 font-mono text-sm outline-none"
- placeholder="Enter query..."
- rows={2}
- bind:value={query}
- >
- </textarea>
- <div class="bg-white/5 p-4">
- <div class="flex items-center justify-between">
- <span class="text-lg font-thin">Documents</span>
- {#if response}
- <div class="flex items-center gap-2 text-sm">
- <CircleCheckBig class="text-green-500" size={14} />
- {#if response.latencyMs}
- <span class="text-white/25">Latency:</span>
- <span class="text-white/75">{response.latencyMs}ms</span>
- {/if}
- {#if rerankResponse}
- <span class="text-white/25">Prompt tokens:</span>
- <span class="text-white/75">
- {rerankResponse.meta?.tokens.input_tokens ?? rerankResponse.usage?.prompt_tokens}
- </span>
- <span class="text-white/25">Total tokens:</span>
- <span class="text-white/75">
- {rerankResponse.meta?.billed_units.total_tokens ?? rerankResponse.usage?.total_tokens}
- </span>
- {/if}
- </div>
- {/if}
- </div>
- </div>
- {#each docs as doc, idx (doc.id)}
- <div class="flex items-start gap-2 border-b border-white/5 p-2 py-4 last:border-b-0">
- <div class="flex w-12 flex-col items-center justify-center">
- {#if doc.score !== undefined}
- <div class="text-sm text-white/50">#{idx + 1}</div>
- <div class="line-clamp-3 text-center font-mono text-xs break-all">
- {doc.score.toFixed(10)}
- </div>
- {/if}
- </div>
- <Textarea
- class="flex-1 resize-none text-sm"
- placeholder="Paste document here..."
- rows={2}
- bind:value={doc.text}
- />
- <button
- class="mt-1 shrink-0 opacity-25 hover:opacity-75 disabled:cursor-not-allowed disabled:opacity-20"
- disabled={docs.length <= 2}
- onclick={() => removeDoc(idx)}
- >
- <X size={14} strokeWidth={2} />
- </button>
- </div>
- {/each}
- <div class="p-2 text-right">
- <Button variant="ghost" onclick={() => addDoc()}>
- <Plus size={14} strokeWidth={2} />
- Add
- </Button>
- </div>
- <TestResult {error} {loading} {response}>
- {#snippet result()}
- <div class="flex h-48 items-center justify-center text-sm text-white/50">No errors</div>
- {/snippet}
- </TestResult>
- </GlassPane>
|