|
|
|
|
|
|
|
|
|
interface ModalConfig { |
|
tokenId: string; |
|
tokenSecret: string; |
|
baseUrl: string; |
|
} |
|
|
|
interface DocumentProcessingTask { |
|
taskId: string; |
|
status: 'pending' | 'running' | 'completed' | 'failed'; |
|
result?: any; |
|
error?: string; |
|
} |
|
|
|
interface VectorProcessingRequest { |
|
documents: Array<{ |
|
id: string; |
|
content: string; |
|
metadata?: Record<string, any>; |
|
}>; |
|
modelName?: string; |
|
batchSize?: number; |
|
} |
|
|
|
class ModalClient { |
|
private config: ModalConfig; |
|
private authToken: string; |
|
|
|
constructor() { |
|
|
|
const tokenId = process.env.MODAL_TOKEN_ID; |
|
const tokenSecret = process.env.MODAL_TOKEN_SECRET; |
|
|
|
if (!tokenId || !tokenSecret) { |
|
throw new Error('Modal credentials not configured. Please set MODAL_TOKEN_ID and MODAL_TOKEN_SECRET environment variables.'); |
|
} |
|
|
|
this.config = { |
|
tokenId, |
|
tokenSecret, |
|
baseUrl: 'https://fazeelusmani18--knowledgebridge-main-fastapi-app.modal.run' |
|
}; |
|
|
|
|
|
this.authToken = Buffer.from(`${this.config.tokenId}:${this.config.tokenSecret}`).toString('base64'); |
|
} |
|
|
|
private async makeRequest(endpoint: string, options: RequestInit = {}) { |
|
const url = `${this.config.baseUrl}${endpoint}`; |
|
|
|
console.log(`Modal API request: ${options.method || 'GET'} ${url}`); |
|
|
|
const response = await fetch(url, { |
|
...options, |
|
headers: { |
|
'Content-Type': 'application/json', |
|
...options.headers, |
|
}, |
|
}); |
|
|
|
console.log(`Modal API response: ${response.status} ${response.statusText}`); |
|
|
|
if (!response.ok) { |
|
throw new Error(`Modal API request failed: ${response.status} ${response.statusText}`); |
|
} |
|
|
|
return response.json(); |
|
} |
|
|
|
|
|
|
|
|
|
async batchProcessDocuments(request: VectorProcessingRequest): Promise<DocumentProcessingTask> { |
|
return this.makeRequest('/batch-process', { |
|
method: 'POST', |
|
body: JSON.stringify({ |
|
documents: request.documents, |
|
config: { |
|
modelName: request.modelName || 'text-embedding-3-small', |
|
batchSize: request.batchSize || 50, |
|
computeResources: { |
|
cpu: 2, |
|
memory: '4Gi' |
|
} |
|
} |
|
}) |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
async getTaskStatus(taskId: string): Promise<DocumentProcessingTask> { |
|
return this.makeRequest(`/task/${taskId}`); |
|
} |
|
|
|
|
|
|
|
|
|
async buildVectorIndex(documents: any[], indexConfig?: any): Promise<DocumentProcessingTask> { |
|
const indexName = indexConfig?.indexName || 'main_index'; |
|
return this.makeRequest('/build-index', { |
|
method: 'POST', |
|
body: JSON.stringify({ |
|
documents, |
|
index_name: indexName |
|
}) |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
async vectorSearch(query: string, indexId: string, k: number = 10): Promise<any> { |
|
return this.makeRequest('/vector-search', { |
|
method: 'POST', |
|
body: JSON.stringify({ |
|
query, |
|
indexId, |
|
k, |
|
includeMetadata: true |
|
}) |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
async extractTextFromDocuments(documentUrls: string[]): Promise<DocumentProcessingTask> { |
|
return this.makeRequest('/ocr-extract', { |
|
method: 'POST', |
|
body: JSON.stringify({ |
|
documentUrls, |
|
config: { |
|
languages: ['en'], |
|
outputFormat: 'structured' |
|
} |
|
}) |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
async categorizeDocuments(documents: any[]): Promise<DocumentProcessingTask> { |
|
return this.makeRequest('/categorize', { |
|
method: 'POST', |
|
body: JSON.stringify({ |
|
documents, |
|
categories: [ |
|
'academic_paper', |
|
'technical_documentation', |
|
'research_report', |
|
'code_repository', |
|
'blog_post', |
|
'news_article' |
|
] |
|
}) |
|
}); |
|
} |
|
} |
|
|
|
export const modalClient = new ModalClient(); |
|
export type { DocumentProcessingTask, VectorProcessingRequest }; |