Compare commits

...

6 Commits

Author SHA1 Message Date
CncAnon e66b529d70 Oops 2023-08-30 11:17:23 +03:00
CncAnon 7eda3c9127 Use --no-audit and remove infinite loop for the *nix script. 2023-08-30 11:16:58 +03:00
CncAnon dbdd1c7a21 Fix #1 by adding proper error handling (and notifying the user about it) 2023-08-30 11:09:12 +03:00
CncAnon af3da23d5d Fix #2, add execute bit to start.sh 2023-08-30 10:52:24 +03:00
CncAnon 52fe62d520 Merge branch 'master' into 'master'
Added start files

See merge request cncanon/sg-proxy!3
2023-08-30 07:22:24 +00:00
totallynotcrazy 833fa12d2c Update 2 files
- /start.bat
- /start.sh
2023-08-29 21:09:07 +00:00
3 changed files with 45 additions and 11 deletions

49
main.js
View File

@ -3,12 +3,31 @@ require("dotenv").config();
const express = require('express');
const axios = require('axios');
const app = express();
const StringDecoder = require('string_decoder').StringDecoder;
const bodyParser = require('body-parser');
app.use(bodyParser.json({ limit: '100mb' }));
app.use(bodyParser.urlencoded({limit: '100mb', extended: true}));
const API_TOKEN = process.env.API_TOKEN;
function handleError(res, isStream) {
// If the request hasn't finished, notify the user that there was an error and finish
// the request properly, so that ST isn't left hanging.
const errMsg = "\n**Received an error during the request, please check sg-proxy logs!**";
let jsonResp = {completion: errMsg, stop_reason: "stop_sequence"};
if (!res.writableEnded) {
if (isStream) {
res.write(`event: completion\r\ndata: ${JSON.stringify(jsonResp)}\r\n\r\n`);
} else {
// This is unlikely to trigger, but can happen if the error was caught
// before the request was sent (without streaming)
res.json(jsonResp);
}
res.end();
}
}
app.post('/v1/complete', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream; charset=utf-8');
res.setHeader('Cache-Control', 'no-cache');
@ -17,17 +36,18 @@ app.post('/v1/complete', async (req, res) => {
res.flushHeaders();
//console.log(req.body);
// Those are required and must always be present
const model = req.body.model;
const maxTokens = req.body.max_tokens_to_sample;
const prompt = req.body.prompt;
const temp = req.body.temperature || 1.0;
const top_p = req.body.top_p || null;
const top_k = req.body.top_k || null;
const maxTokens = req.body.max_tokens_to_sample;
const stopSequences = req.body.stop_sequences || null;
const isStream = req.body.stream || false;
const prompt = req.body.prompt;
console.log(`Doing a request with stream = ${isStream}.`)
// Set up axios instance for SSE
const sourcegraph = axios.create({
baseURL: 'https://sourcegraph.com/.api/completions/stream',
@ -55,10 +75,12 @@ app.post('/v1/complete', async (req, res) => {
const response = await sourcegraph.post('', postData);
let previousCompletion = "";
// GPT-4 told me to use a StringDecoder so that multi-byte characters are correctly handled across chunks
let decoder = new StringDecoder('utf8');
let buffer = ""; // Buffer to hold incomplete lines
response.data.on('data', (chunk) => {
buffer += chunk.toString();
buffer += decoder.write(chunk);
let lines = buffer.split("\n");
buffer = lines.pop(); // Keep the last (potentially incomplete) line in the buffer
@ -71,7 +93,8 @@ app.post('/v1/complete', async (req, res) => {
//console.log(resp);
if (isStream) {
// SourceGraph API always returns the full string, but we need the diff
const newPart = parsedData.completion.replace(previousCompletion, '');
// We can use .length because StringDecoder correctly handles multi-byte characters
const newPart = parsedData.completion.substring(previousCompletion.length);
previousCompletion = parsedData.completion;
let resp = { completion: newPart, stop_reason: null };
res.write(`event: completion\r\ndata: ${JSON.stringify(resp)}\r\n\r\n`);
@ -81,12 +104,16 @@ app.post('/v1/complete', async (req, res) => {
}
}
} catch (error) {
// If an error is thrown, the JSON is not valid
console.error('Invalid JSON:', chunk);
console.log("Invalid JSON chunk: ", chunk);
// do nothing, the JSON chunk is incomplete
}})
});
response.data.on('end', () => {
// Since the last char will be a newline char and not a multi-byte one, we're sure that
// the decoder will be empty, so we can just end it.
decoder.end();
if (isStream) {
let finalResp = {completion: "", stop_reason: "stop_sequence"};
res.write(`event: completion\r\ndata: ${JSON.stringify(finalResp)}\r\n\r\n`);
@ -99,14 +126,14 @@ app.post('/v1/complete', async (req, res) => {
});
} catch (error) {
console.error("Got an error: ", error);
res.status(500).send('An error occurred while making the request.');
console.error("Got an error in the main route:\n", error);
handleError(res, isStream);
}
});
app.use((err, req, res, next) => {
console.log(err);
res.status(500).json({"error": true});
console.log("Got an unhandled exception:\n", err);
handleError(res, req.body && req.body.stream || false);
});
process.on('uncaughtException', (err) => {

3
start.bat Normal file
View File

@ -0,0 +1,3 @@
call npm install --no-audit
node main.js
pause

4
start.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
npm install --no-audit
node main.js