Enhance Gemini web search directive for clarity, improve ChatArea streaming message handling, and ensure conversation saving logic in ChatWindow. Update AIFunction enum and AIFunctionService for RequestWebSearch functionality. Modify ConversationFileSystemService to include function call response handling.

This commit is contained in:
Andrew Beal 2025-10-07 00:24:45 +01:00
parent 62104f0abd
commit 6f0e6d0342
6 changed files with 54 additions and 26 deletions

View file

@ -71,11 +71,19 @@ export class Gemini implements IAIClass {
text: this.aiPrompt.systemInstruction()
},
{
text: `IMPORTANT: When you need current information from the web (recent events, news, current prices, weather, etc.), you should:
1. First call the 'request_web_search' function to indicate you need web access
2. After that, you'll be given access to Google Search
3. Once you have the information from the search, you can answer the user's question
4. Subsequent communication will return to providing custom function calls`
text: `## IMPORTANT: Web Search Directive
**You *must* proactively use the web search tool whenever a user's query requires current, real-time, or frequently changing information.** This includes, but is not limited to:
- Current date or time.
- Current weather conditions or forecasts.
- Recent news, events, or happenings.
- Up-to-date prices, statistics, or factual data that is dynamic.
- Any information where "current," "latest," or "today's" is implied or explicitly requested.
When you need current information from the web, you *must* follow these steps:
1. First call the \`request_web_search\` function with a clear and concise \`reasoning\` explaining why web search is needed.
2. After calling this, you will be given access to Google Search.
3. Once you have obtained the necessary information from the search results, use it to formulate your complete and accurate answer.
4. Subsequent interactions will revert to standard function calls or general assistance as appropriate.`
},
{
text: await this.aiPrompt.userInstruction()

View file

@ -171,22 +171,32 @@
</div>
</div>
{:else}
{#if isStreaming && messageIndex === messages.length - 1}
<!-- Streaming message -->
{#if message.content && message.content !== ''}
<div class="message-container assistant">
<div class="message-bubble assistant">
<div class="markdown-content fade-in-fast" class:streaming={isStreaming && messageIndex === messages.length - 1}>
<!-- Streaming message: use action for initialization -->
{#if isStreaming && messageIndex === messages.length - 1}
<div class="markdown-content fade-in-fast streaming">
<div use:streamingAction={`${message.role}-${messageIndex}`} class="streaming-content"></div>
<StreamingIndicator/>
<ChatAreaThought/>
{:else}
<!-- Static message: use traditional rendering -->
{@html getStaticHTML(message, messageIndex)}
{/if}
</div>
</div>
</div>
{/if}
<StreamingIndicator/>
<ChatAreaThought/>
{:else}
<!-- Static message: use traditional rendering -->
{#if message.content && message.content !== ''}
<div class="message-container assistant">
<div class="message-bubble assistant">
<div class="markdown-content fade-in-fast">
{@html getStaticHTML(message, messageIndex)}
</div>
</div>
</div>
{/if}
{/if}
{/if}
{/if}
{/each}

View file

@ -97,6 +97,7 @@
: msg
);
isStreaming = false;
await conversationService.saveConversation(conversation);
break;
}
@ -115,12 +116,17 @@
if (chunk.isComplete) {
isStreaming = false;
conversation.contents = conversation.contents.map((msg, messageIndex) =>
messageIndex === aiMessageIndex
? { ...msg, content: accumulatedContent }
: msg
);
// Only save the message if it has content or a function call
if (accumulatedContent.trim() !== "" || capturedFunctionCall) {
conversation.contents = conversation.contents.map((msg, messageIndex) =>
messageIndex === aiMessageIndex
? { ...msg, content: accumulatedContent }
: msg
);
} else {
// Remove the empty placeholder message
conversation.contents = conversation.contents.filter((_, messageIndex) => messageIndex !== aiMessageIndex);
}
await conversationService.saveConversation(conversation);
}
}

View file

@ -1,3 +1,5 @@
export enum AIFunction {
ListVaultFiles = "list_vault_files"
ListVaultFiles = "list_vault_files",
RequestWebSearch = "request_web_search"
}

View file

@ -13,10 +13,12 @@ export class AIFunctionService {
public async performAIFunction(functionCall: AIFunctionCall): Promise<AIFunctionResponse> {
switch (functionCall.name) {
case AIFunction.ListVaultFiles:
return new AIFunctionResponse(
functionCall.name,
await this.listVaultFiles()
);
return new AIFunctionResponse(functionCall.name, await this.listVaultFiles());
// this is only used by gemini
case AIFunction.RequestWebSearch:
return new AIFunctionResponse(functionCall.name, {})
default:
const error = `Unknown function request ${functionCall.name}`
console.error(error);

View file

@ -77,7 +77,7 @@ export class ConversationFileSystemService {
conversation.created = new Date(data.created);
conversation.contents = data.contents.map(content => {
return new ConversationContent(
content.role, content.content, new Date(content.timestamp), content.isFunctionCall);
content.role, content.content, new Date(content.timestamp), content.isFunctionCall, content.isFunctionCallResponse);
});
conversations.push(conversation);
}