|
| 1 | +const crypto = require('crypto'); |
1 | 2 | const fs = require('fs');
|
| 3 | +const multer = require('multer'); |
2 | 4 | const path = require('path');
|
3 |
| -const crypto = require('crypto'); |
4 | 5 | const { PDFLoader } = require('@langchain/community/document_loaders/fs/pdf');
|
5 | 6 | const { RecursiveCharacterTextSplitter } = require('@langchain/textsplitters');
|
6 | 7 | const { HuggingFaceInferenceEmbeddings } = require('@langchain/community/embeddings/hf');
|
@@ -448,3 +449,296 @@ exports.postRagAsk = async (req, res) => {
|
448 | 449 | await client.close();
|
449 | 450 | }
|
450 | 451 | };
|
| 452 | + |
| 453 | +/** |
| 454 | + * GET /ai/openai-moderation |
| 455 | + * OpenAI Moderation API example. |
| 456 | + */ |
| 457 | +exports.getOpenAIModeration = (req, res) => { |
| 458 | + res.render('ai/openai-moderation', { |
| 459 | + title: 'OpenAI Input Moderation', |
| 460 | + result: null, |
| 461 | + error: null, |
| 462 | + input: '', |
| 463 | + }); |
| 464 | +}; |
| 465 | + |
| 466 | +/** |
| 467 | + * POST /ai/openai-moderation |
| 468 | + * OpenAI Moderation API example. |
| 469 | + */ |
| 470 | +exports.postOpenAIModeration = async (req, res) => { |
| 471 | + const openAiKey = process.env.OPENAI_API_KEY; |
| 472 | + const inputText = req.body.inputText || ''; |
| 473 | + let result = null; |
| 474 | + let error = null; |
| 475 | + |
| 476 | + if (!openAiKey) { |
| 477 | + error = 'OpenAI API key is not set in environment variables.'; |
| 478 | + } else if (!inputText.trim()) { |
| 479 | + error = 'Text for input modaration check:'; |
| 480 | + } else { |
| 481 | + try { |
| 482 | + const response = await fetch('https://api.openai.com/v1/moderations', { |
| 483 | + method: 'POST', |
| 484 | + headers: { |
| 485 | + 'Content-Type': 'application/json', |
| 486 | + Authorization: `Bearer ${openAiKey}`, |
| 487 | + }, |
| 488 | + body: JSON.stringify({ |
| 489 | + model: 'text-moderation-latest', |
| 490 | + input: inputText, |
| 491 | + }), |
| 492 | + }); |
| 493 | + if (!response.ok) { |
| 494 | + const errData = await response.json().catch(() => ({})); |
| 495 | + error = errData.error && errData.error.message ? errData.error.message : `API Error: ${response.status}`; |
| 496 | + } else { |
| 497 | + const data = await response.json(); |
| 498 | + result = data.results && data.results[0]; |
| 499 | + } |
| 500 | + } catch (err) { |
| 501 | + console.error('OpenAI Moderation API Error:', err); |
| 502 | + error = 'Failed to call OpenAI Moderation API.'; |
| 503 | + } |
| 504 | + } |
| 505 | + |
| 506 | + res.render('ai/openai-moderation', { |
| 507 | + title: 'OpenAI Moderation API', |
| 508 | + result, |
| 509 | + error, |
| 510 | + input: inputText, |
| 511 | + }); |
| 512 | +}; |
| 513 | + |
| 514 | +/** |
| 515 | + * Helper functions and constants for Together AI API Example |
| 516 | + * We are using LLMs to classify text or analyze a picture taken by the user's camera. |
| 517 | + */ |
| 518 | + |
| 519 | +// Shared Together AI API caller |
| 520 | +const callTogetherAiApi = async (apiRequestBody, apiKey) => { |
| 521 | + const response = await fetch('https://api.together.xyz/v1/chat/completions', { |
| 522 | + method: 'POST', |
| 523 | + headers: { |
| 524 | + 'Content-Type': 'application/json', |
| 525 | + Authorization: `Bearer ${apiKey}`, |
| 526 | + }, |
| 527 | + body: JSON.stringify(apiRequestBody), |
| 528 | + }); |
| 529 | + if (!response.ok) { |
| 530 | + const errData = await response.json().catch(() => ({})); |
| 531 | + console.error('Together AI API Error Response:', errData); |
| 532 | + const errorMessage = errData.error && errData.error.message ? errData.error.message : `API Error: ${response.status}`; |
| 533 | + throw new Error(errorMessage); |
| 534 | + } |
| 535 | + return response.json(); |
| 536 | +}; |
| 537 | + |
| 538 | +// Vision-specific functions |
| 539 | +const createVisionLLMRequestBody = (dataUrl, model) => ({ |
| 540 | + model, |
| 541 | + messages: [ |
| 542 | + { |
| 543 | + role: 'user', |
| 544 | + content: [ |
| 545 | + { |
| 546 | + type: 'text', |
| 547 | + text: 'What is in this image?', |
| 548 | + }, |
| 549 | + { |
| 550 | + type: 'image_url', |
| 551 | + image_url: { |
| 552 | + url: dataUrl, |
| 553 | + }, |
| 554 | + }, |
| 555 | + ], |
| 556 | + }, |
| 557 | + ], |
| 558 | +}); |
| 559 | + |
| 560 | +const extractVisionAnalysis = (data) => { |
| 561 | + if (data.choices && Array.isArray(data.choices) && data.choices.length > 0 && data.choices[0].message && data.choices[0].message.content) { |
| 562 | + return data.choices[0].message.content; |
| 563 | + } |
| 564 | + return 'No vision analysis available'; |
| 565 | +}; |
| 566 | + |
| 567 | +// Classifier-specific functions |
| 568 | +const createClassifierLLMRequestBody = (inputText, model, systemPrompt) => ({ |
| 569 | + model, |
| 570 | + messages: [ |
| 571 | + { role: 'system', content: systemPrompt }, |
| 572 | + { role: 'user', content: inputText }, |
| 573 | + ], |
| 574 | + temperature: 0, |
| 575 | + max_tokens: 64, |
| 576 | +}); |
| 577 | + |
| 578 | +const extractClassifierResponse = (content) => { |
| 579 | + let department = null; |
| 580 | + if (content) { |
| 581 | + try { |
| 582 | + // Try to extract JSON from the response |
| 583 | + const jsonStringMatch = content.match(/{.*}/s); |
| 584 | + if (jsonStringMatch) { |
| 585 | + const parsed = JSON.parse(jsonStringMatch[0].replace(/'/g, '"')); |
| 586 | + department = parsed.department; |
| 587 | + } |
| 588 | + } catch (err) { |
| 589 | + console.log('Failed to parse JSON from TogetherAI API response:', err); |
| 590 | + // fallback: try to extract department manually |
| 591 | + const match = content.match(/"department"\s*:\s*"([^"]+)"/); |
| 592 | + if (match) { |
| 593 | + [, department] = match; |
| 594 | + } |
| 595 | + } |
| 596 | + } |
| 597 | + return department || 'Unknown'; |
| 598 | +}; |
| 599 | + |
| 600 | +// System prompt for the classifier |
| 601 | +// This is the system prompt that instructs the LLM on how to classify the customer message |
| 602 | +// into the appropriate department. |
| 603 | +const messageClassifierSystemPrompt = `You are a customer service classifier for an e-commerce platform. Your role is to identify the primary issue described by the customer and return the result in JSON format. Carefully analyze the customer's message and select one of the following departments as the classification result: |
| 604 | +
|
| 605 | +Order Tracking and Status |
| 606 | +Returns and Refunds |
| 607 | +Payments and Billing Issues |
| 608 | +Account Management |
| 609 | +Product Inquiries |
| 610 | +Technical Support |
| 611 | +Shipping and Delivery Issues |
| 612 | +Promotions and Discounts |
| 613 | +Marketplace Seller Support |
| 614 | +Feedback and Complaints |
| 615 | +
|
| 616 | +Provide the output in this JSON structure: |
| 617 | +
|
| 618 | +{ |
| 619 | + "department": "<selected_department>" |
| 620 | +} |
| 621 | +Replace <selected_department> with the name of the most relevant department from the list above. If the inquiry spans multiple categories, choose the department that is most likely to address the customer's issue promptly and effectively.`; |
| 622 | + |
| 623 | +// Image Uploade middleware for Camera uploads |
| 624 | +const createImageUploader = () => { |
| 625 | + const memoryStorage = multer.memoryStorage(); |
| 626 | + return multer({ |
| 627 | + storage: memoryStorage, |
| 628 | + limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit |
| 629 | + }).single('image'); |
| 630 | +}; |
| 631 | + |
| 632 | +exports.imageUploadMiddleware = (req, res, next) => { |
| 633 | + const uploadToMemory = createImageUploader(); |
| 634 | + uploadToMemory(req, res, (err) => { |
| 635 | + if (err) { |
| 636 | + console.error('Upload error:', err); |
| 637 | + return res.status(500).json({ error: err.message }); |
| 638 | + } |
| 639 | + next(); |
| 640 | + }); |
| 641 | +}; |
| 642 | + |
| 643 | +const createImageDataUrl = (file) => { |
| 644 | + const base64Image = file.buffer.toString('base64'); |
| 645 | + return `data:${file.mimetype};base64,${base64Image}`; |
| 646 | +}; |
| 647 | + |
| 648 | +/** |
| 649 | + * GET /ai/togetherai-camera |
| 650 | + * Together AI Camera Analysis Example |
| 651 | + */ |
| 652 | +exports.getTogetherAICamera = (req, res) => { |
| 653 | + res.render('ai/togetherai-camera', { |
| 654 | + title: 'Together.ai Camera Analysis', |
| 655 | + togetherAiModel: process.env.TOGETHERAI_VISION_MODEL, |
| 656 | + }); |
| 657 | +}; |
| 658 | + |
| 659 | +/** |
| 660 | + * POST /ai/togetherai-camera |
| 661 | + * Analyze image using Together AI Vision |
| 662 | + */ |
| 663 | +exports.postTogetherAICamera = async (req, res) => { |
| 664 | + if (!req.file) { |
| 665 | + return res.status(400).json({ error: 'No image provided' }); |
| 666 | + } |
| 667 | + try { |
| 668 | + const togetherAiKey = process.env.TOGETHERAI_API_KEY; |
| 669 | + const togetherAiModel = process.env.TOGETHERAI_VISION_MODEL; |
| 670 | + if (!togetherAiKey) { |
| 671 | + return res.status(500).json({ error: 'TogetherAI API key is not set' }); |
| 672 | + } |
| 673 | + const dataUrl = createImageDataUrl(req.file); |
| 674 | + const apiRequestBody = createVisionLLMRequestBody(dataUrl, togetherAiModel); |
| 675 | + // console.log('Making Vision API request to Together AI...'); |
| 676 | + const data = await callTogetherAiApi(apiRequestBody, togetherAiKey); |
| 677 | + const analysis = extractVisionAnalysis(data); |
| 678 | + // console.log('Vision analysis completed:', analysis); |
| 679 | + res.json({ analysis }); |
| 680 | + } catch (error) { |
| 681 | + console.error('Error analyzing image:', error); |
| 682 | + res.status(500).json({ error: `Error analyzing image: ${error.message}` }); |
| 683 | + } |
| 684 | +}; |
| 685 | + |
| 686 | +/** |
| 687 | + * GET /ai/togetherai-classifier |
| 688 | + * Together AI / LLM API Example. |
| 689 | + */ |
| 690 | +exports.getTogetherAIClassifier = (req, res) => { |
| 691 | + res.render('ai/togetherai-classifier', { |
| 692 | + title: 'Together.ai/LLM Department Classifier', |
| 693 | + result: null, |
| 694 | + togetherAiModel: process.env.TOGETHERAI_MODEL, |
| 695 | + error: null, |
| 696 | + input: '', |
| 697 | + }); |
| 698 | +}; |
| 699 | + |
| 700 | +/** |
| 701 | + * POST /ai/togetherai-classifier |
| 702 | + * Together AI API Example. |
| 703 | + * - Classifies customer service inquiries into departments. |
| 704 | + * - Uses Together AI API with a foundational LLM model to classify the input text. |
| 705 | + * - The systemPrompt is the instructions from the developer to the model for processing |
| 706 | + * the user input. |
| 707 | + */ |
| 708 | +exports.postTogetherAIClassifier = async (req, res) => { |
| 709 | + const togetherAiKey = process.env.TOGETHERAI_API_KEY; |
| 710 | + const togetherAiModel = process.env.TOGETHERAI_MODEL; |
| 711 | + const inputText = (req.body.inputText || '').slice(0, 300); |
| 712 | + let result = null; |
| 713 | + let error = null; |
| 714 | + if (!togetherAiKey) { |
| 715 | + error = 'TogetherAI API key is not set in environment variables.'; |
| 716 | + } else if (!togetherAiModel) { |
| 717 | + error = 'TogetherAI model is not set in environment variables.'; |
| 718 | + } else if (!inputText.trim()) { |
| 719 | + error = 'Please enter the customer message to classify.'; |
| 720 | + } else { |
| 721 | + try { |
| 722 | + const systemPrompt = messageClassifierSystemPrompt; // Your existing system prompt here |
| 723 | + const apiRequestBody = createClassifierLLMRequestBody(inputText, togetherAiModel, systemPrompt); |
| 724 | + const data = await callTogetherAiApi(apiRequestBody, togetherAiKey); |
| 725 | + const content = data.choices && data.choices[0] && data.choices[0].message && data.choices[0].message.content; |
| 726 | + const department = extractClassifierResponse(content); |
| 727 | + result = { |
| 728 | + department, |
| 729 | + raw: content, |
| 730 | + systemPrompt, |
| 731 | + }; |
| 732 | + } catch (err) { |
| 733 | + console.log('TogetherAI Classifier API Error:', err); |
| 734 | + error = 'Failed to call TogetherAI API.'; |
| 735 | + } |
| 736 | + } |
| 737 | + |
| 738 | + res.render('ai/togetherai-classifier', { |
| 739 | + title: 'TogetherAI Department Classifier', |
| 740 | + result, |
| 741 | + error, |
| 742 | + input: inputText, |
| 743 | + }); |
| 744 | +}; |
0 commit comments