Leodass1433 commited on
Commit
6dcf18f
·
verified ·
1 Parent(s): 7d64804

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .commitlintrc +37 -0
  2. .dockerignore +16 -0
  3. .env.example +64 -0
  4. .gitattributes +1 -35
  5. .github/ISSUE_TEMPLATE/bug_report.yml +85 -0
  6. .github/ISSUE_TEMPLATE/config.yml +1 -0
  7. .github/ISSUE_TEMPLATE/feature_request.yml +49 -0
  8. .github/PULL_REQUEST_TEMPLATE.md +18 -0
  9. .github/workflows/auto-bump-and-release.yaml +62 -0
  10. .github/workflows/build-push-docker.yaml +106 -0
  11. .github/workflows/pr-lint.yaml +77 -0
  12. .github/workflows/style-check.yaml +20 -0
  13. .github/workflows/unit-test.yaml +114 -0
  14. .gitignore +483 -0
  15. .pre-commit-config.yaml +71 -0
  16. .python-version +1 -0
  17. CODE_OF_CONDUCT.md +128 -0
  18. CONTRIBUTING.md +115 -0
  19. Dockerfile +18 -0
  20. LICENSE.txt +201 -0
  21. README.md +423 -13
  22. app.py +37 -0
  23. build.sh +10 -0
  24. doc_env_reqs.txt +9 -0
  25. flowsettings.py +423 -0
  26. fly.toml +26 -0
  27. kotaemon_app.egg-info/PKG-INFO +15 -0
  28. kotaemon_app.egg-info/SOURCES.txt +8 -0
  29. kotaemon_app.egg-info/dependency_links.txt +1 -0
  30. kotaemon_app.egg-info/requires.txt +2 -0
  31. kotaemon_app.egg-info/top_level.txt +1 -0
  32. launch.sh +23 -0
  33. libs/kotaemon/README.md +129 -0
  34. libs/kotaemon/kotaemon.egg-info/PKG-INFO +217 -0
  35. libs/kotaemon/kotaemon.egg-info/SOURCES.txt +168 -0
  36. libs/kotaemon/kotaemon.egg-info/dependency_links.txt +1 -0
  37. libs/kotaemon/kotaemon.egg-info/entry_points.txt +2 -0
  38. libs/kotaemon/kotaemon.egg-info/requires.txt +84 -0
  39. libs/kotaemon/kotaemon.egg-info/top_level.txt +1 -0
  40. libs/kotaemon/kotaemon/__init__.py +23 -0
  41. libs/kotaemon/kotaemon/agents/__init__.py +33 -0
  42. libs/kotaemon/kotaemon/agents/base.py +57 -0
  43. libs/kotaemon/kotaemon/agents/io/__init__.py +3 -0
  44. libs/kotaemon/kotaemon/agents/io/base.py +258 -0
  45. libs/kotaemon/kotaemon/agents/langchain_based.py +79 -0
  46. libs/kotaemon/kotaemon/agents/react/__init__.py +3 -0
  47. libs/kotaemon/kotaemon/agents/react/agent.py +361 -0
  48. libs/kotaemon/kotaemon/agents/react/prompt.py +28 -0
  49. libs/kotaemon/kotaemon/agents/rewoo/__init__.py +3 -0
  50. libs/kotaemon/kotaemon/agents/rewoo/agent.py +384 -0
