Merge pull request #18 from TGlide/web-search

add web search
This commit is contained in:
Thomas G. Lopes 2025-06-18 15:29:08 +01:00 committed by GitHub
commit c6a4fb720b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 35 additions and 2 deletions

View file

@ -58,7 +58,7 @@ TODO: add instructions
### Extra ### Extra
- [ ] Web Search - [x] Web Search
- [ ] Chat branching - [ ] Chat branching
- [ ] Regenerate message - [ ] Regenerate message
- [ ] Image generation - [ ] Image generation

View file

@ -131,6 +131,7 @@ export const createAndAddMessage = mutation({
role: args.role, role: args.role,
conversation_id: conversationId, conversation_id: conversationId,
session_token: args.session_token, session_token: args.session_token,
web_search_enabled: args.web_search_enabled,
images: args.images, images: args.images,
}); });

View file

@ -41,6 +41,7 @@ export const create = mutation({
model_id: v.optional(v.string()), model_id: v.optional(v.string()),
provider: v.optional(providerValidator), provider: v.optional(providerValidator),
token_count: v.optional(v.number()), token_count: v.optional(v.number()),
web_search_enabled: v.optional(v.boolean()),
// Optional image attachments // Optional image attachments
images: v.optional( images: v.optional(
v.array( v.array(
@ -84,6 +85,7 @@ export const create = mutation({
model_id: args.model_id, model_id: args.model_id,
provider: args.provider, provider: args.provider,
token_count: args.token_count, token_count: args.token_count,
web_search_enabled: args.web_search_enabled,
// Optional image attachments // Optional image attachments
images: args.images, images: args.images,
}), }),

View file

@ -70,5 +70,6 @@ export default defineSchema({
), ),
cost_usd: v.optional(v.number()), cost_usd: v.optional(v.number()),
generation_id: v.optional(v.string()), generation_id: v.optional(v.string()),
web_search_enabled: v.optional(v.boolean()),
}).index('by_conversation', ['conversation_id']), }).index('by_conversation', ['conversation_id']),
}); });

View file

@ -2,4 +2,5 @@ import { createPersistedObj } from '$lib/spells/persisted-obj.svelte';
export const settings = createPersistedObj('settings', { export const settings = createPersistedObj('settings', {
modelId: undefined as string | undefined, modelId: undefined as string | undefined,
webSearchEnabled: false,
}); });

View file

@ -21,6 +21,7 @@ const reqBodySchema = z.object({
session_token: z.string(), session_token: z.string(),
conversation_id: z.string().optional(), conversation_id: z.string().optional(),
web_search_enabled: z.boolean().optional(),
images: z images: z
.array( .array(
z.object({ z.object({
@ -292,10 +293,16 @@ ${attachedRules.map((r) => `- ${r.name}: ${r.rule}`).join('\n')}`,
return; return;
} }
// Check if web search is enabled for the last user message
const lastUserMessage = messages.filter(m => m.role === 'user').pop();
const webSearchEnabled = lastUserMessage?.web_search_enabled ?? false;
const modelId = webSearchEnabled ? `${model.model_id}:online` : model.model_id;
const streamResult = await ResultAsync.fromPromise( const streamResult = await ResultAsync.fromPromise(
openai.chat.completions.create( openai.chat.completions.create(
{ {
model: model.model_id, model: modelId,
messages: messagesToSend, messages: messagesToSend,
temperature: 0.7, temperature: 0.7,
stream: true, stream: true,
@ -537,6 +544,7 @@ export const POST: RequestHandler = async ({ request }) => {
content_html: '', content_html: '',
role: 'user', role: 'user',
images: args.images, images: args.images,
web_search_enabled: args.web_search_enabled,
session_token: sessionToken, session_token: sessionToken,
}), }),
(e) => `Failed to create conversation: ${e}` (e) => `Failed to create conversation: ${e}`
@ -572,6 +580,7 @@ export const POST: RequestHandler = async ({ request }) => {
model_id: args.model_id, model_id: args.model_id,
role: 'user', role: 'user',
images: args.images, images: args.images,
web_search_enabled: args.web_search_enabled,
}), }),
(e) => `Failed to create user message: ${e}` (e) => `Failed to create user message: ${e}`
); );

View file

@ -32,6 +32,7 @@
import Settings2Icon from '~icons/lucide/settings-2'; import Settings2Icon from '~icons/lucide/settings-2';
import UploadIcon from '~icons/lucide/upload'; import UploadIcon from '~icons/lucide/upload';
import XIcon from '~icons/lucide/x'; import XIcon from '~icons/lucide/x';
import SearchIcon from '~icons/lucide/search';
import { callGenerateMessage } from '../api/generate-message/call.js'; import { callGenerateMessage } from '../api/generate-message/call.js';
import { callCancelGeneration } from '../api/cancel-generation/call.js'; import { callCancelGeneration } from '../api/cancel-generation/call.js';
import ModelPicker from './model-picker.svelte'; import ModelPicker from './model-picker.svelte';
@ -97,6 +98,7 @@
conversation_id: page.params.id ?? undefined, conversation_id: page.params.id ?? undefined,
model_id: settings.modelId, model_id: settings.modelId,
images: imagesCopy.length > 0 ? imagesCopy : undefined, images: imagesCopy.length > 0 ? imagesCopy : undefined,
web_search_enabled: settings.webSearchEnabled,
}); });
if (res.isErr()) { if (res.isErr()) {
@ -559,6 +561,17 @@
</div> </div>
<div class="flex flex-col gap-2 pr-2 sm:flex-row sm:items-center"> <div class="flex flex-col gap-2 pr-2 sm:flex-row sm:items-center">
<ModelPicker /> <ModelPicker />
<button
type="button"
class={cn(
'border-border flex items-center gap-1 rounded-full border px-2 py-1 text-xs transition-colors',
settings.webSearchEnabled ? 'bg-accent/50' : 'hover:bg-accent/20'
)}
onclick={() => (settings.webSearchEnabled = !settings.webSearchEnabled)}
>
<SearchIcon class="!size-3" />
<span class="whitespace-nowrap">Web search</span>
</button>
{#if currentModelSupportsImages} {#if currentModelSupportsImages}
<button <button
type="button" type="button"

View file

@ -46,6 +46,12 @@
settings.modelId = lastMessage.model_id; settings.modelId = lastMessage.model_id;
} }
// Auto-enable/disable web search based on last user message
const lastUserMessage = messages.data.filter((m) => m.role === 'user').pop();
if (lastUserMessage) {
settings.webSearchEnabled = Boolean(lastUserMessage.web_search_enabled);
}
changedRoute = false; changedRoute = false;
}); });
</script> </script>