feat: added a view image ai command
This commit is contained in:
@@ -292,7 +292,8 @@ export class ChatEngine {
|
||||
args: [],
|
||||
});
|
||||
|
||||
if (result.rows.length > 0) {
|
||||
// Return saved prompt if it exists and is non-empty
|
||||
if (result.rows.length > 0 && result.rows[0].value) {
|
||||
return result.rows[0].value as string;
|
||||
}
|
||||
|
||||
@@ -300,7 +301,8 @@ export class ChatEngine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default system prompt for new conversations
|
||||
* Set default system prompt for new conversations.
|
||||
* Pass empty string to reset to built-in default.
|
||||
*/
|
||||
async setDefaultSystemPrompt(prompt: string): Promise<void> {
|
||||
const client = this.db.getLocalClient();
|
||||
@@ -308,6 +310,15 @@ export class ChatEngine {
|
||||
throw new Error('Database not initialized');
|
||||
}
|
||||
|
||||
// If empty string, delete the setting to use built-in default
|
||||
if (!prompt || prompt.trim() === '') {
|
||||
await client.execute({
|
||||
sql: `DELETE FROM settings WHERE key = ?`,
|
||||
args: ['chat_system_prompt'],
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
await client.execute({
|
||||
sql: `INSERT OR REPLACE INTO settings (key, value, updated_at) VALUES (?, ?, ?)`,
|
||||
@@ -331,6 +342,7 @@ Available Tools:
|
||||
- list_posts: List posts with optional filtering by status, category, or tags.
|
||||
- get_media: Get information about a specific media file by ID.
|
||||
- list_media: List media files with optional MIME type filtering.
|
||||
- view_image: View an image to analyze its visual content. Use this when you need to describe or analyze what an image looks like.
|
||||
- update_post_metadata: Update a post's title, excerpt, tags, or categories.
|
||||
- update_media_metadata: Update a media file's alt text, caption, or tags.
|
||||
- list_tags: List all tags with post counts.
|
||||
@@ -340,7 +352,8 @@ When answering questions:
|
||||
1. USE THE TOOLS to find information. Never make up data about posts or media.
|
||||
2. If asked about something outside your tools (weather, news, websites), explain that you can only access the user's local blog content.
|
||||
3. Be concise and helpful. Format post information clearly when displaying it.
|
||||
4. If a search returns no results, suggest alternative queries or filters.`;
|
||||
4. If a search returns no results, suggest alternative queries or filters.
|
||||
5. When asked to describe or analyze an image, use the view_image tool to see the actual image content.`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -99,7 +99,22 @@ interface AnthropicContentBlock {
|
||||
name?: string;
|
||||
input?: unknown;
|
||||
tool_use_id?: string;
|
||||
content?: string;
|
||||
content?: string | AnthropicToolResultContent[];
|
||||
source?: {
|
||||
type: 'base64';
|
||||
media_type: string;
|
||||
data: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface AnthropicToolResultContent {
|
||||
type: 'text' | 'image';
|
||||
text?: string;
|
||||
source?: {
|
||||
type: 'base64';
|
||||
media_type: string;
|
||||
data: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface HttpResponse {
|
||||
@@ -441,11 +456,44 @@ export class OpenCodeManager {
|
||||
callbacks.onToolResult({ name: toolName, result });
|
||||
}
|
||||
|
||||
toolResults.push({
|
||||
type: 'tool_result',
|
||||
tool_use_id: toolUseId,
|
||||
content: JSON.stringify(result),
|
||||
});
|
||||
// Check if this is an image result that needs special formatting
|
||||
if (result && typeof result === 'object' && (result as Record<string, unknown>).__isImageResult) {
|
||||
const imageResult = result as {
|
||||
__isImageResult: boolean;
|
||||
success: boolean;
|
||||
mediaType: string;
|
||||
base64: string;
|
||||
metadata: Record<string, unknown>;
|
||||
};
|
||||
|
||||
// Format as Anthropic multimodal content (image + text)
|
||||
const imageContent: AnthropicToolResultContent[] = [
|
||||
{
|
||||
type: 'image',
|
||||
source: {
|
||||
type: 'base64',
|
||||
media_type: imageResult.mediaType,
|
||||
data: imageResult.base64,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify({ success: true, metadata: imageResult.metadata }),
|
||||
},
|
||||
];
|
||||
|
||||
toolResults.push({
|
||||
type: 'tool_result',
|
||||
tool_use_id: toolUseId,
|
||||
content: imageContent,
|
||||
});
|
||||
} else {
|
||||
toolResults.push({
|
||||
type: 'tool_result',
|
||||
tool_use_id: toolUseId,
|
||||
content: JSON.stringify(result),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add assistant response and tool results to messages for next round
|
||||
@@ -702,6 +750,22 @@ export class OpenCodeManager {
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'view_image',
|
||||
description: 'View an image to analyze its visual content. Returns the actual image for visual inspection. Use this when you need to see, describe, or analyze what an image looks like. Only works with image files (not PDFs or other media types).',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
mediaId: { type: 'string', description: 'The unique ID of the image to view' },
|
||||
size: {
|
||||
type: 'string',
|
||||
enum: ['small', 'medium', 'large'],
|
||||
description: 'Image size: small (150px) for quick reference, medium (400px, default) for details, large (800px) for full analysis',
|
||||
},
|
||||
},
|
||||
required: ['mediaId'],
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -872,6 +936,49 @@ export class OpenCodeManager {
|
||||
};
|
||||
}
|
||||
|
||||
case 'view_image': {
|
||||
const mediaId = args.mediaId as string;
|
||||
const size = (args.size as 'small' | 'medium' | 'large') || 'medium';
|
||||
|
||||
// Get media metadata first
|
||||
const mediaItem = await this.mediaEngine.getMedia(mediaId);
|
||||
if (!mediaItem) {
|
||||
return { success: false, error: 'Image not found' };
|
||||
}
|
||||
|
||||
// Verify it's an image (not PDF, video, etc)
|
||||
if (!mediaItem.mimeType.startsWith('image/')) {
|
||||
return { success: false, error: `Cannot view this file type: ${mediaItem.mimeType}. Only images are supported.` };
|
||||
}
|
||||
|
||||
// Get thumbnail data URL (base64)
|
||||
const dataUrl = await this.mediaEngine.getThumbnailDataUrl(mediaId, size);
|
||||
if (!dataUrl) {
|
||||
return { success: false, error: 'Thumbnail not available. Try regenerating thumbnails from Settings.' };
|
||||
}
|
||||
|
||||
// Extract base64 data (remove data:image/webp;base64, prefix)
|
||||
const base64Data = dataUrl.replace(/^data:image\/\w+;base64,/, '');
|
||||
|
||||
// Return special image result format
|
||||
return {
|
||||
__isImageResult: true,
|
||||
success: true,
|
||||
mediaType: 'image/webp',
|
||||
base64: base64Data,
|
||||
metadata: {
|
||||
id: mediaItem.id,
|
||||
filename: mediaItem.filename,
|
||||
originalName: mediaItem.originalName,
|
||||
width: mediaItem.width,
|
||||
height: mediaItem.height,
|
||||
alt: mediaItem.alt,
|
||||
caption: mediaItem.caption,
|
||||
size: size,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return { success: false, error: `Unknown tool: ${name}` };
|
||||
}
|
||||
|
||||
@@ -159,8 +159,8 @@ export const SettingsView: React.FC = () => {
|
||||
// Load AI settings
|
||||
try {
|
||||
const promptResult = await window.electronAPI?.chat.getSystemPrompt();
|
||||
if (promptResult?.success && promptResult.prompt) {
|
||||
setAiSystemPrompt(promptResult.prompt);
|
||||
if (promptResult?.success) {
|
||||
setAiSystemPrompt(promptResult.prompt || '');
|
||||
}
|
||||
|
||||
const keyResult = await window.electronAPI?.chat.getApiKey();
|
||||
@@ -512,8 +512,8 @@ export const SettingsView: React.FC = () => {
|
||||
// Set to empty to use built-in default
|
||||
await window.electronAPI?.chat.setSystemPrompt('');
|
||||
const result = await window.electronAPI?.chat.getSystemPrompt();
|
||||
if (result?.prompt) {
|
||||
setAiSystemPrompt(result.prompt);
|
||||
if (result?.success) {
|
||||
setAiSystemPrompt(result.prompt || '');
|
||||
setAiSystemPromptModified(false);
|
||||
showToast.success('System prompt reset to default');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user