Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions packages/shared/__tests__/file-processing.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { describe, expect, it } from 'vitest'

import {
FeatureCapabilitySchema,
FileProcessorOverrideSchema,
FileProcessorTemplateSchema,
FileProcessorTemplatesSchema,
PRESETS_FILE_PROCESSORS
} from '../data/presets/file-processing'
import { FILE_TYPE } from '../data/types/file'

describe('FeatureCapabilitySchema', () => {
it('supports multiple input types for a single capability', () => {
const result = FeatureCapabilitySchema.safeParse({
feature: 'text_extraction',
inputs: [FILE_TYPE.IMAGE, FILE_TYPE.DOCUMENT],
output: FILE_TYPE.TEXT
})

expect(result.success).toBe(true)
})
})

describe('FileProcessorTemplatesSchema', () => {
it('validates built-in presets', () => {
expect(() => FileProcessorTemplatesSchema.parse(PRESETS_FILE_PROCESSORS)).not.toThrow()
})

it('rejects duplicate features in a single processor template', () => {
const result = FileProcessorTemplateSchema.safeParse({
id: 'paddleocr',
type: 'api',
capabilities: [
{
feature: 'text_extraction',
inputs: [FILE_TYPE.IMAGE],
output: FILE_TYPE.TEXT
},
{
feature: 'text_extraction',
inputs: [FILE_TYPE.DOCUMENT],
output: FILE_TYPE.TEXT
}
]
})

expect(result.success).toBe(false)
})
})

describe('FileProcessorOverrideSchema', () => {
it('accepts valid overrides', () => {
const result = FileProcessorOverrideSchema.safeParse({
apiKeys: ['test-key'],
capabilities: {
text_extraction: {
apiHost: 'https://example.com',
modelId: 'model-1'
}
},
options: {
langs: ['eng', 'chi_sim']
}
})

expect(result.success).toBe(true)
})

it('rejects invalid urls', () => {
const result = FileProcessorOverrideSchema.safeParse({
capabilities: {
markdown_conversion: {
apiHost: 'not-a-url'
}
}
})

expect(result.success).toBe(false)
})

it('rejects unknown feature overrides', () => {
const result = FileProcessorOverrideSchema.safeParse({
capabilities: {
vision: {
apiHost: 'https://example.com'
}
}
})

expect(result.success).toBe(false)
})
})
15 changes: 15 additions & 0 deletions packages/shared/__tests__/file-type.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { describe, expect, it } from 'vitest'

import { FILE_TYPE, FileTypeSchema } from '../data/types/file'

describe('FileTypeSchema', () => {
it('accepts canonical file types', () => {
expect(FileTypeSchema.safeParse(FILE_TYPE.IMAGE).success).toBe(true)
expect(FileTypeSchema.safeParse(FILE_TYPE.DOCUMENT).success).toBe(true)
expect(FileTypeSchema.safeParse(FILE_TYPE.TEXT).success).toBe(true)
})

it('rejects unknown file types', () => {
expect(FileTypeSchema.safeParse('markdown').success).toBe(false)
})
})
49 changes: 49 additions & 0 deletions packages/shared/data/api/schemas/fileProcessing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* File Processing API Schema definitions
*
* Contains file processing endpoints for:
* - Listing available processors
* - Reading and updating processor configuration
*/

import type { FileProcessorId, FileProcessorOverride } from '@shared/data/preference/preferenceTypes'
import type { FileProcessorMerged } from '@shared/data/presets/file-processing'

// ============================================================================
// API Schema Definitions
// ============================================================================

/**
* File Processing API Schema definitions
*/
export interface FileProcessingSchemas {
/**
* List available processors
* @example GET /file-processing/processors
*/
'/file-processing/processors': {
/** Get list of available processors */
GET: {
response: FileProcessorMerged[]
}
}

/**
* Get or update processor configuration
* @example GET /file-processing/processors/tesseract
* @example PATCH /file-processing/processors/tesseract { "apiKeys": ["xxx"] }
*/
'/file-processing/processors/:id': {
/** Get processor configuration */
GET: {
params: { id: FileProcessorId }
response: FileProcessorMerged
}
/** Update processor configuration */
PATCH: {
params: { id: FileProcessorId }
body: FileProcessorOverride
response: FileProcessorMerged
}
}
}
3 changes: 2 additions & 1 deletion packages/shared/data/api/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*/

