diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a8361c4b4d..efeea9d202 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: CI for ASW2425 +name: CI for wichat_0 on: release: @@ -54,14 +54,13 @@ jobs: uses: elgohr/Publish-Docker-Github-Action@v5 env: API_URI: http://${{ secrets.DEPLOY_HOST }}:8000 - LLM_API_KEY: ${{ secrets.LLM_API_KEY }} with: - name: pglez82/asw2425_0/webapp + name: arquisoft/wichat_0/webapp username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io workdir: webapp - buildargs: API_URI,LLM_API_KEY + buildargs: API_URI docker-push-authservice: name: Push auth service Docker Image to GitHub Packages runs-on: ubuntu-latest @@ -76,7 +75,7 @@ jobs: - name: Publish to Registry uses: elgohr/Publish-Docker-Github-Action@v5 with: - name: pglez82/asw2425_0/authservice + name: arquisoft/wichat_0/authservice username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io @@ -95,7 +94,7 @@ jobs: - name: Publish to Registry uses: elgohr/Publish-Docker-Github-Action@v5 with: - name: pglez82/asw2425_0/userservice + name: arquisoft/wichat_0/userservice username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io @@ -113,7 +112,7 @@ jobs: - name: Publish to Registry uses: elgohr/Publish-Docker-Github-Action@v5 with: - name: pglez82/asw2425_0/llmservice + name: arquisoft/wichat_0/llmservice username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io @@ -135,7 +134,7 @@ jobs: - name: Publish to Registry uses: elgohr/Publish-Docker-Github-Action@v5 with: - name: pglez82/asw2425_0/gatewayservice + name: arquisoft/wichat_0/gatewayservice username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} registry: ghcr.io @@ -152,6 +151,6 @@ jobs: user: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_KEY }} command: | - wget https://raw.githubusercontent.com/pglez82/asw2425_0/master/docker-compose.yml -O docker-compose.yml + wget https://raw.githubusercontent.com/arquisoft/wichat_0/master/docker-compose.yml -O docker-compose.yml docker compose --profile prod down docker compose --profile prod up -d --pull always diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..6b0e5abfc1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "postman.settings.dotenv-detection-notification-visibility": false +} \ No newline at end of file diff --git a/README.md b/README.md index 2eb7e4ac36..e2a18e1da6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# asw2425_0 +# wichat_0 -[![Actions Status](https://github.com/pglez82/asw2425_0/workflows/CI%20for%20ASW2425/badge.svg)](https://github.com/pglez82/asw2425_0/actions) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=pglez82_asw2425_0&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=pglez82_asw2425_0) -[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=pglez82_asw2425_0&metric=coverage)](https://sonarcloud.io/summary/new_code?id=pglez82_asw2425_0) +[![Actions Status](https://github.com/arquisoft/wichat_0/workflows/CI%20for%20wichat_0/badge.svg)](https://github.com/arquisoft/wichat_0/actions) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=Arquisoft_wichat_0&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=Arquisoft_wichat_0) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=Arquisoft_wichat_0&metric=coverage)](https://sonarcloud.io/summary/new_code?id=Arquisoft_wichat_0)

