Advertisement
Amiminoru

fix

Sep 3rd, 2023
466
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. require("dotenv").config();
  2.  
  3. const express = require('express');
  4. const axios = require('axios');
  5. const app = express();
  6. const StringDecoder = require('string_decoder').StringDecoder;
  7.  
  8. const bodyParser = require('body-parser');
  9. app.use(bodyParser.json({ limit: '100mb' }));
  10. app.use(bodyParser.urlencoded({limit: '100mb', extended: true}));
  11.  
  12. const API_TOKEN = process.env.API_TOKEN;
  13.  
  14. function handleError(res, isStream) {
  15.   // If the request hasn't finished, notify the user that there was an error and finish
  16.   // the request properly, so that ST isn't left hanging.
  17.   const errMsg = "\n**Received an error during the request, please check sg-proxy logs!**";
  18.   let jsonResp = {completion: errMsg, stop_reason: "stop_sequence"};
  19.   if (!res.writableEnded) {
  20.     if (isStream) {
  21.       res.write(`event: completion\r\ndata: ${JSON.stringify(jsonResp)}\r\n\r\n`);
  22.     } else {
  23.       // This is unlikely to trigger, but can happen if the error was caught
  24.       // before the request was sent (without streaming)
  25.       res.json(jsonResp);
  26.     }
  27.     res.end();
  28.   }
  29. }
  30.  
  31. app.post('/v1/complete', async (req, res) => {
  32.   res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
  33.   res.setHeader('Cache-Control', 'no-cache');
  34.   res.setHeader('Connection', 'keep-alive');
  35.   res.setHeader('Transfer-Encoding', 'chunked');
  36.   res.flushHeaders();
  37.  
  38.   //console.log(req.body);
  39.   // Those are required and must always be present
  40.   const model = req.body.model;
  41.   const maxTokens = req.body.max_tokens_to_sample;
  42.   const prompt = req.body.prompt;
  43.  
  44.   const temp = req.body.temperature || 1.0;
  45.   const top_p = req.body.top_p || null;
  46.   const top_k = req.body.top_k || null;
  47.   const stopSequences = req.body.stop_sequences || null;
  48.   const isStream = req.body.stream || false;
  49.  
  50.   console.log(`Doing a request with stream = ${isStream}.`)
  51.   // Set up axios instance for SSE
  52.   const sourcegraph = axios.create({
  53.     baseURL: 'https://sourcegraph.com/.api/completions/stream',
  54.     authority: 'sourcegraph.com',
  55.     scheme: 'https',
  56.     path: '/.api/completions/stream',
  57.     method: 'POST',
  58.     headers: {
  59.       'Content-Type': 'application/json',
  60.       'Accept': 'text/event-stream',
  61.       'Accept-Encoding': 'gzip, deflate, br',
  62.       'Origin': 'https://sourcegraph.com',
  63.       'Sec-Fetch-Dest': 'empty',
  64.       'Sec-Fetch-Mode': 'cors',
  65.       'Sec-Fetch-Site': 'same-origin',
  66.       'X-Requested-With': 'Sourcegraph',
  67.       'X-Sourcegraph-Client': 'https://sourcegraph.com',
  68.       'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 YaBrowser/23.7.4.971 Yowser/2.5 Safari/537.36',
  69.       'Authorization': `token ${API_TOKEN}`
  70.     },
  71.     responseType: 'stream',
  72.     timeout: 180000,
  73.   });
  74.  
  75.   let fullContent = "";
  76.  
  77.   try {
  78.     let postData = {
  79.       model: model,
  80.       prompt: prompt,
  81.       maxTokensToSample: maxTokens
  82.     };
  83.    
  84.     if (temp) postData.temperature = temp;
  85.     if (stopSequences) postData.stop_sequences = stopSequences;
  86.     if (top_p) postData.top_p = top_p;
  87.     if (top_k) postData.top_k = top_k;
  88.     const response = await sourcegraph.post('', postData);
  89.  
  90.     let previousCompletion = "";
  91.     // GPT-4 told me to use a StringDecoder so that multi-byte characters are correctly handled across chunks
  92.     let decoder = new StringDecoder('utf8');
  93.     let buffer = ""; // Buffer to hold incomplete lines
  94.  
  95.     response.data.on('data', (chunk) => {
  96.       buffer += decoder.write(chunk);
  97.       let lines = buffer.split("\n");
  98.       buffer = lines.pop(); // Keep the last (potentially incomplete) line in the buffer
  99.  
  100.       const data = lines.filter(line => line.startsWith('data: ')).map(line => line.replace(/^data: /, ''));
  101.  
  102.       data.forEach((chunk) => {
  103.         try {
  104.           const parsedData = JSON.parse(chunk);
  105.           if ('completion' in parsedData) {
  106.             //console.log(resp);
  107.             if (isStream) {
  108.               // SourceGraph API always returns the full string, but we need the diff
  109.               // We can use .length because StringDecoder correctly handles multi-byte characters
  110.               const newPart = parsedData.completion.substring(previousCompletion.length);
  111.               previousCompletion = parsedData.completion;
  112.               let resp = { completion: newPart, stop_reason: null };
  113.               res.write(`event: completion\r\ndata: ${JSON.stringify(resp)}\r\n\r\n`);
  114.             }
  115.             else {
  116.               fullContent = parsedData.completion;
  117.             }
  118.           }
  119.         } catch (error) {
  120.           console.log("Invalid JSON chunk: ", chunk);
  121.           // do nothing, the JSON chunk is incomplete
  122.       }})
  123.     });
  124.  
  125.     response.data.on('end', () => {
  126.       // Since the last char will be a newline char and not a multi-byte one, we're sure that
  127.       // the decoder will be empty, so we can just end it.
  128.       decoder.end();
  129.      
  130.       if (isStream) {
  131.         let finalResp = {completion: "", stop_reason: "stop_sequence"};
  132.         res.write(`event: completion\r\ndata: ${JSON.stringify(finalResp)}\r\n\r\n`);
  133.       }
  134.       else {
  135.         res.write(JSON.stringify({completion: fullContent, stop_reason: "stop_sequence"}));
  136.       }
  137.       res.end();
  138.       console.log(`Request done.`)
  139.     });
  140.  
  141.   } catch (error) {
  142.     console.error("Got an error in the main route:\n", error);
  143.     handleError(res, isStream);
  144.   }
  145. });
  146.  
  147. app.use((err, req, res, next) => {
  148.   console.log("Got an unhandled exception:\n", err);
  149.   handleError(res, req.body && req.body.stream || false);
  150. });
  151.  
  152. process.on('uncaughtException', (err) => {
  153.   console.error('Uncaught exception:', err);
  154. });
  155.  
  156. process.on('unhandledRejection', (reason, promise) => {
  157.   console.error('Unhandled Promise Rejection:', reason);
  158. });
  159.  
  160. async function checkToken(token) {
  161.   const data = {
  162.     query: 'query { currentUser { username } }'
  163.   };
  164.  
  165.   const config = {
  166.     method: 'post',
  167.     url: 'https://sourcegraph.com/.api/graphql',
  168.     headers: {
  169.       'Authorization': `token ${token}`
  170.     },
  171.     data: data
  172.   };
  173.  
  174.   try {
  175.     const response = await axios(config);
  176.     if(response.data && response.data.data && response.data.data.currentUser) {
  177.       console.log(`Token works, username: ${response.data.data.currentUser.username}`);
  178.       return true;
  179.     } else {
  180.       return false;
  181.     }
  182.   } catch (error) {
  183.     return false;
  184.   }
  185. }
  186.  
  187. // Two basic checks
  188. if (!API_TOKEN) {
  189.   console.error("SourceGraph API token not found! Create a file named '.env' and put your token there as an API_TOKEN. See .env.example for an example.");
  190.   process.exit(1);
  191. }
  192. else if (API_TOKEN.indexOf("sgp_") == -1) {
  193.   console.error("Invalid SourceGraph API token! Make sure you copied the whole token starting with sgp_, like 'sgp_blablabla'.");
  194.   process.exit(1);
  195. }
  196.  
  197. // Check token validity
  198. checkToken(API_TOKEN).then(isValid => {
  199.   if (!isValid) {
  200.     console.error("Invalid SourceGraph API token! Make sure you copied the whole token and that the token is not revoked.");
  201.     process.exit(1);
  202.   }
  203.  
  204.   const port = process.env.PORT || 3000;
  205.   app.listen(port, () => console.log(`Server listening on port ${port}`));
  206. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement