Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recommended development environment is to use docker as defined in /develop/README.md . We can keep this .gitignore as is.

Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

# user defined files
.env
docker-compose.override.yml
docker-compose.override.yml
2 changes: 2 additions & 0 deletions develop/dev.env
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ GRACEFUL_SHUTDOWN_DELAY_SECONDS=0
HISTORY_V1_HOST=history-v1
LISTEN_ADDRESS=0.0.0.0
MONGO_HOST=mongo
LLM_HOST=llm
MONGO_URL=mongodb://mongo/sharelatex?directConnection=true
NOTIFICATIONS_HOST=notifications
PROJECT_HISTORY_HOST=project-history
REALTIME_HOST=real-time
REDIS_HOST=redis
REDIS_URL=redis://redis:6379
SPELLING_HOST=spelling
WEBPACK_HOST=webpack
WEB_API_PASSWORD=overleaf
Expand Down
16 changes: 14 additions & 2 deletions develop/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ services:
environment:
- NODE_OPTIONS=--inspect=0.0.0.0:9229
ports:
- "127.0.0.1:9236:9229"
- "127.0.0.1:9238:9229"
volumes:
- ../services/references/app:/overleaf/services/references/app
- ../services/references/config:/overleaf/services/references/config
Expand Down Expand Up @@ -123,8 +123,20 @@ services:
- ../services/real-time/app.js:/overleaf/services/real-time/app.js
- ../services/real-time/config:/overleaf/services/real-time/config

llm:
command: ["node", "--watch", "app.js"]
environment:
- NODE_OPTIONS=--inspect=0.0.0.0:9229
ports:
- "127.0.0.1:9241:9229"
volumes:
- ../services/llm/app:/overleaf/app
- ../services/llm/app.js:/overleaf/services/llm/app.js
- ../services/llm/config:/overleaf/services/llm/config


web:
command: ["node", "--watch", "app.js", "--watch-locales"]
command: ["node", "--watch", "app.mjs", "--watch-locales"]
environment:
- NODE_OPTIONS=--inspect=0.0.0.0:9229
ports:
Expand Down
17 changes: 16 additions & 1 deletion develop/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ services:
dockerfile: services/real-time/Dockerfile
env_file:
- dev.env
environment:
- SESSION_SECRET=helloworld

redis:
image: redis:5
Expand All @@ -140,6 +142,16 @@ services:
volumes:
- spelling-cache:/overleaf/services/spelling/cache

llm:
build:
context: ..
dockerfile: services/llm/Dockerfile
env_file:
- dev.env
depends_on:
- mongo
- redis

web:
build:
context: ..
Expand All @@ -153,7 +165,8 @@ services:
- EMAIL_CONFIRMATION_DISABLED=true
- NODE_ENV=development
- OVERLEAF_ALLOW_PUBLIC_ACCESS=true
command: ["node", "app.js"]
- SESSION_SECRET=helloworld
command: ["node", "app.mjs"]
volumes:
- sharelatex-data:/var/lib/overleaf
- web-data:/overleaf/services/web/data
Expand All @@ -172,8 +185,10 @@ services:
- project-history
- real-time
- spelling
- llm

webpack:
user: root

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary for this service to run?

build:
context: ..
dockerfile: services/web/Dockerfile
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"services/history-v1",
"services/idp",
"services/latexqc",
"services/llm",
"services/notifications",
"services/project-history",
"services/real-time",
Expand Down
22 changes: 22 additions & 0 deletions services/llm/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM node:20.19.5 AS base

WORKDIR /overleaf/services/llm


FROM base AS app

COPY package.json package-lock.json /overleaf/
COPY services/llm/package.json /overleaf/services/llm/
COPY libraries/ /overleaf/libraries/
COPY patches/ /overleaf/patches/


RUN cd /overleaf && npm install

COPY services/llm/ /overleaf/services/llm/

FROM app
USER node
CMD ["node", "--expose-gc", "app.js"]


27 changes: 27 additions & 0 deletions services/llm/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// src/app.js
import express from 'express';
import connectDB from './config/db.js';
import keysRoutes from './app/routes/keys.routes.js';
import llmRoutes from './app/routes/llm.routes.js';
import cookieParser from 'cookie-parser';
import { PORT,LISTEN_ADDRESS} from './config/settings.defaults.cjs';

const app = express();

