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:
parent
8781388760
commit
0337c6649b
@ -6,5 +6,6 @@ CREATE TABLE IF NOT EXISTS files (
|
|||||||
size INTEGER NOT NULL,
|
size INTEGER NOT NULL,
|
||||||
ext TEXT NOT NULL,
|
ext TEXT NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL,
|
||||||
created_at TEXT NOT NULL
|
created_at TEXT NOT NULL,
|
||||||
|
count INTEGER DEFAULT 1
|
||||||
)
|
)
|
||||||
|
|||||||
@ -91,7 +91,8 @@ export class File {
|
|||||||
created_at: stats.birthtime,
|
created_at: stats.birthtime,
|
||||||
size: stats.size,
|
size: stats.size,
|
||||||
ext: ext,
|
ext: ext,
|
||||||
type: fileType
|
type: fileType,
|
||||||
|
count: 1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -102,7 +103,12 @@ export class File {
|
|||||||
const duplicateFile = await this.findDuplicateFile(file.path)
|
const duplicateFile = await this.findDuplicateFile(file.path)
|
||||||
|
|
||||||
if (duplicateFile) {
|
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()
|
const uuid = uuidv4()
|
||||||
@ -122,12 +128,13 @@ export class File {
|
|||||||
created_at: stats.birthtime,
|
created_at: stats.birthtime,
|
||||||
size: stats.size,
|
size: stats.size,
|
||||||
ext: ext,
|
ext: ext,
|
||||||
type: fileType
|
type: fileType,
|
||||||
|
count: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const stmt = this.db.prepare(`
|
const stmt = this.db.prepare(`
|
||||||
INSERT INTO files (id, name, file_name, path, size, ext, type, created_at)
|
INSERT INTO files (id, name, file_name, path, size, ext, type, created_at, count)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
`)
|
`)
|
||||||
|
|
||||||
stmt.run(
|
stmt.run(
|
||||||
@ -138,7 +145,8 @@ export class File {
|
|||||||
fileMetadata.size,
|
fileMetadata.size,
|
||||||
fileMetadata.ext,
|
fileMetadata.ext,
|
||||||
fileMetadata.type,
|
fileMetadata.type,
|
||||||
fileMetadata.created_at.toISOString()
|
fileMetadata.created_at.toISOString(),
|
||||||
|
fileMetadata.count
|
||||||
)
|
)
|
||||||
|
|
||||||
return fileMetadata
|
return fileMetadata
|
||||||
@ -147,9 +155,16 @@ export class File {
|
|||||||
async deleteFile(fileId: string): Promise<void> {
|
async deleteFile(fileId: string): Promise<void> {
|
||||||
const fileMetadata = this.getFile(fileId)
|
const fileMetadata = this.getFile(fileId)
|
||||||
if (fileMetadata) {
|
if (fileMetadata) {
|
||||||
|
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)
|
await fs.promises.unlink(fileMetadata.path)
|
||||||
const stmt = this.db.prepare('DELETE FROM files WHERE id = ?')
|
const deleteStmt = this.db.prepare('DELETE FROM files WHERE id = ?')
|
||||||
stmt.run(fileId)
|
deleteStmt.run(fileId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,21 +181,12 @@ export class File {
|
|||||||
getFile(id: string): FileMetadata | null {
|
getFile(id: string): FileMetadata | null {
|
||||||
const stmt = this.db.prepare('SELECT * FROM files WHERE id = ?')
|
const stmt = this.db.prepare('SELECT * FROM files WHERE id = ?')
|
||||||
const row = stmt.get(id) as any
|
const row = stmt.get(id) as any
|
||||||
if (row) {
|
return row ? { ...row, created_at: new Date(row.created_at) } : null
|
||||||
return {
|
|
||||||
...row,
|
|
||||||
created_at: new Date(row.created_at)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllFiles(): FileMetadata[] {
|
getAllFiles(): FileMetadata[] {
|
||||||
const stmt = this.db.prepare('SELECT * FROM files')
|
const stmt = this.db.prepare('SELECT * FROM files')
|
||||||
const rows = stmt.all() as any[]
|
const rows = stmt.all() as any[]
|
||||||
return rows.map((row) => ({
|
return rows.map((row) => ({ ...row, created_at: new Date(row.created_at) }))
|
||||||
...row,
|
|
||||||
created_at: new Date(row.created_at)
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Model } from '@renderer/types'
|
import { Model } from '@renderer/types'
|
||||||
|
|
||||||
const TEXT_TO_IMAGE_REGEX = /flux|diffusion|stabilityai|sd-turbo|dall|cogview/i
|
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
|
const EMBEDDING_REGEX = /embedding/i
|
||||||
|
|
||||||
export const SYSTEM_MODELS: Record<string, Model[]> = {
|
export const SYSTEM_MODELS: Record<string, Model[]> = {
|
||||||
|
|||||||
@ -29,9 +29,8 @@ export function useAssistants() {
|
|||||||
removeAssistant: (id: string) => {
|
removeAssistant: (id: string) => {
|
||||||
dispatch(removeAssistant({ id }))
|
dispatch(removeAssistant({ id }))
|
||||||
const assistant = assistants.find((a) => a.id === id)
|
const assistant = assistants.find((a) => a.id === id)
|
||||||
if (assistant) {
|
const topics = assistant?.topics || []
|
||||||
assistant.topics.forEach(({ id }) => LocalStorage.removeTopic(id))
|
topics.forEach(({ id }) => LocalStorage.removeTopic(id))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,7 +44,10 @@ export function useAssistant(id: string) {
|
|||||||
assistant,
|
assistant,
|
||||||
model: assistant?.model ?? defaultModel,
|
model: assistant?.model ?? defaultModel,
|
||||||
addTopic: (topic: Topic) => dispatch(addTopic({ assistantId: assistant.id, topic })),
|
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 })),
|
updateTopic: (topic: Topic) => dispatch(updateTopic({ assistantId: assistant.id, topic })),
|
||||||
updateTopics: (topics: Topic[]) => dispatch(updateTopics({ assistantId: assistant.id, topics })),
|
updateTopics: (topics: Topic[]) => dispatch(updateTopics({ assistantId: assistant.id, topics })),
|
||||||
removeAllTopics: () => dispatch(removeAllTopics({ assistantId: assistant.id })),
|
removeAllTopics: () => dispatch(removeAllTopics({ assistantId: assistant.id })),
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
|
|||||||
import { VStack } from '@renderer/components/Layout'
|
import { VStack } from '@renderer/components/Layout'
|
||||||
import { FileMetadata } from '@renderer/types'
|
import { FileMetadata } from '@renderer/types'
|
||||||
import { Image, Table } from 'antd'
|
import { Image, Table } from 'antd'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
import { FC, useEffect, useState } from 'react'
|
import { FC, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -18,7 +19,7 @@ const FilesPage: FC = () => {
|
|||||||
file: <Image src={'file://' + file.path} preview={false} style={{ maxHeight: '40px' }} />,
|
file: <Image src={'file://' + file.path} preview={false} style={{ maxHeight: '40px' }} />,
|
||||||
name: <a href={'file://' + file.path}>{file.name}</a>,
|
name: <a href={'file://' + file.path}>{file.name}</a>,
|
||||||
size: `${(file.size / 1024 / 1024).toFixed(2)} MB`,
|
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 = [
|
const columns = [
|
||||||
|
|||||||
@ -18,6 +18,31 @@ export default class AnthropicProvider extends BaseProvider {
|
|||||||
this.sdk = new Anthropic({ apiKey: provider.apiKey, baseURL: this.getBaseURL() })
|
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(
|
public async completions(
|
||||||
messages: Message[],
|
messages: Message[],
|
||||||
assistant: Assistant,
|
assistant: Assistant,
|
||||||
@ -27,12 +52,14 @@ export default class AnthropicProvider extends BaseProvider {
|
|||||||
const model = assistant.model || defaultModel
|
const model = assistant.model || defaultModel
|
||||||
const { contextCount, maxTokens } = getAssistantSettings(assistant)
|
const { contextCount, maxTokens } = getAssistantSettings(assistant)
|
||||||
|
|
||||||
const userMessages = filterMessages(filterContextMessages(takeRight(messages, contextCount + 2))).map((message) => {
|
const userMessages: MessageParam[] = []
|
||||||
return {
|
|
||||||
|
for (const message of filterMessages(filterContextMessages(takeRight(messages, contextCount + 2)))) {
|
||||||
|
userMessages.push({
|
||||||
role: message.role,
|
role: message.role,
|
||||||
content: message.content
|
content: await this.getMessageContent(message)
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (first(userMessages)?.role === 'assistant') {
|
if (first(userMessages)?.role === 'assistant') {
|
||||||
userMessages.shift()
|
userMessages.shift()
|
||||||
|
|||||||
@ -96,6 +96,7 @@ export interface FileMetadata {
|
|||||||
ext: string
|
ext: string
|
||||||
type: FileType
|
type: FileType
|
||||||
created_at: Date
|
created_at: Date
|
||||||
|
count: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FileType {
|
export enum FileType {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user