diff --git a/env.d/development/common.dist b/env.d/development/common.dist index 712c4dbd1..0b9212615 100644 --- a/env.d/development/common.dist +++ b/env.d/development/common.dist @@ -39,3 +39,6 @@ LOGOUT_REDIRECT_URL=http://localhost:3000 OIDC_REDIRECT_ALLOWED_HOSTS=["http://localhost:8083", "http://localhost:3000"] OIDC_AUTH_REQUEST_EXTRA_PARAMS={"acr_values": "eidas1"} + +AI_BASE_URL=https://openaiendpoint.com +AI_API_KEY=password \ No newline at end of file diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index 7db914818..a66eb5c18 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -1,5 +1,6 @@ """API endpoints""" +import json import os import re import uuid @@ -17,6 +18,9 @@ from django.db.models import ( from django.http import Http404 from botocore.exceptions import ClientError + +from openai import OpenAI + from rest_framework import ( decorators, exceptions, @@ -371,8 +375,7 @@ class DocumentViewSet( @decorators.action(detail=True, methods=["post"], url_path="ai") def ai(self, request, *args, **kwargs): """ - Return the document's versions but only those created after the user got access - to the document + Process text using AI based on the specified action. """ if not request.user.is_authenticated: raise exceptions.PermissionDenied("Authentication required.") @@ -380,7 +383,26 @@ class DocumentViewSet( action = request.data.get("action") text = request.data.get("text") - return drf_response.Response(text.upper()) + client = OpenAI( + base_url = settings.AI_BASE_URL, + api_key= settings.AI_API_KEY + ) + + if action == "rephrase": + try: + response = client.chat.completions.create( + model="meta-llama/Meta-Llama-3.1-70B-Instruct", + messages=[ + {"role": "system", "content": 'Tu es un correcteur de texte. Corrige uniquement la grammaire et l\'orthographe de la phrase donnée. Renvoie uniquement un JSON au format suivant: {"phrase_corrigee": "ta phrase corrigée"}. Ne donne aucune autre information.'}, + {"role": "user", "content": f'{{"phrase": "{text}"}}'}, + ] + ) + corrected_response = json.loads(response.choices[0].message.content) + return drf_response.Response(corrected_response['phrase_corrigee']) + except (json.JSONDecodeError, KeyError) as e: + return drf_response.Response({"error": f"Error processing AI response: {str(e)}"}, status=500) + else: + return drf_response.Response({"error": "Invalid action"}, status=400) @decorators.action(detail=True, methods=["get"], url_path="versions") diff --git a/src/backend/impress/settings.py b/src/backend/impress/settings.py index 5c1058b3c..ab4671258 100755 --- a/src/backend/impress/settings.py +++ b/src/backend/impress/settings.py @@ -387,6 +387,12 @@ class Base(Configuration): ALLOW_LOGOUT_GET_METHOD = values.BooleanValue( default=True, environ_name="ALLOW_LOGOUT_GET_METHOD", environ_prefix=None ) + AI_BASE_URL = values.Value( + None, environ_name="AI_BASE_URL", environ_prefix=None + ) + AI_API_KEY = values.Value( + None, environ_name="AI_API_KEY", environ_prefix=None + ) # pylint: disable=invalid-name @property diff --git a/src/backend/pyproject.toml b/src/backend/pyproject.toml index 14b0500f2..46c126edb 100644 --- a/src/backend/pyproject.toml +++ b/src/backend/pyproject.toml @@ -47,6 +47,7 @@ dependencies = [ "jsonschema==4.23.0", "markdown==3.7", "nested-multipart-parser==1.5.0", + "openai==1.44.1", "psycopg[binary]==3.2.1", "PyJWT==2.9.0", "pypandoc==1.13", diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx index 79bf9253a..dce432767 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/AIButton.tsx @@ -36,6 +36,8 @@ export function AIButton({ doc }: AIButtonProps) { action, }); + console.log('AI response:', newText); + editor.insertInlineContent([ newText, //{ type: 'text', text: 'World', styles: { bold: true } },