feat: Added tracking column to files table and updated FileMetadata interface.

- Added a "count" column with default value 1 to the "files" table for tracking purposes.
- Improved file duplication and deletion handling.
- Updated regular expression for vision models to include additional providers.
- Improved removal of topics for assistants from local storage.
- Added support for human-readable date formats in file metadata.
- Improved handling of messages with image attachments to include base64 encoded images in the response.
- Added new 'count' property to the FileMetadata interface.
This commit is contained in:
kangfenmao 2024-09-11 15:20:12 +08:00
parent 8781388760
commit 0337c6649b
7 changed files with 70 additions and 32 deletions

View File

@ -6,5 +6,6 @@ CREATE TABLE IF NOT EXISTS files (
size INTEGER NOT NULL,
ext TEXT NOT NULL,
type TEXT NOT NULL,
created_at TEXT NOT NULL
created_at TEXT NOT NULL,
count INTEGER DEFAULT 1
)

View File

@ -91,7 +91,8 @@ export class File {
created_at: stats.birthtime,
size: stats.size,
ext: ext,
type: fileType
type: fileType,
count: 1
}
})
@ -102,7 +103,12 @@ export class File {
const duplicateFile = await this.findDuplicateFile(file.path)
if (duplicateFile) {
return duplicateFile
// Increment the count for the duplicate file
const updateStmt = this.db.prepare('UPDATE files SET count = count + 1 WHERE id = ?')
updateStmt.run(duplicateFile.id)
// Fetch the updated file metadata
return this.getFile(duplicateFile.id)!
}
const uuid = uuidv4()
@ -122,12 +128,13 @@ export class File {
created_at: stats.birthtime,
size: stats.size,
ext: ext,
type: fileType
type: fileType,
count: 1
}
const stmt = this.db.prepare(`
INSERT INTO files (id, name, file_name, path, size, ext, type, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
INSERT INTO files (id, name, file_name, path, size, ext, type, created_at, count)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`)
stmt.run(
@ -138,7 +145,8 @@ export class File {
fileMetadata.size,
fileMetadata.ext,
fileMetadata.type,
fileMetadata.created_at.toISOString()
fileMetadata.created_at.toISOString(),
fileMetadata.count
)
return fileMetadata
@ -147,9 +155,16 @@ export class File {
async deleteFile(fileId: string): Promise<void> {
const fileMetadata = this.getFile(fileId)
if (fileMetadata) {
await fs.promises.unlink(fileMetadata.path)
const stmt = this.db.prepare('DELETE FROM files WHERE id = ?')
stmt.run(fileId)
if (fileMetadata.count > 1) {
// Decrement the count if there are multiple references
const updateStmt = this.db.prepare('UPDATE files SET count = count - 1 WHERE id = ?')
updateStmt.run(fileId)
} else {
// Delete the file and database entry if this is the last reference
await fs.promises.unlink(fileMetadata.path)
const deleteStmt = this.db.prepare('DELETE FROM files WHERE id = ?')
deleteStmt.run(fileId)
}
}
}
@ -166,21 +181,12 @@ export class File {
getFile(id: string): FileMetadata | null {
const stmt = this.db.prepare('SELECT * FROM files WHERE id = ?')
const row = stmt.get(id) as any
if (row) {
return {
...row,
created_at: new Date(row.created_at)
}
}
return null
return row ? { ...row, created_at: new Date(row.created_at) } : null
}
getAllFiles(): FileMetadata[] {
const stmt = this.db.prepare('SELECT * FROM files')
const rows = stmt.all() as any[]
return rows.map((row) => ({
...row,
created_at: new Date(row.created_at)
}))
return rows.map((row) => ({ ...row, created_at: new Date(row.created_at) }))
}
}

View File

@ -1,7 +1,7 @@
import { Model } from '@renderer/types'
const TEXT_TO_IMAGE_REGEX = /flux|diffusion|stabilityai|sd-turbo|dall|cogview/i
const VISION_REGEX = /llava|moondream|minicpm|gemini/i
const VISION_REGEX = /llava|moondream|minicpm|gemini|claude|vision/i
const EMBEDDING_REGEX = /embedding/i
export const SYSTEM_MODELS: Record<string, Model[]> = {

View File

@ -29,9 +29,8 @@ export function useAssistants() {
removeAssistant: (id: string) => {
dispatch(removeAssistant({ id }))
const assistant = assistants.find((a) => a.id === id)
if (assistant) {
assistant.topics.forEach(({ id }) => LocalStorage.removeTopic(id))
}
const topics = assistant?.topics || []
topics.forEach(({ id }) => LocalStorage.removeTopic(id))
}
}
}
@ -45,7 +44,10 @@ export function useAssistant(id: string) {
assistant,
model: assistant?.model ?? defaultModel,
addTopic: (topic: Topic) => dispatch(addTopic({ assistantId: assistant.id, topic })),
removeTopic: (topic: Topic) => dispatch(removeTopic({ assistantId: assistant.id, topic })),
removeTopic: (topic: Topic) => {
LocalStorage.removeTopic(topic.id)
dispatch(removeTopic({ assistantId: assistant.id, topic }))
},
updateTopic: (topic: Topic) => dispatch(updateTopic({ assistantId: assistant.id, topic })),
updateTopics: (topics: Topic[]) => dispatch(updateTopics({ assistantId: assistant.id, topics })),
removeAllTopics: () => dispatch(removeAllTopics({ assistantId: assistant.id })),

View File

@ -2,6 +2,7 @@ import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
import { VStack } from '@renderer/components/Layout'
import { FileMetadata } from '@renderer/types'
import { Image, Table } from 'antd'
import dayjs from 'dayjs'
import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
@ -18,7 +19,7 @@ const FilesPage: FC = () => {
file: <Image src={'file://' + file.path} preview={false} style={{ maxHeight: '40px' }} />,
name: <a href={'file://' + file.path}>{file.name}</a>,
size: `${(file.size / 1024 / 1024).toFixed(2)} MB`,
created_at: file.created_at.toISOString().split('T')[0]
created_at: dayjs(file.created_at).format('MM-DD HH:mm')
}))
const columns = [

View File

@ -18,6 +18,31 @@ export default class AnthropicProvider extends BaseProvider {
this.sdk = new Anthropic({ apiKey: provider.apiKey, baseURL: this.getBaseURL() })
}
private async getMessageContent(message: Message): Promise<MessageParam['content']> {
const file = first(message.files)
if (!file) {
return message.content
}
if (file.type === 'image') {
const base64Data = await window.api.image.base64(file.path)
return [
{ type: 'text', text: message.content },
{
type: 'image',
source: {
data: base64Data.base64,
media_type: base64Data.mime.replace('jpg', 'jpeg') as any,
type: 'base64'
}
}
]
}
return message.content
}
public async completions(
messages: Message[],
assistant: Assistant,
@ -27,12 +52,14 @@ export default class AnthropicProvider extends BaseProvider {
const model = assistant.model || defaultModel
const { contextCount, maxTokens } = getAssistantSettings(assistant)
const userMessages = filterMessages(filterContextMessages(takeRight(messages, contextCount + 2))).map((message) => {
return {
const userMessages: MessageParam[] = []
for (const message of filterMessages(filterContextMessages(takeRight(messages, contextCount + 2)))) {
userMessages.push({
role: message.role,
content: message.content
}
})
content: await this.getMessageContent(message)
})
}
if (first(userMessages)?.role === 'assistant') {
userMessages.shift()

View File

@ -96,6 +96,7 @@ export interface FileMetadata {
ext: string
type: FileType
created_at: Date
count: number
}
export enum FileType {