dmartincy commited on
Commit
9f97180
·
1 Parent(s): 481612a

Add basic Dockerfile

Browse files
.eslintignore ADDED
@@ -0,0 +1 @@
 
 
1
+ /public/lib/
.eslintrc.cjs ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ root: true,
3
+ env: { browser: true, es2020: true },
4
+ extends: [
5
+ 'eslint:recommended',
6
+ 'plugin:@typescript-eslint/recommended',
7
+ 'plugin:react-hooks/recommended',
8
+ ],
9
+ ignorePatterns: ['dist', '.eslintrc.cjs'],
10
+ parser: '@typescript-eslint/parser',
11
+ plugins: ['react-refresh'],
12
+ rules: {
13
+ 'react-refresh/only-export-components': [
14
+ 'warn',
15
+ { allowConstantExport: true },
16
+ ],
17
+ },
18
+ }
.gitignore ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Logs
2
+ logs
3
+ *.log
4
+ npm-debug.log*
5
+ yarn-debug.log*
6
+ yarn-error.log*
7
+ pnpm-debug.log*
8
+ lerna-debug.log*
9
+
10
+ node_modules
11
+ dist
12
+ dist-ssr
13
+ *.local
14
+
15
+ /public/lib/
16
+ package-lock.json
17
+
18
+ # Editor directories and files
19
+ .vscode/*
20
+ !.vscode/extensions.json
21
+ .idea
22
+ .DS_Store
23
+ *.suo
24
+ *.ntvs*
25
+ *.njsproj
26
+ *.sln
27
+ *.sw?
.tool-versions ADDED
@@ -0,0 +1 @@
 
 
1
+ nodejs 20.9.0
Dockerfile ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # First stage: Get files from ai-assistant
2
+ FROM pspdfkit/ai-assistant:nightly as ai-assistant
3
+
4
+ # Main stage
5
+ FROM pgvector/pgvector:pg16
6
+
7
+ # Install Node.js and other dependencies
8
+ RUN apt-get update && apt-get install -y \
9
+ curl \
10
+ wget \
11
+ nginx \
12
+ python3 \
13
+ python3-pip \
14
+ git \
15
+ unzip \
16
+ && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
17
+ && apt-get install -y nodejs \
18
+ && rm -rf /var/lib/apt/lists/*
19
+
20
+ WORKDIR /app
21
+
22
+ # Create necessary directories and set up users
23
+ RUN mkdir -p /base /var/log/llamafiler /var/lib/nginx /var/log/nginx /run/nginx /var/run/postgresql && \
24
+ useradd -m -u 1000 -s /bin/bash appuser && \
25
+ chown -R appuser:appuser /base /var/log/llamafiler /app && \
26
+ chown -R www-data:www-data /var/lib/nginx /var/log/nginx /run/nginx && \
27
+ mkdir -p /var/lib/postgresql/data && \
28
+ chmod 700 /var/lib/postgresql/data && \
29
+ chown -R appuser:appuser /var/lib/postgresql/data && \
30
+ chown -R appuser:appuser /var/run/postgresql && \
31
+ chmod 777 /var/run/postgresql
32
+
33
+ # Copy entire ai-assistant to a separate directory
34
+ COPY --from=ai-assistant /base/app /ai-assistant
35
+ RUN chown -R appuser:appuser /ai-assistant
36
+
37
+ # Copy our configuration files
38
+ COPY --chown=appuser:appuser service-config.yml /app/
39
+ COPY --chown=appuser:appuser start-services.sh /app/
40
+ COPY --chown=appuser:appuser package*.json /app/
41
+
42
+ # Install node dependencies for webapp
43
+ RUN npm install
44
+
45
+ # Copy webapp files
46
+ COPY --chown=appuser:appuser . /app/
47
+
48
+ # Configure nginx to route traffic
49
+ RUN echo '\
50
+ events { worker_connections 1024; } \n\
51
+ http { \n\
52
+ server { \n\
53
+ listen 7860; \n\
54
+ location /v1 { \n\
55
+ proxy_pass http://localhost:8080; \n\
56
+ } \n\
57
+ location /api { \n\
58
+ proxy_pass http://localhost:4000; \n\
59
+ } \n\
60
+ location / { \n\
61
+ proxy_pass http://localhost:3000; \n\
62
+ } \n\
63
+ } \n\
64
+ }' > /etc/nginx/nginx.conf
65
+
66
+ # Set up environment variables
67
+ ENV OPENAI_API_KEY="dummy-key" \
68
+ PGUSER=appuser \
69
+ PGPASSWORD=password \
70
+ PGDATABASE=postgres \
71
+ PGHOST=localhost \
72
+ PGPORT=5432 \
73
+ API_AUTH_TOKEN=secret \
74
+ DASHBOARD_USERNAME=dashboard \
75
+ DASHBOARD_PASSWORD=secret \
76
+ SECRET_KEY_BASE=secret-key-base \
77
+ NODE_OPTIONS="--experimental-vm-modules --experimental-json-modules" \
78
+ PATH="/usr/lib/postgresql/16/bin:${PATH}" \
79
+ POSTGRES_USER=appuser \
80
+ POSTGRES_PASSWORD=password \
81
+ POSTGRES_DB=postgres \
82
+ PGDATA=/var/lib/postgresql/data
83
+
84
+ # Create local bin directory
85
+ RUN mkdir -p /.local/bin && \
86
+ chown -R appuser:appuser /.local
87
+
88
+ # Make start script executable
89
+ RUN chmod +x /app/start-services.sh
90
+
91
+ # Switch to appuser
92
+ USER appuser
93
+
94
+ # Expose the Space port
95
+ EXPOSE 7860
96
+
97
+ # Start all services
98
+ CMD ["/app/start-services.sh"]
Modelfile ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ FROM /models/gemma-2-2b-it-Q4_K_M.gguf
2
+
Modelfile.embeddings ADDED
@@ -0,0 +1 @@
 
 
1
+ FROM /models/mxbai-embed-large-v1-f16.gguf
index.html ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Document Authoring SDK</title>
8
+ </head>
9
+ <body>
10
+ <ul>
11
+ <li><a href='./javascript.html'>JavaScript example</a></li>
12
+ </ul>
13
+ </body>
14
+ </html>
javascript.html ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Document Authoring SDK</title>
7
+ <style>
8
+ .spinner {
9
+ display: inline-block;
10
+ width: 16px;
11
+ height: 16px;
12
+ border: 2px solid #f3f3f3;
13
+ border-top: 2px solid #3498db;
14
+ border-radius: 50%;
15
+ animation: spin 1s linear infinite;
16
+ margin-right: 8px;
17
+ vertical-align: middle;
18
+ }
19
+
20
+ @keyframes spin {
21
+ 0% { transform: rotate(0deg); }
22
+ 100% { transform: rotate(360deg); }
23
+ }
24
+
25
+ #translateButton:disabled {
26
+ cursor: not-allowed;
27
+ opacity: 0.7;
28
+ }
29
+ </style>
30
+ </head>
31
+ <body>
32
+ <!--
33
+ IMPORTANT: An editor target element needs to have its `position` set to a value other than the default or `static`!
34
+ If unsure use `relative`.
35
+ -->
36
+ <button id="translateButton">Translate to English</button>
37
+ <div id="editor" style="border: 1px solid black; width: 1024px; height: 600px; position: relative"></div>
38
+ <script type="module" src="./src/javascript-app.js"></script>
39
+ </body>
40
+ </html>
package.json ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "document-authoring-sdk-example",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "author": "PSPDFKit (https://pspdfkit.com)",
7
+ "homepage": "https://pspdfkit.com/pdf-sdk/web/document-authoring/",
8
+ "scripts": {
9
+ "dev": "vite",
10
+ "build": "vite build",
11
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
12
+ },
13
+ "dependencies": {
14
+ "@pspdfkit/document-authoring": "1.0.26",
15
+ "@wllama/wllama": "^1.16.4",
16
+ "react": "^18.2.0",
17
+ "react-dom": "^18.2.0"
18
+ },
19
+ "devDependencies": {
20
+ "@types/react": "^18.2.66",
21
+ "@types/react-dom": "^18.2.22",
22
+ "@typescript-eslint/eslint-plugin": "^7.2.0",
23
+ "@typescript-eslint/parser": "^7.2.0",
24
+ "@vitejs/plugin-react": "^4.2.1",
25
+ "eslint": "^8.57.0",
26
+ "eslint-plugin-react-hooks": "^4.6.0",
27
+ "eslint-plugin-react-refresh": "^0.4.6",
28
+ "typescript": "^5.2.2",
29
+ "vite": "^5.2.0"
30
+ }
31
+ }
public/Spanish.docx ADDED
Binary file (5.24 kB). View file
 
public/sample.docx ADDED
Binary file (16.4 kB). View file
 
public/sample.json ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "type": "https://pspdfkit.com/document-authoring/persistence/container",
3
+ "container": {
4
+ "document": {
5
+ "body": {
6
+ "sections": [
7
+ {
8
+ "elements": [
9
+ {
10
+ "type": "p",
11
+ "pPr": {
12
+ "styleId": "Heading 1",
13
+ "alignment": "center"
14
+ },
15
+ "elements": [
16
+ {
17
+ "type": "r",
18
+ "text": "Heading"
19
+ }
20
+ ]
21
+ },
22
+ {
23
+ "type": "p",
24
+ "elements": [
25
+ {
26
+ "type": "r",
27
+ "text": "Simple paragraphs consist of one `r` typed element containing all the text."
28
+ }
29
+ ]
30
+ },
31
+ {
32
+ "type": "p",
33
+ "elements": [
34
+ {
35
+ "type": "r",
36
+ "text": "There is "
37
+ },
38
+ {
39
+ "type": "r",
40
+ "rPr": {
41
+ "bold": true,
42
+ "color": "#ff0000",
43
+ "underline": true
44
+ },
45
+ "text": "formatted"
46
+ },
47
+ {
48
+ "type": "r",
49
+ "text": " text in this paragraph."
50
+ }
51
+ ]
52
+ }
53
+ ]
54
+ }
55
+ ]
56
+ }
57
+ }
58
+ }
59
+ }
public/script-tag.html ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Document Authoring SDK</title>
7
+ </head>
8
+ <body>
9
+ <!--
10
+ IMPORTANT: An editor target element needs to have its `position` set to a value other than the default or `static`!
11
+ If unsure use `relative`.
12
+ -->
13
+ <div id="editor" style="border: 1px solid black; width: 1024px; height: 600px; position: relative"></div>
14
+ <script src="https://document-authoring-cdn.pspdfkit.com/releases/document-authoring-1.0.26-umd.js"></script>
15
+ <script>
16
+ (async ()=>{
17
+ const docAuthSystem = await DocAuth.createDocAuthSystem({
18
+ // assets: { base: '<CUSTOM_DOCUMENT_AUTHORING_ASSETS_LOCATION>' },
19
+ // licenseKey: '<YOUR_LICENSE_KEY>',
20
+ })
21
+
22
+ const editor = await docAuthSystem.createEditor(document.getElementById('editor'),{
23
+ document: await docAuthSystem.createDocumentFromPlaintext('Hi there!'),
24
+ });
25
+
26
+ // Dev exports.
27
+
28
+ window.DocAuth = DocAuth;
29
+ window.docAuthSystem = docAuthSystem
30
+ window.editor = editor;
31
+ })()
32
+ </script>
33
+ </body>
34
+ </html>
public/vite.svg ADDED
service-config.yml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '1'
2
+
3
+ aiServices:
4
+ chat:
5
+ provider:
6
+ name: 'openai-compat'
7
+ baseUrl: http://localhost:8080/v1
8
+ model: 'gemma-2b'
9
+ textEmbeddings:
10
+ provider:
11
+ name: 'openai-compat'
12
+ baseUrl: http://localhost:8080/v1
13
+ model: 'all-MiniLM-L6-v2'
14
+ headless:
15
+ - provider:
16
+ name: 'openai-compat'
17
+ baseUrl: http://localhost:8080/v1
18
+ model:
19
+ name: 'gemma-2b'
20
+ id: 'gemma-2b'
src/App.css ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .App {
2
+ display: flex;
3
+ flex-direction: column;
4
+ width: 100%;
5
+ height: 100vh;
6
+ overflow: hidden;
7
+ }
8
+
9
+ .Header {
10
+ border-bottom: solid 1px black;
11
+
12
+ button {
13
+ margin: 10px;
14
+ padding: 3px;
15
+ }
16
+ }
src/App.tsx ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import DocumentEditor from './components/DocumentEditor'
2
+ import { DocAuthEditor, DocAuthSystem } from '@pspdfkit/document-authoring'
3
+ import { useCallback, useState } from 'react'
4
+ import './App.css'
5
+
6
+ function App() {
7
+ const [editor,setEditor] = useState<DocAuthEditor|null>(null)
8
+
9
+ const clear = async () => {
10
+ if( editor ) {
11
+ const docAuthSystem = editor.docAuthSystem()
12
+ editor.setCurrentDocument(await docAuthSystem.createDocumentFromPlaintext(''))
13
+ }
14
+ }
15
+
16
+ const loadDocx = async () => {
17
+ if( editor ) {
18
+ const docAuthSystem = editor.docAuthSystem()
19
+ editor.setCurrentDocument(await docAuthSystem.importDOCX(fetch('./sample.docx')))
20
+ }
21
+ }
22
+
23
+ const loadJson = async () => {
24
+ if( editor ) {
25
+ const docAuthSystem = editor.docAuthSystem()
26
+ editor.setCurrentDocument(await docAuthSystem.loadDocument(fetch('./sample.json')))
27
+ }
28
+ }
29
+
30
+ const createInitialDocument = useCallback((docAuthSystem:DocAuthSystem) => {
31
+ return docAuthSystem.createDocumentFromPlaintext('Hi there!')
32
+ },[])
33
+
34
+ return (
35
+ <div className='App'>
36
+ <>
37
+ <div className='Header'>
38
+ <button onClick={clear}>Clear</button>
39
+ <button onClick={loadDocx}>Load example.docx</button>
40
+ <button onClick={loadJson}>Load example.json</button>
41
+ </div>
42
+ <DocumentEditor initialDocument={createInitialDocument} onEditor={setEditor} />
43
+ </>
44
+ </div>
45
+ )
46
+ }
47
+
48
+ export default App
src/components/DocumentEditor.tsx ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useEffect, useRef } from 'react';
2
+ import { DocAuthSystem, DocAuthDocument, DocAuthEditor, createDocAuthSystem } from '@pspdfkit/document-authoring';
3
+
4
+ const DocumentEditor = (props:{
5
+ initialDocument?: (docAuthSystem:DocAuthSystem)=>Promise<DocAuthDocument>,
6
+ onEditor?: (editor:DocAuthEditor)=>void,
7
+ }) => {
8
+ const {initialDocument,onEditor} = props
9
+ const containerRef = useRef<HTMLDivElement|null>(null)
10
+
11
+ useEffect(()=>{
12
+ const target = document.createElement('div') as HTMLDivElement
13
+ let removed = false
14
+
15
+ const docAuthSystemAndEditorPromise = createDocAuthSystem({
16
+ // assets: { base: '<CUSTOM_DOCUMENT_AUTHORING_ASSETS_LOCATION>' },
17
+ // licenseKey: '<YOUR_LICENSE_KEY>',
18
+ }).then(async (docAuthSystem)=>{
19
+ const doc = await (initialDocument ? initialDocument(docAuthSystem) : undefined)
20
+
21
+ const editor = await docAuthSystem.createEditor(target,{
22
+ document: doc,
23
+ })
24
+
25
+ if( !removed ) {
26
+ if( containerRef.current ) {
27
+ containerRef.current.append(target)
28
+ }
29
+
30
+ if( onEditor ) {
31
+ onEditor(editor)
32
+ }
33
+ }
34
+
35
+ return {
36
+ docAuthSystem,
37
+ editor,
38
+ }
39
+ })
40
+
41
+ return ()=>{
42
+ target.remove()
43
+ removed = true
44
+
45
+ docAuthSystemAndEditorPromise.then(({docAuthSystem,editor})=>{
46
+ editor.destroy()
47
+ docAuthSystem.destroy()
48
+ })
49
+ }
50
+ },[onEditor,initialDocument])
51
+
52
+ return <div ref={containerRef} style={{ flexGrow: '1', position: 'relative' }} />
53
+ }
54
+
55
+ export default DocumentEditor
src/index.css ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ font-weight: 400;
4
+
5
+ font-synthesis: none;
6
+ text-rendering: optimizeLegibility;
7
+ -webkit-font-smoothing: antialiased;
8
+ -moz-osx-font-smoothing: grayscale;
9
+ }
10
+
11
+ body {
12
+ margin: 0;
13
+ padding: 0;
14
+ }
15
+
16
+ #root {
17
+ display: flex;
18
+ }
src/javascript-app.js ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import DocAuth from "@pspdfkit/document-authoring";
2
+
3
+ const docAuthSystem = await DocAuth.createDocAuthSystem({
4
+ // assets: { base: '<CUSTOM_DOCUMENT_AUTHORING_ASSETS_LOCATION>' },
5
+ // licenseKey: '<YOUR_LICENSE_KEY>',
6
+ });
7
+
8
+ const editor = await docAuthSystem.createEditor(
9
+ document.getElementById("editor"),
10
+ {
11
+ document: await docAuthSystem.importDOCX(fetch("./Spanish.docx")),
12
+ }
13
+ );
14
+
15
+ // Translation function.
16
+ async function translate(content, lang = "English") {
17
+ try {
18
+ const response = await fetch('/api/aia/v1/chat/completions', {
19
+ method: 'POST',
20
+ headers: {
21
+ 'Content-Type': 'application/json',
22
+ 'Authorization': 'Token token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3MzMyNjQ2MDcsImV4cCI6MTczMzUyMzgwN30.WWyks6NOU43_2OcZX82z3P4QUwQiUA61SeTZsNobIGoVg3uDTkedXWqISIB7QqTnRyf_D92t_pGr5g_GzqBaOTqaXYJJPco9mtZjtAyUpdWr1UHu4jx-zoKUpCeguZQZbHG6vqYzu1BNV1iPdij2OIoUrr_DY--G-6m5-31E0LcDZb7ysUyZL1Ai_q7PPRwTS8mEtvuF4QO6Cw_a77X7Qh9x_V8a_WwNMFwQMRkIUAsRx3IOI10q0hdwP-k8xYtTbHQO-z66pRl6I71ATclR7FS5FraoLLmWlq8lqNKfzeOgER8Kcf8uB87zHQbfk0QR8AfJGmZDE8pG5F3QG29MeA'
23
+ },
24
+ body: JSON.stringify({
25
+ messages: [
26
+ {
27
+ role: "system",
28
+ content: "You are a translator. Only provide the translation, no explanations or additional text."
29
+ },
30
+ {
31
+ role: "user",
32
+ content: `Translate the following text to ${lang}: ${content}`
33
+ }
34
+ ],
35
+ model: "gemma-2b",
36
+ stream: false
37
+ })
38
+ });
39
+
40
+ if (!response.ok) {
41
+ throw new Error('Translation request failed');
42
+ }
43
+
44
+ const data = await response.json();
45
+ console.log('Translation API response:', data);
46
+ return data.choices[0].message.content;
47
+ } catch (error) {
48
+ console.error("Translation error:", error);
49
+ throw new Error("Failed to generate translation");
50
+ }
51
+ }
52
+
53
+ // Function to recursively translate text in the JSON structure.
54
+ async function translateRecursive(obj) {
55
+ for (let key in obj) {
56
+ if (typeof obj[key] === "string" && key === "text" && obj[key].length > 0) {
57
+ obj[key] = await translate(obj[key]);
58
+ } else if (typeof obj[key] === "object") {
59
+ await translateRecursive(obj[key]);
60
+ }
61
+ }
62
+ }
63
+
64
+ // Function to translate the document.
65
+ async function translateDocument() {
66
+ try {
67
+ translateButton.disabled = true;
68
+ translateButton.innerHTML = '<span class="spinner"></span> Translating...';
69
+
70
+ const jsonDoc = await editor.currentDocument().saveDocument();
71
+ await translateRecursive(jsonDoc.container.document);
72
+
73
+ const translatedDoc = await docAuthSystem.loadDocument(jsonDoc);
74
+ editor.setCurrentDocument(translatedDoc);
75
+ } catch (error) {
76
+ console.error("Translation error:", error);
77
+ } finally {
78
+ translateButton.disabled = false;
79
+ translateButton.innerHTML = 'Translate to English';
80
+ }
81
+ }
82
+
83
+ // Add click event listener to the translate button.
84
+ const translateButton = document.getElementById("translateButton");
85
+ translateButton.addEventListener("click", translateDocument);
86
+
87
+ // Dev exports.
88
+ window.DocAuth = DocAuth;
89
+ window.docAuthSystem = docAuthSystem;
90
+ window.editor = editor;
src/javascript-shared-app.js ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Load the JS module.
2
+ import DocAuth from '@pspdfkit/document-authoring'
3
+
4
+ // Load the base Document Authoring system. The WASM and font code is initialized here.
5
+ // The `docAuthSystem` can be re-used between multiple editor instances or used to
6
+ // export PDFs or import DOCX documents without the need to instantiate the visual
7
+ // editor.
8
+ const docAuthSystem = await DocAuth.createDocAuthSystem({
9
+ // assets: { base: '<CUSTOM_DOCUMENT_AUTHORING_ASSETS_LOCATION>' },
10
+ // licenseKey: '<YOUR_LICENSE_KEY>',
11
+ });
12
+
13
+ // A PDF can be created without the need for an editor instance.
14
+ document.getElementById('headless').onclick = async () => {
15
+ const createdDocument = await docAuthSystem.createDocumentFromPlaintext(`Hello World!\n\nISO date: ${new Date().toISOString()}`);
16
+ const pdfBlob = await createdDocument.exportPDF();
17
+ window.open(URL.createObjectURL(new Blob([pdfBlob], { type: 'application/pdf' })));
18
+ };
19
+
20
+ // Instantiate a visual document authoring editor.
21
+ const editor1 = await docAuthSystem.createEditor(
22
+ document.getElementById('editor1'),{
23
+ document: await docAuthSystem.createDocumentFromPlaintext('Hi there!\n\nHow are you?'),
24
+ });
25
+
26
+ // Instantiate another visual document authoring editor.
27
+ const editor2 = await docAuthSystem.createEditor(
28
+ document.getElementById('editor2'),
29
+ {
30
+ document: await docAuthSystem.loadDocument(await fetch('./sample.json').then((e) => e.json())),
31
+ });
32
+
33
+ document.getElementById('docx').onclick = async () => {
34
+ const doc = await docAuthSystem.importDOCX(fetch('./sample.docx'));
35
+ editor1.setCurrentDocument(doc)
36
+ };
37
+
38
+ // We can copy the document from `editor1` to `editor2` by exporting and importing it as either
39
+ // a JSON string (`exportDocumentJSON`) or an equivalent object (`exportDocument`).
40
+ document.getElementById('copy1to2').onclick = async () => {
41
+ // "Clone" the document by saving and loading it again.
42
+ editor2.setCurrentDocument(await docAuthSystem.loadDocument(editor1.currentDocument().saveDocument()))
43
+ }
44
+
45
+ // Dev exports.
46
+
47
+ window.DocAuth = DocAuth;
48
+ window.docAuthSystem = docAuthSystem;
49
+ window.editor1 = editor1;
50
+ window.editor2 = editor2;
src/main.tsx ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import App from './App.tsx'
4
+ import './index.css'
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')!).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ )
src/vite-env.d.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ /// <reference types="vite/client" />
start-services.sh ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+ set -e # Exit on error
3
+
4
+ # Create download directory
5
+ mkdir -p /tmp/downloads
6
+ cd /tmp/downloads
7
+
8
+ # Download models
9
+ echo "Downloading models..."
10
+ wget -q https://huggingface.co/bartowski/gemma-2-2b-it-GGUF/resolve/main/gemma-2-2b-it-Q4_K_M.gguf -O /base/gemma-2b.gguf
11
+ wget -q https://huggingface.co/leliuga/all-MiniLM-L6-v2-GGUF/resolve/main/all-MiniLM-L6-v2.F16.gguf -O /base/embeddings.gguf
12
+
13
+ # Download and setup llamafiler
14
+ echo "Setting up llamafiler..."
15
+ wget --no-check-certificate https://github.com/Mozilla-Ocho/llamafile/releases/download/0.8.17/llamafile-0.8.17.zip
16
+ unzip llamafile-0.8.17.zip
17
+ mkdir -p /.local/bin
18
+ cp llamafile-0.8.17/bin/llamafiler /.local/bin/
19
+ chmod +x /.local/bin/llamafiler
20
+
21
+ # Clean up downloads
22
+ cd /app
23
+ rm -rf /tmp/downloads
24
+
25
+ # Initialize PostgreSQL if needed
26
+ echo "Initializing PostgreSQL..."
27
+ if [ ! -f "$PGDATA/PG_VERSION" ]; then
28
+ initdb -D "$PGDATA" --auth=trust
29
+
30
+ # Configure PostgreSQL
31
+ echo "Configuring PostgreSQL..."
32
+ cat >> "$PGDATA/postgresql.conf" << EOF
33
+ shared_preload_libraries = 'pgvector'
34
+ listen_addresses = '*'
35
+ port = 5432
36
+ unix_socket_directories = '/var/run/postgresql'
37
+ EOF
38
+
39
+ # Configure client authentication
40
+ echo "host all all all trust" >> "$PGDATA/pg_hba.conf"
41
+ fi
42
+
43
+ # Start PostgreSQL
44
+ echo "Starting PostgreSQL..."
45
+ pg_ctl -D "$PGDATA" -l "$PGDATA/logfile" start
46
+
47
+ # If PostgreSQL fails to start, show the logs
48
+ if [ $? -ne 0 ]; then
49
+ echo "PostgreSQL failed to start. Here are the logs:"
50
+ cat "$PGDATA/logfile"
51
+ echo "Contents of postgresql.conf:"
52
+ cat "$PGDATA/postgresql.conf"
53
+ echo "Contents of pg_hba.conf:"
54
+ cat "$PGDATA/pg_hba.conf"
55
+ exit 1
56
+ fi
57
+
58
+ # Wait for PostgreSQL to be ready
59
+ until pg_isready -h localhost; do
60
+ echo "Waiting for PostgreSQL to start..."
61
+ echo "PostgreSQL log output:"
62
+ cat "$PGDATA/logfile"
63
+ sleep 2
64
+ done
65
+
66
+ echo "PostgreSQL is ready"
67
+
68
+ # Create extension if not exists
69
+ psql -h localhost -d postgres -c 'CREATE EXTENSION IF NOT EXISTS vector;'
70
+
71
+ # Start nginx
72
+ echo "Starting nginx..."
73
+ nginx
74
+
75
+ # Start llamafiler instances
76
+ echo "Starting llamafiler services..."
77
+ /.local/bin/llamafiler --model /base/gemma-2b.gguf --listen 0.0.0.0:8082 &
78
+ GEMMA_PID=$!
79
+ /.local/bin/llamafiler --model /base/embeddings.gguf --listen 0.0.0.0:8081 &
80
+ EMBEDDINGS_PID=$!
81
+
82
+ # Start the AI assistant
83
+ echo "Starting AI assistant..."
84
+ cd /ai-assistant && ./bin/entrypoint.sh node app/main.bundle.js &
85
+ AI_ASSISTANT_PID=$!
86
+
87
+ # Start the webapp
88
+ echo "Starting webapp..."
89
+ cd /app && node --experimental-modules ./node_modules/vite/bin/vite.js serve --host 0.0.0.0 --port 3000 &
90
+ WEBAPP_PID=$!
91
+
92
+ # Wait for any process to exit
93
+ wait -n
94
+
95
+ # Exit with status of process that exited first
96
+ exit $?
tsconfig.json ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noEmit": true,
15
+ "jsx": "react-jsx",
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "noFallthroughCasesInSwitch": true
22
+ },
23
+ "include": ["src"],
24
+ "references": [{ "path": "./tsconfig.node.json" }]
25
+ }
tsconfig.node.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "bundler",
7
+ "allowSyntheticDefaultImports": true,
8
+ "strict": true
9
+ },
10
+ "include": ["vite.config.ts"]
11
+ }
vite.config.ts ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import { resolve } from 'path'
4
+
5
+ // https://vitejs.dev/config/
6
+ export default defineConfig({
7
+ plugins: [react()],
8
+ appType: 'mpa',
9
+ server: {
10
+ host: '0.0.0.0', // Listen on all available network interfaces
11
+ port: 3000,
12
+ headers: {
13
+ 'Cross-Origin-Embedder-Policy': 'require-corp',
14
+ 'Cross-Origin-Opener-Policy': 'same-origin'
15
+ },
16
+ proxy: {
17
+ '/api/aia': {
18
+ target: 'http://aia:4000',
19
+ changeOrigin: true,
20
+ rewrite: (path) => {
21
+ console.log('Rewriting path:', path);
22
+ const rewritten = path.replace(/^\/api\/aia/, '/client/api');
23
+ console.log('Rewritten to:', rewritten);
24
+ return rewritten;
25
+ },
26
+ configure: (proxy, options) => {
27
+ console.log('Configuring proxy:', options);
28
+
29
+ proxy.on('error', (err, req, res) => {
30
+ console.log('Proxy error:', err);
31
+ console.log('Request:', req.method, req.url);
32
+ });
33
+
34
+ proxy.on('proxyReq', (proxyReq, req, res) => {
35
+ console.log('Proxying request:', {
36
+ method: req.method,
37
+ url: req.url,
38
+ targetUrl: proxyReq.path
39
+ });
40
+ });
41
+
42
+ proxy.on('proxyRes', (proxyRes, req, res) => {
43
+ console.log('Received response:', {
44
+ status: proxyRes.statusCode,
45
+ url: req.url,
46
+ headers: proxyRes.headers
47
+ });
48
+ });
49
+ },
50
+ ws: true,
51
+ secure: false,
52
+ debug: true
53
+ }
54
+ }
55
+ },
56
+ esbuild: {
57
+ supported: {
58
+ 'top-level-await': true,
59
+ },
60
+ },
61
+ build: {
62
+ rollupOptions: {
63
+ input: {
64
+ 'index.html': resolve(__dirname,'index.html'),
65
+ 'react.html': resolve(__dirname,'react.html'),
66
+ 'javascript.html': resolve(__dirname,'javascript.html'),
67
+ 'javascript-shared.html': resolve(__dirname,'javascript-shared.html'),
68
+ }
69
+ },
70
+ chunkSizeWarningLimit: 2048,
71
+ }
72
+ })