app.use(cookieParser());

app.use(express.json());

// connect to database
await connectDB();

// register routes
app.use('/api/v1/llm', keysRoutes);

app.use('/api/v1/llm', llmRoutes);

app.listen(PORT, LISTEN_ADDRESS, () => {
console.log(`server running on ${PORT}`);
});


80 changes: 80 additions & 0 deletions services/llm/app/controllers/keys.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { KeysService } from '../services/keys.service.js';
import {getUserIdentifier} from '../utils/common.js';
export class KeysController {
constructor() {
this.keysService = new KeysService();
}

async saveKey(req, res) {
try {
const sid = req.cookies['overleaf.sid'];
const userIdentifier = await getUserIdentifier(sid);
const { name, baseUrl, apiKey } = req.body;
await this.keysService.saveApiKey(userIdentifier, name, baseUrl, apiKey);
res.status(200).json({ success: true });
} catch (error) {
res.status(400).json({ success: false, data: error.message });
}
}

async deleteKey(req, res) {
try {
const sid = req.cookies['overleaf.sid'];
const userIdentifier = await getUserIdentifier(sid);
const { name } = req.body;
const result = await this.keysService.deleteApiKey(userIdentifier, name);
res.status(200).json({ success: true, data: result });
} catch (error) {
console.log(error.message)
res.status(400).json({ success: false, data: error.message });
}
}

async getLlmInfo(req, res) {

try {
const sid = req.cookies['overleaf.sid'];
const userIdentifier = await getUserIdentifier(sid);
const result = await this.keysService.getLlmInfo(userIdentifier);
res.status(200).json({ success: true, data: result });
} catch (error) {
res.status(400).json({ success: false, data: error.message });
}
}
async getUsingLlm(req, res) {
try {
const sid = req.cookies['overleaf.sid'];
const userIdentifier = await getUserIdentifier(sid);
const result = await this.keysService.getUsingLlm(userIdentifier);
res.status(200).json({ success: true, data: result });
} catch (error) {
res.status(400).json({ success: false, data: error.message });
}
}


async updateUsingLlm(req, res) {
try {
const sid = req.cookies['overleaf.sid'];
const userIdentifier = await getUserIdentifier(sid);
const { usingLlm } = req.body;
await this.keysService.updateUsingLlm(userIdentifier, usingLlm);
res.status(200).json({ success: true });
} catch (error) {
res.status(400).json({ success: false, data: error.message });
}
}
async updateUsingModel(req, res) {
try {
const sid = req.cookies['overleaf.sid'];
const userIdentifier = await getUserIdentifier(sid);
const { name, chatOrCompletion, newModel } = req.body;
await this.keysService.updateUsingModel(userIdentifier, name, chatOrCompletion, newModel);
res.status(200).json({ success: true });
} catch (error) {
res.status(400).json({ success: false, data: error.message });
}
}
}


53 changes: 53 additions & 0 deletions services/llm/app/controllers/llm.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { LLMService } from "../services/llm.service.js";
import {getUserIdentifier} from '../utils/common.js';
export class LLMController {
constructor() {
this.llmService = new LLMService();
}
async chat(req, res) {
try {
const sid = req.cookies['overleaf.sid'];
const userIdentifier = await getUserIdentifier(sid);
// console.log('llmcontroller:',userIdentifier)

const { ask, selection, filelist, outline, mode } = req.body;
//console.log('llmcontroller body:',ask, selection, filelist, outline, mode)
const content = await this.llmService.chat(
userIdentifier, ask, selection, filelist, outline, mode
);
res.status(200).json({
success: true,
data: content
});
} catch (error) {
res.status(400).json({ success: false, data:error.message });
}
}
/**
* completion
*/
async completion(req, res) {
try {
const sid = req.cookies['overleaf.sid'];
const userIdentifier = await getUserIdentifier(sid);
console.log('userIdentifier:', userIdentifier);
const { cursorOffset, leftContext, rightContext, language, maxLength, fileList, outline } = req.body;

const content = await this.llmService.completion(
userIdentifier, cursorOffset, leftContext, rightContext, language, maxLength, fileList, outline
);
//const content = "helo world";
console.log('completion content:', content);
res.status(200).json({
success: true,
data: content
});
} catch (err) {
console.error('completion error:', err);
res.status(400).json({ success: false, data:err.message });
}
}
}



Loading