From e26fcdf985012d0f36d7f0624e63f5e736becfa8 Mon Sep 17 00:00:00 2001 From: Nathan Vasse Date: Tue, 11 Feb 2025 15:53:29 +0100 Subject: [PATCH] wip --- docker-compose.yml | 23 +- src/backend/core/api/viewsets.py | 35 + src/backend/core/services/ai_services.py | 16 + src/frontend/apps/impress/package.json | 1 + .../impress/public/assets/ia_baguette.png | Bin 0 -> 9742 bytes .../assets/ia_baguette_question_mark.png | Bin 0 -> 17012 bytes .../doc-editor/components/Ai/AiButton.tsx | 253 + .../docs/doc-editor/components/DocEditor.tsx | 2 + src/frontend/package-lock.json | 25849 ++++++++++++++ src/frontend/yarn.lock | 29316 ++++++++-------- 10 files changed, 40818 insertions(+), 14677 deletions(-) create mode 100644 src/frontend/apps/impress/public/assets/ia_baguette.png create mode 100644 src/frontend/apps/impress/public/assets/ia_baguette_question_mark.png create mode 100644 src/frontend/apps/impress/src/features/docs/doc-editor/components/Ai/AiButton.tsx create mode 100644 src/frontend/package-lock.json diff --git a/docker-compose.yml b/docker-compose.yml index 74c90c08..235bbdd7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,8 +21,8 @@ services: - MINIO_ROOT_USER=impress - MINIO_ROOT_PASSWORD=password ports: - - '9000:9000' - - '9001:9001' + - "9000:9000" + - "9001:9001" entrypoint: "" command: minio server --console-address :9001 /data volumes: @@ -59,11 +59,11 @@ services: - ./src/backend:/app - ./data/static:/data/static depends_on: - - postgresql - - mailcatcher - - redis - - createbuckets - + - postgresql + - mailcatcher + - redis + - createbuckets + celery-dev: user: ${DOCKER_USER:-1000} image: impress:backend-development @@ -122,7 +122,7 @@ services: frontend-dev: user: "${DOCKER_USER:-1000}" - build: + build: context: . dockerfile: ./src/frontend/Dockerfile target: frontend-production @@ -151,13 +151,13 @@ services: image: node:18 user: "${DOCKER_USER:-1000}" environment: - HOME: /tmp + HOME: /tmp volumes: - ".:/app" y-provider: user: ${DOCKER_USER:-1000} - build: + build: context: . dockerfile: ./src/frontend/servers/y-provider/Dockerfile target: y-provider @@ -169,6 +169,7 @@ services: kc_postgresql: image: postgres:14.3 + platform: linux/amd64 ports: - "5433:5432" env_file: @@ -196,7 +197,7 @@ services: KC_DB_PASSWORD: pass KC_DB_USERNAME: impress KC_DB_SCHEMA: public - PROXY_ADDRESS_FORWARDING: 'true' + PROXY_ADDRESS_FORWARDING: "true" ports: - "8080:8080" depends_on: diff --git a/src/backend/core/api/viewsets.py b/src/backend/core/api/viewsets.py index e9616aff..e6a51944 100644 --- a/src/backend/core/api/viewsets.py +++ b/src/backend/core/api/viewsets.py @@ -1079,6 +1079,41 @@ class DocumentViewSet( return drf.response.Response(response, status=drf.status.HTTP_200_OK) + @drf.decorators.action( + detail=True, + methods=["post"], + name="Just proxy ai call", + url_path="ai-proxy" + ) + def ai_proxy(self, request, *args, **kwargs): + """ + POST /api/v1.0/documents//ai-transform + with expected data: + - text: str + - action: str [prompt, correct, rephrase, summarize] + Return JSON response with the processed text. + """ + print('PROXY 1') + # Check permissions first + # self.get_object() + + print('PROXY 2') + print(request.data) + # serializer = serializers.AITransformSerializer(data=request.data) + # serializer.is_valid(raise_exception=True) + + print('PROXY 3') + system_content = request.data["system"] + text = request.data["text"] + + print('PROXY 4') + response = AIService().call_proxy(system_content, text) + + print('PROXY 5') + print(response) + + return drf.response.Response(response, status=drf.status.HTTP_200_OK) + @drf.decorators.action( detail=True, methods=["post"], diff --git a/src/backend/core/services/ai_services.py b/src/backend/core/services/ai_services.py index 102e8689..e00761b8 100644 --- a/src/backend/core/services/ai_services.py +++ b/src/backend/core/services/ai_services.py @@ -55,6 +55,22 @@ class AIService: raise ImproperlyConfigured("AI configuration not set") self.client = OpenAI(base_url=settings.AI_BASE_URL, api_key=settings.AI_API_KEY) + def call_proxy(self, system_content, text): + messages = [ + {"role": "system", "content": system_content}, + {"role": "user", "content": text}, + ] + print('REQUEST', messages) + response = self.client.chat.completions.create( + model='meta-llama/Llama-3.1-8B-Instruct', + messages=messages, + ) + + print('RESPONSE', response) + content = response.choices[0].message.content + print('CONTENT', content) + return content + def call_ai_api(self, system_content, text): """Helper method to call the OpenAI API and process the response.""" response = self.client.chat.completions.create( diff --git a/src/frontend/apps/impress/package.json b/src/frontend/apps/impress/package.json index e36903db..e8400745 100644 --- a/src/frontend/apps/impress/package.json +++ b/src/frontend/apps/impress/package.json @@ -34,6 +34,7 @@ "idb": "8.0.2", "lodash": "4.17.21", "luxon": "3.5.0", + "marked": "^15.0.7", "next": "15.1.6", "posthog-js": "1.215.6", "react": "*", diff --git a/src/frontend/apps/impress/public/assets/ia_baguette.png b/src/frontend/apps/impress/public/assets/ia_baguette.png new file mode 100644 index 0000000000000000000000000000000000000000..c5c2e0f3c484239088d4106e624940f04e652210 GIT binary patch literal 9742 zcmV+pCh^&cP)~5NZi_u7Zx7`Hp`W8Y-AF`u1P9ICFB57NvHt3l2Qd# zKvjaXfpCPxt^fv|)6>WM`oI78H)kgR2_ZOS?zKtw(4% z;ycU$=WGB!#WEeGht|7=uy##MuwC`_Y*3#A$H%96&V@>xEl|>f2*N-Y`4p6?8E^G7h<$NnsW42&GJT3s5 z;!)G?-7Erbqs1Gwc+{XRUTm>YXfjvji@>1(ad_BfqoeM2=uEo$w%Xd@Cg&aiq~~7d zn?vUnK$GLl?FSCn0F5|K86FUYMMa!|Kx+ZG~?jHCy$wl=Vt?g7BzKArF82gJMrn2F86f4?n1l}NR&T(*q8j_>jn)yavGt}jgcMp|?n zIFO_$iv1dGi-g0Rz3+YRUGPA-H{4@5EbqS__r8CB622|-2P@L~!gHal590TKG>GKe z-9$G5V?~P06V*_eD?yy%4@3|2&C8Zq8(GTnS3vR{3`tfTXg*a6JoP~Qt%C+X)x`k4GQslL9HMIwdr z^Ncep*_mfn(R%(IP%kHn&*(e)$RsY_7=*qIFhnEq~pN*kt4M&uN6c3R(WAr+)P0V6afS z3i$>PV70aVj_umjCqc|50nF>JJB4l7a0<&M86XZ&aQ>`FB$aQ}A-Fk#IO|cKEIK)5 z5(F{{e*k!}HY+Rh1Ky@hT@tvta@uSNoVab9QT>e{AOC<>gr0_>eb8G>8Dr9<>kvuSy`D^zwO%>V}pYU z_Qo51^w-aJ>^Lwb7vFT#nXIK{RZjFIoczE3)z&f8jkUGb&B_v;dp2Xn0Q$jGY|m&= z$F2(o13v_4x%Trgux)LxvU~5{mG*lKD!}^mr%z#*UAAJ*gA5QKee^B)myUADjQ|fI zs)&u(s4>BuDTp%yFrCcwox6s5_e{gJx5UD>|-m~@gf*W&pmhLBoJ|}(9_wFe)ut#s0#vP zfMyfuo)SQph21zOsGz{*zFZJ=9oz(yGdh!a_uZc2J{T{#Aiu!^Q3>$s>Ppx`A%F)_ZW6Bf=9^z+*-$9t z8Z@P)i#E95qz2wh)lH(b0BYH*m8tXR=suoP?t@k5w>Iy(YxRNxPZOlU-FL6?Y}>Ty zEh+x9Aw8C4S9}GZ3M=_1Lv2&J6lqZ~e!$(JYa9Q|Uv9smu#o>RxNqsw?87Yp?&Om< zdA8kp>ne8JZ69U}nJ&Kgr|iWS52V)N;J@?Ebu1ft_gxjCNhItGR<69GLx>hJlf2<1 z6x{4OGin^wcZG%QKk=DM?lTEuYN3M%dfUU&?y6X>S{#^zDQ<9UZC;^_3qP6(MqS%njk03PaU zOv)z2W{T01Pk!IWA^G6Cxe0{H22BbMv%1XL!a6%Uc~et0r&wJ||7A-*|M_0eQwoD1 zUF82nA;0L))+f|sOkk}A^R%`$s4$qXOkdL^5vmC>&-V6a=U`kW!3qkp2M|R_CLynP zA`_f@zZlIXyrxx@>IG2El`d@Ku2Gpmgt}iSvlXnnZe16@<(7{}X#I42lZ)wJe(`zP zW&glVWJ)JSA!X)Dm^$vLsk*10`nc0+qb21HFkkS+!-2X)kob@5$oQ$JMkI^m20U-+ zpQ>Fs@r)Ngp`62{Y_k+--T+c#-wr<<-hj43J@YJr&c;;eOdq79gIOIN)nvc~CHLh* zd~V(lm$bPqg6j4$IZkj?=2RaG^J zrY8Bbdi>9Y^Zs5FpPKwBuU)(PSvI6Qa9CEbuBqv52^Oj#v@v7qLCXXW`cI3_zP>I# zI$93U>#?2-0R5uHuRHvttHsQ~@q7?U^Ks67`{Fa&A9~QA@Xf{_)UFzhecjHTutlo4 z;ZAVVXAipUs3z#QwE^u0P6iBZ!{1e9>R{Q4P&}2Ub>_F;OhWDE_1FJK0;mak=Anl! z%=nvJP`dNNO7^Fa($VVGHL|Kr6V%0A%MgP>#ZNTmb(Heu9Ua>RLTkt@;d`?j-_jdQ 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 literal 0 HcmV?d00001 diff --git a/src/frontend/apps/impress/public/assets/ia_baguette_question_mark.png b/src/frontend/apps/impress/public/assets/ia_baguette_question_mark.png new file mode 100644 index 0000000000000000000000000000000000000000..45d830509297aee3831b2c97545f486634aac526 GIT binary patch literal 17012 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 Xta_B-5kYA?00000NkvXXu0mjf5j+-I literal 0 HcmV?d00001 diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/Ai/AiButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/Ai/AiButton.tsx new file mode 100644 index 00000000..6d87781a --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/Ai/AiButton.tsx @@ -0,0 +1,253 @@ +import { Button, Input, Loader, TextArea } from '@openfun/cunningham-react'; +import { marked } from 'marked'; +import { useState } from 'react'; +import { ButtonProps } from 'react-aria-components'; +import styled from 'styled-components'; + +import { fetchAPI } from '@/api'; +import { Box, Text } from '@/components'; +import { Doc } from '@/features/docs'; + +import { useEditorStore } from '../../stores/useEditorStore'; + +export const AIButtonEl = styled.button` + background-image: url('/assets/ia_baguette.png'); + background-size: cover; + width: 48px; + height: 48px; + border-radius: 50%; + border: none; + cursor: pointer; + background-color: transparent; +`; + +export const SuggestionButton = styled.button` + display: flex; + align-items: center; + border: none; + cursor: pointer; + background-color: white; + color: var(--c--theme--colors--greyscale-900); + height: 32px; + padding: 0 0.5rem; + border-radius: 4px; + font-weight: 600; + &:hover, + &:focus { + background-color: var(--c--theme--colors--greyscale-100); + } + + span.material-icons { + margin-right: 4px; + } + + span.sub { + color: var(--c--theme--colors--greyscale-600); + margin-left: 4px; + font-weight: 500; + } +`; + +export const AiButton = ({ doc }: { doc: Doc }) => { + const [isOpen, setIsOpen] = useState(true); + + return ( + <> + + setIsOpen(true)} + /> + + + setIsOpen(false)} /> + + ); +}; +type Message = { + role: 'user' | 'assistant'; + content: string; +}; + +const AiChat = (props: { isOpen: boolean; onClose: () => void; doc: Doc }) => { + const [prompt, setPrompt] = useState(''); + const { editor } = useEditorStore(); + const [isLoading, setIsLoading] = useState(false); + + const [messages, setMessages] = useState([]); + + if (!props.isOpen) { + return null; + } + + const submit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!editor) { + return; + } + + setIsLoading(true); + setMessages([...messages, { role: 'user', content: prompt }]); + setPrompt(''); // Clear the prompt after submitting the form + + const editorContentFormatted = await editor.blocksToMarkdownLossy(); + + console.log('submit', e, prompt); + console.log('editorContentFormatted', editorContentFormatted); + + const response = await fetchAPI(`documents/${props.doc.id}/ai-proxy/`, { + method: 'POST', + body: JSON.stringify({ + system: + 'You are a helpful assistant. You are given a text in markdown format and you need to answer the question. Here is the text: ' + + editorContentFormatted, + text: prompt, + }), + }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const data = await response.json(); + + console.log('response', data); + setMessages((messages) => [ + ...messages, + { role: 'assistant', content: data }, + ]); + setIsLoading(false); + }; + + return ( + + + + {messages.length == 0 ? '' : 'Demander à Albert'} + +