import type { AssertValidSchemas } from '../apiTypes'
import type { FileProcessingSchemas } from './fileProcessing'
import type { MessageSchemas } from './messages'
import type { TestSchemas } from './test'
import type { TopicSchemas } from './topics'
Expand All @@ -36,4 +37,4 @@ import type { TopicSchemas } from './topics'
* 1. Create the schema file (e.g., topic.ts)
* 2. Import and add to intersection below
*/
export type ApiSchemas = AssertValidSchemas<TestSchemas & TopicSchemas & MessageSchemas>
export type ApiSchemas = AssertValidSchemas<TestSchemas & TopicSchemas & MessageSchemas & FileProcessingSchemas>
15 changes: 12 additions & 3 deletions packages/shared/data/preference/preferenceSchemas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Auto-generated preferences configuration
* Generated at: 2026-03-08T07:54:09.830Z
* Generated at: 2026-03-12T12:32:08.496Z
*
* This file is automatically generated from classification.json
* To update this file, modify classification.json and run:
Expand Down Expand Up @@ -404,6 +404,12 @@ export interface PreferenceSchemas {
'feature.translate.model_prompt': string
// redux/settings/targetLanguage
'feature.translate.target_language': string
// redux/preprocess/defaultProvider
'file_processing.default.markdown_conversion': PreferenceTypes.FileProcessorId
// redux/ocr/imageProviderId
'file_processing.default.text_extraction': PreferenceTypes.FileProcessorId | null
// target-key-definitions/complex/complex
'file_processing.overrides': PreferenceTypes.FileProcessorOverrides
// redux/shortcuts/shortcuts.exit_fullscreen
'shortcut.app.exit_fullscreen': Record<string, unknown>
// redux/shortcuts/shortcuts.search_message
Expand Down Expand Up @@ -683,6 +689,9 @@ export const DefaultPreferences: PreferenceSchemas = {
'feature.selection.trigger_mode': PreferenceTypes.SelectionTriggerMode.Selected,
'feature.translate.model_prompt': TRANSLATE_PROMPT,
'feature.translate.target_language': 'en-us',
'file_processing.default.markdown_conversion': 'mineru',
'file_processing.default.text_extraction': null,
'file_processing.overrides': {} as PreferenceTypes.FileProcessorOverrides,
'shortcut.app.exit_fullscreen': { editable: false, enabled: true, key: ['Escape'], system: true },
'shortcut.app.search_message': {
editable: true,
Expand Down Expand Up @@ -751,8 +760,8 @@ export const DefaultPreferences: PreferenceSchemas = {

/**
* 生成统计:
* - 总配置项: 215
* - 总配置项: 218
* - electronStore项: 1
* - redux项: 203
* - redux项: 205
* - localStorage项: 0
*/
38 changes: 38 additions & 0 deletions packages/shared/data/preference/preferenceTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,44 @@ export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold' | 'grid'

export type MultiModelGridPopoverTrigger = 'hover' | 'click'

export const FILE_PROCESSOR_TYPES = ['api', 'builtin'] as const

export type FileProcessorType = (typeof FILE_PROCESSOR_TYPES)[number]

export const FILE_PROCESSOR_FEATURES = ['text_extraction', 'markdown_conversion'] as const

export type FileProcessorFeature = (typeof FILE_PROCESSOR_FEATURES)[number]

export const FILE_PROCESSOR_IDS = [
'tesseract',
'system',
'paddleocr',
'ovocr',
'mineru',
'doc2x',
'mistral',
'open-mineru'
] as const

export type FileProcessorId = (typeof FILE_PROCESSOR_IDS)[number]

export type FileProcessorOptions = Record<string, unknown>

export type CapabilityOverride = {
apiHost?: string
modelId?: string
metadata?: Record<string, unknown>
}

export type FileProcessorCapabilityOverrides = Partial<Record<FileProcessorFeature, CapabilityOverride>>

export type FileProcessorOverride = {
apiKeys?: string[]
capabilities?: FileProcessorCapabilityOverrides
options?: FileProcessorOptions
}

export type FileProcessorOverrides = Partial<Record<FileProcessorId, FileProcessorOverride>>
Comment on lines +108 to +145
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Agreeing with @EurFelux's earlier comment — since there are already Zod schemas defined in file-processing.ts for these same types (e.g., FileProcessorOverrideSchema, CapabilityOverrideSchema), having plain TypeScript type definitions here creates a dual-source-of-truth risk. If either side drifts, the types will silently diverge. Consider deriving these types from the Zod schemas using `z.infer` (or vice versa) to keep them in sync, similar to how `FileProcessorTemplate` is already done.

// ============================================================================
// WebSearch Types
// ============================================================================
Expand Down
Loading
Loading