Download src/controllers/chatController.ts from nexusbert/zurri: direct link, hf CLI and curl.
- Browser
- Download file 7.01 kB
-
https://huggingface.co/spaces/nexusbert/zurri/resolve/main/src/controllers/chatController.ts
- Command line
-
hf download hf://spaces/nexusbert/zurri/src/controllers/chatController.ts
-
curl -L -o chatController.ts https://huggingface.co/spaces/nexusbert/zurri/resolve/main/src/controllers/chatController.ts
7.01 kB
| import { Request, Response } from 'express'; | |
| import { AppDataSource } from '../config/database'; | |
| import { Agent, AgentStatus } from '../entities/Agent'; | |
| import { Subscription, SubscriptionStatus } from '../entities/Subscription'; | |
| import { Transaction, TransactionType, TransactionStatus } from '../entities/Transaction'; | |
| import { ChatProtocolService } from '../services/chatProtocol'; | |
| import { WalletService } from '../services/walletService'; | |
| const agentRepository = AppDataSource.getRepository(Agent); | |
| const subscriptionRepository = AppDataSource.getRepository(Subscription); | |
| const chatService = new ChatProtocolService(); | |
| const walletService = new WalletService(); | |
| export class ChatController { | |
| /** | |
| * Send chat message to agent (with optional file uploads) | |
| */ | |
| async sendMessage(req: Request, res: Response) { | |
| try { | |
| const { id: agentId } = req.params; | |
| const { message, conversationId, metadata } = req.body; | |
| const userId = (req as any).user?.id; | |
| // Extract files from multer - support both 'file' (singular) and 'files' (plural) | |
| interface MulterFile { | |
| fieldname: string; | |
| originalname: string; | |
| encoding: string; | |
| mimetype: string; | |
| size: number; | |
| buffer: Buffer; | |
| } | |
| const multerFiles = (req as any).files; | |
| const files = Array.isArray(multerFiles) | |
| ? multerFiles as Array<MulterFile> | |
| : multerFiles | |
| ? [multerFiles as MulterFile] | |
| : undefined; | |
| // Message is optional if files are provided | |
| if (!message && (!files || files.length === 0)) { | |
| return res.status(400).json({ | |
| error: 'Message or files are required' | |
| }); | |
| } | |
| const user = (req as any).user; | |
| const isAdmin = user?.isAdmin || false; | |
| // Get agent - admins can test pending/rejected agents, regular users only approved | |
| const agent = await agentRepository.findOne({ | |
| where: { | |
| id: agentId, | |
| ...(isAdmin ? {} : { status: AgentStatus.APPROVED }) | |
| }, | |
| }); | |
| if (!agent) { | |
| return res.status(404).json({ error: 'Agent not found' }); | |
| } | |
| // Admins don't pay for unapproved agents (pending/rejected) - they're testing quality | |
| const isAdminTestingUnapproved = isAdmin && agent.status !== AgentStatus.APPROVED; | |
| const shouldCharge = userId && !isAdminTestingUnapproved; | |
| // Check wallet balance and charge points (first 2 tasks free) | |
| if (shouldCharge) { | |
| const chargeResult = await walletService.chargeForTask(userId, agentId, agent); | |
| if (!chargeResult.success) { | |
| const balance = await walletService.getBalance(userId); | |
| const pointsRequired = Number(agent.pointsPerTask) || 0; | |
| const freeTasksRemaining = await walletService.getFreeTasksRemaining(userId); | |
| return res.status(402).json({ | |
| error: 'Insufficient wallet balance', | |
| details: { | |
| balance, | |
| required: pointsRequired, | |
| freeTasksRemaining, | |
| message: freeTasksRemaining > 0 | |
| ? `You have ${freeTasksRemaining} free task(s) remaining. After that, you need ${pointsRequired} points ($${(pointsRequired * 0.05).toFixed(2)}) per task.` | |
| : `You need ${pointsRequired} points ($${(pointsRequired * 0.05).toFixed(2)}) to use this agent. Please fund your wallet.`, | |
| }, | |
| }); | |
| } | |
| } else if (isAdminTestingUnapproved) { | |
| // Create a free transaction for admin testing (for tracking purposes) | |
| const wallet = await walletService.getOrCreateWallet(userId); | |
| const transactionRepository = AppDataSource.getRepository(Transaction); | |
| const transaction = transactionRepository.create({ | |
| walletId: wallet.id, | |
| userId, | |
| agentId, | |
| type: TransactionType.ADMIN_TEST, | |
| status: TransactionStatus.COMPLETED, | |
| amount: 0, | |
| balanceAfter: Number(wallet.balance), | |
| description: `Admin test of ${agent.name} (${agent.status}) - no charge`, | |
| metadata: { | |
| agentId, | |
| agentName: agent.name, | |
| agentStatus: agent.status, | |
| isAdminTest: true | |
| }, | |
| }); | |
| await transactionRepository.save(transaction); | |
| } | |
| // Convert multer files to chat protocol format | |
| const chatFiles = files?.map(file => ({ | |
| fieldname: file.fieldname, | |
| originalname: file.originalname, | |
| mimetype: file.mimetype, | |
| size: file.size, | |
| buffer: file.buffer, | |
| })); | |
| // Send message via chat protocol | |
| const response = await chatService.sendMessage(agent, { | |
| message: message || '', | |
| conversationId, | |
| userId, | |
| metadata: metadata ? (typeof metadata === 'string' ? JSON.parse(metadata) : metadata) : undefined, | |
| files: chatFiles, | |
| }); | |
| res.json(response); | |
| } catch (error: any) { | |
| console.error('Chat error:', error); | |
| res.status(500).json({ | |
| error: error.message || 'Failed to send message to agent', | |
| }); | |
| } | |
| } | |
| /** | |
| * Get conversation history | |
| */ | |
| async getHistory(req: Request, res: Response) { | |
| try { | |
| const { id: agentId } = req.params; | |
| const { conversationId, limit } = req.query; | |
| const user = (req as any).user; | |
| const userId = user?.id; | |
| const isAdmin = user?.isAdmin || false; | |
| if (!userId) { | |
| return res.status(401).json({ error: 'Authentication required' }); | |
| } | |
| // Admins can view history for any agent, regular users only approved agents | |
| const agent = await agentRepository.findOne({ | |
| where: { | |
| id: agentId, | |
| ...(isAdmin ? {} : { status: AgentStatus.APPROVED }) | |
| }, | |
| }); | |
| if (!agent) { | |
| return res.status(404).json({ error: 'Agent not found' }); | |
| } | |
| const messages = await chatService.getConversationHistory( | |
| agentId, | |
| userId, | |
| conversationId as string, | |
| limit ? Number(limit) : 50 | |
| ); | |
| res.json({ messages }); | |
| } catch (error) { | |
| console.error('Get history error:', error); | |
| res.status(500).json({ error: 'Failed to get conversation history' }); | |
| } | |
| } | |
| /** | |
| * Check if user has access to agent | |
| */ | |
| private async checkUserAccess( | |
| userId: string, | |
| agentId: string | |
| ): Promise<boolean> { | |
| // Check for active subscription | |
| const subscription = await subscriptionRepository.findOne({ | |
| where: { | |
| userId, | |
| agentId, | |
| status: SubscriptionStatus.ACTIVE, | |
| isPaymentVerified: true, | |
| }, | |
| }); | |
| if (!subscription) { | |
| return false; | |
| } | |
| // Check if subscription is expired | |
| if (subscription.expiresAt && subscription.expiresAt < new Date()) { | |
| subscription.status = SubscriptionStatus.EXPIRED; | |
| await subscriptionRepository.save(subscription); | |
| return false; | |
| } | |
| return true; | |
| } | |
| } | |