Compare commits

..

27 Commits

Author SHA1 Message Date
Nicolas Mowen
ba4d4d242a Add thumbnail images to object results 2026-02-20 15:06:20 -07:00
Nicolas Mowen
445bfe4e33 Add support for markdown tables 2026-02-20 14:15:29 -07:00
Nicolas Mowen
66eb1148e6 Fix loading 2026-02-20 14:15:29 -07:00
Nicolas Mowen
0e750fe999 Cleanup UI bubbles 2026-02-20 14:15:29 -07:00
Nicolas Mowen
427db766d2 Cleanup UI and prompt 2026-02-20 14:15:29 -07:00
Nicolas Mowen
15add1e542 Cleanup 2026-02-20 14:15:29 -07:00
Nicolas Mowen
82a0d0134e Add sub label to event tool filtering 2026-02-20 14:15:29 -07:00
Nicolas Mowen
5f141b65c9 Implement message editing 2026-02-20 14:15:29 -07:00
Nicolas Mowen
aab7070e79 Improve default behavior 2026-02-20 14:15:29 -07:00
Nicolas Mowen
e9b234d4a6 Improvements to UI 2026-02-20 14:15:29 -07:00
Nicolas Mowen
641b24e819 Add copy button 2026-02-20 14:15:29 -07:00
Nicolas Mowen
d554d09c32 Fix tool calling 2026-02-20 14:15:29 -07:00
Nicolas Mowen
826554e132 Undo 2026-02-20 14:15:29 -07:00
Nicolas Mowen
bfdd6aaf64 Full streaming support 2026-02-20 14:15:28 -07:00
Nicolas Mowen
985b7e7294 Support streaming 2026-02-20 14:13:54 -07:00
Nicolas Mowen
bb9a315e85 Improve UI handling 2026-02-20 14:13:54 -07:00
Nicolas Mowen
964d1331f9 Add title 2026-02-20 14:13:54 -07:00
Nicolas Mowen
c16e83ff9c Show tool calls separately from message 2026-02-20 14:13:54 -07:00
Nicolas Mowen
ee0322b132 More time parsing improvements 2026-02-20 14:13:54 -07:00
Nicolas Mowen
a386bd9e4d Reduce fields in response 2026-02-20 14:13:54 -07:00
Nicolas Mowen
27f3db1f9a Adjust timing format 2026-02-20 14:13:54 -07:00
Nicolas Mowen
d9f7aadf2b Improvements 2026-02-20 14:13:54 -07:00
Nicolas Mowen
e1a3fcdebc Add markdown 2026-02-20 14:13:54 -07:00
Nicolas Mowen
6106f54063 processing 2026-02-20 14:13:54 -07:00
Nicolas Mowen
23e26f3248 Add chat history 2026-02-20 14:13:54 -07:00
Nicolas Mowen
d7d96ce725 Add basic chat page with entry 2026-02-20 14:13:54 -07:00
Nicolas Mowen
ee80475528 Set model in llama.cpp config 2026-02-20 14:13:28 -07:00
4 changed files with 88 additions and 2 deletions

View File

@ -69,6 +69,7 @@ class LlamaCppClient(GenAIClient):
# Build request payload with llama.cpp native options
payload = {
"model": self.genai_config.model,
"messages": [
{
"role": "user",
@ -120,7 +121,7 @@ class LlamaCppClient(GenAIClient):
elif tool_choice == "required":
openai_tool_choice = "required"
payload: dict[str, Any] = {"messages": messages}
payload: dict[str, Any] = {"messages": messages, "model": self.genai_config.model}
if stream:
payload["stream"] = True
if tools:

View File

@ -0,0 +1,42 @@
import { useApiHost } from "@/api";
type ChatEventThumbnailsRowProps = {
events: { id: string }[];
};
/**
* Horizontal scroll row of event thumbnail images for chat (e.g. after search_objects).
* Renders nothing when events is empty.
*/
export function ChatEventThumbnailsRow({
events,
}: ChatEventThumbnailsRowProps) {
const apiHost = useApiHost();
if (events.length === 0) return null;
return (
<div className="flex min-w-0 max-w-full flex-col gap-1 self-start">
<div className="scrollbar-container min-w-0 overflow-x-auto">
<div className="flex w-max gap-2">
{events.map((event) => (
<a
key={event.id}
href={`/explore?event_id=${event.id}`}
target="_blank"
rel="noopener noreferrer"
className="relative aspect-square size-32 shrink-0 overflow-hidden rounded-lg"
>
<img
className="size-full object-cover"
src={`${apiHost}api/events/${event.id}/thumbnail.webp`}
alt=""
loading="lazy"
/>
</a>
))}
</div>
</div>
</div>
);
}

View File

@ -4,10 +4,14 @@ import { FaArrowUpLong } from "react-icons/fa6";
import { useTranslation } from "react-i18next";
import { useState, useCallback } from "react";
import axios from "axios";
import { ChatEventThumbnailsRow } from "@/components/chat/ChatEventThumbnailsRow";
import { MessageBubble } from "@/components/chat/ChatMessage";
import { ToolCallBubble } from "@/components/chat/ToolCallBubble";
import type { ChatMessage } from "@/types/chat";
import { streamChatCompletion } from "@/utils/chatUtil";
import {
getEventIdsFromSearchObjectsToolCalls,
streamChatCompletion,
} from "@/utils/chatUtil";
export default function ChatPage() {
const { t } = useTranslation(["views/chat"]);
@ -118,6 +122,15 @@ export default function ChatPage() {
msg.role === "user" || !isLoading || i < messages.length - 1
}
/>
{msg.role === "assistant" &&
(() => {
const isComplete = !isLoading || i < messages.length - 1;
if (!isComplete) return null;
const events = getEventIdsFromSearchObjectsToolCalls(
msg.toolCalls,
);
return <ChatEventThumbnailsRow events={events} />;
})()}
</div>
);
})}

View File

@ -161,3 +161,33 @@ export async function streamChatCompletion(
onDone();
}
}
/**
* Parse search_objects tool call response(s) into event ids for thumbnails.
*/
export function getEventIdsFromSearchObjectsToolCalls(
toolCalls: ToolCall[] | undefined,
): { id: string }[] {
if (!toolCalls?.length) return [];
const results: { id: string }[] = [];
for (const tc of toolCalls) {
if (tc.name !== "search_objects" || !tc.response?.trim()) continue;
try {
const parsed = JSON.parse(tc.response) as unknown;
if (!Array.isArray(parsed)) continue;
for (const item of parsed) {
if (
item &&
typeof item === "object" &&
"id" in item &&
typeof (item as { id: unknown }).id === "string"
) {
results.push({ id: (item as { id: string }).id });
}
}
} catch {
// ignore parse errors
}
}
return results;
}