.commitlintrc ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "extends": ["@commitlint/config-conventional"],
3
+ "defaultIgnores": true,
4
+ "rules": {
5
+ "body-leading-blank": [1, "always"],
6
+ "body-max-line-length": [2, "always", 100],
7
+ "footer-leading-blank": [1, "always"],
8
+ "footer-max-line-length": [2, "always", 10000],
9
+ "header-max-length": [2, "always", 200],
10
+ "subject-case": [
11
+ 2,
12
+ "never",
13
+ []
14
+ ],
15
+ "subject-empty": [2, "never"],
16
+ "subject-full-stop": [2, "never", "."],
17
+ "type-case": [2, "always", "lower-case"],
18
+ "type-empty": [2, "never"],
19
+ "type-enum": [
20
+ 2,
21
+ "always",
22
+ [
23
+ "build",
24
+ "chore",
25
+ "ci",
26
+ "docs",
27
+ "feat",
28
+ "fix",
29
+ "perf",
30
+ "refactor",
31
+ "revert",
32
+ "style",
33
+ "test"
34
+ ]
35
+ ]
36
+ }
37
+ }
.dockerignore ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .github/
2
+ .git/
3
+ .mypy_cache/
4
+ __pycache__/
5
+ ktem_app_data/
6
+ env/
7
+ .pre-commit-config.yaml
8
+ .commitlintrc
9
+ .gitignore
10
+ .gitattributes
11
+ README.md
12
+ *.zip
13
+ *.sh
14
+
15
+ !/launch.sh
16
+ !/scripts/download_pdfjs.sh
.env.example ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # this is an example .env file, use it to create your own .env file and place it in the root of the project
2
+
3
+ # settings for OpenAI
4
+ OPENAI_API_BASE=https://api.openai.com/v1
5
+ OPENAI_API_KEY=<YOUR_OPENAI_KEY>
6
+ OPENAI_CHAT_MODEL=gpt-4o-mini
7
+ OPENAI_EMBEDDINGS_MODEL=text-embedding-3-large
8
+
9
+ # settings for Azure OpenAI
10
+ AZURE_OPENAI_ENDPOINT=
11
+ AZURE_OPENAI_API_KEY=
12
+ OPENAI_API_VERSION=2024-02-15-preview
13
+ AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-35-turbo
14
+ AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT=text-embedding-ada-002
15
+
16
+ # settings for Cohere
17
+ COHERE_API_KEY=<COHERE_API_KEY>
18
+
19
+ # settings for Mistral
20
+ # MISTRAL_API_KEY=placeholder
21
+
22
+ # settings for VoyageAI
23
+ VOYAGE_API_KEY=<VOYAGE_API_KEY>
24
+
25
+ # settings for local models
26
+ LOCAL_MODEL=qwen2.5:7b
27
+ LOCAL_MODEL_EMBEDDINGS=nomic-embed-text
28
+
29
+ # settings for GraphRAG
30
+ GRAPHRAG_API_KEY=<YOUR_OPENAI_KEY>
31
+ GRAPHRAG_LLM_MODEL=gpt-4o-mini
32
+ GRAPHRAG_EMBEDDING_MODEL=text-embedding-3-small
33
+
34
+ # set to true if you want to use customized GraphRAG config file
35
+ USE_CUSTOMIZED_GRAPHRAG_SETTING=false
36
+
37
+ # settings for Azure DI
38
+ AZURE_DI_ENDPOINT=
39
+ AZURE_DI_CREDENTIAL=
40
+
41
+ # settings for Adobe API
42
+ # get free credential at https://acrobatservices.adobe.com/dc-integration-creation-app-cdn/main.html?api=pdf-extract-api
43
+ # also install pip install "pdfservices-sdk@git+https://github.com/niallcm/pdfservices-python-sdk.git@bump-and-unfreeze-requirements"
44
+ PDF_SERVICES_CLIENT_ID=
45
+ PDF_SERVICES_CLIENT_SECRET=
46
+
47
+ # settings for PDF.js
48
+ PDFJS_VERSION_DIST="pdfjs-4.0.379-dist"
49
+
50
+ # settings for PaddleOCR
51
+ PADDLE_DEVICE=gpu
52
+
53
+ # variable for authentication method selection
54
+ # for authentication with google leave empty
55
+ # for authentication with keycloak :
56
+ # AUTHENTICATION_METHOD="KEYCLOAK"
57
+
58
+ AUTHENTICATION_METHOD=
59
+
60
+ # settings for keycloak
61
+ KEYCLOAK_SERVER_URL=
62
+ KEYCLOAK_CLIENT_ID=
63
+ KEYCLOAK_REALM=
64
+ KEYCLOAK_CLIENT_SECRET=
.gitattributes CHANGED
@@ -1,35 +1 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.bat text eol=crlf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.github/ISSUE_TEMPLATE/bug_report.yml ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: "Bug Report"
2
+ description: Report something that is not working as expected
3
+ title: "[BUG] "
4
+ labels: ["bug"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ *Please fill this form with as much information as possible.*
10
+ - type: textarea
11
+ id: description
12
+ attributes:
13
+ label: "Description"
14
+ description: Please enter an explicit description of your issue
15
+ placeholder: Short and explicit description of your incident...
16
+ validations:
17
+ required: true
18
+ - type: textarea
19
+ id: reprod
20
+ attributes:
21
+ label: "Reproduction steps"
22
+ description: Please enter an explicit description of your issue
23
+ value: |
24
+ 1. Go to '...'
25
+ 2. Click on '....'
26
+ 3. Scroll down to '....'
27
+ 4. See error
28
+ render: bash
29
+ validations:
30
+ required: true
31
+ - type: textarea
32
+ id: screenshot
33
+ attributes:
34
+ label: "Screenshots"
35
+ description: If applicable, add screenshots to help explain your problem.
36
+ value: |
37
+ ![DESCRIPTION](LINK.png)
38
+ render: bash
39
+ validations:
40
+ required: false
41
+ - type: textarea
42
+ id: logs
43
+ attributes:
44
+ label: "Logs"
45
+ description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
46
+ render: bash
47
+ validations:
48
+ required: false
49
+ - type: dropdown
50
+ id: browsers
51
+ attributes:
52
+ label: "Browsers"
53
+ description: What browsers are you seeing the problem on ?
54
+ multiple: true
55
+ options:
56
+ - Firefox
57
+ - Chrome
58
+ - Safari
59
+ - Microsoft Edge
60
+ - Opera
61
+ - Brave
62
+ - Other
63
+ validations:
64
+ required: false
65
+ - type: dropdown
66
+ id: os
67
+ attributes:
68
+ label: "OS"
69
+ description: What is the impacted environment ?
70
+ multiple: true
71
+ options:
72
+ - Windows
73
+ - MacOS
74
+ - Linux
75
+ - Other
76
+ validations:
77
+ required: false
78
+ - type: textarea
79
+ id: additional_information
80
+ attributes:
81
+ label: "Additional information"
82
+ description: Add any relevant information or context.
83
+ placeholder:
84
+ validations:
85
+ required: false
.github/ISSUE_TEMPLATE/config.yml ADDED
@@ -0,0 +1 @@
 
 
1
+ blank_issues_enabled: false
.github/ISSUE_TEMPLATE/feature_request.yml ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: "Feature Request"
2
+ description: Brainstorm and propose new features for the project
3
+ title: "[REQUEST] "
4
+ labels: ["enhancement"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ *Please fill this form with as much information as possible.*
10
+ - type: textarea
11
+ id: reference_issues
12
+ attributes:
13
+ label: "Reference Issues"
14
+ description: Common issues
15
+ placeholder: "#Issues IDs"
16
+ validations:
17
+ required: false
18
+ - type: textarea
19
+ id: summary
20
+ attributes:
21
+ label: "Summary"
22
+ description: Provide a brief explanation of the feature
23
+ placeholder: Describe in a few lines your feature request
24
+ validations:
25
+ required: true
26
+ - type: textarea
27
+ id: basic_example
28
+ attributes:
29
+ label: "Basic Example"
30
+ description: Indicate here some basic examples of your feature.
31
+ placeholder: A few specific words about your feature request.
32
+ validations:
33
+ required: true
34
+ - type: textarea
35
+ id: drawbacks
36
+ attributes:
37
+ label: "Drawbacks"
38
+ description: What are the drawbacks/impacts of your feature request ?
39
+ placeholder: Identify the drawbacks and impacts while being neutral on your feature request
40
+ validations:
41
+ required: true
42
+ - type: textarea
43
+ id: additional_information
44
+ attributes:
45
+ label: "Additional information"
46
+ description: Add any additional information that you think is important for your feature request
47
+ placeholder:
48
+ validations:
49
+ required: false
.github/PULL_REQUEST_TEMPLATE.md ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Description
2
+
3
+ - Please include a summary of the changes and the related issue.
4
+ - Fixes # (issue)
5
+
6
+ ## Type of change
7
+
8
+ - [ ] New features (non-breaking change).
9
+ - [ ] Bug fix (non-breaking change).
10
+ - [ ] Breaking change (fix or feature that would cause existing functionality not to work as expected).
11
+
12
+ ## Checklist
13
+
14
+ - [ ] I have performed a self-review of my code.
15
+ - [ ] I have added thorough tests if it is a core feature.
16
+ - [ ] There is a reference to the original bug report and related work.
17
+ - [ ] I have commented on my code, particularly in hard-to-understand areas.
18
+ - [ ] The feature is well documented.
.github/workflows/auto-bump-and-release.yaml ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Auto Bump and Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ auto-bump-and-release:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Clone the repo
13
+ uses: actions/checkout@v4
14
+ with:
15
+ fetch-depth: 0
16
+ - name: Update Application Version
17
+ id: update-version
18
+ uses: anothrNick/github-tag-action@v1
19
+ env:
20
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21
+ WITH_V: true
22
+ DEFAULT_BUMP: patch
23
+ MAJOR_STRING_TOKEN: "bump:major"
24
+ MINOR_STRING_TOKEN: "bump:minor"
25
+ PATCH_STRING_TOKEN: "bump:patch"
26
+ - name: Create release for ${{ steps.update-version.outputs.new_tag }}
27
+ # need to repeat this if statement because Github Action doesn't support early
28
+ # stopping for steps
29
+ if: ${{ steps.update-version.outputs.new_tag != steps.update-version.outputs.old_tag }}
30
+ run: |
31
+ echo Create release folder
32
+ mkdir kotaemon-app
33
+ echo ${{ steps.update-version.outputs.new_tag }} > kotaemon-app/VERSION
34
+ cp LICENSE.txt kotaemon-app/
35
+ cp flowsettings.py kotaemon-app/
36
+ cp app.py kotaemon-app/
37
+ cp .env.example kotaemon-app/.env
38
+ cp -r scripts kotaemon-app/
39
+ mkdir -p kotaemon-app/libs/ktem/ktem/
40
+ cp -r libs/ktem/ktem/assets kotaemon-app/libs/ktem/ktem/
41
+
42
+ tree kotaemon-app
43
+ zip -r kotaemon-app.zip kotaemon-app
44
+ - name: Release ${{ steps.update-version.outputs.new_tag }}
45
+ if: ${{ steps.update-version.outputs.new_tag != steps.update-version.outputs.old_tag }}
46
+ uses: softprops/action-gh-release@v2
47
+ with:
48
+ files: kotaemon-app.zip
49
+ fail_on_unmatched_files: true
50
+ token: ${{ secrets.GITHUB_TOKEN }}
51
+ generate_release_notes: true
52
+ tag_name: ${{ steps.update-version.outputs.new_tag }}
53
+ make_latest: true
54
+ - name: Setup latest branch locally without switching current branch
55
+ if: ${{ steps.update-version.outputs.new_tag != steps.update-version.outputs.old_tag }}
56
+ run: git fetch origin latest:latest
57
+ - name: Update latest branch
58
+ if: ${{ steps.update-version.outputs.new_tag != steps.update-version.outputs.old_tag }}
59
+ run: |
60
+ git branch -f latest tags/${{ steps.update-version.outputs.new_tag }}
61
+ git checkout latest
62
+ git push -f -u origin latest
.github/workflows/build-push-docker.yaml ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Build and Push Docker Image
2
+
3
+ on:
4
+ release:
5
+ types:
6
+ - created
7
+
8
+ push:
9
+ tags:
10
+ - "v[0-9]+.[0-9]+.[0-9]+"
11
+
12
+ workflow_dispatch:
13
+
14
+ env:
15
+ REGISTRY: ghcr.io
16
+
17
+ jobs:
18
+ build:
19
+ name: Build and push container
20
+ runs-on: ubuntu-latest
21
+ permissions:
22
+ contents: read
23
+ packages: write
24
+ attestations: write
25
+ id-token: write
26
+ strategy:
27
+ matrix:
28
+ target:
29
+ - lite
30
+ - full
31
+ - ollama
32
+ # The maximum number of jobs that can run simultaneously
33
+ max-parallel: 1
34
+ steps:
35
+ - name: Free Disk Space (Ubuntu)
36
+ uses: jlumbroso/free-disk-space@main
37
+ with:
38
+ # this might remove tools that are actually needed,
39
+ # if set to "true" but frees about 6 GB
40
+ tool-cache: true
41
+
42
+ # all of these default to true, but feel free to set to
43
+ # "false" if necessary for your workflow
44
+ android: true
45
+ dotnet: true
46
+ haskell: true
47
+ large-packages: true
48
+ docker-images: true
49
+ swap-storage: true
50
+
51
+ - name: Set repository and image name
52
+ run: |
53
+ echo "FULL_IMAGE_NAME=${{ env.REGISTRY }}/${IMAGE_NAME,,}" >>${GITHUB_ENV}
54
+ env:
55
+ IMAGE_NAME: "${{ github.repository }}"
56
+
57
+ - name: Checkout
58
+ uses: actions/checkout@v4
59
+
60
+ - name: Set up QEMU
61
+ uses: docker/setup-qemu-action@v3
62
+ with:
63
+ image: tonistiigi/binfmt:latest
64
+ platforms: arm64,arm
65
+
66
+ - name: Set up Docker Buildx
67
+ id: buildx
68
+ uses: docker/setup-buildx-action@v3
69
+
70
+ - name: Set up Docker meta
71
+ id: meta
72
+ uses: docker/metadata-action@v5
73
+ with:
74
+ images: ${{ env.FULL_IMAGE_NAME }}
75
+ tags: |
76
+ # branch
77
+ type=ref,event=branch,suffix=-${{ matrix.target }}
78
+ # semver with suffix for lite/full targets
79
+ type=semver,pattern={{version}},suffix=-${{ matrix.target }}
80
+ # latest tag with suffix for lite/full targets
81
+ type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/') && !contains(github.ref, 'pre') }},suffix=-${{ matrix.target }}
82
+ flavor: |
83
+ # This is disabled here so we can use the raw form above
84
+ latest=false
85
+ # Suffix is not used here since there's no way to disable it above
86
+
87
+ - name: Log in to the Container registry
88
+ uses: docker/login-action@v3
89
+ with:
90
+ registry: ${{ env.REGISTRY }}
91
+ username: ${{ github.actor }}
92
+ password: ${{ secrets.GITHUB_TOKEN }}
93
+
94
+ - name: Build docker image
95
+ uses: docker/build-push-action@v6
96
+ with:
97
+ file: Dockerfile
98
+ context: .
99
+ push: true
100
+ platforms: linux/amd64, linux/arm64
101
+ tags: |
102
+ ${{ steps.meta.outputs.tags }}
103
+ labels: ${{ steps.meta.outputs.labels }}
104
+ target: ${{ matrix.target }}
105
+ cache-from: type=gha
106
+ cache-to: type=gha,mode=max
.github/workflows/pr-lint.yaml ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: "Lint PR"
2
+
3
+ on:
4
+ pull_request:
5
+ types:
6
+ - opened
7
+ - edited
8
+ - synchronize
9
+
10
+ permissions:
11
+ pull-requests: write
12
+
13
+ jobs:
14
+ pr-title:
15
+ name: Validate PR title
16
+ runs-on: ubuntu-latest
17
+ permissions: write-all
18
+ steps:
19
+ - uses: amannn/action-semantic-pull-request@v5
20
+ id: lint_pr_title
21
+ env:
22
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23
+
24
+ - uses: marocchino/sticky-pull-request-comment@v2
25
+ # When the previous steps fails, the workflow would stop. By adding this
26
+ # condition you can continue the execution with the populated error message.
27
+ if: always() && (steps.lint_pr_title.outputs.error_message != null)
28
+ with:
29
+ header: pr-title-lint-error
30
+ message: |
31
+ Hey there and thank you for opening this pull request! 👋🏼
32
+
33
+ We require pull request titles to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and it looks like your proposed title needs to be adjusted.
34
+ Details:
35
+ ```
36
+ ${{ steps.lint_pr_title.outputs.error_message }}
37
+ ```
38
+
39
+ # Delete a previous comment when the issue has been resolved
40
+ - if: ${{ steps.lint_pr_title.outputs.error_message == null }}
41
+ uses: marocchino/sticky-pull-request-comment@v2
42
+ with:
43
+ header: pr-title-lint-error
44
+ delete: true
45
+
46
+ commitlint:
47
+ if: false # Disable this job for now
48
+ name: Validate commit messages
49
+ runs-on: ubuntu-latest
50
+ permissions: write-all
51
+ steps:
52
+ - uses: actions/checkout@v4
53
+ - uses: wagoid/commitlint-github-action@v6
54
+ id: commitlint
55
+ env:
56
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57
+ with:
58
+ configFile: ./.commitlintrc
59
+ - uses: buildingcash/json-to-markdown-table-action@v1
60
+ if: always() && (steps.commitlint.outcome != 'success')
61
+ id: table
62
+ with:
63
+ json: ${{ steps.commitlint.outputs.results }}
64
+ - uses: marocchino/sticky-pull-request-comment@v2
65
+ if: always() && (steps.commitlint.outcome != 'success')
66
+ with:
67
+ header: commitlint-error
68
+ message: |
69
+ **All commits** in this PR need to follow the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/) and [.commitlintrc](${{ github.server_url }}/${{ github.repository }}/blob/${{ github.head_ref || github.ref_name }}/.commitlintrc).
70
+ Details:
71
+ ${{ steps.table.outputs.table }}
72
+
73
+ - if: ${{ steps.commitlint.outcome == 'success' }}
74
+ uses: marocchino/sticky-pull-request-comment@v2
75
+ with:
76
+ header: commitlint-error
77
+ delete: true
.github/workflows/style-check.yaml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: style-check
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main, develop]
6
+ push:
7
+ branches: [main, develop]
8
+
9
+ jobs:
10
+ pre-commit:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Clone the repo
14
+ uses: actions/checkout@v4
15
+ - name: Setup python
16
+ uses: actions/setup-python@v4
17
+ with:
18
+ python-version: "3.10"
19
+ - name: run pre-commit
20
+ uses: pre-commit/action@v3.0.0
.github/workflows/unit-test.yaml ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: unit-test
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+
9
+ env:
10
+ THEFLOW_TEMP_PATH: ./tmp
11
+
12
+ jobs:
13
+ unit-test:
14
+ # if: false # temporary disable this job due to legacy interface
15
+ #TODO: enable this job after the new interface is ready
16
+ if: ${{ !cancelled() }}
17
+ runs-on: ${{ matrix.os }}
18
+ timeout-minutes: 20
19
+ defaults:
20
+ run:
21
+ shell: ${{ matrix.shell }}
22
+ strategy:
23
+ matrix:
24
+ python-version: ["3.10", "3.11"]
25
+ include:
26
+ - os: ubuntu-latest
27
+ shell: bash
28
+ ACTIVATE_ENV: ". env/bin/activate"
29
+ GITHUB_OUTPUT: "$GITHUB_OUTPUT"
30
+ # - os: windows-latest
31
+ # shell: pwsh
32
+ # ACTIVATE_ENV: env/Scripts/activate.ps1
33
+ # GITHUB_OUTPUT: "$env:GITHUB_OUTPUT"
34
+
35
+ name: unit testing with python ${{ matrix.python-version }}
36
+ steps:
37
+ - name: Clone the repo
38
+ uses: actions/checkout@v4
39
+ with:
40
+ ref: ${{ github.event.pull_request.head.sha }}
41
+
42
+ - name: Get Head Commit Message
43
+ id: get-head-commit-message
44
+ run: echo "message=$(git show -s --format=%s)" | tee -a ${{ matrix.GITHUB_OUTPUT }}
45
+
46
+ - name: Check ignore caching
47
+ id: check-ignore-cache
48
+ run: |
49
+ ignore_cache=${{ contains(steps.get-head-commit-message.outputs.message, '[ignore cache]') }}
50
+ echo "check=$ignore_cache" | tee -a ${{ matrix.GITHUB_OUTPUT }}
51
+
52
+ - name: Set up Python ${{ matrix.python-version }} on ${{ runner.os }}
53
+ uses: actions/setup-python@v4
54
+ id: setup_python
55
+ with:
56
+ python-version: ${{ matrix.python-version }}
57
+ architecture: x64
58
+
59
+ - name: Install uv
60
+ uses: astral-sh/setup-uv@v5
61
+ with:
62
+ python-version: ${{ matrix.python-version }}
63
+ enable-cache: true
64
+
65
+ - name: Get cache key
66
+ id: get-cache-key
67
+ run: |
68
+ pip install "setuptools-git-versioning>=2.0,<3"
69
+ package_version=$(setuptools-git-versioning)
70
+ cache_key="${{ runner.os }}-py${{ matrix.python-version }}-v${package_version}"
71
+ echo "key=$cache_key" | tee -a ${{ matrix.GITHUB_OUTPUT }}
72
+
73
+ - name: Try to restore dependencies from ${{ steps.get-cache-key.outputs.key }}
74
+ id: restore-dependencies
75
+ if: steps.check-ignore-cache.outputs.check != 'true'
76
+ uses: actions/cache/restore@v3
77
+ with:
78
+ path: ${{ env.pythonLocation }}
79
+ key: ${{ steps.get-cache-key.outputs.key }}
80
+ # could using cache of previous ver to reuse unchanged packages
81
+ restore-keys: ${{ runner.os }}-py${{ matrix.python-version }}
82
+
83
+ - name: Check cache hit
84
+ id: check-cache-hit
85
+ run: |
86
+ echo "cache-hit=${{ steps.restore-dependencies.outputs.cache-hit }}"
87
+ echo "cache-matched-key=${{ steps.restore-dependencies.outputs.cache-matched-key }}"
88
+ cache_hit=${{ steps.restore-dependencies.outputs.cache-primary-key == steps.restore-dependencies.outputs.cache-matched-key }}
89
+ echo "check=$cache_hit" | tee -a ${{ matrix.GITHUB_OUTPUT }}
90
+
91
+ - name: Install additional dependencies (if any)
92
+ run: |
93
+ uv sync --frozen --no-cache
94
+
95
+ - name: New dependencies cache for key ${{ steps.restore-dependencies.outputs.cache-primary-key }}
96
+ if: |
97
+ steps.check-ignore-cache.outputs.check != 'true' &&
98
+ steps.check-cache-hit.outputs.check != 'true'
99
+ uses: actions/cache/save@v3
100
+ with:
101
+ path: ${{ env.pythonLocation }}
102
+ key: ${{ steps.restore-dependencies.outputs.cache-primary-key }}
103
+
104
+ - name: Install OS-based packages
105
+ run: |
106
+ sudo apt update -qqy
107
+ sudo apt install -y poppler-utils libpoppler-dev tesseract-ocr
108
+
109
+ - name: Test kotaemon with pytest
110
+ run: |
111
+ source .venv/bin/activate
112
+ uv pip show pytest
113
+ cd libs/kotaemon
114
+ pytest
.gitignore ADDED
@@ -0,0 +1,483 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Created by https://www.toptal.com/developers/gitignore/api/python,linux,macos,windows,vim,emacs,visualstudiocode,pycharm
2
+ # Edit at https://www.toptal.com/developers/gitignore?templates=python,linux,macos,windows,vim,emacs,visualstudiocode,pycharm
3
+
4
+ activate*
5
+ activate/*
6
+ kotaemon-env*
7
+ .env
8
+ .venv/
9
+ ktem_app_data/
10
+
11
+ ### Emacs ###
12
+ # -*- mode: gitignore; -*-
13
+ *~
14
+ \#*\#
15
+ /.emacs.desktop
16
+ /.emacs.desktop.lock
17
+ *.elc
18
+ auto-save-list
19
+ tramp
20
+ .\#*
21
+
22
+ # Org-mode
23
+ .org-id-locations
24
+ *_archive
25
+
26
+ # flymake-mode
27
+ *_flymake.*
28
+
29
+ # eshell files
30
+ /eshell/history
31
+ /eshell/lastdir
32
+
33
+ # elpa packages
34
+ /elpa/
35
+
36
+ # reftex files
37
+ *.rel
38
+
39
+ # AUCTeX auto folder
40
+ /auto/
41
+
42
+ # cask packages
43
+ .cask/
44
+ dist/
45
+
46
+ # Flycheck
47
+ flycheck_*.el
48
+
49
+ # server auth directory
50
+ /server/
51
+
52
+ # projectiles files
53
+ .projectile
54
+
55
+ # directory configuration
56
+ .dir-locals.el
57
+
58
+ # network security
59
+ /network-security.data
60
+
61
+ ### Linux ###
62
+
63
+ # temporary files which can be created if a process still has a handle open of a deleted file
64
+ .fuse_hidden*
65
+
66
+ # KDE directory preferences
67
+ .directory
68
+
69
+ # Linux trash folder which might appear on any partition or disk
70
+ .Trash-*
71
+
72
+ # .nfs files are created when an open file is removed but is still being accessed
73
+ .nfs*
74
+
75
+ ### macOS ###
76
+ # General
77
+ .DS_Store
78
+ .AppleDouble
79
+ .LSOverride
80
+
81
+ # Icon must end with two \r
82
+ Icon
83
+
84
+ # Thumbnails
85
+ ._*
86
+
87
+ # Files that might appear in the root of a volume
88
+ .DocumentRevisions-V100
89
+ .fseventsd
90
+ .Spotlight-V100
91
+ .TemporaryItems
92
+ .Trashes
93
+ .VolumeIcon.icns
94
+ .com.apple.timemachine.donotpresent
95
+
96
+ # Directories potentially created on remote AFP share
97
+ .AppleDB
98
+ .AppleDesktop
99
+ Network Trash Folder
100
+ Temporary Items
101
+ .apdisk
102
+
103
+ ### macOS Patch ###
104
+ # iCloud generated files
105
+ *.icloud
106
+
107
+ ### PyCharm ###
108
+ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
109
+ # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
110
+
111
+ # User-specific stuff
112
+ .idea/**/workspace.xml
113
+ .idea/**/tasks.xml
114
+ .idea/**/usage.statistics.xml
115
+ .idea/**/dictionaries
116
+ .idea/**/shelf
117
+
118
+ # AWS User-specific
119
+ .idea/**/aws.xml
120
+
121
+ # Generated files
122
+ .idea/**/contentModel.xml
123
+
124
+ # Sensitive or high-churn files
125
+ .idea/**/dataSources/
126
+ .idea/**/dataSources.ids
127
+ .idea/**/dataSources.local.xml
128
+ .idea/**/sqlDataSources.xml
129
+ .idea/**/dynamic.xml
130
+ .idea/**/uiDesigner.xml
131
+ .idea/**/dbnavigator.xml
132
+
133
+ # Gradle
134
+ .idea/**/gradle.xml
135
+ .idea/**/libraries
136
+
137
+ # Gradle and Maven with auto-import
138
+ # When using Gradle or Maven with auto-import, you should exclude module files,
139
+ # since they will be recreated, and may cause churn. Uncomment if using
140
+ # auto-import.
141
+ # .idea/artifacts
142
+ # .idea/compiler.xml
143
+ # .idea/jarRepositories.xml
144
+ # .idea/modules.xml
145
+ # .idea/*.iml
146
+ # .idea/modules
147
+ # *.iml
148
+ # *.ipr
149
+
150
+ # CMake
151
+ cmake-build-*/
152
+
153
+ # Mongo Explorer plugin
154
+ .idea/**/mongoSettings.xml
155
+
156
+ # File-based project format
157
+ *.iws
158
+
159
+ # IntelliJ
160
+ out/
161
+
162
+ # mpeltonen/sbt-idea plugin
163
+ .idea_modules/
164
+
165
+ # JIRA plugin
166
+ atlassian-ide-plugin.xml
167
+
168
+ # Cursive Clojure plugin
169
+ .idea/replstate.xml
170
+
171
+ # SonarLint plugin
172
+ .idea/sonarlint/
173
+
174
+ # Crashlytics plugin (for Android Studio and IntelliJ)
175
+ com_crashlytics_export_strings.xml
176
+ crashlytics.properties
177
+ crashlytics-build.properties
178
+ fabric.properties
179
+
180
+ # Editor-based Rest Client
181
+ .idea/httpRequests
182
+
183
+ # Android studio 3.1+ serialized cache file
184
+ .idea/caches/build_file_checksums.ser
185
+
186
+ ### PyCharm Patch ###
187
+ # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
188
+
189
+ # *.iml
190
+ # modules.xml
191
+ # .idea/misc.xml
192
+ # *.ipr
193
+
194
+ # Sonarlint plugin
195
+ # https://plugins.jetbrains.com/plugin/7973-sonarlint
196
+ .idea/**/sonarlint/
197
+
198
+ # SonarQube Plugin
199
+ # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
200
+ .idea/**/sonarIssues.xml
201
+
202
+ # Markdown Navigator plugin
203
+ # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
204
+ .idea/**/markdown-navigator.xml
205
+ .idea/**/markdown-navigator-enh.xml
206
+ .idea/**/markdown-navigator/
207
+
208
+ # Cache file creation bug
209
+ # See https://youtrack.jetbrains.com/issue/JBR-2257
210
+ .idea/$CACHE_FILE$
211
+
212
+ # CodeStream plugin
213
+ # https://plugins.jetbrains.com/plugin/12206-codestream
214
+ .idea/codestream.xml
215
+
216
+ # Azure Toolkit for IntelliJ plugin
217
+ # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
218
+ .idea/**/azureSettings.xml
219
+
220
+ ### Python ###
221
+ # Byte-compiled / optimized / DLL files
222
+ __pycache__/
223
+ *.py[cod]
224
+ *$py.class
225
+
226
+ # C extensions
227
+ *.so
228
+
229
+ # Distribution / packaging
230
+ .Python
231
+ build/
232
+ develop-eggs/
233
+ downloads/
234
+ eggs/
235
+ .eggs/
236
+ lib/
237
+ lib64/
238
+ parts/
239
+ sdist/
240
+ var/
241
+ wheels/
242
+ share/python-wheels/
243
+ *.egg-info/
244
+ .installed.cfg
245
+ *.egg
246
+ MANIFEST
247
+
248
+ # PyInstaller
249
+ # Usually these files are written by a python script from a template
250
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
251
+ *.manifest
252
+ *.spec
253
+
254
+ # Installer logs
255
+ pip-log.txt
256
+ pip-delete-this-directory.txt
257
+
258
+ # Unit test / coverage reports
259
+ htmlcov/
260
+ .tox/
261
+ .nox/
262
+ .coverage
263
+ .coverage.*
264
+ .cache
265
+ nosetests.xml
266
+ coverage.xml
267
+ *.cover
268
+ *.py,cover
269
+ .hypothesis/
270
+ .pytest_cache/
271
+ cover/
272
+
273
+ # Translations
274
+ *.mo
275
+ *.pot
276
+
277
+ # Django stuff:
278
+ *.log
279
+ local_settings.py
280
+ db.sqlite3
281
+ db.sqlite3-journal
282
+
283
+ # Flask stuff:
284
+ instance/
285
+ .webassets-cache
286
+
287
+ # Scrapy stuff:
288
+ .scrapy
289
+
290
+ # Sphinx documentation
291
+ docs/_build/
292
+
293
+ # PyBuilder
294
+ .pybuilder/
295
+ target/
296
+
297
+ # Jupyter Notebook
298
+ .ipynb_checkpoints
299
+
300
+ # IPython
301
+ profile_default/
302
+ ipython_config.py
303
+
304
+ # pyenv
305
+ # For a library or package, you might want to ignore these files since the code is
306
+ # intended to run in multiple environments; otherwise, check them in:
307
+ # .python-version
308
+
309
+ # pipenv
310
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
311
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
312
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
313
+ # install all needed dependencies.
314
+ #Pipfile.lock
315
+
316
+ # poetry
317
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
318
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
319
+ # commonly ignored for libraries.
320
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
321
+ #poetry.lock
322
+
323
+ # pdm
324
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
325
+ #pdm.lock
326
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
327
+ # in version control.
328
+ # https://pdm.fming.dev/#use-with-ide
329
+ .pdm.toml
330
+
331
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
332
+ __pypackages__/
333
+
334
+ # Celery stuff
335
+ celerybeat-schedule
336
+ celerybeat.pid
337
+
338
+ # SageMath parsed files
339
+ *.sage.py
340
+
341
+ # Environments
342
+ .venv
343
+ env/
344
+ venv/
345
+ ENV/
346
+ env.bak/
347
+ venv.bak/
348
+
349
+ # Spyder project settings
350
+ .spyderproject
351
+ .spyproject
352
+
353
+ # Rope project settings
354
+ .ropeproject
355
+
356
+ # mkdocs documentation
357
+ /site
358
+
359
+ # mypy
360
+ .mypy_cache/
361
+ .dmypy.json
362
+ dmypy.json
363
+
364
+ # Pyre type checker
365
+ .pyre/
366
+
367
+ # pytype static type analyzer
368
+ .pytype/
369
+
370
+ # Cython debug symbols
371
+ cython_debug/
372
+
373
+ # PyCharm
374
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
375
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
376
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
377
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
378
+ #.idea/
379
+
380
+ ### Python Patch ###
381
+ # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
382
+ poetry.toml
383
+
384
+ # ruff
385
+ .ruff_cache/
386
+
387
+ # LSP config files
388
+ pyrightconfig.json
389
+
390
+ ### Vim ###
391
+ # Swap
392
+ [._]*.s[a-v][a-z]
393
+ !*.svg # comment out if you don't need vector files
394
+ [._]*.sw[a-p]
395
+ [._]s[a-rt-v][a-z]
396
+ [._]ss[a-gi-z]
397
+ [._]sw[a-p]
398
+
399
+ # Session
400
+ Session.vim
401
+ Sessionx.vim
402
+
403
+ # Temporary
404
+ .netrwhist
405
+ # Auto-generated tag files
406
+ tags
407
+ # Persistent undo
408
+ [._]*.un~
409
+
410
+ ### VisualStudioCode ###
411
+ .vscode/*
412
+ !.vscode/settings.json
413
+ !.vscode/tasks.json
414
+ !.vscode/launch.json
415
+ !.vscode/extensions.json
416
+ !.vscode/*.code-snippets
417
+
418
+ # Local History for Visual Studio Code
419
+ .history/
420
+
421
+ # Built Visual Studio Code Extensions
422
+ *.vsix
423
+
424
+ ### VisualStudioCode Patch ###
425
+ # Ignore all local history of files
426
+ .history
427
+ .ionide
428
+
429
+ ### Windows ###
430
+ # Windows thumbnail cache files
431
+ Thumbs.db
432
+ Thumbs.db:encryptable
433
+ ehthumbs.db
434
+ ehthumbs_vista.db
435
+
436
+ # Dump file
437
+ *.stackdump
438
+
439
+ # Folder config file
440
+ [Dd]esktop.ini
441
+
442
+ # Recycle Bin used on file shares
443
+ $RECYCLE.BIN/
444
+
445
+ # Windows Installer files
446
+ *.cab
447
+ *.msi
448
+ *.msix
449
+ *.msm
450
+ *.msp
451
+
452
+ # Windows shortcuts
453
+ *.lnk
454
+
455
+ # PDF files
456
+ *.pdf
457
+ !libs/kotaemon/tests/resources/*.pdf
458
+
459
+ .theflow/
460
+
461
+ # End of https://www.toptal.com/developers/gitignore/api/python,linux,macos,windows,vim,emacs,visualstudiocode,pycharm
462
+ *.py[coid]
463
+
464
+ logs/
465
+ .gitsecret/keys/random_seed
466
+ !*.secret
467
+ .envrc
468
+ .env
469
+
470
+ S.gpg-agent*
471
+ .vscode/settings.json
472
+ examples/example1/assets
473
+ storage/*
474
+
475
+ # Conda and env storages
476
+ *install_dir/
477
+ doc_env/
478
+
479
+ # application data
480
+ ktem_app_data/
481
+ gradio_tmp/
482
+
483
+ Dockerfile.full
.pre-commit-config.yaml ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.3.0
4
+ hooks:
5
+ - id: check-yaml
6
+ args: ["--unsafe"]
7
+ - id: check-toml
8
+ - id: end-of-file-fixer
9
+ - id: trailing-whitespace
10
+ - id: mixed-line-ending
11
+ - id: detect-aws-credentials
12
+ args: ["--allow-missing-credentials"]
13
+ - id: detect-private-key
14
+ - id: check-added-large-files
15
+ args: ["--maxkb=750"]
16
+ - id: debug-statements
17
+ - repo: https://github.com/ambv/black
18
+ rev: 22.3.0
19
+ hooks:
20
+ - id: black
21
+ language_version: python3
22
+ - repo: https://github.com/pycqa/isort
23
+ rev: 5.12.0
24
+ hooks:
25
+ - id: isort
26
+ args: ["--profile", "black"]
27
+ language_version: python3.10
28
+ - repo: https://github.com/pycqa/flake8
29
+ rev: 7.0.0
30
+ hooks:
31
+ - id: flake8
32
+ args: ["--max-line-length", "88", "--extend-ignore", "E203"]
33
+ - repo: https://github.com/myint/autoflake
34
+ rev: v1.4
35
+ hooks:
36
+ - id: autoflake
37
+ additional_dependencies: [setuptools]
38
+ args:
39
+ [
40
+ "--in-place",
41
+ "--remove-unused-variables",
42
+ "--remove-all-unused-imports",
43
+ "--ignore-init-module-imports",
44
+ "--exclude=tests/*",
45
+ ]
46
+ - repo: https://github.com/pre-commit/mirrors-prettier
47
+ rev: v2.7.1
48
+ hooks:
49
+ - id: prettier
50
+ types_or: [markdown, yaml]
51
+ - repo: https://github.com/pre-commit/mirrors-mypy
52
+ rev: "v1.7.1"
53
+ hooks:
54
+ - id: mypy
55
+ additional_dependencies:
56
+ [
57
+ types-PyYAML==6.0.12.11,
58
+ "types-requests",
59
+ "sqlmodel",
60
+ "types-Markdown",
61
+ "types-cachetools",
62
+ types-tzlocal,
63
+ ]
64
+ args: ["--check-untyped-defs", "--ignore-missing-imports"]
65
+ exclude: "^templates/"
66
+ - repo: https://github.com/codespell-project/codespell
67
+ rev: v2.2.4
68
+ hooks:
69
+ - id: codespell
70
+ additional_dependencies:
71
+ - tomli
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.10
CODE_OF_CONDUCT.md ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity
10
+ and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ - Demonstrating empathy and kindness toward other people
21
+ - Being respectful of differing opinions, viewpoints, and experiences
22
+ - Giving and gracefully accepting constructive feedback
23
+ - Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ - Focusing on what is best not just for us as individuals, but for the
26
+ overall community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ - The use of sexualized language or imagery, and sexual attention or
31
+ advances of any kind
32
+ - Trolling, insulting or derogatory comments, and personal or political attacks
33
+ - Public or private harassment
34
+ - Publishing others' private information, such as a physical or email
35
+ address, without their explicit permission
36
+ - Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official e-mail address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ .
64
+ All complaints will be reviewed and investigated promptly and fairly.
65
+
66
+ All community leaders are obligated to respect the privacy and security of the
67
+ reporter of any incident.
68
+
69
+ ## Enforcement Guidelines
70
+
71
+ Community leaders will follow these Community Impact Guidelines in determining
72
+ the consequences for any action they deem in violation of this Code of Conduct:
73
+
74
+ ### 1. Correction
75
+
76
+ **Community Impact**: Use of inappropriate language or other behavior deemed
77
+ unprofessional or unwelcome in the community.
78
+
79
+ **Consequence**: A private, written warning from community leaders, providing
80
+ clarity around the nature of the violation and an explanation of why the
81
+ behavior was inappropriate. A public apology may be requested.
82
+
83
+ ### 2. Warning
84
+
85
+ **Community Impact**: A violation through a single incident or series
86
+ of actions.
87
+
88
+ **Consequence**: A warning with consequences for continued behavior. No
89
+ interaction with the people involved, including unsolicited interaction with
90
+ those enforcing the Code of Conduct, for a specified period of time. This
91
+ includes avoiding interactions in community spaces as well as external channels
92
+ like social media. Violating these terms may lead to a temporary or
93
+ permanent ban.
94
+
95
+ ### 3. Temporary Ban
96
+
97
+ **Community Impact**: A serious violation of community standards, including
98
+ sustained inappropriate behavior.
99
+
100
+ **Consequence**: A temporary ban from any sort of interaction or public
101
+ communication with the community for a specified period of time. No public or
102
+ private interaction with the people involved, including unsolicited interaction
103
+ with those enforcing the Code of Conduct, is allowed during this period.
104
+ Violating these terms may lead to a permanent ban.
105
+
106
+ ### 4. Permanent Ban
107
+
108
+ **Community Impact**: Demonstrating a pattern of violation of community
109
+ standards, including sustained inappropriate behavior, harassment of an
110
+ individual, or aggression toward or disparagement of classes of individuals.
111
+
112
+ **Consequence**: A permanent ban from any sort of public interaction within
113
+ the community.
114
+
115
+ ## Attribution
116
+
117
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118
+ version 2.0, available at
119
+ https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120
+
121
+ Community Impact Guidelines were inspired by [Mozilla's code of conduct
122
+ enforcement ladder](https://github.com/mozilla/diversity).
123
+
124
+ [homepage]: https://www.contributor-covenant.org
125
+
126
+ For answers to common questions about this code of conduct, see the FAQ at
127
+ https://www.contributor-covenant.org/faq. Translations are available at
128
+ https://www.contributor-covenant.org/translations.
CONTRIBUTING.md ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing to Kotaemon
2
+
3
+ Welcome 👋 to the Kotaemon project! We're thrilled that you're interested in contributing. Whether you're fixing bugs, adding new features, or improving documentation, your efforts are highly appreciated. This guide aims to help you get started with contributing to Kotaemon.
4
+
5
+ <a href="https://github.com/Cinnamon/kotaemon/graphs/contributors">
6
+ <img src="https://contrib.rocks/image?repo=Cinnamon/kotaemon" />
7
+ </a>
8
+
9
+ ### Table of Contents
10
+
11
+ 1. [📖 Code of Conduct](#code-of-conduct)
12
+ 2. [🔁 Contributing via Pull Requests](#contributing-via-pull-requests)
13
+ 3. [📥 Opening an Issue](#-opening-an-issue)
14
+ 4. [📝 Commit Messages](#-commit-messages)
15
+ 5. [🧾 License](#-license)
16
+
17
+ ## 📖 Code of Conduct
18
+
19
+ Please review our [code of conduct](./CODE_OF_CONDUCT.md), which is in effect at all times. We expect everyone who contributes to this project to honor it.
20
+
21
+ ## 🔁 Contributing via Pull Requests
22
+
23
+ 1. [**Fork the repository**](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo): Click on the [Fork](https://github.com/Cinnamon/kotaemon/fork) button on the repository's page to create a copy of Kotaemon under your GitHub account.
24
+
25
+ 2. [**Clone your code**](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository): Clone your forked repository to your local machine.
26
+
27
+ 3. [**Create new branch**](https://docs.github.com/en/desktop/making-changes-in-a-branch/managing-branches-in-github-desktop): Create a new branch in your forked repo with a descriptive name that reflects your changes.
28
+
29
+ ```sh
30
+ git checkout -b descriptive-name-for-your-changes
31
+ ```
32
+
33
+ 4. **Setup the development environment**: If you are working on the code, make sure to install the necessary dependencies for development
34
+
35
+ ```sh
36
+ pip install -e "libs/kotaemon[dev]"
37
+ ```
38
+
39
+ 5. **Make your changes**: Ensure your code follows the project's coding style and passes all test cases.
40
+
41
+ - Check the coding style
42
+
43
+ ```sh
44
+ pre-commit run --all-files
45
+ ```
46
+
47
+ - Run the tests
48
+
49
+ ```sh
50
+ pytest libs/kotaemon/tests/
51
+ ```
52
+
53
+ 6. [**Commit your changes**](https://docs.github.com/en/desktop/making-changes-in-a-branch/committing-and-reviewing-changes-to-your-project-in-github-desktop): Once you are done with your changes, add and commit them with clear messages.
54
+
55
+ ```sh
56
+ git add your_changes.py
57
+ git commit -m "clear message described your changes."
58
+ git push -u origin descriptive-name-for-your-changes
59
+ ```
60
+
61
+ 7. [**Create a pull request**](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request): When you are satisfied with your changes, [submit a pull request](https://github.com/Cinnamon/kotaemon/compare) from your forked repository to Kotaemon repository. In the pull request, provide a clear description of your changes and any related issues. For the title of the pull request, please refer to our [commit messages convention](#-commit-messages).
62
+
63
+ 8. **Wait for reviews**: Wait for the maintainers to review your pull request. If everything is okay, your changes will be merged into the Kotaemon project.
64
+
65
+ ### GitHub Actions CI Tests
66
+
67
+ All pull requests must pass the [GitHub Actions Continuous Integration (CI)](https://docs.github.com/en/actions/about-github-actions/about-continuous-integration-with-github-actions) tests before they can be merged. These tests include coding-style checks, PR title validation, unit tests, etc. to ensure that your changes meet the project's quality standards. Please review and fix any CI failures that arise.
68
+
69
+ ## 📥 Opening an Issue
70
+
71
+ Before [creating an issues](https://github.com/Cinnamon/kotaemon/issues/new/choose), search through existing issues to ensure you are not opening a duplicate. If you are reporting a bug or issue, please provide a reproducible example to help us quickly identify the problem.
72
+
73
+ ## 📝 Commit Messages
74
+
75
+ ### Overview
76
+
77
+ We use [Angular convention](https://www.conventionalcommits.org/en/) for commit messages to maintain consistency and clarity in our project history. Please take a moment to familiarize yourself with this convention before making your first commit.
78
+
79
+ _For the sake of simplicity, we use [squashing merge](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits) with pull requests. Therefore, if you contribute via a pull request, just make sure your PR's title, instead of the whole commits, follows this convention._
80
+
81
+ Commit format:
82
+
83
+ ```sh
84
+ <gitmoji> <type>(<scope>): <subject>
85
+ <BLANK LINE>
86
+ <body>
87
+ <BLANK LINE>
88
+ <footer>
89
+ ```
90
+
91
+ Examples:
92
+
93
+ ```sh
94
+ docs(api): update api doc
95
+ ```
96
+
97
+ ### Commit types
98
+
99
+ | Types | Description |
100
+ | :--------- | :------------------------------------------------------------ |
101
+ | `feat` | New features |
102
+ | `fix` | Bug fix |
103
+ | `docs` | Documentation only changes |
104
+ | `build` | Changes that affect the build system or external dependencies |
105
+ | `chore` | Something that doesn’t fit the other types |
106
+ | `ci` | Changes to our CI configuration files and scripts |
107
+ | `perf` | Improve performance |
108
+ | `refactor` | Refactor code |
109
+ | `revert` | Revert a previous commit |
110
+ | `style` | Improve structure/format of the code |
111
+ | `test` | Add, update or pass tests |
112
+
113
+ ## 🧾 License
114
+
115
+ All contributions will be licensed under the project's license: [Apache License 2.0](https://github.com/Cinnamon/kotaemon/blob/main/LICENSE.txt).
Dockerfile ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # syntax=docker/dockerfile:1.0.0-experimental
2
+ FROM ghcr.io/cinnamon/kotaemon:main-full as base_image
3
+
4
+ USER root
5
+ RUN useradd -m -u 1000 user || true
6
+ RUN chmod -R a+rX /usr/local || true
7
+ RUN chown -R user:user /app || true
8
+ RUN chown -R user:user /usr/local/lib/python3.10 || true
9
+
10
+ WORKDIR /app
11
+ COPY --chown=user:user . /app
12
+
13
+ USER user
14
+ ENV GRADIO_SERVER_NAME=0.0.0.0
15
+ ENV GRADIO_SERVER_PORT=7860
16
+
17
+ ENTRYPOINT ["python", "app.py"]
18
+ EXPOSE 7860
LICENSE.txt ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
README.md CHANGED
@@ -1,13 +1,423 @@
1
- ---
2
- title: RAG TecXfluence
3
- emoji: 🐨
4
- colorFrom: gray
5
- colorTo: gray
6
- sdk: gradio
7
- sdk_version: 6.31.0
8
- python_version: '3.12'
9
- app_file: app.py
10
- pinned: false
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Hariharan RAG
3
+ colorFrom: blue
4
+ colorTo: indigo
5
+ sdk: gradio
6
+ sdk_version: 4.44.1
7
+ python_version: '3.10'
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ ---
12
+
13
+ <div align="center">
14
+
15
+ # kotaemon
16
+
17
+ An open-source clean & customizable RAG UI for chatting with your documents. Built with both end users and
18
+ developers in mind.
19
+
20
+ ![Preview](https://raw.githubusercontent.com/Cinnamon/kotaemon/main/docs/images/preview-graph.png)
21
+
22
+ <a href="https://trendshift.io/repositories/11607" target="_blank"><img src="https://trendshift.io/api/badge/repositories/11607" alt="Cinnamon%2Fkotaemon | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
23
+
24
+ [Live Demo #1](https://huggingface.co/spaces/cin-model/kotaemon) |
25
+ [Live Demo #2](https://huggingface.co/spaces/cin-model/kotaemon-demo) |
26
+ [Online Install](https://cinnamon.github.io/kotaemon/online_install/) |
27
+ [Colab Notebook (Local RAG)](https://colab.research.google.com/drive/1eTfieec_UOowNizTJA1NjawBJH9y_1nn)
28
+
29
+ [User Guide](https://cinnamon.github.io/kotaemon/) |
30
+ [Developer Guide](https://cinnamon.github.io/kotaemon/development/) |
31
+ [Feedback](https://github.com/Cinnamon/kotaemon/issues) |
32
+ [Contact](mailto:kotaemon.support@cinnamon.is)
33
+
34
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/release/python-31013/)
35
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
36
+ <a href="https://github.com/Cinnamon/kotaemon/pkgs/container/kotaemon" target="_blank">
37
+ <img src="https://img.shields.io/badge/docker_pull-kotaemon:latest-brightgreen" alt="docker pull ghcr.io/cinnamon/kotaemon:latest"></a>
38
+ ![download](https://img.shields.io/github/downloads/Cinnamon/kotaemon/total.svg?label=downloads&color=blue)
39
+ <a href='https://huggingface.co/spaces/cin-model/kotaemon-demo'><img src='https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue'></a>
40
+ <a href="https://hellogithub.com/en/repository/d3141471a0244d5798bc654982b263eb" target="_blank"><img src="https://abroad.hellogithub.com/v1/widgets/recommend.svg?rid=d3141471a0244d5798bc654982b263eb&claim_uid=RLiD9UZ1rEHNaMf&theme=small" alt="Featured|HelloGitHub" /></a>
41
+
42
+ </div>
43
+
44
+ <!-- start-intro -->
45
+
46
+ ## Introduction
47
+
48
+ This project serves as a functional RAG UI for both end users who want to do QA on their
49
+ documents and developers who want to build their own RAG pipeline.
50
+ <br>
51
+
52
+ ```yml
53
+ +----------------------------------------------------------------------------+
54
+ | End users: Those who use apps built with `kotaemon`. |
55
+ | (You use an app like the one in the demo above) |
56
+ | +----------------------------------------------------------------+ |
57
+ | | Developers: Those who built with `kotaemon`. | |
58
+ | | (You have `import kotaemon` somewhere in your project) | |
59
+ | | +----------------------------------------------------+ | |
60
+ | | | Contributors: Those who make `kotaemon` better. | | |
61
+ | | | (You make PR to this repo) | | |
62
+ | | +----------------------------------------------------+ | |
63
+ | +----------------------------------------------------------------+ |
64
+ +----------------------------------------------------------------------------+
65
+ ```
66
+
67
+ ### For end users
68
+
69
+ - **Clean & Minimalistic UI**: A user-friendly interface for RAG-based QA.
70
+ - **Support for Various LLMs**: Compatible with LLM API providers (OpenAI, AzureOpenAI, Cohere, etc.) and local LLMs (via `ollama` and `llama-cpp-python`).
71
+ - **Easy Installation**: Simple scripts to get you started quickly.
72
+
73
+ ### For developers
74
+
75
+ - **Framework for RAG Pipelines**: Tools to build your own RAG-based document QA pipeline.
76
+ - **Customizable UI**: See your RAG pipeline in action with the provided UI, built with <a href='https://github.com/gradio-app/gradio'>Gradio <img src='https://img.shields.io/github/stars/gradio-app/gradio'></a>.
77
+ - **Gradio Theme**: If you use Gradio for development, check out our theme here: [kotaemon-gradio-theme](https://github.com/lone17/kotaemon-gradio-theme).
78
+
79
+ ## Key Features
80
+
81
+ - **Host your own document QA (RAG) web-UI**: Support multi-user login, organize your files in private/public collections, collaborate and share your favorite chat with others.
82
+
83
+ - **Organize your LLM & Embedding models**: Support both local LLMs & popular API providers (OpenAI, Azure, Ollama, Groq).
84
+
85
+ - **Hybrid RAG pipeline**: Sane default RAG pipeline with hybrid (full-text & vector) retriever and re-ranking to ensure best retrieval quality.
86
+
87
+ - **Multi-modal QA support**: Perform Question Answering on multiple documents with figures and tables support. Support multi-modal document parsing (selectable options on UI).
88
+
89
+ - **Advanced citations with document preview**: By default the system will provide detailed citations to ensure the correctness of LLM answers. View your citations (incl. relevant score) directly in the _in-browser PDF viewer_ with highlights. Warning when retrieval pipeline return low relevant articles.
90
+
91
+ - **Support complex reasoning methods**: Use question decomposition to answer your complex/multi-hop question. Support agent-based reasoning with `ReAct`, `ReWOO` and other agents.
92
+
93
+ - **Configurable settings UI**: You can adjust most important aspects of retrieval & generation process on the UI (incl. prompts).
94
+
95
+ - **Extensible**: Being built on Gradio, you are free to customize or add any UI elements as you like. Also, we aim to support multiple strategies for document indexing & retrieval. `GraphRAG` indexing pipeline is provided as an example.
96
+
97
+ ![Preview](https://raw.githubusercontent.com/Cinnamon/kotaemon/main/docs/images/preview.png)
98
+
99
+ ## Installation
100
+
101
+ > If you are not a developer and just want to use the app, please check out our easy-to-follow [User Guide](https://cinnamon.github.io/kotaemon/). Download the `.zip` file from the [latest release](https://github.com/Cinnamon/kotaemon/releases/latest) to get all the newest features and bug fixes.
102
+
103
+ ### System requirements
104
+
105
+ 1. [Python](https://www.python.org/downloads/) >= 3.10
106
+ 2. [Docker](https://www.docker.com/): optional, if you [install with Docker](#with-docker-recommended)
107
+ 3. [Unstructured](https://docs.unstructured.io/open-source/installation/full-installation#full-installation) if you want to process files other than `.pdf`, `.html`, `.mhtml`, and `.xlsx` documents. Installation steps differ depending on your operating system. Please visit the link and follow the specific instructions provided there.
108
+
109
+ ### With Docker (recommended)
110
+
111
+ 1. We support both `lite` & `full` version of Docker images. With `full` version, the extra packages of `unstructured` will be installed, which can support additional file types (`.doc`, `.docx`, ...) but the cost is larger docker image size. For most users, the `lite` image should work well in most cases.
112
+
113
+ - To use the `full` version.
114
+
115
+ ```shell
116
+ docker run \
117
+ -e GRADIO_SERVER_NAME=0.0.0.0 \
118
+ -e GRADIO_SERVER_PORT=7860 \
119
+ -v ./ktem_app_data:/app/ktem_app_data \
120
+ -p 7860:7860 -it --rm \
121
+ ghcr.io/cinnamon/kotaemon:main-full
122
+ ```
123
+
124
+ - To use the `full` version with bundled **Ollama** for _local / private RAG_.
125
+
126
+ ```shell
127
+ # change image name to
128
+ docker run <...> ghcr.io/cinnamon/kotaemon:main-ollama
129
+ ```
130
+
131
+ - To use the `lite` version.
132
+
133
+ ```shell
134
+ # change image name to
135
+ docker run <...> ghcr.io/cinnamon/kotaemon:main-lite
136
+ ```
137
+
138
+ 2. We currently support and test two platforms: `linux/amd64` and `linux/arm64` (for newer Mac). You can specify the platform by passing `--platform` in the `docker run` command. For example:
139
+
140
+ ```shell
141
+ # To run docker with platform linux/arm64
142
+ docker run \
143
+ -e GRADIO_SERVER_NAME=0.0.0.0 \
144
+ -e GRADIO_SERVER_PORT=7860 \
145
+ -v ./ktem_app_data:/app/ktem_app_data \
146
+ -p 7860:7860 -it --rm \
147
+ --platform linux/arm64 \
148
+ ghcr.io/cinnamon/kotaemon:main-lite
149
+ ```
150
+
151
+ 3. Once everything is set up correctly, you can go to `http://localhost:7860/` to access the WebUI.
152
+
153
+ 4. We use [GHCR](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) to store docker images, all images can be found [here.](https://github.com/Cinnamon/kotaemon/pkgs/container/kotaemon)
154
+
155
+ ### Without Docker
156
+
157
+ 1. Clone the repository:
158
+
159
+ ```shell
160
+ git clone https://github.com/Cinnamon/kotaemon
161
+ cd kotaemon
162
+ ```
163
+
164
+ 2. Setup the environment:
165
+
166
+ - **Option 1: Using [uv](https://docs.astral.sh/uv/getting-started/installation/) (recommended)**
167
+
168
+ ```shell
169
+ uv sync --python 3.10
170
+ source .venv/bin/activate
171
+ ```
172
+
173
+ - **Option 2: Using conda**
174
+
175
+ ```shell
176
+ conda create -n kotaemon python=3.10
177
+ conda activate kotaemon
178
+
179
+ pip install -e "libs/kotaemon[all]"
180
+ pip install -e "libs/ktem"
181
+ ```
182
+
183
+ 3. Create a `.env` file in the root of this project. Use `.env.example` as a template.
184
+
185
+ The `.env` file is there to serve use cases where users want to pre-config the models before starting up the app (e.g. deploy the app on HF hub). The file will only be used to populate the db once upon the first run, it will no longer be used in consequent runs.
186
+
187
+ 4. (Optional) To enable in-browser `PDF_JS` viewer, download [PDF_JS_DIST](https://github.com/mozilla/pdf.js/releases/download/v4.0.379/pdfjs-4.0.379-dist.zip) then extract it to `libs/ktem/ktem/assets/prebuilt`.
188
+
189
+ <img src="https://raw.githubusercontent.com/Cinnamon/kotaemon/main/docs/images/pdf-viewer-setup.png" alt="pdf-setup" width="300">
190
+
191
+ 5. Start the web server:
192
+
193
+ ```shell
194
+ python app.py
195
+ ```
196
+
197
+ - The app will be automatically launched in your browser.
198
+ - Default username and password are both `admin`. You can set up additional users directly through the UI.
199
+
200
+ ![Chat tab](https://raw.githubusercontent.com/Cinnamon/kotaemon/main/docs/images/chat-tab.png)
201
+
202
+ 6. Check the `Resources` tab and `LLMs and Embeddings` and ensure that your `api_key` value is set correctly from your `.env` file. If it is not set, you can set it there.
203
+
204
+ ### Setup GraphRAG
205
+
206
+ > [!NOTE]
207
+ > Official MS GraphRAG indexing only works with OpenAI or Ollama API.
208
+ > We recommend most users to use NanoGraphRAG implementation for straightforward integration with Kotaemon.
209
+
210
+ <details>
211
+
212
+ <summary>Setup Nano GRAPHRAG</summary>
213
+
214
+ - Install nano-GraphRAG: `pip install nano-graphrag`
215
+ - `nano-graphrag` install might introduce version conflicts, see [this issue](https://github.com/Cinnamon/kotaemon/issues/440)
216
+ - To quickly fix: `pip uninstall hnswlib chroma-hnswlib && pip install chroma-hnswlib`
217
+ - Launch Kotaemon with `USE_NANO_GRAPHRAG=true` environment variable.
218
+ - Set your default LLM & Embedding models in Resources setting and it will be recognized automatically from NanoGraphRAG.
219
+
220
+ </details>
221
+
222
+ <details>
223
+
224
+ <summary>Setup LIGHTRAG</summary>
225
+
226
+ - Install LightRAG: `pip install git+https://github.com/HKUDS/LightRAG.git`
227
+ - `LightRAG` install might introduce version conflicts, see [this issue](https://github.com/Cinnamon/kotaemon/issues/440)
228
+ - To quickly fix: `pip uninstall hnswlib chroma-hnswlib && pip install chroma-hnswlib`
229
+ - Launch Kotaemon with `USE_LIGHTRAG=true` environment variable.
230
+ - Set your default LLM & Embedding models in Resources setting and it will be recognized automatically from LightRAG.
231
+
232
+ </details>
233
+
234
+ <details>
235
+
236
+ <summary>Setup MS GRAPHRAG</summary>
237
+
238
+ - **Non-Docker Installation**: If you are not using Docker, install GraphRAG with the following command:
239
+
240
+ ```shell
241
+ pip install "graphrag<=0.3.6" future
242
+ ```
243
+
244
+ - **Setting Up API KEY**: To use the GraphRAG retriever feature, ensure you set the `GRAPHRAG_API_KEY` environment variable. You can do this directly in your environment or by adding it to a `.env` file.
245
+ - **Using Local Models and Custom Settings**: If you want to use GraphRAG with local models (like `Ollama`) or customize the default LLM and other configurations, set the `USE_CUSTOMIZED_GRAPHRAG_SETTING` environment variable to true. Then, adjust your settings in the `settings.yaml.example` file.
246
+
247
+ </details>
248
+
249
+ ### Setup Local Models (for local/private RAG)
250
+
251
+ See [Local model setup](docs/local_model.md).
252
+
253
+ ### Setup multimodal document parsing (OCR, table parsing, figure extraction)
254
+
255
+ These options are available:
256
+
257
+ - [Azure Document Intelligence (API)](https://azure.microsoft.com/en-us/products/ai-services/ai-document-intelligence)
258
+ - [Adobe PDF Extract (API)](https://developer.adobe.com/document-services/docs/overview/pdf-extract-api/)
259
+ - [Docling (local, open-source)](https://github.com/DS4SD/docling) – see [integrations/docling.md](./docs/integrations/docling.md) for Kotaemon-specific setup.
260
+ - [PaddleOCR (local, open-source)](https://github.com/PADDLEPADDLE/PADDLEOCR) – see [integrations/paddle_ocr.md](./docs/integrations/paddle_ocr.md) for Kotaemon-specific setup.
261
+
262
+ Select corresponding loaders in `Settings -> Retrieval Settings -> File loader`
263
+
264
+ ### Customize your application
265
+
266
+ - By default, all application data is stored in the `./ktem_app_data` folder. You can back up or copy this folder to transfer your installation to a new machine.
267
+
268
+ - For advanced users or specific use cases, you can customize these files:
269
+
270
+ - `flowsettings.py`
271
+ - `.env`
272
+
273
+ #### `flowsettings.py`
274
+
275
+ This file contains the configuration of your application. You can use the example
276
+ [here](flowsettings.py) as the starting point.
277
+
278
+ <details>
279
+
280
+ <summary>Notable settings</summary>
281
+
282
+ ```python
283
+ # setup your preferred document store (with full-text search capabilities)
284
+ KH_DOCSTORE=(Elasticsearch | LanceDB | SimpleFileDocumentStore)
285
+
286
+ # setup your preferred vectorstore (for vector-based search)
287
+ KH_VECTORSTORE=(ChromaDB | LanceDB | InMemory | Milvus | Qdrant)
288
+
289
+ # Enable / disable multimodal QA
290
+ KH_REASONINGS_USE_MULTIMODAL=True
291
+
292
+ # Setup your new reasoning pipeline or modify existing one.
293
+ KH_REASONINGS = [
294
+ "ktem.reasoning.simple.FullQAPipeline",
295
+ "ktem.reasoning.simple.FullDecomposeQAPipeline",
296
+ "ktem.reasoning.react.ReactAgentPipeline",
297
+ "ktem.reasoning.rewoo.RewooAgentPipeline",
298
+ ]
299
+ ```
300
+
301
+ </details>
302
+
303
+ #### `.env`
304
+
305
+ This file provides another way to configure your models and credentials.
306
+
307
+ <details>
308
+
309
+ <summary>Configure model via the .env file</summary>
310
+
311
+ - Alternatively, you can configure the models via the `.env` file with the information needed to connect to the LLMs. This file is located in the folder of the application. If you don't see it, you can create one.
312
+
313
+ - Currently, the following providers are supported:
314
+
315
+ - **OpenAI**
316
+
317
+ In the `.env` file, set the `OPENAI_API_KEY` variable with your OpenAI API key in order
318
+ to enable access to OpenAI's models. There are other variables that can be modified,
319
+ please feel free to edit them to fit your case. Otherwise, the default parameter should
320
+ work for most people.
321
+
322
+ ```shell
323
+ OPENAI_API_BASE=https://api.openai.com/v1
324
+ OPENAI_API_KEY=<your OpenAI API key here>
325
+ OPENAI_CHAT_MODEL=gpt-3.5-turbo
326
+ OPENAI_EMBEDDINGS_MODEL=text-embedding-ada-002
327
+ ```
328
+
329
+ - **Azure OpenAI**
330
+
331
+ For OpenAI models via Azure platform, you need to provide your Azure endpoint and API
332
+ key. You might also need to provide your developments' name for the chat model and the
333
+ embedding model depending on how you set up Azure development.
334
+
335
+ ```shell
336
+ AZURE_OPENAI_ENDPOINT=
337
+ AZURE_OPENAI_API_KEY=
338
+ OPENAI_API_VERSION=2024-02-15-preview
339
+ AZURE_OPENAI_CHAT_DEPLOYMENT=gpt-35-turbo
340
+ AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT=text-embedding-ada-002
341
+ ```
342
+
343
+ - **Local Models**
344
+
345
+ - Using `ollama` OpenAI compatible server:
346
+
347
+ - Install [ollama](https://github.com/ollama/ollama) and start the application.
348
+
349
+ - Pull your model, for example:
350
+
351
+ ```shell
352
+ ollama pull llama3.1:8b
353
+ ollama pull nomic-embed-text
354
+ ```
355
+
356
+ - Set the model names on web UI and make it as default:
357
+
358
+ ![Models](https://raw.githubusercontent.com/Cinnamon/kotaemon/main/docs/images/models.png)
359
+
360
+ - Using `GGUF` with `llama-cpp-python`
361
+
362
+ You can search and download a LLM to be ran locally from the [Hugging Face Hub](https://huggingface.co/models). Currently, these model formats are supported:
363
+
364
+ - GGUF
365
+
366
+ You should choose a model whose size is less than your device's memory and should leave
367
+ about 2 GB. For example, if you have 16 GB of RAM in total, of which 12 GB is available,
368
+ then you should choose a model that takes up at most 10 GB of RAM. Bigger models tend to
369
+ give better generation but also take more processing time.
370
+
371
+ Here are some recommendations and their size in memory:
372
+
373
+ - [Qwen1.5-1.8B-Chat-GGUF](https://huggingface.co/Qwen/Qwen1.5-1.8B-Chat-GGUF/resolve/main/qwen1_5-1_8b-chat-q8_0.gguf?download=true): around 2 GB
374
+
375
+ Add a new LlamaCpp model with the provided model name on the web UI.
376
+
377
+ </details>
378
+
379
+ ### Adding your own RAG pipeline
380
+
381
+ #### Custom Reasoning Pipeline
382
+
383
+ 1. Check the default pipeline implementation in [here](libs/ktem/ktem/reasoning/simple.py). You can make quick adjustment to how the default QA pipeline work.
384
+ 2. Add new `.py` implementation in `libs/ktem/ktem/reasoning/` and later include it in `flowssettings` to enable it on the UI.
385
+
386
+ #### Custom Indexing Pipeline
387
+
388
+ - Check sample implementation in `libs/ktem/ktem/index/file/graph`
389
+
390
+ > (more instruction WIP).
391
+
392
+ <!-- end-intro -->
393
+
394
+ ## Citation
395
+
396
+ Please cite this project as
397
+
398
+ ```BibTeX
399
+ @misc{kotaemon2024,
400
+ title = {Kotaemon - An open-source RAG-based tool for chatting with any content.},
401
+ author = {The Kotaemon Team},
402
+ year = {2024},
403
+ howpublished = {\url{https://github.com/Cinnamon/kotaemon}},
404
+ }
405
+ ```
406
+
407
+ ## Star History
408
+
409
+ <a href="https://star-history.com/#Cinnamon/kotaemon&Date">
410
+ <picture>
411
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=Cinnamon/kotaemon&type=Date&theme=dark" />
412
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=Cinnamon/kotaemon&type=Date" />
413
+ <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=Cinnamon/kotaemon&type=Date" />
414
+ </picture>
415
+ </a>
416
+
417
+ ## Contribution
418
+
419
+ Since our project is actively being developed, we greatly value your feedback and contributions. Please see our [Contributing Guide](https://github.com/Cinnamon/kotaemon/blob/main/CONTRIBUTING.md) to get started. Thank you to all our contributors!
420
+
421
+ <a href="https://github.com/Cinnamon/kotaemon/graphs/contributors">
422
+ <img src="https://contrib.rocks/image?repo=Cinnamon/kotaemon" />
423
+ </a>
app.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio.routes
3
+
4
+ # Enable SVG rendering in Gradio
5
+ gradio.routes.XSS_SAFE_MIMETYPES.add("image/svg+xml")
6
+
7
+ from theflow.settings import settings as flowsettings
8
+
9
+ KH_APP_DATA_DIR = getattr(flowsettings, "KH_APP_DATA_DIR", ".")
10
+ KH_GRADIO_SHARE = getattr(flowsettings, "KH_GRADIO_SHARE", False)
11
+ GRADIO_TEMP_DIR = os.getenv("GRADIO_TEMP_DIR", None)
12
+ # override GRADIO_TEMP_DIR if it's not set
13
+ if GRADIO_TEMP_DIR is None:
14
+ GRADIO_TEMP_DIR = os.path.join(KH_APP_DATA_DIR, "gradio_tmp")
15
+ os.environ["GRADIO_TEMP_DIR"] = GRADIO_TEMP_DIR
16
+
17
+
18
+ from ktem.main import App # noqa
19
+
20
+ app = App()
21
+ demo = app.make()
22
+ demo.title = app.app_name
23
+
24
+ server_name = os.environ.get("GRADIO_SERVER_NAME", "0.0.0.0")
25
+ server_port = int(os.environ.get("PORT", os.environ.get("GRADIO_SERVER_PORT", 7860)))
26
+
27
+ demo.queue().launch(
28
+ server_name=server_name,
29
+ server_port=server_port,
30
+ favicon_path=app._favicon,
31
+ inbrowser=False,
32
+ allowed_paths=[
33
+ os.path.abspath("libs/ktem/ktem/assets"),
34
+ os.path.abspath(GRADIO_TEMP_DIR),
35
+ ],
36
+ share=KH_GRADIO_SHARE,
37
+ )
build.sh ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Exit immediately if a command exits with a non-zero status
3
+ set -e
4
+
5
+ echo "=== Installing Dependencies ==="
6
+ pip install --upgrade pip setuptools wheel
7
+ pip install -e libs/kotaemon
8
+ pip install -e libs/ktem
9
+
10
+ echo "=== Build Complete ==="
doc_env_reqs.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ mkdocs
2
+ mkdocstrings[python]
3
+ mkdocs-material
4
+ mkdocs-gen-files
5
+ mkdocs-literate-nav
6
+ mkdocs-git-revision-date-localized-plugin
7
+ mkdocs-section-index
8
+ mkdocs-include-markdown-plugin[cache]
9
+ mdx_truly_sane_lists
flowsettings.py ADDED
@@ -0,0 +1,423 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from importlib.metadata import version
3
+ from inspect import currentframe, getframeinfo
4
+ from pathlib import Path
5
+
6
+ from decouple import config
7
+ from ktem.utils.lang import SUPPORTED_LANGUAGE_MAP
8
+ from theflow.settings.default import * # noqa
9
+
10
+ cur_frame = currentframe()
11
+ if cur_frame is None:
12
+ raise ValueError("Cannot get the current frame.")
13
+ this_file = getframeinfo(cur_frame).filename
14
+ this_dir = Path(this_file).parent
15
+
16
+ # change this if your app use a different name
17
+ KH_PACKAGE_NAME = "kotaemon_app"
18
+ KH_APP_NAME = config("KH_APP_NAME", default="Hariharan")
19
+
20
+ KH_APP_VERSION = config("KH_APP_VERSION", None)
21
+ if not KH_APP_VERSION:
22
+ try:
23
+ # Caution: This might produce the wrong version
24
+ # https://stackoverflow.com/a/59533071
25
+ KH_APP_VERSION = version(KH_PACKAGE_NAME)
26
+ except Exception:
27
+ KH_APP_VERSION = "local"
28
+
29
+ KH_GRADIO_SHARE = config("KH_GRADIO_SHARE", default=False, cast=bool)
30
+ KH_ENABLE_FIRST_SETUP = config("KH_ENABLE_FIRST_SETUP", default=True, cast=bool)
31
+ KH_DEMO_MODE = config("KH_DEMO_MODE", default=False, cast=bool)
32
+ KH_OLLAMA_URL = config("KH_OLLAMA_URL", default="http://localhost:11434/v1/")
33
+
34
+ # App can be ran from anywhere and it's not trivial to decide where to store app data.
35
+ # So let's use the same directory as the flowsetting.py file.
36
+ KH_APP_DATA_DIR = this_dir / "ktem_app_data"
37
+ KH_APP_DATA_EXISTS = KH_APP_DATA_DIR.exists()
38
+ KH_APP_DATA_DIR.mkdir(parents=True, exist_ok=True)
39
+
40
+ # User data directory
41
+ KH_USER_DATA_DIR = KH_APP_DATA_DIR / "user_data"
42
+ KH_USER_DATA_DIR.mkdir(parents=True, exist_ok=True)
43
+
44
+ # markdown output directory
45
+ KH_MARKDOWN_OUTPUT_DIR = KH_APP_DATA_DIR / "markdown_cache_dir"
46
+ KH_MARKDOWN_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
47
+
48
+ # chunks output directory
49
+ KH_CHUNKS_OUTPUT_DIR = KH_APP_DATA_DIR / "chunks_cache_dir"
50
+ KH_CHUNKS_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
51
+
52
+ # zip output directory
53
+ KH_ZIP_OUTPUT_DIR = KH_APP_DATA_DIR / "zip_cache_dir"
54
+ KH_ZIP_OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
55
+
56
+ # zip input directory
57
+ KH_ZIP_INPUT_DIR = KH_APP_DATA_DIR / "zip_cache_dir_in"
58
+ KH_ZIP_INPUT_DIR.mkdir(parents=True, exist_ok=True)
59
+
60
+ # HF models can be big, let's store them in the app data directory so that it's easier
61
+ # for users to manage their storage.
62
+ # ref: https://huggingface.co/docs/huggingface_hub/en/guides/manage-cache
63
+ os.environ["HF_HOME"] = str(KH_APP_DATA_DIR / "huggingface")
64
+ os.environ["HF_HUB_CACHE"] = str(KH_APP_DATA_DIR / "huggingface")
65
+
66
+ # doc directory
67
+ KH_DOC_DIR = this_dir / "docs"
68
+
69
+ KH_MODE = "dev"
70
+ KH_SSO_ENABLED = config("KH_SSO_ENABLED", default=False, cast=bool)
71
+
72
+ KH_FEATURE_CHAT_SUGGESTION = config(
73
+ "KH_FEATURE_CHAT_SUGGESTION", default=False, cast=bool
74
+ )
75
+ KH_FEATURE_USER_MANAGEMENT = config(
76
+ "KH_FEATURE_USER_MANAGEMENT", default=False, cast=bool
77
+ )
78
+ KH_USER_CAN_SEE_PUBLIC = None
79
+ KH_FEATURE_USER_MANAGEMENT_ADMIN = str(
80
+ config("KH_FEATURE_USER_MANAGEMENT_ADMIN", default="admin")
81
+ )
82
+ KH_FEATURE_USER_MANAGEMENT_PASSWORD = str(
83
+ config("KH_FEATURE_USER_MANAGEMENT_PASSWORD", default="admin")
84
+ )
85
+ KH_ENABLE_ALEMBIC = False
86
+ KH_DATABASE = f"sqlite:///{KH_USER_DATA_DIR / 'sql.db'}"
87
+ KH_FILESTORAGE_PATH = str(KH_USER_DATA_DIR / "files")
88
+ KH_WEB_SEARCH_BACKEND = (
89
+ "kotaemon.indices.retrievers.tavily_web_search.WebSearch"
90
+ # "kotaemon.indices.retrievers.jina_web_search.WebSearch"
91
+ )
92
+
93
+ KH_DOCSTORE = {
94
+ # "__type__": "kotaemon.storages.ElasticsearchDocumentStore",
95
+ # "__type__": "kotaemon.storages.SimpleFileDocumentStore",
96
+ "__type__": "kotaemon.storages.LanceDBDocumentStore",
97
+ "path": str(KH_USER_DATA_DIR / "docstore"),
98
+ }
99
+ if config("QDRANT_URL", default=""):
100
+ KH_VECTORSTORE = {
101
+ "__type__": "kotaemon.storages.QdrantVectorStore",
102
+ "url": config("QDRANT_URL"),
103
+ "api_key": config("QDRANT_API_KEY", default=None),
104
+ "collection_name": config("QDRANT_COLLECTION", default="kotaemon"),
105
+ }
106
+ else:
107
+ KH_VECTORSTORE = {
108
+ # "__type__": "kotaemon.storages.LanceDBVectorStore",
109
+ "__type__": "kotaemon.storages.ChromaVectorStore",
110
+ # "__type__": "kotaemon.storages.MilvusVectorStore",
111
+ # "__type__": "kotaemon.storages.QdrantVectorStore",
112
+ "path": str(KH_USER_DATA_DIR / "vectorstore"),
113
+ }
114
+ KH_LLMS = {}
115
+ KH_EMBEDDINGS = {}
116
+ KH_RERANKINGS = {}
117
+
118
+ # populate options from config
119
+ if config("AZURE_OPENAI_API_KEY", default="") and config(
120
+ "AZURE_OPENAI_ENDPOINT", default=""
121
+ ):
122
+ if config("AZURE_OPENAI_CHAT_DEPLOYMENT", default=""):
123
+ KH_LLMS["azure"] = {
124
+ "spec": {
125
+ "__type__": "kotaemon.llms.AzureChatOpenAI",
126
+ "temperature": 0,
127
+ "azure_endpoint": config("AZURE_OPENAI_ENDPOINT", default=""),
128
+ "api_key": config("AZURE_OPENAI_API_KEY", default=""),
129
+ "api_version": config("OPENAI_API_VERSION", default="")
130
+ or "2024-02-15-preview",
131
+ "azure_deployment": config("AZURE_OPENAI_CHAT_DEPLOYMENT", default=""),
132
+ "timeout": 20,
133
+ },
134
+ "default": False,
135
+ }
136
+ if config("AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT", default=""):
137
+ KH_EMBEDDINGS["azure"] = {
138
+ "spec": {
139
+ "__type__": "kotaemon.embeddings.AzureOpenAIEmbeddings",
140
+ "azure_endpoint": config("AZURE_OPENAI_ENDPOINT", default=""),
141
+ "api_key": config("AZURE_OPENAI_API_KEY", default=""),
142
+ "api_version": config("OPENAI_API_VERSION", default="")
143
+ or "2024-02-15-preview",
144
+ "azure_deployment": config(
145
+ "AZURE_OPENAI_EMBEDDINGS_DEPLOYMENT", default=""
146
+ ),
147
+ "timeout": 10,
148
+ },
149
+ "default": False,
150
+ }
151
+
152
+ OPENAI_DEFAULT = "<YOUR_OPENAI_KEY>"
153
+ OPENAI_API_KEY = config("OPENAI_API_KEY", default=OPENAI_DEFAULT)
154
+ GOOGLE_API_KEY = config("GOOGLE_API_KEY", default="your-key")
155
+ IS_OPENAI_DEFAULT = len(OPENAI_API_KEY) > 0 and OPENAI_API_KEY != OPENAI_DEFAULT
156
+
157
+ if OPENAI_API_KEY:
158
+ KH_LLMS["openai"] = {
159
+ "spec": {
160
+ "__type__": "kotaemon.llms.ChatOpenAI",
161
+ "temperature": 0,
162
+ "base_url": config("OPENAI_API_BASE", default="")
163
+ or "https://api.openai.com/v1",
164
+ "api_key": OPENAI_API_KEY,
165
+ "model": config("OPENAI_CHAT_MODEL", default="gpt-4o-mini"),
166
+ "timeout": 20,
167
+ },
168
+ "default": IS_OPENAI_DEFAULT,
169
+ }
170
+ KH_EMBEDDINGS["openai"] = {
171
+ "spec": {
172
+ "__type__": "kotaemon.embeddings.OpenAIEmbeddings",
173
+ "base_url": config("OPENAI_API_BASE", default="https://api.openai.com/v1"),
174
+ "api_key": OPENAI_API_KEY,
175
+ "model": config(
176
+ "OPENAI_EMBEDDINGS_MODEL", default="text-embedding-3-large"
177
+ ),
178
+ "timeout": 10,
179
+ "context_length": 8191,
180
+ },
181
+ "default": IS_OPENAI_DEFAULT,
182
+ }
183
+
184
+ VOYAGE_API_KEY = config("VOYAGE_API_KEY", default="")
185
+ if VOYAGE_API_KEY:
186
+ KH_EMBEDDINGS["voyageai"] = {
187
+ "spec": {
188
+ "__type__": "kotaemon.embeddings.VoyageAIEmbeddings",
189
+ "api_key": VOYAGE_API_KEY,
190
+ "model": config("VOYAGE_EMBEDDINGS_MODEL", default="voyage-3-large"),
191
+ },
192
+ "default": False,
193
+ }
194
+ KH_RERANKINGS["voyageai"] = {
195
+ "spec": {
196
+ "__type__": "kotaemon.rerankings.VoyageAIReranking",
197
+ "model_name": "rerank-2",
198
+ "api_key": VOYAGE_API_KEY,
199
+ },
200
+ "default": False,
201
+ }
202
+
203
+ if config("LOCAL_MODEL", default=""):
204
+ KH_LLMS["ollama"] = {
205
+ "spec": {
206
+ "__type__": "kotaemon.llms.ChatOpenAI",
207
+ "base_url": KH_OLLAMA_URL,
208
+ "model": config("LOCAL_MODEL", default="qwen2.5:7b"),
209
+ "api_key": "ollama",
210
+ },
211
+ "default": False,
212
+ }
213
+ KH_LLMS["ollama-long-context"] = {
214
+ "spec": {
215
+ "__type__": "kotaemon.llms.LCOllamaChat",
216
+ "base_url": KH_OLLAMA_URL.replace("v1/", ""),
217
+ "model": config("LOCAL_MODEL", default="qwen2.5:7b"),
218
+ "num_ctx": 8192,
219
+ },
220
+ "default": False,
221
+ }
222
+
223
+ KH_EMBEDDINGS["ollama"] = {
224
+ "spec": {
225
+ "__type__": "kotaemon.embeddings.OpenAIEmbeddings",
226
+ "base_url": KH_OLLAMA_URL,
227
+ "model": config("LOCAL_MODEL_EMBEDDINGS", default="nomic-embed-text"),
228
+ "api_key": "ollama",
229
+ },
230
+ "default": False,
231
+ }
232
+
233
+
234
+ # additional LLM configurations
235
+ if config("ANTHROPIC_API_KEY", default=""):
236
+ KH_LLMS["claude"] = {
237
+ "spec": {
238
+ "__type__": "kotaemon.llms.chats.LCAnthropicChat",
239
+ "model_name": "claude-3-5-sonnet-20240620",
240
+ "api_key": config("ANTHROPIC_API_KEY"),
241
+ },
242
+ "default": False,
243
+ }
244
+
245
+ KH_LLMS["google"] = {
246
+ "spec": {
247
+ "__type__": "kotaemon.llms.chats.LCGeminiChat",
248
+ "model_name": config("GOOGLE_CHAT_MODEL", default="gemini-3.5-flash-lite"),
249
+ "api_key": GOOGLE_API_KEY,
250
+ "transport": "rest",
251
+ },
252
+ "default": not IS_OPENAI_DEFAULT,
253
+ }
254
+
255
+ if config("GROQ_API_KEY", default=""):
256
+ KH_LLMS["groq"] = {
257
+ "spec": {
258
+ "__type__": "kotaemon.llms.ChatOpenAI",
259
+ "base_url": "https://api.groq.com/openai/v1",
260
+ "model": config("GROQ_CHAT_MODEL", default="openai/gpt-oss-120b"),
261
+ "api_key": config("GROQ_API_KEY"),
262
+ },
263
+ "default": False,
264
+ }
265
+
266
+ if config("COHERE_API_KEY", default=""):
267
+ KH_LLMS["cohere"] = {
268
+ "spec": {
269
+ "__type__": "kotaemon.llms.chats.LCCohereChat",
270
+ "model_name": "command-r-plus-08-2024",
271
+ "api_key": config("COHERE_API_KEY"),
272
+ },
273
+ "default": False,
274
+ }
275
+
276
+ if config("MISTRAL_API_KEY", default=""):
277
+ KH_LLMS["mistral"] = {
278
+ "spec": {
279
+ "__type__": "kotaemon.llms.ChatOpenAI",
280
+ "base_url": "https://api.mistral.ai/v1",
281
+ "model": "ministral-8b-latest",
282
+ "api_key": config("MISTRAL_API_KEY"),
283
+ },
284
+ "default": False,
285
+ }
286
+
287
+ # additional embeddings configurations
288
+ if config("COHERE_API_KEY", default=""):
289
+ KH_EMBEDDINGS["cohere"] = {
290
+ "spec": {
291
+ "__type__": "kotaemon.embeddings.LCCohereEmbeddings",
292
+ "model": "embed-multilingual-v3.0",
293
+ "cohere_api_key": config("COHERE_API_KEY"),
294
+ "user_agent": "default",
295
+ },
296
+ "default": False,
297
+ }
298
+
299
+ KH_EMBEDDINGS["fast_embed"] = {
300
+ "spec": {
301
+ "__type__": "kotaemon.embeddings.FastEmbedEmbeddings",
302
+ "model_name": "BAAI/bge-base-en-v1.5",
303
+ },
304
+ "default": True,
305
+ }
306
+
307
+ KH_EMBEDDINGS["google"] = {
308
+ "spec": {
309
+ "__type__": "kotaemon.embeddings.LCGoogleEmbeddings",
310
+ "model": config("GOOGLE_EMBEDDINGS_MODEL", default="models/gemini-embedding-001"),
311
+ "google_api_key": GOOGLE_API_KEY,
312
+ "transport": "rest",
313
+ },
314
+ "default": False,
315
+ }
316
+
317
+ if config("MISTRAL_API_KEY", default=""):
318
+ KH_EMBEDDINGS["mistral"] = {
319
+ "spec": {
320
+ "__type__": "kotaemon.embeddings.LCMistralEmbeddings",
321
+ "model": "mistral-embed",
322
+ "api_key": config("MISTRAL_API_KEY"),
323
+ },
324
+ "default": False,
325
+ }
326
+
327
+ # default reranking models
328
+ if config("COHERE_API_KEY", default=""):
329
+ KH_RERANKINGS["cohere"] = {
330
+ "spec": {
331
+ "__type__": "kotaemon.rerankings.CohereReranking",
332
+ "model_name": "rerank-v4.0-fast",
333
+ "cohere_api_key": config("COHERE_API_KEY"),
334
+ },
335
+ "default": True,
336
+ }
337
+
338
+ KH_REASONINGS = [
339
+ "ktem.reasoning.simple.FullQAPipeline",
340
+ "ktem.reasoning.simple.FullDecomposeQAPipeline",
341
+ "ktem.reasoning.react.ReactAgentPipeline",
342
+ "ktem.reasoning.rewoo.RewooAgentPipeline",
343
+ ]
344
+ KH_REASONINGS_USE_MULTIMODAL = config("USE_MULTIMODAL", default=False, cast=bool)
345
+ KH_VLM_ENDPOINT = "{0}/openai/deployments/{1}/chat/completions?api-version={2}".format(
346
+ config("AZURE_OPENAI_ENDPOINT", default=""),
347
+ config("OPENAI_VISION_DEPLOYMENT_NAME", default="gpt-4o"),
348
+ config("OPENAI_API_VERSION", default=""),
349
+ )
350
+
351
+
352
+ SETTINGS_APP: dict[str, dict] = {}
353
+
354
+
355
+ SETTINGS_REASONING = {
356
+ "use": {
357
+ "name": "Reasoning options",
358
+ "value": None,
359
+ "choices": [],
360
+ "component": "radio",
361
+ },
362
+ "lang": {
363
+ "name": "Language",
364
+ "value": "en",
365
+ "choices": [(lang, code) for code, lang in SUPPORTED_LANGUAGE_MAP.items()],
366
+ "component": "dropdown",
367
+ },
368
+ "max_context_length": {
369
+ "name": "Max context length (LLM)",
370
+ "value": 32000,
371
+ "component": "number",
372
+ },
373
+ }
374
+
375
+ USE_GLOBAL_GRAPHRAG = config("USE_GLOBAL_GRAPHRAG", default=True, cast=bool)
376
+ USE_NANO_GRAPHRAG = config("USE_NANO_GRAPHRAG", default=False, cast=bool)
377
+ USE_LIGHTRAG = config("USE_LIGHTRAG", default=True, cast=bool)
378
+ USE_MS_GRAPHRAG = config("USE_MS_GRAPHRAG", default=True, cast=bool)
379
+
380
+ GRAPHRAG_INDEX_TYPES = []
381
+
382
+ if USE_MS_GRAPHRAG:
383
+ GRAPHRAG_INDEX_TYPES.append("ktem.index.file.graph.GraphRAGIndex")
384
+ if USE_NANO_GRAPHRAG:
385
+ GRAPHRAG_INDEX_TYPES.append("ktem.index.file.graph.NanoGraphRAGIndex")
386
+ if USE_LIGHTRAG:
387
+ GRAPHRAG_INDEX_TYPES.append("ktem.index.file.graph.LightRAGIndex")
388
+
389
+ KH_INDEX_TYPES = [
390
+ "ktem.index.file.FileIndex",
391
+ *GRAPHRAG_INDEX_TYPES,
392
+ ]
393
+
394
+ GRAPHRAG_INDICES = [
395
+ {
396
+ "name": graph_type.split(".")[-1].replace("Index", "")
397
+ + " Collection", # get last name
398
+ "config": {
399
+ "supported_file_types": (
400
+ ".png, .jpeg, .jpg, .tiff, .tif, .pdf, .xls, .xlsx, .doc, .docx, "
401
+ ".pptx, .csv, .html, .mhtml, .txt, .md, .zip"
402
+ ),
403
+ "private": True,
404
+ },
405
+ "index_type": graph_type,
406
+ }
407
+ for graph_type in GRAPHRAG_INDEX_TYPES
408
+ ]
409
+
410
+ KH_INDICES = [
411
+ {
412
+ "name": "File Collection",
413
+ "config": {
414
+ "supported_file_types": (
415
+ ".png, .jpeg, .jpg, .tiff, .tif, .pdf, .xls, .xlsx, .doc, .docx, "
416
+ ".pptx, .csv, .html, .mhtml, .txt, .md, .zip"
417
+ ),
418
+ "private": True,
419
+ },
420
+ "index_type": "ktem.index.file.FileIndex",
421
+ },
422
+ *GRAPHRAG_INDICES,
423
+ ]
fly.toml ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # fly.toml app configuration file generated for kotaemon on 2024-12-24T20:56:32+07:00
2
+ #
3
+ # See https://fly.io/docs/reference/configuration/ for information about how to use this file.
4
+ #
5
+
6
+ app = 'kotaemon'
7
+ primary_region = 'sin'
8
+
9
+ [build]
10
+
11
+ [mounts]
12
+ destination = "/app/ktem_app_data"
13
+ source = "ktem_volume"
14
+
15
+ [http_service]
16
+ internal_port = 7860
17
+ force_https = true
18
+ auto_stop_machines = 'suspend'
19
+ auto_start_machines = true
20
+ min_machines_running = 0
21
+ processes = ['app']
22
+
23
+ [[vm]]
24
+ memory = '4gb'
25
+ cpu_kind = 'shared'
26
+ cpus = 4
kotaemon_app.egg-info/PKG-INFO ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: kotaemon-app
3
+ Version: 0.12.0
4
+ Summary: Kotaemon App
5
+ Author-email: "@trducng" <john@cinnamon.is>, "@lone17" <ian@cinnamon.is>, "@taprosoft" <tadashi@cinnamon.is>, "@cin-albert" <albert@cinnamon.is>
6
+ Project-URL: Homepage, https://cinnamon.github.io/kotaemon/
7
+ Project-URL: Repository, https://github.com/Cinnamon/kotaemon/
8
+ Project-URL: Documentation, https://cinnamon.github.io/kotaemon/
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ License-File: LICENSE.txt
13
+ Requires-Dist: kotaemon[all]
14
+ Requires-Dist: ktem
15
+ Dynamic: license-file
kotaemon_app.egg-info/SOURCES.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ LICENSE.txt
2
+ README.md
3
+ pyproject.toml
4
+ kotaemon_app.egg-info/PKG-INFO
5
+ kotaemon_app.egg-info/SOURCES.txt
6
+ kotaemon_app.egg-info/dependency_links.txt
7
+ kotaemon_app.egg-info/requires.txt
8
+ kotaemon_app.egg-info/top_level.txt
kotaemon_app.egg-info/dependency_links.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
kotaemon_app.egg-info/requires.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ kotaemon[all]
2
+ ktem
kotaemon_app.egg-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
launch.sh ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ if [ -z "$GRADIO_SERVER_NAME" ]; then
4
+ export GRADIO_SERVER_NAME="0.0.0.0"
5
+ fi
6
+ if [ -z "$GRADIO_SERVER_PORT" ]; then
7
+ export GRADIO_SERVER_PORT="7860"
8
+ fi
9
+
10
+ # Check if environment variable KH_DEMO_MODE is set to true
11
+ if [ "$KH_DEMO_MODE" = "true" ]; then
12
+ echo "KH_DEMO_MODE is true. Launching in demo mode..."
13
+ # Command to launch in demo mode
14
+ GR_FILE_ROOT_PATH="/app" KH_FEATURE_USER_MANAGEMENT=false USE_LIGHTRAG=false .venv/bin/uvicorn sso_app_demo:app --host "$GRADIO_SERVER_NAME" --port "$GRADIO_SERVER_PORT"
15
+ else
16
+ if [ "$KH_SSO_ENABLED" = "true" ]; then
17
+ echo "KH_SSO_ENABLED is true. Launching in SSO mode..."
18
+ GR_FILE_ROOT_PATH="/app" KH_SSO_ENABLED=true .venv/bin/uvicorn sso_app:app --host "$GRADIO_SERVER_NAME" --port "$GRADIO_SERVER_PORT"
19
+ else
20
+ ollama serve &
21
+ .venv/bin/python app.py
22
+ fi
23
+ fi
libs/kotaemon/README.md ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # kotaemon
2
+
3
+ Quick and easy AI components to build Kotaemon
4
+
5
+ ## Documentation
6
+
7
+ TBD
8
+
9
+ ## Install
10
+
11
+ ```shell
12
+ pip install kotaemon@git+ssh://git@github.com/Cinnamon/kotaemon.git
13
+ ```
14
+
15
+ ## Contribute
16
+
17
+ ### Setup
18
+
19
+ - Create conda environment (suggest 3.10)
20
+
21
+ ```shell
22
+ conda create -n kotaemon python=3.10
23
+ conda activate kotaemon
24
+ ```
25
+
26
+ - Clone the repo
27
+
28
+ ```shell
29
+ git clone git@github.com:Cinnamon/kotaemon.git
30
+ cd kotaemon
31
+ ```
32
+
33
+ - Install all
34
+
35
+ ```shell
36
+ pip install -e ".[dev]"
37
+ ```
38
+
39
+ - Pre-commit
40
+
41
+ ```shell
42
+ pre-commit install
43
+ ```
44
+
45
+ - Test
46
+
47
+ ```shell
48
+ pytest tests
49
+ ```
50
+
51
+ ### Credential sharing
52
+
53
+ This repo uses [git-secret](https://sobolevn.me/git-secret/) to share credentials, which
54
+ internally uses `gpg` to encrypt and decrypt secret files.
55
+
56
+ This repo uses `python-dotenv` to manage credentials stored as environment variable.
57
+ Please note that the use of `python-dotenv` and credentials are for development
58
+ purposes only. Thus, it should not be used in the main source code (i.e. `kotaemon/` and `tests/`), but can be used in `examples/`.
59
+
60
+ #### Install git-secret
61
+
62
+ Please follow the [official guide](https://sobolevn.me/git-secret/installation) to install git-secret.
63
+
64
+ For Windows users, see [For Windows users](#for-windows-users).
65
+
66
+ For users who don't have sudo privilege to install packages, follow the `Manual Installation` in the [official guide](https://sobolevn.me/git-secret/installation) and set `PREFIX` to a path that you have access to. And please don't forget to add `PREFIX` to your `PATH`.
67
+
68
+ #### Gaining access
69
+
70
+ In order to gain access to the secret files, you must provide your gpg public file to anyone who has access and ask them to ask your key to the keyring. For a quick tutorial on generating your gpg key pair, you can refer to the `Using gpg` section from the [git-secret main page](https://sobolevn.me/git-secret/).
71
+
72
+ #### Decrypt the secret file
73
+
74
+ The credentials are encrypted in the `.env.secret` file. To print the decrypted content to stdout, run
75
+
76
+ ```shell
77
+ git-secret cat [filename]
78
+ ```
79
+
80
+ Or to get the decrypted `.env` file, run
81
+
82
+ ```shell
83
+ git-secret reveal [filename]
84
+ ```
85
+
86
+ #### For Windows users
87
+
88
+ git-secret is currently not available for Windows, thus the easiest way is to use it in WSL (please use the latest version of WSL2). From there you have 2 options:
89
+
90
+ 1. Using the gpg of WSL.
91
+
92
+ This is the most straight-forward option since you would use WSL just like any other unix environment. However, the downside is that you have to make WSL your main environment, which means WSL must have write permission on your repo. To achieve this, you must either:
93
+
94
+ - Clone and store your repo inside WSL's file system.
95
+ - Provide WSL with necessary permission on your Windows file system. This can be achieve by setting `automount` options for WSL. To do that, add these content to `/etc/wsl.conf` and then restart your sub-system.
96
+
97
+ ```shell
98
+ [automount]
99
+ options = "metadata,umask=022,fmask=011"
100
+ ```
101
+
102
+ This enables all permissions for user owner.
103
+
104
+ 2. Using the gpg of Windows but with git-secret from WSL.
105
+
106
+ For those who use Windows as the main environment, having to switch back and forth between Windows and WSL will be inconvenient. You can instead stay within your Windows environment and apply some tricks to use `git-secret` from WSL.
107
+
108
+ - Install and setup `gpg` on Windows.
109
+ - Install `git-secret` on WSL. Now in Windows, you can invoke `git-secret` using `wsl git-secret`.
110
+ - Alternatively you can setup alias in CMD to shorten the syntax. Please refer to [this SO answer](https://stackoverflow.com/a/65823225) for the instruction. Some recommended aliases are:
111
+
112
+ ```bat
113
+ @echo off
114
+
115
+ :: Commands
116
+ DOSKEY ls=dir /B $*
117
+ DOSKEY ll=dir /a $*
118
+ DOSKEY git-secret=wsl git-secret $*
119
+ DOSKEY gs=wsl git-secret $*
120
+ ```
121
+
122
+ Now you can invoke `git-secret` in CMD using `git-secret` or `gs`.
123
+
124
+ - For Powershell users, similar behaviours can be achieved using `Set-Alias` and `profile.ps1`. Please refer this [SO thread](https://stackoverflow.com/questions/61081434/how-do-i-create-a-permanent-alias-file-in-powershell-core) as an example.
125
+
126
+ ### Code base structure
127
+
128
+ - documents: define document
129
+ - loaders
libs/kotaemon/kotaemon.egg-info/PKG-INFO ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Metadata-Version: 2.4
2
+ Name: kotaemon
3
+ Version: 0.12.0
4
+ Summary: Kotaemon core library for AI development.
5
+ Author-email: "@trducng" <john@cinnamon.is>, "@lone17" <ian@cinnamon.is>, "@taprosoft" <tadashi@cinnamon.is>, "@cin-albert" <albert@cinnamon.is>
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: Operating System :: OS Independent
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: azure-ai-documentintelligence
11
+ Requires-Dist: beautifulsoup4<4.13,>=4.12.3
12
+ Requires-Dist: click<9,>=8.1.7
13
+ Requires-Dist: cohere<6,>=5.3.2
14
+ Requires-Dist: cookiecutter<2.7,>=2.6.0
15
+ Requires-Dist: fast_langdetect
16
+ Requires-Dist: fastapi<=0.112.1
17
+ Requires-Dist: gradio<5,>=4.31.0
18
+ Requires-Dist: html2text==2024.2.26
19
+ Requires-Dist: langchain<2
20
+ Requires-Dist: langchain-community<1
21
+ Requires-Dist: langchain-openai<2
22
+ Requires-Dist: langchain-google-genai<5
23
+ Requires-Dist: langchain-anthropic<2
24
+ Requires-Dist: langchain-ollama<2
25
+ Requires-Dist: langchain-mistralai<2
26
+ Requires-Dist: langchain-cohere<1
27
+ Requires-Dist: llama-hub<0.1.0,>=0.0.79
28
+ Requires-Dist: llama-index<0.11.0,>=0.10.40
29
+ Requires-Dist: chromadb<=0.5.16
30
+ Requires-Dist: llama-index-vector-stores-chroma>=0.1.9
31
+ Requires-Dist: llama-index-vector-stores-lancedb
32
+ Requires-Dist: openai<2,>=1.23.6
33
+ Requires-Dist: matplotlib
34
+ Requires-Dist: matplotlib-inline
35
+ Requires-Dist: openpyxl<3.2,>=3.1.2
36
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc>=1.25.0
37
+ Requires-Dist: pandas<2.3,>=2.2.2
38
+ Requires-Dist: plotly<6.0.0
39
+ Requires-Dist: PyMuPDF<=1.24.11,>=1.23
40
+ Requires-Dist: pypdf<4.3,>=4.2.0
41
+ Requires-Dist: pylance
42
+ Requires-Dist: python-decouple
43
+ Requires-Dist: python-docx<1.2,>=1.1.0
44
+ Requires-Dist: python-dotenv<1.1,>=1.0.1
45
+ Requires-Dist: tenacity<8.3,>=8.2.3
46
+ Requires-Dist: theflow<0.9.0,>=0.8.6
47
+ Requires-Dist: trogon<0.6,>=0.5.0
48
+ Requires-Dist: umap-learn==0.5.5
49
+ Requires-Dist: tavily-python>=0.4.0
50
+ Requires-Dist: pydantic<=2.10.6
51
+ Provides-Extra: adv
52
+ Requires-Dist: duckduckgo-search<6.2,>=6.1.0; extra == "adv"
53
+ Requires-Dist: elasticsearch<8.14,>=8.13.0; extra == "adv"
54
+ Requires-Dist: fastembed; extra == "adv"
55
+ Requires-Dist: onnxruntime<v1.20; extra == "adv"
56
+ Requires-Dist: googlesearch-python<1.3,>=1.2.4; extra == "adv"
57
+ Requires-Dist: llama-cpp-python<0.2.8; extra == "adv"
58
+ Requires-Dist: llama-index<0.11.0,>=0.10.40; extra == "adv"
59
+ Requires-Dist: llama-index-vector-stores-milvus; extra == "adv"
60
+ Requires-Dist: llama-index-vector-stores-qdrant; extra == "adv"
61
+ Requires-Dist: mcp[cli]>=1.0.0; extra == "adv"
62
+ Requires-Dist: sentence-transformers; extra == "adv"
63
+ Requires-Dist: tabulate; extra == "adv"
64
+ Requires-Dist: unstructured<0.16,>=0.15.8; extra == "adv"
65
+ Requires-Dist: wikipedia<1.5,>=1.4.0; extra == "adv"
66
+ Requires-Dist: voyageai>=0.3.0; extra == "adv"
67
+ Provides-Extra: dev
68
+ Requires-Dist: black; extra == "dev"
69
+ Requires-Dist: coverage; extra == "dev"
70
+ Requires-Dist: flake8; extra == "dev"
71
+ Requires-Dist: ipython; extra == "dev"
72
+ Requires-Dist: pre-commit; extra == "dev"
73
+ Requires-Dist: pytest; extra == "dev"
74
+ Requires-Dist: pytest-mock; extra == "dev"
75
+ Requires-Dist: sphinx; extra == "dev"
76
+ Provides-Extra: docling
77
+ Requires-Dist: docling<=2.5.2; extra == "docling"
78
+ Provides-Extra: paddleocr
79
+ Requires-Dist: paddleocr[doc-parser]; extra == "paddleocr"
80
+ Provides-Extra: lightrag
81
+ Requires-Dist: aioboto3; extra == "lightrag"
82
+ Requires-Dist: nano-vectordb; extra == "lightrag"
83
+ Requires-Dist: ollama; extra == "lightrag"
84
+ Requires-Dist: xxhash; extra == "lightrag"
85
+ Requires-Dist: lightrag-hku<=1.3.0; extra == "lightrag"
86
+ Provides-Extra: all
87
+ Requires-Dist: kotaemon[adv,dev]; extra == "all"
88
+
89
+ # kotaemon
90
+
91
+ Quick and easy AI components to build Kotaemon
92
+
93
+ ## Documentation
94
+
95
+ TBD
96
+
97
+ ## Install
98
+
99
+ ```shell
100
+ pip install kotaemon@git+ssh://git@github.com/Cinnamon/kotaemon.git
101
+ ```
102
+
103
+ ## Contribute
104
+
105
+ ### Setup
106
+
107
+ - Create conda environment (suggest 3.10)
108
+
109
+ ```shell
110
+ conda create -n kotaemon python=3.10
111
+ conda activate kotaemon
112
+ ```
113
+
114
+ - Clone the repo
115
+
116
+ ```shell
117
+ git clone git@github.com:Cinnamon/kotaemon.git
118
+ cd kotaemon
119
+ ```
120
+
121
+ - Install all
122
+
123
+ ```shell
124
+ pip install -e ".[dev]"
125
+ ```
126
+
127
+ - Pre-commit
128
+
129
+ ```shell
130
+ pre-commit install
131
+ ```
132
+
133
+ - Test
134
+
135
+ ```shell
136
+ pytest tests
137
+ ```
138
+
139
+ ### Credential sharing
140
+
141
+ This repo uses [git-secret](https://sobolevn.me/git-secret/) to share credentials, which
142
+ internally uses `gpg` to encrypt and decrypt secret files.
143
+
144
+ This repo uses `python-dotenv` to manage credentials stored as environment variable.
145
+ Please note that the use of `python-dotenv` and credentials are for development
146
+ purposes only. Thus, it should not be used in the main source code (i.e. `kotaemon/` and `tests/`), but can be used in `examples/`.
147
+
148
+ #### Install git-secret
149
+
150
+ Please follow the [official guide](https://sobolevn.me/git-secret/installation) to install git-secret.
151
+
152
+ For Windows users, see [For Windows users](#for-windows-users).
153
+
154
+ For users who don't have sudo privilege to install packages, follow the `Manual Installation` in the [official guide](https://sobolevn.me/git-secret/installation) and set `PREFIX` to a path that you have access to. And please don't forget to add `PREFIX` to your `PATH`.
155
+
156
+ #### Gaining access
157
+
158
+ In order to gain access to the secret files, you must provide your gpg public file to anyone who has access and ask them to ask your key to the keyring. For a quick tutorial on generating your gpg key pair, you can refer to the `Using gpg` section from the [git-secret main page](https://sobolevn.me/git-secret/).
159
+
160
+ #### Decrypt the secret file
161
+
162
+ The credentials are encrypted in the `.env.secret` file. To print the decrypted content to stdout, run
163
+
164
+ ```shell
165
+ git-secret cat [filename]
166
+ ```
167
+
168
+ Or to get the decrypted `.env` file, run
169
+
170
+ ```shell
171
+ git-secret reveal [filename]
172
+ ```
173
+
174
+ #### For Windows users
175
+
176
+ git-secret is currently not available for Windows, thus the easiest way is to use it in WSL (please use the latest version of WSL2). From there you have 2 options:
177
+
178
+ 1. Using the gpg of WSL.
179
+
180
+ This is the most straight-forward option since you would use WSL just like any other unix environment. However, the downside is that you have to make WSL your main environment, which means WSL must have write permission on your repo. To achieve this, you must either:
181
+
182
+ - Clone and store your repo inside WSL's file system.
183
+ - Provide WSL with necessary permission on your Windows file system. This can be achieve by setting `automount` options for WSL. To do that, add these content to `/etc/wsl.conf` and then restart your sub-system.
184
+
185
+ ```shell
186
+ [automount]
187
+ options = "metadata,umask=022,fmask=011"
188
+ ```
189
+
190
+ This enables all permissions for user owner.
191
+
192
+ 2. Using the gpg of Windows but with git-secret from WSL.
193
+
194
+ For those who use Windows as the main environment, having to switch back and forth between Windows and WSL will be inconvenient. You can instead stay within your Windows environment and apply some tricks to use `git-secret` from WSL.
195
+
196
+ - Install and setup `gpg` on Windows.
197
+ - Install `git-secret` on WSL. Now in Windows, you can invoke `git-secret` using `wsl git-secret`.
198
+ - Alternatively you can setup alias in CMD to shorten the syntax. Please refer to [this SO answer](https://stackoverflow.com/a/65823225) for the instruction. Some recommended aliases are:
199
+
200
+ ```bat
201
+ @echo off
202
+
203
+ :: Commands
204
+ DOSKEY ls=dir /B $*
205
+ DOSKEY ll=dir /a $*
206
+ DOSKEY git-secret=wsl git-secret $*
207
+ DOSKEY gs=wsl git-secret $*
208
+ ```
209
+
210
+ Now you can invoke `git-secret` in CMD using `git-secret` or `gs`.
211
+
212
+ - For Powershell users, similar behaviours can be achieved using `Set-Alias` and `profile.ps1`. Please refer this [SO thread](https://stackoverflow.com/questions/61081434/how-do-i-create-a-permanent-alias-file-in-powershell-core) as an example.
213
+
214
+ ### Code base structure
215
+
216
+ - documents: define document
217
+ - loaders
libs/kotaemon/kotaemon.egg-info/SOURCES.txt ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ README.md
2
+ pyproject.toml
3
+ kotaemon/__init__.py
4
+ kotaemon/cli.py
5
+ kotaemon.egg-info/PKG-INFO
6
+ kotaemon.egg-info/SOURCES.txt
7
+ kotaemon.egg-info/dependency_links.txt
8
+ kotaemon.egg-info/entry_points.txt
9
+ kotaemon.egg-info/requires.txt
10
+ kotaemon.egg-info/top_level.txt
11
+ kotaemon/agents/__init__.py
12
+ kotaemon/agents/base.py
13
+ kotaemon/agents/langchain_based.py
14
+ kotaemon/agents/utils.py
15
+ kotaemon/agents/io/__init__.py
16
+ kotaemon/agents/io/base.py
17
+ kotaemon/agents/react/__init__.py
18
+ kotaemon/agents/react/agent.py
19
+ kotaemon/agents/react/prompt.py
20
+ kotaemon/agents/rewoo/__init__.py
21
+ kotaemon/agents/rewoo/agent.py
22
+ kotaemon/agents/rewoo/planner.py
23
+ kotaemon/agents/rewoo/prompt.py
24
+ kotaemon/agents/rewoo/solver.py
25
+ kotaemon/agents/tools/__init__.py
26
+ kotaemon/agents/tools/base.py
27
+ kotaemon/agents/tools/google.py
28
+ kotaemon/agents/tools/llm.py
29
+ kotaemon/agents/tools/mcp.py
30
+ kotaemon/agents/tools/wikipedia.py
31
+ kotaemon/base/__init__.py
32
+ kotaemon/base/component.py
33
+ kotaemon/base/schema.py
34
+ kotaemon/chatbot/__init__.py
35
+ kotaemon/chatbot/base.py
36
+ kotaemon/chatbot/simple_respondent.py
37
+ kotaemon/contribs/__init__.py
38
+ kotaemon/contribs/docs.py
39
+ kotaemon/contribs/promptui/__init__.py
40
+ kotaemon/contribs/promptui/base.py
41
+ kotaemon/contribs/promptui/cli.py
42
+ kotaemon/contribs/promptui/config.py
43
+ kotaemon/contribs/promptui/export.py
44
+ kotaemon/contribs/promptui/logs.py
45
+ kotaemon/contribs/promptui/themes.py
46
+ kotaemon/contribs/promptui/tunnel.py
47
+ kotaemon/contribs/promptui/ui/__init__.py
48
+ kotaemon/contribs/promptui/ui/blocks.py
49
+ kotaemon/contribs/promptui/ui/chat.py
50
+ kotaemon/contribs/promptui/ui/pipeline.py
51
+ kotaemon/embeddings/__init__.py
52
+ kotaemon/embeddings/base.py
53
+ kotaemon/embeddings/endpoint_based.py
54
+ kotaemon/embeddings/fastembed.py
55
+ kotaemon/embeddings/langchain_based.py
56
+ kotaemon/embeddings/openai.py
57
+ kotaemon/embeddings/tei_endpoint_embed.py
58
+ kotaemon/embeddings/voyageai.py
59
+ kotaemon/indices/__init__.py
60
+ kotaemon/indices/base.py
61
+ kotaemon/indices/vectorindex.py
62
+ kotaemon/indices/extractors/__init__.py
63
+ kotaemon/indices/extractors/doc_parsers.py
64
+ kotaemon/indices/ingests/__init__.py
65
+ kotaemon/indices/ingests/files.py
66
+ kotaemon/indices/qa/__init__.py
67
+ kotaemon/indices/qa/citation.py
68
+ kotaemon/indices/qa/citation_qa.py
69
+ kotaemon/indices/qa/citation_qa_inline.py
70
+ kotaemon/indices/qa/format_context.py
71
+ kotaemon/indices/qa/utils.py
72
+ kotaemon/indices/rankings/__init__.py
73
+ kotaemon/indices/rankings/base.py
74
+ kotaemon/indices/rankings/cohere.py
75
+ kotaemon/indices/rankings/llm.py
76
+ kotaemon/indices/rankings/llm_scoring.py
77
+ kotaemon/indices/rankings/llm_trulens.py
78
+ kotaemon/indices/retrievers/__init__.py
79
+ kotaemon/indices/retrievers/jina_web_search.py
80
+ kotaemon/indices/retrievers/tavily_web_search.py
81
+ kotaemon/indices/splitters/__init__.py
82
+ kotaemon/llms/__init__.py
83
+ kotaemon/llms/base.py
84
+ kotaemon/llms/branching.py
85
+ kotaemon/llms/cot.py
86
+ kotaemon/llms/linear.py
87
+ kotaemon/llms/chats/__init__.py
88
+ kotaemon/llms/chats/base.py
89
+ kotaemon/llms/chats/endpoint_based.py
90
+ kotaemon/llms/chats/langchain_based.py
91
+ kotaemon/llms/chats/llamacpp.py
92
+ kotaemon/llms/chats/openai.py
93
+ kotaemon/llms/completions/__init__.py
94
+ kotaemon/llms/completions/base.py
95
+ kotaemon/llms/completions/langchain_based.py
96
+ kotaemon/llms/prompts/__init__.py
97
+ kotaemon/llms/prompts/base.py
98
+ kotaemon/llms/prompts/template.py
99
+ kotaemon/loaders/__init__.py
100
+ kotaemon/loaders/adobe_loader.py
101
+ kotaemon/loaders/azureai_document_intelligence_loader.py
102
+ kotaemon/loaders/base.py
103
+ kotaemon/loaders/composite_loader.py
104
+ kotaemon/loaders/docling_loader.py
105
+ kotaemon/loaders/docx_loader.py
106
+ kotaemon/loaders/excel_loader.py
107
+ kotaemon/loaders/html_loader.py
108
+ kotaemon/loaders/mathpix_loader.py
109
+ kotaemon/loaders/ocr_loader.py
110
+ kotaemon/loaders/pdf_loader.py
111
+ kotaemon/loaders/txt_loader.py
112
+ kotaemon/loaders/unstructured_loader.py
113
+ kotaemon/loaders/web_loader.py
114
+ kotaemon/loaders/paddleocr_loader/__init__.py
115
+ kotaemon/loaders/paddleocr_loader/adapter.py
116
+ kotaemon/loaders/paddleocr_loader/paddleocr_vl_loader.py
117
+ kotaemon/loaders/paddleocr_loader/ppstructure_v3_loader.py
118
+ kotaemon/loaders/utils/__init__.py
119
+ kotaemon/loaders/utils/adobe.py
120
+ kotaemon/loaders/utils/box.py
121
+ kotaemon/loaders/utils/gpt4v.py
122
+ kotaemon/loaders/utils/pdf_ocr.py
123
+ kotaemon/loaders/utils/table.py
124
+ kotaemon/parsers/__init__.py
125
+ kotaemon/parsers/regex_extractor.py
126
+ kotaemon/rerankings/__init__.py
127
+ kotaemon/rerankings/base.py
128
+ kotaemon/rerankings/cohere.py
129
+ kotaemon/rerankings/tei_fast_rerank.py
130
+ kotaemon/rerankings/voyageai.py
131
+ kotaemon/storages/__init__.py
132
+ kotaemon/storages/docstores/__init__.py
133
+ kotaemon/storages/docstores/base.py
134
+ kotaemon/storages/docstores/elasticsearch.py
135
+ kotaemon/storages/docstores/in_memory.py
136
+ kotaemon/storages/docstores/lancedb.py
137
+ kotaemon/storages/docstores/simple_file.py
138
+ kotaemon/storages/vectorstores/__init__.py
139
+ kotaemon/storages/vectorstores/base.py
140
+ kotaemon/storages/vectorstores/chroma.py
141
+ kotaemon/storages/vectorstores/in_memory.py
142
+ kotaemon/storages/vectorstores/lancedb.py
143
+ kotaemon/storages/vectorstores/milvus.py
144
+ kotaemon/storages/vectorstores/qdrant.py
145
+ kotaemon/storages/vectorstores/simple_file.py
146
+ tests/test_agent.py
147
+ tests/test_composite.py
148
+ tests/test_cot.py
149
+ tests/test_docstores.py
150
+ tests/test_documents.py
151
+ tests/test_embedding_models.py
152
+ tests/test_indexing_retrieval.py
153
+ tests/test_ingestor.py
154
+ tests/test_llms_chat_models.py
155
+ tests/test_llms_completion_models.py
156
+ tests/test_mcp_tools.py
157
+ tests/test_paddleocr_loader.py
158
+ tests/test_post_processing.py
159
+ tests/test_prompt.py
160
+ tests/test_promptui.py
161
+ tests/test_reader.py
162
+ tests/test_reranking.py
163
+ tests/test_splitter.py
164
+ tests/test_table_reader.py
165
+ tests/test_telemetry.py
166
+ tests/test_template.py
167
+ tests/test_tools.py
168
+ tests/test_vectorstore.py
libs/kotaemon/kotaemon.egg-info/dependency_links.txt ADDED
@@ -0,0 +1 @@
 
 
1
+
libs/kotaemon/kotaemon.egg-info/entry_points.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ [console_scripts]
2
+ kotaemon = kotaemon.cli:main
libs/kotaemon/kotaemon.egg-info/requires.txt ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ azure-ai-documentintelligence
2
+ beautifulsoup4<4.13,>=4.12.3
3
+ click<9,>=8.1.7
4
+ cohere<6,>=5.3.2
5
+ cookiecutter<2.7,>=2.6.0
6
+ fast_langdetect
7
+ fastapi<=0.112.1
8
+ gradio<5,>=4.31.0
9
+ html2text==2024.2.26
10
+ langchain<2
11
+ langchain-community<1
12
+ langchain-openai<2
13
+ langchain-google-genai<5
14
+ langchain-anthropic<2
15
+ langchain-ollama<2
16
+ langchain-mistralai<2
17
+ langchain-cohere<1
18
+ llama-hub<0.1.0,>=0.0.79
19
+ llama-index<0.11.0,>=0.10.40
20
+ chromadb<=0.5.16
21
+ llama-index-vector-stores-chroma>=0.1.9
22
+ llama-index-vector-stores-lancedb
23
+ openai<2,>=1.23.6
24
+ matplotlib
25
+ matplotlib-inline
26
+ openpyxl<3.2,>=3.1.2
27
+ opentelemetry-exporter-otlp-proto-grpc>=1.25.0
28
+ pandas<2.3,>=2.2.2
29
+ plotly<6.0.0
30
+ PyMuPDF<=1.24.11,>=1.23
31
+ pypdf<4.3,>=4.2.0
32
+ pylance
33
+ python-decouple
34
+ python-docx<1.2,>=1.1.0
35
+ python-dotenv<1.1,>=1.0.1
36
+ tenacity<8.3,>=8.2.3
37
+ theflow<0.9.0,>=0.8.6
38
+ trogon<0.6,>=0.5.0
39
+ umap-learn==0.5.5
40
+ tavily-python>=0.4.0
41
+ pydantic<=2.10.6
42
+
43
+ [adv]
44
+ duckduckgo-search<6.2,>=6.1.0
45
+ elasticsearch<8.14,>=8.13.0
46
+ fastembed
47
+ onnxruntime<v1.20
48
+ googlesearch-python<1.3,>=1.2.4
49
+ llama-cpp-python<0.2.8
50
+ llama-index<0.11.0,>=0.10.40
51
+ llama-index-vector-stores-milvus
52
+ llama-index-vector-stores-qdrant
53
+ mcp[cli]>=1.0.0
54
+ sentence-transformers
55
+ tabulate
56
+ unstructured<0.16,>=0.15.8
57
+ wikipedia<1.5,>=1.4.0
58
+ voyageai>=0.3.0
59
+
60
+ [all]
61
+ kotaemon[adv,dev]
62
+
63
+ [dev]
64
+ black
65
+ coverage
66
+ flake8
67
+ ipython
68
+ pre-commit
69
+ pytest
70
+ pytest-mock
71
+ sphinx
72
+
73
+ [docling]
74
+ docling<=2.5.2
75
+
76
+ [lightrag]
77
+ aioboto3
78
+ nano-vectordb
79
+ ollama
80
+ xxhash
81
+ lightrag-hku<=1.3.0
82
+
83
+ [paddleocr]
84
+ paddleocr[doc-parser]
libs/kotaemon/kotaemon.egg-info/top_level.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ kotaemon
libs/kotaemon/kotaemon/__init__.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Disable telemetry with monkey patching
2
+ import logging
3
+
4
+ logger = logging.getLogger(__name__)
5
+ try:
6
+ import posthog
7
+
8
+ def capture(*args, **kwargs):
9
+ logger.info("posthog.capture called with args: %s, kwargs: %s", args, kwargs)
10
+
11
+ posthog.capture = capture
12
+ except ImportError:
13
+ pass
14
+
15
+ try:
16
+ import os
17
+
18
+ os.environ["HAYSTACK_TELEMETRY_ENABLED"] = "False"
19
+ import haystack.telemetry
20
+
21
+ haystack.telemetry.telemetry = None
22
+ except ImportError:
23
+ pass
libs/kotaemon/kotaemon/agents/__init__.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .base import BaseAgent
2
+ from .io import AgentFinish, AgentOutput, AgentType, BaseScratchPad
3
+ from .langchain_based import LangchainAgent
4
+ from .react.agent import ReactAgent
5
+ from .rewoo.agent import RewooAgent
6
+ from .tools import (
7
+ BaseTool,
8
+ ComponentTool,
9
+ GoogleSearchTool,
10
+ LLMTool,
11
+ MCPTool,
12
+ WikipediaTool,
13
+ )
14
+
15
+ __all__ = [
16
+ # agent
17
+ "BaseAgent",
18
+ "ReactAgent",
19
+ "RewooAgent",
20
+ "LangchainAgent",
21
+ # tool
22
+ "BaseTool",
23
+ "ComponentTool",
24
+ "GoogleSearchTool",
25
+ "WikipediaTool",
26
+ "LLMTool",
27
+ "MCPTool",
28
+ # io
29
+ "AgentType",
30
+ "AgentOutput",
31
+ "AgentFinish",
32
+ "BaseScratchPad",
33
+ ]
libs/kotaemon/kotaemon/agents/base.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Union
2
+
3
+ from kotaemon.base import BaseComponent, Node, Param
4
+ from kotaemon.llms import BaseLLM, PromptTemplate
5
+
6
+ from .io import AgentOutput, AgentType
7
+ from .tools import BaseTool
8
+
9
+
10
+ class BaseAgent(BaseComponent):
11
+ """Define base agent interface"""
12
+
13
+ name: str = Param(help="Name of the agent.")
14
+ agent_type: AgentType = Param(help="Agent type, must be one of AgentType")
15
+ description: str = Param(
16
+ help=(
17
+ "Description used to tell the model how/when/why to use the agent. You can"
18
+ " provide few-shot examples as a part of the description. This will be"
19
+ " input to the prompt of LLM."
20
+ )
21
+ )
22
+ llm: Optional[BaseLLM] = Node(
23
+ help=(
24
+ "LLM to be used for the agent (optional). LLM must implement BaseLLM"
25
+ " interface."
26
+ )
27
+ )
28
+ prompt_template: Optional[Union[PromptTemplate, dict[str, PromptTemplate]]] = Param(
29
+ help="A prompt template or a dict to supply different prompt to the agent"
30
+ )
31
+ plugins: list[BaseTool] = Param(
32
+ default_callback=lambda _: [],
33
+ help="List of plugins / tools to be used in the agent",
34
+ )
35
+
36
+ @staticmethod
37
+ def safeguard_run(run_func, *args, **kwargs):
38
+ def wrapper(self, *args, **kwargs):
39
+ try:
40
+ return run_func(self, *args, **kwargs)
41
+ except Exception as e:
42
+ return AgentOutput(
43
+ text="",
44
+ agent_type=self.agent_type,
45
+ status="failed",
46
+ error=str(e),
47
+ )
48
+
49
+ return wrapper
50
+
51
+ def add_tools(self, tools: list[BaseTool]) -> None:
52
+ """Helper method to add tools and update agent state if needed"""
53
+ self.plugins.extend(tools)
54
+
55
+ def run(self, *args, **kwargs) -> AgentOutput | list[AgentOutput]:
56
+ """Run the component."""
57
+ raise NotImplementedError()
libs/kotaemon/kotaemon/agents/io/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .base import AgentAction, AgentFinish, AgentOutput, AgentType, BaseScratchPad
2
+
3
+ __all__ = ["AgentOutput", "AgentFinish", "BaseScratchPad", "AgentType", "AgentAction"]
libs/kotaemon/kotaemon/agents/io/base.py ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import logging
3
+ import os
4
+ from dataclasses import dataclass
5
+ from enum import Enum
6
+ from typing import Any, Dict, Literal, NamedTuple, Optional, Union
7
+
8
+ from pydantic import ConfigDict
9
+
10
+ from kotaemon.base import LLMInterface
11
+
12
+
13
+ def check_log():
14
+ """
15
+ Checks if logging has been enabled.
16
+ :return: True if logging has been enabled, False otherwise.
17
+ :rtype: bool
18
+ """
19
+ return os.environ.get("LOG_PATH", None) is not None
20
+
21
+
22
+ class AgentType(Enum):
23
+ """
24
+ Enumerated type for agent types.
25
+ """
26
+
27
+ openai = "openai"
28
+ openai_multi = "openai_multi"
29
+ openai_tool = "openai_tool"
30
+ self_ask = "self_ask"
31
+ react = "react"
32
+ rewoo = "rewoo"
33
+ vanilla = "vanilla"
34
+
35
+
36
+ class BaseScratchPad:
37
+ """
38
+ Base class for output handlers.
39
+
40
+ Attributes:
41
+ -----------
42
+ logger : logging.Logger
43
+ The logger object to log messages.
44
+
45
+ Methods:
46
+ --------
47
+ stop():
48
+ Stop the output.
49
+
50
+ update_status(output: str, **kwargs):
51
+ Update the status of the output.
52
+
53
+ thinking(name: str):
54
+ Log that a process is thinking.
55
+
56
+ done(_all=False):
57
+ Log that the process is done.
58
+
59
+ stream_print(item: str):
60
+ Not implemented.
61
+
62
+ json_print(item: Dict[str, Any]):
63
+ Log a JSON object.
64
+
65
+ panel_print(item: Any, title: str = "Output", stream: bool = False):
66
+ Log a panel output.
67
+
68
+ clear():
69
+ Not implemented.
70
+
71
+ print(content: str, **kwargs):
72
+ Log arbitrary content.
73
+
74
+ format_json(json_obj: str):
75
+ Format a JSON object.
76
+
77
+ debug(content: str, **kwargs):
78
+ Log a debug message.
79
+
80
+ info(content: str, **kwargs):
81
+ Log an informational message.
82
+
83
+ warning(content: str, **kwargs):
84
+ Log a warning message.
85
+
86
+ error(content: str, **kwargs):
87
+ Log an error message.
88
+
89
+ critical(content: str, **kwargs):
90
+ Log a critical message.
91
+ """
92
+
93
+ def __init__(self):
94
+ """
95
+ Initialize the BaseOutput object.
96
+
97
+ """
98
+ self.logger = logging
99
+ self.log = []
100
+
101
+ def stop(self):
102
+ """
103
+ Stop the output.
104
+ """
105
+
106
+ def update_status(self, output: str, **kwargs):
107
+ """
108
+ Update the status of the output.
109
+ """
110
+ if check_log():
111
+ self.logger.info(output)
112
+
113
+ def thinking(self, name: str):
114
+ """
115
+ Log that a process is thinking.
116
+ """
117
+ if check_log():
118
+ self.logger.info(f"{name} is thinking...")
119
+
120
+ def done(self, _all=False):
121
+ """
122
+ Log that the process is done.
123
+ """
124
+
125
+ if check_log():
126
+ self.logger.info("Done")
127
+
128
+ def stream_print(self, item: str):
129
+ """
130
+ Stream print.
131
+ """
132
+
133
+ def json_print(self, item: Dict[str, Any]):
134
+ """
135
+ Log a JSON object.
136
+ """
137
+ if check_log():
138
+ self.logger.info(json.dumps(item, indent=2))
139
+
140
+ def panel_print(self, item: Any, title: str = "Output", stream: bool = False):
141
+ """
142
+ Log a panel output.
143
+
144
+ Args:
145
+ item : Any
146
+ The item to log.
147
+ title : str, optional
148
+ The title of the panel, defaults to "Output".
149
+ stream : bool, optional
150
+ """
151
+ if not stream:
152
+ self.log.append(item)
153
+ if check_log():
154
+ self.logger.info("-" * 20)
155
+ self.logger.info(item)
156
+ self.logger.info("-" * 20)
157
+
158
+ def clear(self):
159
+ """
160
+ Not implemented.
161
+ """
162
+
163
+ def print(self, content: str, **kwargs):
164
+ """
165
+ Log arbitrary content.
166
+ """
167
+ self.log.append(content)
168
+ if check_log():
169
+ self.logger.info(content)
170
+
171
+ def format_json(self, json_obj: str):
172
+ """
173
+ Format a JSON object.
174
+ """
175
+ formatted_json = json.dumps(json_obj, indent=2)
176
+ return formatted_json
177
+
178
+ def debug(self, content: str, **kwargs):
179
+ """
180
+ Log a debug message.
181
+ """
182
+ if check_log():
183
+ self.logger.debug(content, **kwargs)
184
+
185
+ def info(self, content: str, **kwargs):
186
+ """
187
+ Log an informational message.
188
+ """
189
+ if check_log():
190
+ self.logger.info(content, **kwargs)
191
+
192
+ def warning(self, content: str, **kwargs):
193
+ """
194
+ Log a warning message.
195
+ """
196
+ if check_log():
197
+ self.logger.warning(content, **kwargs)
198
+
199
+ def error(self, content: str, **kwargs):
200
+ """
201
+ Log an error message.
202
+ """
203
+ if check_log():
204
+ self.logger.error(content, **kwargs)
205
+
206
+ def critical(self, content: str, **kwargs):
207
+ """
208
+ Log a critical message.
209
+ """
210
+ if check_log():
211
+ self.logger.critical(content, **kwargs)
212
+
213
+
214
+ @dataclass
215
+ class AgentAction:
216
+ """Agent's action to take.
217
+
218
+ Args:
219
+ tool: The tool to invoke.
220
+ tool_input: The input to the tool.
221
+ log: The log message.
222
+ """
223
+
224
+ tool: str
225
+ tool_input: Union[str, dict]
226
+ log: str
227
+
228
+
229
+ class AgentFinish(NamedTuple):
230
+ """Agent's return value when finishing execution.
231
+
232
+ Args:
233
+ return_values: The return values of the agent.
234
+ log: The log message.
235
+ """
236
+
237
+ return_values: dict
238
+ log: str
239
+
240
+
241
+ class AgentOutput(LLMInterface):
242
+ """Output from an agent.
243
+
244
+ Args:
245
+ text: The text output from the agent.
246
+ agent_type: The type of agent.
247
+ status: The status after executing the agent.
248
+ error: The error message if any.
249
+ """
250
+
251
+ model_config = ConfigDict(extra="allow")
252
+
253
+ text: str
254
+ type: str = "agent"
255
+ agent_type: AgentType
256
+ status: Literal["thinking", "finished", "stopped", "failed"]
257
+ error: Optional[str] = None
258
+ intermediate_steps: Optional[list] = None
libs/kotaemon/kotaemon/agents/langchain_based.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Optional
2
+
3
+ from langchain.agents import AgentType as LCAgentType
4
+ from langchain.agents import initialize_agent
5
+ from langchain.agents.agent import AgentExecutor as LCAgentExecutor
6
+
7
+ from kotaemon.llms import LLM, ChatLLM
8
+
9
+ from .base import BaseAgent
10
+ from .io import AgentOutput, AgentType
11
+ from .tools import BaseTool
12
+
13
+
14
+ class LangchainAgent(BaseAgent):
15
+ """Wrapper for Langchain Agent"""
16
+
17
+ name: str = "LangchainAgent"
18
+ agent_type: AgentType
19
+ description: str = "LangchainAgent for answering multi-step reasoning questions"
20
+ AGENT_TYPE_MAP = {
21
+ AgentType.openai: LCAgentType.OPENAI_FUNCTIONS,
22
+ AgentType.openai_multi: LCAgentType.OPENAI_MULTI_FUNCTIONS,
23
+ AgentType.react: LCAgentType.ZERO_SHOT_REACT_DESCRIPTION,
24
+ AgentType.self_ask: LCAgentType.SELF_ASK_WITH_SEARCH,
25
+ }
26
+ agent: Optional[LCAgentExecutor] = None
27
+
28
+ def __init__(self, *args, **kwargs):
29
+ super().__init__(*args, **kwargs)
30
+
31
+ if self.agent_type not in self.AGENT_TYPE_MAP:
32
+ raise NotImplementedError(
33
+ f"AgentType {self.agent_type } not supported by Langchain wrapper"
34
+ )
35
+ self.update_agent_tools()
36
+
37
+ def update_agent_tools(self):
38
+ assert isinstance(self.llm, (ChatLLM, LLM))
39
+ langchain_plugins = [tool.to_langchain_format() for tool in self.plugins]
40
+
41
+ # a fix for search_doc tool name:
42
+ # use "Intermediate Answer" for self-ask agent
43
+ found_search_tool = False
44
+ if self.agent_type == AgentType.self_ask:
45
+ for plugin in langchain_plugins:
46
+ if plugin.name == "search_doc":
47
+ plugin.name = "Intermediate Answer"
48
+ langchain_plugins = [plugin]
49
+ found_search_tool = True
50
+ break
51
+
52
+ if self.agent_type != AgentType.self_ask or found_search_tool:
53
+ # reinit Langchain AgentExecutor
54
+ self.agent = initialize_agent(
55
+ langchain_plugins,
56
+ self.llm.to_langchain_format(),
57
+ agent=self.AGENT_TYPE_MAP[self.agent_type],
58
+ handle_parsing_errors=True,
59
+ verbose=True,
60
+ )
61
+
62
+ def add_tools(self, tools: List[BaseTool]) -> None:
63
+ super().add_tools(tools)
64
+ self.update_agent_tools()
65
+ return
66
+
67
+ def run(self, instruction: str) -> AgentOutput:
68
+ assert (
69
+ self.agent is not None
70
+ ), "Lanchain AgentExecutor is not correctly initialized"
71
+
72
+ # Langchain AgentExecutor call
73
+ output = self.agent(instruction)["output"]
74
+
75
+ return AgentOutput(
76
+ text=output,
77
+ agent_type=self.agent_type,
78
+ status="finished",
79
+ )
libs/kotaemon/kotaemon/agents/react/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .agent import ReactAgent
2
+
3
+ __all__ = ["ReactAgent"]
libs/kotaemon/kotaemon/agents/react/agent.py ADDED
@@ -0,0 +1,361 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import re
3
+ from functools import partial
4
+ from typing import Optional
5
+
6
+ import tiktoken
7
+
8
+ from kotaemon.agents.base import BaseAgent, BaseLLM
9
+ from kotaemon.agents.io import AgentAction, AgentFinish, AgentOutput, AgentType
10
+ from kotaemon.agents.tools import BaseTool
11
+ from kotaemon.base import Document, Param
12
+ from kotaemon.indices.splitters import TokenSplitter
13
+ from kotaemon.llms import PromptTemplate
14
+
15
+ FINAL_ANSWER_ACTION = "Final Answer:"
16
+
17
+
18
+ class ReactAgent(BaseAgent):
19
+ """
20
+ Sequential ReactAgent class inherited from BaseAgent.
21
+ Implementing ReAct agent paradigm https://arxiv.org/pdf/2210.03629.pdf
22
+ """
23
+
24
+ name: str = "ReactAgent"
25
+ agent_type: AgentType = AgentType.react
26
+ description: str = "ReactAgent for answering multi-step reasoning questions"
27
+ llm: BaseLLM
28
+ prompt_template: Optional[PromptTemplate] = None
29
+ output_lang: str = "English"
30
+ plugins: list[BaseTool] = Param(
31
+ default_callback=lambda _: [], help="List of tools to be used in the agent. "
32
+ )
33
+ examples: dict[str, str | list[str]] = Param(
34
+ default_callback=lambda _: {}, help="Examples to be used in the agent. "
35
+ )
36
+ intermediate_steps: list[tuple[AgentAction | AgentFinish, str]] = Param(
37
+ default_callback=lambda _: [],
38
+ help="List of AgentAction and observation (tool) output",
39
+ )
40
+ max_iterations: int = 5
41
+ strict_decode: bool = False
42
+ max_context_length: int = Param(
43
+ default=3000,
44
+ help="Max context length for each tool output.",
45
+ )
46
+ trim_func: TokenSplitter | None = None
47
+
48
+ def _compose_plugin_description(self) -> str:
49
+ """
50
+ Compose the worker prompt from the workers.
51
+
52
+ Example:
53
+ toolname1[input]: tool1 description
54
+ toolname2[input]: tool2 description
55
+ """
56
+ prompt = ""
57
+ try:
58
+ for plugin in self.plugins:
59
+ prompt += f"{plugin.name}[input]: {plugin.description}\n"
60
+ except Exception:
61
+ raise ValueError("Worker must have a name and description.")
62
+ return prompt
63
+
64
+ def _construct_scratchpad(
65
+ self, intermediate_steps: list[tuple[AgentAction | AgentFinish, str]] = []
66
+ ) -> str:
67
+ """Construct the scratchpad that lets the agent continue its thought process."""
68
+ thoughts = ""
69
+ for action, observation in intermediate_steps:
70
+ thoughts += action.log
71
+ thoughts += f"\nObservation: {observation}\nThought:"
72
+ return thoughts
73
+
74
+ def _parse_output(self, text: str) -> Optional[AgentAction | AgentFinish]:
75
+ """
76
+ Parse text output from LLM for the next Action or Final Answer
77
+ Using Regex to parse "Action:\n Action Input:\n" for the next Action
78
+ Using FINAL_ANSWER_ACTION to parse Final Answer
79
+
80
+ Args:
81
+ text[str]: input text to parse
82
+ """
83
+ includes_answer = FINAL_ANSWER_ACTION in text
84
+ regex = (
85
+ r"Action\s*\d*\s*:[\s]*(.*?)[\s]*Action\s*\d*\s*Input\s*\d*\s*:[\s]*(.*)"
86
+ )
87
+ action_match = re.search(regex, text, re.DOTALL)
88
+ action_output: Optional[AgentAction | AgentFinish] = None
89
+ if action_match:
90
+ if includes_answer:
91
+ raise Exception(
92
+ "Parsing LLM output produced both a final answer "
93
+ f"and a parse-able action: {text}"
94
+ )
95
+ action = action_match.group(1).strip()
96
+ action_input = action_match.group(2)
97
+ tool_input = action_input.strip(" ")
98
+ # ensure if its a well formed SQL query we don't remove any trailing " chars
99
+ if tool_input.startswith("SELECT ") is False:
100
+ tool_input = tool_input.strip('"')
101
+
102
+ action_output = AgentAction(action, tool_input, text)
103
+
104
+ elif includes_answer:
105
+ action_output = AgentFinish(
106
+ {"output": text.split(FINAL_ANSWER_ACTION)[-1].strip()}, text
107
+ )
108
+ else:
109
+ if self.strict_decode:
110
+ raise Exception(f"Could not parse LLM output: `{text}`")
111
+ else:
112
+ action_output = AgentFinish({"output": text}, text)
113
+
114
+ return action_output
115
+
116
+ def _compose_prompt(self, instruction) -> str:
117
+ """
118
+ Compose the prompt from template, worker description, examples and instruction.
119
+ """
120
+ agent_scratchpad = self._construct_scratchpad(self.intermediate_steps)
121
+ tool_description = self._compose_plugin_description()
122
+ tool_names = ", ".join([plugin.name for plugin in self.plugins])
123
+ if self.prompt_template is None:
124
+ from .prompt import zero_shot_react_prompt
125
+
126
+ self.prompt_template = zero_shot_react_prompt
127
+ return self.prompt_template.populate(
128
+ instruction=instruction,
129
+ agent_scratchpad=agent_scratchpad,
130
+ tool_description=tool_description,
131
+ tool_names=tool_names,
132
+ lang=self.output_lang,
133
+ )
134
+
135
+ def _format_function_map(self) -> dict[str, BaseTool]:
136
+ """Format the function map for the open AI function API.
137
+
138
+ Return:
139
+ Dict[str, Callable]: The function map.
140
+ """
141
+ # Map the function name to the real function object.
142
+ function_map = {}
143
+ for plugin in self.plugins:
144
+ function_map[plugin.name] = plugin
145
+ return function_map
146
+
147
+ def _trim(self, text: str | Document) -> str:
148
+ """
149
+ Trim the text to the maximum token length.
150
+ """
151
+ evidence_trim_func = (
152
+ self.trim_func
153
+ if self.trim_func
154
+ else TokenSplitter(
155
+ chunk_size=self.max_context_length,
156
+ chunk_overlap=0,
157
+ separator=" ",
158
+ tokenizer=partial(
159
+ tiktoken.encoding_for_model("gpt-3.5-turbo").encode,
160
+ allowed_special=set(),
161
+ disallowed_special="all",
162
+ ),
163
+ )
164
+ )
165
+ if isinstance(text, str):
166
+ texts = evidence_trim_func([Document(text=text)])
167
+ elif isinstance(text, Document):
168
+ texts = evidence_trim_func([text])
169
+ else:
170
+ raise ValueError("Invalid text type to trim")
171
+ trim_text = texts[0].text
172
+ logging.info(f"len (trimmed): {len(trim_text)}")
173
+ return trim_text
174
+
175
+ def clear(self):
176
+ """
177
+ Clear and reset the agent.
178
+ """
179
+ self.intermediate_steps = []
180
+
181
+ def run(self, instruction, max_iterations=None) -> AgentOutput:
182
+ """
183
+ Run the agent with the given instruction.
184
+
185
+ Args:
186
+ instruction: Instruction to run the agent with.
187
+ max_iterations: Maximum number of iterations
188
+ of reasoning steps, defaults to 10.
189
+
190
+ Return:
191
+ AgentOutput object.
192
+ """
193
+ if not max_iterations:
194
+ max_iterations = self.max_iterations
195
+ assert max_iterations > 0
196
+
197
+ self.clear()
198
+ logging.info(f"Running {self.name} with instruction: {instruction}")
199
+ total_cost = 0.0
200
+ total_token = 0
201
+ status = "failed"
202
+ response_text = None
203
+
204
+ for step_count in range(1, max_iterations + 1):
205
+ prompt = self._compose_prompt(instruction)
206
+ logging.info(f"Prompt: {prompt}")
207
+ response = self.llm(
208
+ prompt, stop=["Observation:"]
209
+ ) # could cause bugs if llm doesn't have `stop` as a parameter
210
+ response_text = response.text
211
+ logging.info(f"Response: {response_text}")
212
+ action_step = self._parse_output(response_text)
213
+ if action_step is None:
214
+ raise ValueError("Invalid action")
215
+ is_finished_chain = isinstance(action_step, AgentFinish)
216
+ if is_finished_chain:
217
+ result = ""
218
+ else:
219
+ assert isinstance(action_step, AgentAction)
220
+ action_name = action_step.tool
221
+ tool_input = action_step.tool_input
222
+ logging.info(f"Action: {action_name}")
223
+ logging.info(f"Tool Input: {tool_input}")
224
+ function_map = self._format_function_map()
225
+ if action_name not in function_map:
226
+ available = ", ".join(function_map.keys())
227
+ result = (
228
+ f"Tool '{action_name}' not found. "
229
+ f"Available tools: {available}"
230
+ )
231
+ else:
232
+ result = function_map[action_name](tool_input)
233
+
234
+ # trim the worker output to 1000 tokens, as we are appending
235
+ # all workers' logs and it can exceed the token limit if we
236
+ # don't limit each. Fix this number regarding to the LLM capacity.
237
+ result = self._trim(result)
238
+ logging.info(f"Result: {result}")
239
+
240
+ self.intermediate_steps.append((action_step, result))
241
+ if is_finished_chain:
242
+ logging.info(f"Finished after {step_count} steps.")
243
+ status = "finished"
244
+ break
245
+ else:
246
+ status = "stopped"
247
+
248
+ return AgentOutput(
249
+ text=response_text,
250
+ agent_type=self.agent_type,
251
+ status=status,
252
+ total_tokens=total_token,
253
+ total_cost=total_cost,
254
+ intermediate_steps=self.intermediate_steps,
255
+ max_iterations=max_iterations,
256
+ )
257
+
258
+ def stream(self, instruction, max_iterations=None):
259
+ """
260
+ Stream the agent with the given instruction.
261
+
262
+ Args:
263
+ instruction: Instruction to run the agent with.
264
+ max_iterations: Maximum number of iterations
265
+ of reasoning steps, defaults to 10.
266
+
267
+ Return:
268
+ AgentOutput object.
269
+ """
270
+ if not max_iterations:
271
+ max_iterations = self.max_iterations
272
+ assert max_iterations > 0
273
+
274
+ self.clear()
275
+ logging.info(f"Running {self.name} with instruction: {instruction}")
276
+ print(f"Running {self.name} with instruction: {instruction}")
277
+ total_cost = 0.0
278
+ total_token = 0
279
+ status = "failed"
280
+ response_text = None
281
+
282
+ for step_count in range(1, max_iterations + 1):
283
+ prompt = self._compose_prompt(instruction)
284
+ logging.info(f"Prompt: {prompt}")
285
+ print(f"Prompt: {prompt}")
286
+ response = self.llm(
287
+ prompt, stop=["Observation:"]
288
+ ) # TODO: could cause bugs if llm doesn't have `stop` as a parameter
289
+ response_text = response.text
290
+ logging.info(f"Response: {response_text}")
291
+ print(f"Response: {response_text}")
292
+ action_step = self._parse_output(response_text)
293
+ if action_step is None:
294
+ raise ValueError("Invalid action")
295
+ is_finished_chain = isinstance(action_step, AgentFinish)
296
+ if is_finished_chain:
297
+ result = response_text
298
+ if "Final Answer:" in response_text:
299
+ result = response_text.split("Final Answer:")[-1].strip()
300
+ else:
301
+ assert isinstance(action_step, AgentAction)
302
+ action_name = action_step.tool
303
+ tool_input = action_step.tool_input
304
+ logging.info(f"Action: {action_name}")
305
+ print(f"Action: {action_name}")
306
+ logging.info(f"Tool Input: {tool_input}")
307
+ print(f"Tool Input: {tool_input}")
308
+ function_map = self._format_function_map()
309
+ if action_name not in function_map:
310
+ available = ", ".join(function_map.keys())
311
+ result = (
312
+ f"Tool '{action_name}' not found. "
313
+ f"Available tools: {available}"
314
+ )
315
+ else:
316
+ result = function_map[action_name](tool_input)
317
+
318
+ # trim the worker output to 1000 tokens, as we are appending
319
+ # all workers' logs and it can exceed the token limit if we
320
+ # don't limit each. Fix this number regarding to the LLM capacity.
321
+ result = self._trim(result)
322
+ logging.info(f"Result: {result}")
323
+ print(f"Result: {result}")
324
+
325
+ self.intermediate_steps.append((action_step, result))
326
+ if is_finished_chain:
327
+ logging.info(f"Finished after {step_count} steps.")
328
+ status = "finished"
329
+ yield AgentOutput(
330
+ text=result,
331
+ agent_type=self.agent_type,
332
+ status=status,
333
+ intermediate_steps=self.intermediate_steps[-1],
334
+ )
335
+ break
336
+ else:
337
+ yield AgentOutput(
338
+ text="",
339
+ agent_type=self.agent_type,
340
+ status="thinking",
341
+ intermediate_steps=self.intermediate_steps[-1],
342
+ )
343
+
344
+ else:
345
+ status = "stopped"
346
+ yield AgentOutput(
347
+ text="",
348
+ agent_type=self.agent_type,
349
+ status=status,
350
+ intermediate_steps=self.intermediate_steps[-1],
351
+ )
352
+
353
+ return AgentOutput(
354
+ text=response_text,
355
+ agent_type=self.agent_type,
356
+ status=status,
357
+ total_tokens=total_token,
358
+ total_cost=total_cost,
359
+ intermediate_steps=self.intermediate_steps,
360
+ max_iterations=max_iterations,
361
+ )
libs/kotaemon/kotaemon/agents/react/prompt.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # flake8: noqa
2
+
3
+ from kotaemon.llms import PromptTemplate
4
+
5
+ zero_shot_react_prompt = PromptTemplate(
6
+ template="""Answer the following questions as best you can. Give answer in {lang}. You have access to the following tools:
7
+ {tool_description}
8
+ Use the following format:
9
+
10
+ Question: the input question you must answer
11
+ Thought: you should always think about what to do
12
+
13
+ Action: the action to take, should be one of [{tool_names}]
14
+
15
+ Action Input: the input to the action, should be different from the action input of the same action in previous steps.
16
+
17
+ Observation: the result of the action
18
+
19
+ ... (this Thought/Action/Action Input/Observation can repeat N times)
20
+ #Thought: I now know the final answer
21
+ Final Answer: the final answer to the original input question
22
+
23
+ Begin! After each Action Input.
24
+
25
+ Question: {instruction}
26
+ Thought:{agent_scratchpad}
27
+ """
28
+ )
libs/kotaemon/kotaemon/agents/rewoo/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .agent import RewooAgent
2
+
3
+ __all__ = ["RewooAgent"]
libs/kotaemon/kotaemon/agents/rewoo/agent.py ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import re
3
+ from concurrent.futures import ThreadPoolExecutor
4
+ from functools import partial
5
+ from typing import Any
6
+
7
+ import tiktoken
8
+
9
+ from kotaemon.agents.base import BaseAgent
10
+ from kotaemon.agents.io import AgentOutput, AgentType, BaseScratchPad
11
+ from kotaemon.agents.tools import BaseTool
12
+ from kotaemon.agents.utils import get_plugin_response_content
13
+ from kotaemon.base import Document, Node, Param
14
+ from kotaemon.indices.qa.citation import CitationPipeline
15
+ from kotaemon.indices.splitters import TokenSplitter
16
+ from kotaemon.llms import BaseLLM, PromptTemplate
17
+
18
+ from .planner import Planner
19
+ from .solver import Solver
20
+
21
+
22
+ class RewooAgent(BaseAgent):
23
+ """Distributive RewooAgent class inherited from BaseAgent.
24
+ Implementing ReWOO paradigm https://arxiv.org/pdf/2305.18323.pdf"""
25
+
26
+ name: str = "RewooAgent"
27
+ agent_type: AgentType = AgentType.rewoo
28
+ description: str = "RewooAgent for answering multi-step reasoning questions"
29
+ output_lang: str = "English"
30
+ planner_llm: BaseLLM
31
+ solver_llm: BaseLLM
32
+ prompt_template: dict[str, PromptTemplate] = Param(
33
+ default_callback=lambda _: {},
34
+ help="A dict to supply different prompt to the agent.",
35
+ )
36
+ plugins: list[BaseTool] = Param(
37
+ default_callback=lambda _: [], help="A list of plugins to be used in the model."
38
+ )
39
+ examples: dict[str, str | list[str]] = Param(
40
+ default_callback=lambda _: {}, help="Examples to be used in the agent."
41
+ )
42
+ max_context_length: int = Param(
43
+ default=3000,
44
+ help="Max context length for each tool output.",
45
+ )
46
+ trim_func: TokenSplitter | None = None
47
+
48
+ @Node.auto(depends_on=["planner_llm", "plugins", "prompt_template", "examples"])
49
+ def planner(self):
50
+ return Planner(
51
+ model=self.planner_llm,
52
+ plugins=self.plugins,
53
+ prompt_template=self.prompt_template.get("Planner", None),
54
+ examples=self.examples.get("Planner", None),
55
+ )
56
+
57
+ @Node.auto(depends_on=["solver_llm", "prompt_template", "examples"])
58
+ def solver(self):
59
+ return Solver(
60
+ model=self.solver_llm,
61
+ prompt_template=self.prompt_template.get("Solver", None),
62
+ examples=self.examples.get("Solver", None),
63
+ output_lang=self.output_lang,
64
+ )
65
+
66
+ def _parse_plan_map(
67
+ self, planner_response: str
68
+ ) -> tuple[dict[str, list[str]], dict[str, str]]:
69
+ """
70
+ Parse planner output. It should be an n-to-n mapping from Plans to #Es.
71
+ This is because sometimes LLM cannot follow the strict output format.
72
+ Example:
73
+ #Plan1
74
+ #E1
75
+ #E2
76
+ should result in: {"#Plan1": ["#E1", "#E2"]}
77
+ Or:
78
+ #Plan1
79
+ #Plan2
80
+ #E1
81
+ should result in: {"#Plan1": [], "#Plan2": ["#E1"]}
82
+ This function should also return a plan map.
83
+
84
+ Returns:
85
+ tuple[Dict[str, List[str]], Dict[str, str]]: A list of plan map
86
+ """
87
+ valid_chunk = [
88
+ line
89
+ for line in planner_response.splitlines()
90
+ if line.startswith("#Plan") or line.startswith("#E")
91
+ ]
92
+
93
+ plan_to_es: dict[str, list[str]] = dict()
94
+ plans: dict[str, str] = dict()
95
+ prev_key = ""
96
+ for line in valid_chunk:
97
+ key, description = line.split(":", 1)
98
+ key = key.strip()
99
+ if key.startswith("#Plan"):
100
+ plans[key] = description.strip()
101
+ plan_to_es[key] = []
102
+ prev_key = key
103
+ elif key.startswith("#E"):
104
+ plan_to_es[prev_key].append(key)
105
+
106
+ return plan_to_es, plans
107
+
108
+ def _parse_planner_evidences(
109
+ self, planner_response: str
110
+ ) -> tuple[dict[str, str], list[list[str]]]:
111
+ """
112
+ Parse planner output. This should return a mapping from #E to tool call.
113
+ It should also identify the level of each #E in dependency map.
114
+ Example:
115
+ {
116
+ "#E1": "Tool1", "#E2": "Tool2",
117
+ "#E3": "Tool3", "#E4": "Tool4"
118
+ }, [[#E1, #E2], [#E3, #E4]]
119
+
120
+ Returns:
121
+ tuple[dict[str, str], List[List[str]]]:
122
+ A mapping from #E to tool call and a list of levels.
123
+ """
124
+ evidences: dict[str, str] = dict()
125
+ dependence: dict[str, list[str]] = dict()
126
+ for line in planner_response.splitlines():
127
+ if line.startswith("#E") and line[2].isdigit():
128
+ e, tool_call = line.split(":", 1)
129
+ e, tool_call = e.strip(), tool_call.strip()
130
+ if len(e) == 3:
131
+ dependence[e] = []
132
+ evidences[e] = tool_call
133
+ for var in re.findall(r"#E\d+", tool_call):
134
+ if var in evidences:
135
+ dependence[e].append(var)
136
+ else:
137
+ evidences[e] = "No evidence found"
138
+ level = []
139
+ while dependence:
140
+ select = [i for i in dependence if not dependence[i]]
141
+ if len(select) == 0:
142
+ raise ValueError("Circular dependency detected.")
143
+ level.append(select)
144
+ for item in select:
145
+ dependence.pop(item)
146
+ for item in dependence:
147
+ for i in select:
148
+ if i in dependence[item]:
149
+ dependence[item].remove(i)
150
+
151
+ return evidences, level
152
+
153
+ def _run_plugin(
154
+ self,
155
+ e: str,
156
+ planner_evidences: dict[str, str],
157
+ worker_evidences: dict[str, str],
158
+ output=BaseScratchPad(),
159
+ ):
160
+ """
161
+ Run a plugin for a given evidence.
162
+ This function should also cumulate the cost and tokens.
163
+ """
164
+ result = dict(e=e, plugin_cost=0, plugin_token=0, evidence="")
165
+ tool_call = planner_evidences[e]
166
+ if "[" not in tool_call:
167
+ result["evidence"] = tool_call
168
+ else:
169
+ tool, tool_input = tool_call.split("[", 1)
170
+ tool_input = tool_input[:-1]
171
+ # find variables in input and replace with previous evidences
172
+ for var in re.findall(r"#E\d+", tool_input):
173
+ print("Tool input: ", tool_input)
174
+ print("Var: ", var)
175
+ print("Worker evidences: ", worker_evidences)
176
+ if var in worker_evidences:
177
+ tool_input = tool_input.replace(
178
+ var, worker_evidences.get(var, "") or ""
179
+ )
180
+ try:
181
+ selected_plugin = self._find_plugin(tool)
182
+ if selected_plugin is None:
183
+ raise ValueError("Invalid plugin detected")
184
+ tool_response = selected_plugin(tool_input)
185
+ result["evidence"] = get_plugin_response_content(tool_response)
186
+ except ValueError:
187
+ result["evidence"] = "No evidence found."
188
+ finally:
189
+ output.panel_print(
190
+ result["evidence"], f"[green] Function Response of [blue]{tool}: "
191
+ )
192
+ return result
193
+
194
+ def _get_worker_evidence(
195
+ self,
196
+ planner_evidences: dict[str, str],
197
+ evidences_level: list[list[str]],
198
+ output=BaseScratchPad(),
199
+ ) -> Any:
200
+ """
201
+ Parallel execution of plugins in DAG for speedup.
202
+ This is one of core benefits of ReWOO agents.
203
+
204
+ Args:
205
+ planner_evidences: A mapping from #E to tool call.
206
+ evidences_level: A list of levels of evidences.
207
+ Calculated from DAG of plugin calls.
208
+ output: Output object, defaults to BaseOutput().
209
+ Returns:
210
+ A mapping from #E to tool call.
211
+ """
212
+ worker_evidences: dict[str, str] = dict()
213
+ plugin_cost, plugin_token = 0.0, 0.0
214
+ with ThreadPoolExecutor() as pool:
215
+ for level in evidences_level:
216
+ results = []
217
+ for e in level:
218
+ results.append(
219
+ pool.submit(
220
+ self._run_plugin,
221
+ e,
222
+ planner_evidences,
223
+ worker_evidences,
224
+ output,
225
+ )
226
+ )
227
+ if len(results) > 1:
228
+ output.update_status(f"Running tasks {level} in parallel.")
229
+ else:
230
+ output.update_status(f"Running task {level[0]}.")
231
+ for r in results:
232
+ resp = r.result()
233
+ plugin_cost += resp["plugin_cost"]
234
+ plugin_token += resp["plugin_token"]
235
+ worker_evidences[resp["e"]] = self._trim_evidence(resp["evidence"])
236
+ output.done()
237
+
238
+ return worker_evidences, plugin_cost, plugin_token
239
+
240
+ def _find_plugin(self, name: str):
241
+ for p in self.plugins:
242
+ if p.name == name:
243
+ return p
244
+
245
+ def _trim_evidence(self, evidence: str):
246
+ evidence_trim_func = (
247
+ self.trim_func
248
+ if self.trim_func
249
+ else TokenSplitter(
250
+ chunk_size=self.max_context_length,
251
+ chunk_overlap=0,
252
+ separator=" ",
253
+ tokenizer=partial(
254
+ tiktoken.encoding_for_model("gpt-3.5-turbo").encode,
255
+ allowed_special=set(),
256
+ disallowed_special="all",
257
+ ),
258
+ )
259
+ )
260
+ if evidence:
261
+ texts = evidence_trim_func([Document(text=evidence)])
262
+ evidence = texts[0].text
263
+ logging.info(f"len (trimmed): {len(evidence)}")
264
+ return evidence
265
+
266
+ @BaseAgent.safeguard_run
267
+ def run(self, instruction: str, use_citation: bool = False) -> AgentOutput:
268
+ """
269
+ Run the agent with a given instruction.
270
+ """
271
+ logging.info(f"Running {self.name} with instruction: {instruction}")
272
+ total_cost = 0.0
273
+ total_token = 0
274
+
275
+ # Plan
276
+ planner_output = self.planner(instruction)
277
+ planner_text_output = planner_output.text
278
+ plan_to_es, plans = self._parse_plan_map(planner_text_output)
279
+ planner_evidences, evidence_level = self._parse_planner_evidences(
280
+ planner_text_output
281
+ )
282
+
283
+ # Work
284
+ worker_evidences, plugin_cost, plugin_token = self._get_worker_evidence(
285
+ planner_evidences, evidence_level
286
+ )
287
+ worker_log = ""
288
+ for plan in plan_to_es:
289
+ worker_log += f"{plan}: {plans[plan]}\n"
290
+ for e in plan_to_es[plan]:
291
+ worker_log += f"{e}: {worker_evidences[e]}\n"
292
+
293
+ # Solve
294
+ solver_output = self.solver(instruction, worker_log)
295
+ solver_output_text = solver_output.text
296
+ if use_citation:
297
+ citation_pipeline = CitationPipeline(llm=self.solver_llm)
298
+ citation = citation_pipeline(context=worker_log, question=instruction)
299
+ else:
300
+ citation = None
301
+
302
+ return AgentOutput(
303
+ text=solver_output_text,
304
+ agent_type=self.agent_type,
305
+ status="finished",
306
+ total_tokens=total_token,
307
+ total_cost=total_cost,
308
+ citation=citation,
309
+ metadata={"citation": citation, "worker_log": worker_log},
310
+ )
311
+
312
+ def stream(self, instruction: str, use_citation: bool = False):
313
+ """
314
+ Stream the agent with a given instruction.
315
+ """
316
+ logging.info(f"Streaming {self.name} with instruction: {instruction}")
317
+ total_cost = 0.0
318
+ total_token = 0
319
+
320
+ # Plan
321
+ planner_output = self.planner(instruction)
322
+ planner_text_output = planner_output.text
323
+ plan_to_es, plans = self._parse_plan_map(planner_text_output)
324
+ planner_evidences, evidence_level = self._parse_planner_evidences(
325
+ planner_text_output
326
+ )
327
+
328
+ print("Planner output:", planner_text_output)
329
+ # output planner to info panel
330
+ yield AgentOutput(
331
+ text="",
332
+ agent_type=self.agent_type,
333
+ status="thinking",
334
+ intermediate_steps=[{"planner_log": planner_text_output}],
335
+ )
336
+
337
+ # Work
338
+ worker_evidences, plugin_cost, plugin_token = self._get_worker_evidence(
339
+ planner_evidences, evidence_level
340
+ )
341
+ worker_log = ""
342
+ for plan in plan_to_es:
343
+ worker_log += f"{plan}: {plans[plan]}\n"
344
+ current_progress = f"{plan}: {plans[plan]}\n"
345
+ for e in plan_to_es[plan]:
346
+ worker_log += f"#Action: {planner_evidences.get(e, None)}\n"
347
+ worker_log += f"{e}: {worker_evidences[e]}\n"
348
+ current_progress += f"#Action: {planner_evidences.get(e, None)}\n"
349
+ current_progress += f"{e}: {worker_evidences[e]}\n"
350
+
351
+ yield AgentOutput(
352
+ text="",
353
+ agent_type=self.agent_type,
354
+ status="thinking",
355
+ intermediate_steps=[{"worker_log": current_progress}],
356
+ )
357
+
358
+ # Solve
359
+ solver_response = ""
360
+ for solver_output in self.solver.stream(instruction, worker_log):
361
+ solver_output_text = solver_output.text
362
+ solver_response += solver_output_text
363
+ yield AgentOutput(
364
+ text=solver_output_text,
365
+ agent_type=self.agent_type,
366
+ status="thinking",
367
+ )
368
+ if use_citation:
369
+ citation_pipeline = CitationPipeline(llm=self.solver_llm)
370
+ citation = citation_pipeline.invoke(
371
+ context=worker_log, question=instruction
372
+ )
373
+ else:
374
+ citation = None
375
+
376
+ return AgentOutput(
377
+ text="",
378
+ agent_type=self.agent_type,
379
+ status="finished",
380
+ total_tokens=total_token,
381
+ total_cost=total_cost,
382
+ citation=citation,
383
+ metadata={"citation": citation, "worker_log": worker_log},
384
+ )