@@ -23,16 +23,16 @@ Both the user and auth service share a Mongo database that is accessed with mong First, clone the project: -```git clone git@github.com:pglez82/asw2425_0.git``` +```git clone git@github.com:arquisoft/wichat_0.git``` ### LLM API key configuration In order to communicate with the LLM integrated in this project, we need to setup an API key. Two integrations are available in this propotipe: gemini and empaphy. The API key provided must match the LLM provider used. We need to create two .env files. -- The first one in the webapp directory (for executing the webapp using ```npm start```). The content of this .env file should be as follows: +- The first one in the llmservice directory (for executing the llmservice using ```npm start```). The content of this .env file should be as follows: ``` -REACT_APP_LLM_API_KEY="YOUR-API-KEY" +LLM_API_KEY="YOUR-API-KEY" ``` - The second one located in the root of the project (along the docker-compose.yml). This .env file is used for the docker-compose when launching the app with docker. The content of this .env file should be as follows: ``` @@ -41,8 +41,7 @@ LLM_API_KEY="YOUR-API-KEY" Note that these files must NOT be uploaded to the github repository (they are excluded in the .gitignore). -An extra configuration for the LLM to work in the deployed version of the app is to include it as a repository secret (LLM_API_KEY). This secret will be used by GitHub Action when building and deploying the application. - +An extra configuration for the LLM to work in the deployed version of the app is to create the same .env file (with the LLM_API_KEY variable) in the virtual machine (in the home of the azureuser directory). ### Launching Using docker For launching the propotipe using docker compose, just type: @@ -106,7 +105,7 @@ deploy: user: ${{ secrets.DEPLOY_USER }} key: ${{ secrets.DEPLOY_KEY }} command: | - wget https://raw.githubusercontent.com/pglez82/asw2425_0/master/docker-compose.yml -O docker-compose.yml + wget https://raw.githubusercontent.com/arquisoft/wichat_0/master/docker-compose.yml -O docker-compose.yml docker compose --profile prod down docker compose --profile prod up -d --pull always ``` diff --git a/docker-compose.yml b/docker-compose.yml index 2f78c8c7bf..d3830bb859 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: mongodb: - container_name: mongodb-asw2425_0 + container_name: mongodb-wichat_0 image: mongo profiles: ["dev", "prod"] volumes: @@ -11,8 +11,8 @@ services: - mynetwork authservice: - container_name: authservice-asw2425_0 - image: ghcr.io/pglez82/asw2425_0/authservice:latest + container_name: authservice-wichat_0 + image: ghcr.io/arquisoft/wichat_0/authservice:latest profiles: ["dev", "prod"] build: ./users/authservice depends_on: @@ -25,8 +25,8 @@ services: MONGODB_URI: mongodb://mongodb:27017/userdb userservice: - container_name: userservice-asw2425_0 - image: ghcr.io/pglez82/asw2425_0/userservice:latest + container_name: userservice-wichat_0 + image: ghcr.io/arquisoft/wichat_0/userservice:latest profiles: ["dev", "prod"] build: ./users/userservice depends_on: @@ -39,18 +39,21 @@ services: MONGODB_URI: mongodb://mongodb:27017/userdb llmservice: - container_name: llmservice-asw2425_0 - image: ghcr.io/pglez82/asw2425_0/llmservice:latest + container_name: llmservice-wichat_0 + image: ghcr.io/arquisoft/wichat_0/llmservice:latest profiles: ["dev", "prod"] - build: ./llmservice + env_file: + - .env + build: + context: ./llmservice ports: - "8003:8003" networks: - mynetwork gatewayservice: - container_name: gatewayservice-asw2425_0 - image: ghcr.io/pglez82/asw2425_0/gatewayservice:latest + container_name: gatewayservice-wichat_0 + image: ghcr.io/arquisoft/wichat_0/gatewayservice:latest profiles: ["dev", "prod"] build: ./gatewayservice depends_on: @@ -68,13 +71,10 @@ services: LLM_SERVICE_URL: http://llmservice:8003 webapp: - container_name: webapp-asw2425_0 - image: ghcr.io/pglez82/asw2425_0/webapp:latest + container_name: webapp-wichat_0 + image: ghcr.io/arquisoft/wichat_0/webapp:latest profiles: ["dev", "prod"] - build: - context: ./webapp - args: - LLM_API_KEY: ${LLM_API_KEY} + build: ./webapp depends_on: - gatewayservice ports: @@ -82,7 +82,7 @@ services: prometheus: image: prom/prometheus - container_name: prometheus-asw2425_0 + container_name: prometheus-wichat_0 profiles: ["dev"] networks: - mynetwork @@ -96,7 +96,7 @@ services: grafana: image: grafana/grafana - container_name: grafana-asw2425_0 + container_name: grafana-wichat_0 profiles: ["dev"] networks: - mynetwork diff --git a/docs/README.md b/docs/README.md index afe4c42863..daa34fa0fd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,6 @@ ## The documentation In this project, the documentation is compiled locally and deployed to GitHub pages. -The deployment url is: [https://pglez82.github.io/asw2425_0/](https://pglez82.github.io/asw2425_0/). +The deployment url is: [https://arquisoft.github.io/wichat_0/](https://arquisoft.github.io/wichat_0/). ### Documentation build For the documentation, we are going to use [AsciiDoc](https://asciidoc.org/) and [PlantUML](https://plantuml.com) and follow the [Arc42](https://github.com/arc42/arc42-template) template. If you want to be able to generate the doc locally you need to install Ruby, Java and some dependencies to translate the AsciiDoc code into html. If you are in Linux you can install Ruby and Java simply by executing: @@ -30,6 +30,6 @@ npm run build The documentation will be generated under the `docs/build` directory. ### Documentation deployment -If we want to deploy it to GitHub pages, so it is accessible via [https://pglez82.github.io/asw2425_0/](https://pglez82.github.io/asw2425_0/), we need to execute `npm run deploy`. +If we want to deploy it to GitHub pages, so it is accessible via [https://arquisoft.github.io/wichat_0/](https://arquisoft.github.io/wichat_0/), we need to execute `npm run deploy`. If you check the `package.json` in this directory you can see how deploying is as easy as executing `gh-pages -d build`, which can be directly executed using `npm run deploy` in the docs directory. The `gh-pages` package is in charge of pushing the documentation generated directory (basically some htmls) to a special github branch called gh-pages. Everything pushed to this branch is accessible on the repository page. Note that we only want to push there the documentation. Also is important that the documentation build is not pushed to the other branches of the project. \ No newline at end of file diff --git a/docs/index.adoc b/docs/index.adoc index 6aadbb6fc8..afa2411371 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -6,7 +6,7 @@ // configure EN settings for asciidoc include::src/config.adoc[] -= image:arc42-logo.png[arc42] Template += image:arc42-logo.png[arc42] wichat_0 :revnumber: 8.2 EN :revdate: January 2023 :revremark: (based upon AsciiDoc version) diff --git a/gatewayservice/package.json b/gatewayservice/package.json index d1cd2d047c..47bef53ee1 100644 --- a/gatewayservice/package.json +++ b/gatewayservice/package.json @@ -9,14 +9,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/pglez82/asw2425_0.git" + "url": "git+https://github.com/arquisoft/wichat_0.git" }, "author": "", "license": "ISC", "bugs": { - "url": "https://github.com/pglez82/asw2425_0/issues" + "url": "https://github.com/arquisoft/wichat_0/issues" }, - "homepage": "https://github.com/pglez82/asw2425_0#readme", + "homepage": "https://github.com/arquisoft/wichat_0#readme", "dependencies": { "axios": "^1.7.9", "cors": "^2.8.5", diff --git a/llmservice/.dockerignore b/llmservice/.dockerignore index 3091757a3b..c92776b4b6 100644 --- a/llmservice/.dockerignore +++ b/llmservice/.dockerignore @@ -1,2 +1,3 @@ node_modules -coverage \ No newline at end of file +coverage +.env \ No newline at end of file diff --git a/llmservice/llm-service.js b/llmservice/llm-service.js index da0ff837cb..e4bdb3d38d 100644 --- a/llmservice/llm-service.js +++ b/llmservice/llm-service.js @@ -6,6 +6,8 @@ const port = 8003; // Middleware to parse JSON in request body app.use(express.json()); +// Load enviornment variables +require('dotenv').config(); // Define configurations for different LLM APIs const llmConfigs = { @@ -17,9 +19,9 @@ const llmConfigs = { transformResponse: (response) => response.data.candidates[0]?.content?.parts[0]?.text }, empathy: { - url: () => 'https://empathyai.staging.empathy.co/v1/chat/completions', + url: () => 'https://empathyai.prod.empathy.co/v1/chat/completions', transformRequest: (question) => ({ - model: "qwen/Qwen2.5-Coder-7B-Instruct", + model: "mistralai/Mistral-7B-Instruct-v0.3", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: question } @@ -71,9 +73,14 @@ async function sendQuestionToLLM(question, apiKey, model = 'gemini') { app.post('/ask', async (req, res) => { try { // Check if required fields are present in the request body - validateRequiredFields(req, ['question', 'model', 'apiKey']); + validateRequiredFields(req, ['question', 'model']); - const { question, model, apiKey } = req.body; + const { question, model } = req.body; + //load the api key from an environment variable + const apiKey = process.env.LLM_API_KEY; + if (!apiKey) { + return res.status(400).json({ error: 'API key is missing.' }); + } const answer = await sendQuestionToLLM(question, apiKey, model); res.json({ answer }); diff --git a/llmservice/llm-service.test.js b/llmservice/llm-service.test.js index e8b0b7cf45..e72e5d5266 100644 --- a/llmservice/llm-service.test.js +++ b/llmservice/llm-service.test.js @@ -1,3 +1,6 @@ +//set a fake api key +process.env.LLM_API_KEY = 'test-api-key'; + const request = require('supertest'); const axios = require('axios'); const app = require('./llm-service'); @@ -13,19 +16,25 @@ describe('LLM Service', () => { axios.post.mockImplementation((url, data) => { if (url.startsWith('https://generativelanguage')) { return Promise.resolve({ data: { candidates: [{ content: { parts: [{ text: 'llmanswer' }] } }] } }); - } else if (url.endsWith('https://empathyai')) { - return Promise.resolve({ data: { answer: 'llmanswer' } }); + } else if (url.startsWith('https://empathyai')) { + return Promise.resolve({ data: { choices: [ {message: { content: 'llmanswer' } } ] } }); } }); // Test /ask endpoint it('the llm should reply', async () => { - const response = await request(app) + const response1 = await request(app) + .post('/ask') + .send({ question: 'a question', model: 'gemini' }); + + const response2 = await request(app) .post('/ask') - .send({ question: 'a question', apiKey: 'apiKey', model: 'gemini' }); + .send({ question: 'a question', model: 'empathy' }); - expect(response.statusCode).toBe(200); - expect(response.body.answer).toBe('llmanswer'); + expect(response1.statusCode).toBe(200); + expect(response1.body.answer).toBe('llmanswer'); + expect(response2.statusCode).toBe(200); + expect(response2.body.answer).toBe('llmanswer'); }); }); \ No newline at end of file diff --git a/llmservice/package-lock.json b/llmservice/package-lock.json index c801c4a74c..f88e73de50 100644 --- a/llmservice/package-lock.json +++ b/llmservice/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "axios": "^1.7.9", + "dotenv": "^16.4.7", "express": "^4.21.2" }, "devDependencies": { @@ -1803,6 +1804,18 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", diff --git a/llmservice/package.json b/llmservice/package.json index 53e3625674..b2cd6f085e 100644 --- a/llmservice/package.json +++ b/llmservice/package.json @@ -8,13 +8,14 @@ "author": "", "license": "ISC", "description": "", - "homepage": "https://github.com/pglez82/asw2425_0#readme", - "dependencies": { - "axios": "^1.7.9", - "express": "^4.21.2" - }, - "devDependencies": { - "jest": "^29.7.0", - "supertest": "^7.0.0" - } + "homepage": "https://github.com/arquisoft/wichat_0#readme", + "dependencies": { + "axios": "^1.7.9", + "dotenv": "^16.4.7", + "express": "^4.21.2" + }, + "devDependencies": { + "jest": "^29.7.0", + "supertest": "^7.0.0" + } } diff --git a/sonar-project.properties b/sonar-project.properties index 2aff7d0041..e3f4d6c311 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,9 +1,9 @@ -sonar.organization=pglez82 +sonar.organization=arquisoft sonar.host.url=https://sonarcloud.io -sonar.projectKey=pglez82_asw2425_0 +sonar.projectKey=Arquisoft_wichat_0 sonar.language=js -sonar.projectName=asw2425_0 +sonar.projectName=wichat_0 sonar.coverage.exclusions=**/*.test.js sonar.sources=webapp/src/components,users/authservice,users/userservice,llmservice,gatewayservice diff --git a/users/authservice/package.json b/users/authservice/package.json index 089e13e342..826832edcf 100644 --- a/users/authservice/package.json +++ b/users/authservice/package.json @@ -9,14 +9,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/pglez82/asw2425_0.git" + "url": "git+https://github.com/arquisoft/wichat_0.git" }, "author": "", "license": "ISC", "bugs": { - "url": "https://github.com/pglez82/asw2425_0/issues" + "url": "https://github.com/arquisoft/wichat_0/issues" }, - "homepage": "https://github.com/pglez82/asw2425_0#readme", + "homepage": "https://github.com/arquisoft/wichat_0#readme", "dependencies": { "bcrypt": "^5.1.1", "body-parser": "^1.20.3", diff --git a/users/userservice/package.json b/users/userservice/package.json index c177c38826..cfdc8c40d6 100644 --- a/users/userservice/package.json +++ b/users/userservice/package.json @@ -9,14 +9,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/pglez82/asw2425_0.git" + "url": "git+https://github.com/arquisoft/wichat_0.git" }, "author": "", "license": "ISC", "bugs": { - "url": "https://github.com/pglez82/asw2425_0/issues" + "url": "https://github.com/arquisoft/wichat_0/issues" }, - "homepage": "https://github.com/pglez82/asw2425_0#readme", + "homepage": "https://github.com/arquisoft/wichat_0#readme", "dependencies": { "bcrypt": "^5.1.1", "express": "^4.21.2", diff --git a/webapp/Dockerfile b/webapp/Dockerfile index dcf26fe93a..20c397350e 100644 --- a/webapp/Dockerfile +++ b/webapp/Dockerfile @@ -4,17 +4,15 @@ COPY . /app WORKDIR /app #Install the dependencies -RUN npm install +RUN npm install --omit=dev ARG API_URI="http://localhost:8000" -ARG LLM_API_KEY ENV REACT_APP_API_ENDPOINT=$API_URI -ENV REACT_APP_LLM_API_KEY=$LLM_API_KEY #Create an optimized version of the webapp RUN npm run build -RUN npm install serve +RUN npm install -g serve --production #Execute npm run prod to run the server CMD [ "npm", "run", "prod" ] -#CMD ["npm", "start"] \ No newline at end of file +#CMD ["npm", "start"] diff --git a/webapp/public/index.html b/webapp/public/index.html index 0d76d3dadc..e98a2199eb 100644 --- a/webapp/public/index.html +++ b/webapp/public/index.html @@ -24,7 +24,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - ASW 2024-2025 + wichat_0 diff --git a/webapp/src/App.js b/webapp/src/App.js index 1f6f1b29d1..f1e3fc993e 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -17,7 +17,7 @@ function App() { - Welcome to the 2025 edition of the Software Architecture course + Welcome to the 2025 edition of the Software Architecture course! {showLogin ? : } diff --git a/webapp/src/components/Login.js b/webapp/src/components/Login.js index c9b4097be1..afbe048e02 100644 --- a/webapp/src/components/Login.js +++ b/webapp/src/components/Login.js @@ -14,7 +14,7 @@ const Login = () => { const [openSnackbar, setOpenSnackbar] = useState(false); const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; - const apiKey = process.env.REACT_APP_LLM_API_KEY || 'None'; + const loginUser = async () => { try { @@ -22,14 +22,8 @@ const Login = () => { const question = "Please, generate a greeting message for a student called " + username + " that is a student of the Software Architecture course in the University of Oviedo. Be nice and polite. Two to three sentences max."; const model = "empathy" - - if (apiKey==='None'){ - setMessage("LLM API key is not set. Cannot contact the LLM."); - } - else{ - const message = await axios.post(`${apiEndpoint}/askllm`, { question, model, apiKey }) - setMessage(message.data.answer); - } + const message = await axios.post(`${apiEndpoint}/askllm`, { question, model }) + setMessage(message.data.answer); // Extract data from the response const { createdAt: userCreatedAt } = response.data;