diff --git a/.github/workflows/pyright-type-check.yml b/.github/workflows/pyright-type-check.yml new file mode 100644 index 0000000..d663f0c --- /dev/null +++ b/.github/workflows/pyright-type-check.yml @@ -0,0 +1,30 @@ +name: Pyright Type Check + +on: + pull_request: + branches: ["*"] + push: + branches: ["*"] + +jobs: + pyright: + name: Pyright Type Check + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version-file: '.python-version' + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + + - name: Install dependencies (locked) + run: uv sync --frozen + + - name: Run Pyright + run: uv run pyright \ No newline at end of file diff --git a/.github/workflows/pytest-testcases-check.yml b/.github/workflows/pytest-testcases-check.yml new file mode 100644 index 0000000..a147521 --- /dev/null +++ b/.github/workflows/pytest-testcases-check.yml @@ -0,0 +1,28 @@ +name: Pytest Testcases Check + +on: + pull_request: + branches: ["*"] # run on PRs to any branch + push: + branches: [main, dev, testing, wip] # optional; expand to ["*"] if you want all pushes + +jobs: + pytest-testcases: + name: Pytest Testcases Check + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version-file: '.python-version' + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + + # Format check only — fails if files are not formatted + - name: Run test cases using Pytest + run: uv run pytest tests \ No newline at end of file diff --git a/.github/workflows/ruff-format-check.yml b/.github/workflows/ruff-format-check.yml new file mode 100644 index 0000000..480ab3a --- /dev/null +++ b/.github/workflows/ruff-format-check.yml @@ -0,0 +1,28 @@ +name: Ruff Format Check + +on: + pull_request: + branches: ["*"] # run on PRs to any branch + push: + branches: ["*"] # optional; expand to ["*"] if you want all pushes + +jobs: + ruff-format: + name: Ruff Format Check + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version-file: '.python-version' + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + + # Format check only — fails if files are not formatted + - name: Ruff format check + run: uvx ruff format --check . \ No newline at end of file diff --git a/.github/workflows/ruff-lint-check.yml b/.github/workflows/ruff-lint-check.yml new file mode 100644 index 0000000..b4fb1cd --- /dev/null +++ b/.github/workflows/ruff-lint-check.yml @@ -0,0 +1,28 @@ +name: Ruff Lint Check + +on: + pull_request: + branches: ["*"] # run on PRs to any branch + push: + branches: ["*"] # optional; broaden to ["*"] if desired + +jobs: + ruff: + name: Ruff Lint & Format Check + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version-file: '.python-version' + + - name: Set up uv + uses: astral-sh/setup-uv@v6 + + # Lint: exits non-zero on violations -> job fails + - name: Ruff lint + run: uvx ruff check --output-format=github . diff --git a/.github/workflows/uv-env-check.yml b/.github/workflows/uv-env-check.yml index 5e6c4b1..5fd81da 100644 --- a/.github/workflows/uv-env-check.yml +++ b/.github/workflows/uv-env-check.yml @@ -1,10 +1,10 @@ -name: Environment Check (uv sync) +name: UV Environment Check on: pull_request: - branches: [wip, dev, testing, main] + branches: ["*"] push: - branches: [wip, dev, testing, main] + branches: ["*"] jobs: checks: @@ -15,16 +15,14 @@ jobs: uses: actions/checkout@v4 - name: Setup uv (with cache) - uses: astral-sh/setup-uv@v3 + uses: astral-sh/setup-uv@v6 with: - enable-cache: true + version: "0.8.15" - # If you commit .python-version, this ensures the exact interpreter is available - - name: Pin Python - run: | - uv python install 3.12.10 - uv python pin 3.12.10 - uv run python -c "import sys; print(sys.version)" + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version-file: '.python-version' # The authoritative guard: MUST match uv.lock and Python markers - name: Sync (frozen) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3688650..2d55770 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -74,7 +74,7 @@ For more help, check the [uv documentation](https://docs.astral.sh/uv/) - Located in `.github/workflows/uv-env-check.yml` -- This GitHub actions check runs to check whether there are any conflicts between the lockfile and pyproject.toml. If it fails +- This GitHub actions check runs to check whether there are any conflicts between the lockfile and pyproject.toml. If it fails, then there has been some dependency update to the pyproject.toml without updating the lockfile ### Type check for Python diff --git a/README.md b/README.md index e69de29..08ef0b6 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,26 @@ +# BYK-RAG (Retrieval-Augmented Generation Module) + +The **BYK-RAG Module** is part of the Burokratt ecosystem, designed to provide **retrieval-augmented generation (RAG)** capabilities for Estonian government digital services. It ensures reliable, multilingual, and compliant AI-powered responses by integrating with multiple LLM providers, syncing with knowledge bases, and exposing flexible configuration and monitoring features for administrators. + +--- + +## Features + +- **Configurable LLM Providers** + - Support for AWS Bedrock, Azure AI, Google Cloud, OpenAI, Anthropic, and self-hosted open-source LLMs. + - Admins can create "connections" and switch providers/models without downtime. + - Models searchable via dropdown with cache-enabled indicators. + +- **Knowledge Base Integration** + - Continuous sync with central knowledge base (CKB). + - Last sync timestamp displayed in UI. + - LLMs restricted to answering only from CKB content. + - “I don’t know” payload returned when confidence is low. + +- **Citations & Transparency** + - All responses are accompanied with **clear citations**. + +- **Analytics & Monitoring** + - External **Langfuse dashboard** for API usage, inference trends, cost analysis, and performance logs. + - Agencies can configure cost alerts and view alerts via LLM Alerts UI. + - Logs integrated with **Grafana Loki**. diff --git a/pyproject.toml b/pyproject.toml index 9c334f4..d0a97b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,4 +4,46 @@ version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = "==3.12.10" -dependencies = [] +dependencies = [ + "pyright>=1.1.404", + "pytest>=8.4.1", +] + +[tool.pyright] +# --- Environment & discovery --- +pythonVersion = "3.12.10" # Target Python semantics (pattern matching, typing features, stdlib types). +venvPath = "." # Where virtual envs live relative to repo root. +venv = ".venv" # The specific env name uv manages (uv sync creates .venv). + +# --- What to analyze --- +include = ["src", "tests"] # Top-level packages & tests to check. +exclude = [ + "**/.venv", "**/__pycache__", "build", "dist", ".git", + ".ruff_cache", ".mypy_cache" +] + +# --- Global strictness --- +typeCheckingMode = "strict" # Enforce full strict mode repo-wide (see notes below). +useLibraryCodeForTypes = true # If a lib lacks stubs, inspect its code to infer types where possible. + +# Make the most common "loose" mistakes fail fast in strict mode. +# You can tune these individually if you need a temporary carve-out. +reportMissingTypeStubs = "error" # Untyped third-party libs must have type info (stubs or inline). +reportUnknownVariableType = "error" # Vars with unknown/implicit Any are not allowed. +reportUnknownMemberType = "error" # Members on unknowns are not allowed. +reportUnknownArgumentType = "error" # Call arguments can't be unknown. +reportUnknownLambdaType = "error" # Lambda params must be typed in strict contexts. +reportImplicitOptional = "error" # T | None must be explicit; no silent Optional. +reportMissingTypeArgument = "error" # Generic types must specify their parameters. +reportIncompatibleVariableOverride = "error" # Subclass fields must type-refine correctly. +reportInvalidTypeVarUse = "error" # Catch misuse of TypeVar/variance. +reportUntypedFunctionDecorator = "error" # Decorators must be typed (prevents Any leakage). +reportUnusedVariable = "error" # Ditto; promote to "error" if you want hard hygiene. +reportUnusedImport = "warning" # Hygiene: warn, but don’t fail builds. + + +# Tests often deserialize lots of data and patch frameworks; keep them strict, +# but relax "missing stubs" so untyped test-only libs don’t block you. +[[tool.pyright.overrides]] +module = "tests/**" +reportMissingTypeStubs = "warning" diff --git a/main.py b/src/main.py similarity index 92% rename from main.py rename to src/main.py index fc01c13..599a6db 100644 --- a/main.py +++ b/src/main.py @@ -1,4 +1,5 @@ def main(): + "" print("Hello from rag-module!") diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..893b75c --- /dev/null +++ b/tests/README.md @@ -0,0 +1 @@ +This folder contains all the testcases for the Python code \ No newline at end of file diff --git a/tests/test_dummy.py b/tests/test_dummy.py new file mode 100644 index 0000000..2cdad08 --- /dev/null +++ b/tests/test_dummy.py @@ -0,0 +1,6 @@ +# tests/test_smoke.py +def test_smoke(): + """ + This is a dummy to test ensure the CI check works before actual testcases are written + """ + assert 1 + 1 == 2 diff --git a/uv.lock b/uv.lock index 9279ef4..e30503d 100644 --- a/uv.lock +++ b/uv.lock @@ -2,7 +2,109 @@ version = 1 revision = 2 requires-python = "==3.12.10" +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyright" +version = "1.1.404" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e2/6e/026be64c43af681d5632722acd100b06d3d39f383ec382ff50a71a6d5bce/pyright-1.1.404.tar.gz", hash = "sha256:455e881a558ca6be9ecca0b30ce08aa78343ecc031d37a198ffa9a7a1abeb63e", size = 4065679, upload-time = "2025-08-20T18:46:14.029Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/30/89aa7f7d7a875bbb9a577d4b1dc5a3e404e3d2ae2657354808e905e358e0/pyright-1.1.404-py3-none-any.whl", hash = "sha256:c7b7ff1fdb7219c643079e4c3e7d4125f0dafcc19d253b47e898d130ea426419", size = 5902951, upload-time = "2025-08-20T18:46:12.096Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/08/ba/45911d754e8eba3d5a841a5ce61a65a685ff1798421ac054f85aa8747dfb/pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c", size = 1517714, upload-time = "2025-06-18T05:48:06.109Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/16/c8a903f4c4dffe7a12843191437d7cd8e32751d5de349d45d3fe69544e87/pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", size = 365474, upload-time = "2025-06-18T05:48:03.955Z" }, +] + [[package]] name = "rag-module" version = "0.1.0" source = { virtual = "." } +dependencies = [ + { name = "pyright" }, + { name = "pytest" }, +] + +[package.metadata] +requires-dist = [ + { name = "pyright", specifier = ">=1.1.404" }, + { name = "pytest", specifier = ">=8.4.1" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +]