Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
69a5087
chore(release): finalize v0.5.0
fvadicamo Apr 25, 2026
254f7ae
docs(changelog): adopt always-on [Unreleased] per Keep a Changelog 1.1.0
fvadicamo Apr 25, 2026
64a9b04
docs(v0.5.0): align reference and READMEs with widget + namespace config
fvadicamo Apr 25, 2026
43315c0
docs(v0.5.0): fix widget URL and clarify nullability
fvadicamo Apr 25, 2026
d52c625
docs(v0.5.0): close remaining audit gaps before release
fvadicamo Apr 26, 2026
be7d9e7
docs(learn): fix endpoint scope table in README
fvadicamo Apr 26, 2026
6347ccd
docs(v0.5.0): close LOW residual + fix architecture/index.md drift
fvadicamo Apr 26, 2026
d816ae0
docs(v0.5.0): correct architecture index and resolver signatures
fvadicamo Apr 26, 2026
66c1fdf
docs(v0.5.0): close remaining drift in component READMEs and indexes
fvadicamo Apr 26, 2026
9bdc266
docs(v0.5.0): align EventEmitter and analytics endpoint refs with code
fvadicamo Apr 26, 2026
c9af8d0
docs(v0.5.0): tighten analytics and ingest README claims
fvadicamo Apr 26, 2026
0985546
docs(vektra-shared): correct startup helpers description
fvadicamo Apr 26, 2026
05f57fb
docs(branding): adopt "Vektra RAG" as user-facing product name
fvadicamo Apr 26, 2026
0300086
docs(branding): align bug report and gemini styleguide to "Vektra RAG"
fvadicamo Apr 26, 2026
c6e075d
feat(v050): widget label "VektraLabs" + consolidated integration guide
fvadicamo Apr 26, 2026
e1667ab
fix(docs): correct token endpoint signature in widget guide + JSDoc
fvadicamo Apr 26, 2026
3533fa6
fix(docs): escape HTML interpolation in FastAPI widget example
fvadicamo Apr 26, 2026
6c2e1a7
docs(guides): fix anchor links to FEAT-suffixed section IDs
fvadicamo Apr 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@
# Example: VEKTRA_LLM_API_BASE=http://localhost:8000/v1
# VEKTRA_LLM_API_BASE=

# Extra JSON body forwarded to litellm. Useful for vLLM thinking models
# (Qwen3.5, DeepSeek-R1) to disable thinking mode.
# Example: VEKTRA_LLM_EXTRA_BODY={"chat_template_kwargs": {"enable_thinking": false}}
# VEKTRA_LLM_EXTRA_BODY=

# Or set provider-specific keys directly:
# OPENAI_API_KEY=sk-...
# ANTHROPIC_API_KEY=sk-ant-...
Expand Down Expand Up @@ -83,6 +88,12 @@
# VEKTRA_CONTEXT_CHUNK_RATIO=0.6
# VEKTRA_PROMPT_TEMPLATES_DIR=

# Default RAG grounding policy. Per-namespace override via
# PATCH /api/v1/admin/namespaces/{id}/config.
# strict - answer from retrieved context + history only
# hybrid - fall back to model knowledge when confident
# VEKTRA_PROMPT_GROUNDING_MODE=strict

# Query rewriting (AdvancedQueryPipeline)
# VEKTRA_QUERY_REWRITE_ENABLED=true
# VEKTRA_QUERY_REWRITE_MODEL=
Expand Down Expand Up @@ -141,6 +152,14 @@
# VEKTRA_AUDIT_RETENTION_DAYS=90
# VEKTRA_EVAL_MODE=false

# Persist QueryTrace rows for every query (DEBT-011). When unset, defaults
# to true if vektra-analytics is configured, false otherwise.
# VEKTRA_ANALYTICS_STORE_TRACES=

# Log original and rewritten query text at debug level. Development only;
# query text is otherwise redacted from logs.
# VEKTRA_DEBUG_LOG_QUERIES=false

# --------------------------------------------------------------------------
# Security
# --------------------------------------------------------------------------
Expand Down Expand Up @@ -174,3 +193,10 @@
# Set to false when an external LMS (e.g. Moodle) manages enrollment.
# When false, namespace is derived from JWT (namespace claim or course_id fallback).
# VEKTRA_LEARN_REQUIRE_ENROLLMENT=true

# Default visibility of the source-citations section in the chatbot widget
# (FEAT-014). Resolution chain: data-show-sources attr (client) >
# namespaces.config.show_sources (per-course) > this env var > hardcoded true.
# The API always returns the full sources list; this flag only instructs the
# widget whether to render them.
# VEKTRA_LEARN_SHOW_SOURCES=true
2 changes: 1 addition & 1 deletion .gemini/styleguide.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Vektra coding conventions for Gemini Code Assist
# Vektra RAG coding conventions for Gemini Code Assist

## General

Expand Down
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: Bug report
description: Report a bug in Vektra
description: Report a bug in Vektra RAG
labels: ["bug"]
body:
- type: markdown
Expand Down
2 changes: 1 addition & 1 deletion .s2s/CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ See [architecture.md](architecture.md) for complete architecture documentation.
- ChunkingStrategy: pluggable chunking. FixedSizeChunking (Phase 1), DualStrategyChunking with semantic splitting (Phase 2)
- QueryPipeline: RAG pipeline abstraction returning QueryResponse + QueryTrace. Phase 2: AdvancedQueryPipeline with query rewriting (ARCH-061), reranking, hybrid search (implemented)
- SafeguardHook: pre/post query safeguards (3 trust boundary points) with content modification support (ARCH-049)
- EventEmitter: internal event hooks. NoOpEventEmitter (Phase 1), LogEventEmitter (Phase 2)
- EventEmitter: internal event hooks. NoOpEventEmitter (Phase 1), WebhookEventEmitter (Phase 2: HMAC-SHA256 signed HTTP POST)

**Key decisions** (64 total, 25 ADRs):
- [ADR-0003](decisions/ADR-0003-modular-monolith-phase1.md): Modular monolith for Phase 1
Expand Down
10 changes: 5 additions & 5 deletions .s2s/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ See [section 8.4](#84-deployment) for Docker Compose specification and resource
- **ARCH-035 - EmbeddingProvider Protocol**: Dedicated Protocol for embedding generation. Separates embed_documents() from embed_query() for asymmetric models. Single shared instance between ingest and core. Configurable provider and model via env vars.
- **ARCH-036 - QueryPipeline Protocol**: Abstraction of RAG query pipeline. execute() returns (QueryResponse, QueryTrace). Phase 1: SimpleQueryPipeline (embed -> search -> relevance_filter -> build_prompt -> LLM, with graceful degradation). relevance_filter applies minimum score threshold and overlap deduplication (ARCH-056). build_prompt calculates token budget from model context window (ARCH-055), renders composable Jinja2 templates (ARCH-054). Phase 2: AdvancedQueryPipeline adds query_rewrite (ARCH-061), query_classify, rerank, confidence scoring steps. Selectable via VEKTRA_QUERY_PIPELINE.
- **ARCH-037 - ChunkingStrategy Protocol**: Abstraction of text chunking. Receives DocumentChunk stream, returns chunked stream. Phase 1: FixedSizeChunking. Selectable via VEKTRA_CHUNKING_STRATEGY.
- **ARCH-038 - EventEmitter interface**: Internal event hooks with NoOp default. Emission points at document lifecycle, query completion, safeguard triggers, API key operations. Phase 2 (implemented): LogEventEmitter. WebhookEventEmitter with HMAC-SHA256 deferred to Phase 3.
- **ARCH-038 - EventEmitter interface**: Internal event hooks with NoOp default. Emission points at document lifecycle, query completion, safeguard triggers, API key operations. Phase 2 (implemented): WebhookEventEmitter (HMAC-SHA256 signed HTTP POST, activated via `VEKTRA_WEBHOOK_URL`). LogEventEmitter (structlog-based) was scoped during planning but never implemented; the webhook path subsumed the use case.
- **ARCH-039 - ProviderRegistry pattern**: Unified registry for all Protocol implementations. Dict-based in Phase 1, extensible to entry_points plugin discovery in Phase 2. Consistent VEKTRA_* env var configuration pattern.
- **ARCH-040 - Forward-compatible data model**: Phase 1 schema includes fields for Phase 2 features: response_id and citation_id for feedback loops, document version and supersedes_id for versioning, deleted_at and deletion_reason for soft delete, index_version for zero-downtime reindex. All fields nullable/defaulted, no behavioral change in Phase 1.
- **ARCH-041 - Audit/analytics separation**: QueryTrace (per-step timing, chunk refs, model info) emitted separately from audit log. QueryTrace does not contain query text or response content (REQ-051 compliance). Phase 1: structlog emission. Phase 2 (implemented): dedicated query_traces table with reporting API (vektra-analytics).
Expand Down Expand Up @@ -704,7 +704,7 @@ class EventEmitter(Protocol):
async def emit(event_type: str, payload: dict) -> None
```

Phase 1: NoOpEventEmitter. Emission points: document.indexed, document.failed, query.completed, safeguard.triggered, apikey.created, apikey.revoked. Phase 2 (implemented): LogEventEmitter (structlog-based event logging). WebhookEventEmitter with HMAC-SHA256 deferred to Phase 3.
Phase 1: NoOpEventEmitter. Emission points: document.indexed, document.failed, query.completed, safeguard.triggered, apikey.created, apikey.revoked. Phase 2 (implemented): WebhookEventEmitter — HMAC-SHA256 signed HTTP POST with configurable URL, secret, and timeout (`VEKTRA_WEBHOOK_URL`, `VEKTRA_WEBHOOK_SECRET`, `VEKTRA_WEBHOOK_TIMEOUT`). LogEventEmitter was originally scoped for Phase 2 but never built; structlog already emits the same events at module boundaries, so the webhook path was prioritized instead.

### 8.3.1 Extended types

Expand Down Expand Up @@ -1989,7 +1989,7 @@ Quality scenarios (QS-xx) define measurable targets. Validation scenarios ([vali
| TD-05 | No OAuth/OIDC | API keys sufficient for Phase 1 | Add for vektra-learn in Phase 2 |
| TD-06 | Confidence scoring undefined | Algorithm needs research spike, confidence_tier field exists in QueryResponse | OQ-014, Phase 2 spike, field ready (ARCH-040) |
| TD-07 | Dense-only vector search | Hybrid search Protocol ready (SearchMode enum), implementation deferred | REQ-050, swap PgvectorProvider or add QdrantVectorStoreProvider in Phase 2 (ARCH-051) |
| TD-08 | NoOp event emission | EventEmitter hooks in place, no handler | REQ-061, swap to WebhookEventEmitter in Phase 2 |
| TD-08 | NoOp event emission | EventEmitter hooks in place, no handler | REQ-061, WebhookEventEmitter implemented in Phase 2 |
| TD-09 | Passthrough safeguards | SafeguardHook hooks in place, no enforcement | REQ-044, swap to Presidio-based in Phase 2 |
| TD-10 | No sparse embeddings | SparseEmbeddingProvider Protocol defined, not registered | ARCH-053, register FastEmbedBM25Provider or SPLADEProvider in Phase 2 |

Expand Down Expand Up @@ -2133,7 +2133,7 @@ Quality scenarios (QS-xx) define measurable targets. Validation scenarios ([vali
| REQ-058 Content type detection | ARCH-042 Magic bytes | Reliable dispatch, mismatch warnings |
| REQ-059 LLM graceful degradation | ARCH-043 Graceful degradation, ARCH-056 Retrieval quality | Fallback model, context-only response, no-relevant-context path |
| REQ-060 QueryTrace | ARCH-041 Audit/analytics separation | Per-step RAG tracing, GDPR-safe |
| REQ-061 EventEmitter | ARCH-038 EventEmitter interface | NoOp Phase 1, LogEventEmitter Phase 2 |
| REQ-061 EventEmitter | ARCH-038 EventEmitter interface | NoOp Phase 1, WebhookEventEmitter Phase 2 |
| REQ-062 ProviderRegistry | ARCH-039 ProviderRegistry pattern | Unified provider configuration |
| REQ-063 Metadata filtering | ARCH-044 Chunk metadata filtering | JSONB + GIN index, SearchFilters in search() |
| REQ-064 Zero-downtime reindex | ARCH-045 Index version | Atomic version switch, no downtime |
Expand Down Expand Up @@ -2218,4 +2218,4 @@ Quality scenarios (QS-xx) define measurable targets. Validation scenarios ([vali
*Version 1.9 - Quality scenarios: Section 10 expanded from 12 to 18 QS entries, quality tree restructured, 6 architecture-derived scenarios added (ARCH-043 degradation, ARCH-057 startup, ARCH-039 extensibility, ARCH-040 evolvability, NFR-008 retention, NFR-010 progress), validation scenario cross-reference (10.4)*
*Version 1.9.1 - Conversational query rewriting: ARCH-061 (pre-retrieval query rewriting in AdvancedQueryPipeline), ADR-0023, rewrite.j2 template added to ARCH-054, ARCH-036 Phase 2 updated. Multilingual embedding note added to ADR-0013.*
*Version 1.10 - OQ-018/OQ-019 resolution: ARCH-062 (admin UI server-side rendering, ADR-0024), ARCH-063 (learn chatbot widget, ADR-0025), ARCH-064 (Phase 2 hardware target 8GB/4CPU). Glossary: HTMX added. Deferred table: 3 entries added.*
*Version 2.0 - Phase 2 delivery annotations: all Phase 2 features marked as implemented (v0.2.0). Component table expanded with vektra-analytics and vektra-learn. Protocol implementations updated: QdrantVectorStoreProvider, UnstructuredExtractor, DualStrategyChunking, AdvancedQueryPipeline, FastEmbedBM25Provider, LogEventEmitter. WebhookEventEmitter deferred to Phase 3.*
*Version 2.0 - Phase 2 delivery annotations: all Phase 2 features marked as implemented (v0.2.0). Component table expanded with vektra-analytics and vektra-learn. Protocol implementations updated: QdrantVectorStoreProvider, UnstructuredExtractor, DualStrategyChunking, AdvancedQueryPipeline, FastEmbedBM25Provider, WebhookEventEmitter. LogEventEmitter was originally scoped for Phase 2 but never built; the webhook path subsumed the use case.*
54 changes: 52 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,24 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased] — v0.5.0 "Widget production-ready + instructor configuration"
<!--
Convention (Keep a Changelog 1.1.0):
- Add new entries under "[Unreleased]" using sections: Added, Changed,
Deprecated, Removed, Fixed, Security.
- At release time: rename "[Unreleased]" to "[X.Y.Z] - YYYY-MM-DD" AND
add a fresh empty "[Unreleased]" block above it. The file must always
have an "[Unreleased]" section at the top, even if empty.
- Releases are listed newest-first below "[Unreleased]".
- See CONTRIBUTING.md > Changelog for the full process.
-->

## [Unreleased]

<!-- Add entries under: Added, Changed, Deprecated, Removed, Fixed, Security -->

## [0.5.0] - 2026-04-25
Comment thread
fvadicamo marked this conversation as resolved.

Widget production-ready + instructor configuration.

### Added

Expand All @@ -14,14 +31,47 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
- **vektra-learn**: `GET /api/v1/learn/conversations/{id}/turns` JWT-scoped endpoint so the widget can restore a conversation after a page reload. Returns decrypted question/answer + created_at; admin-only metadata is not exposed. 403 on namespace mismatch, 404 on missing.
- **vektra-core**: `document_name` field on every source citation, joined from `source_documents.filename` so the widget renders `[1] lecture-07.pdf` instead of chunk UUIDs. Propagates through both `SimpleQueryPipeline` and `AdvancedQueryPipeline`, JSON and SSE paths. Soft-deleted source documents (REQ-057) keep their citation with an `(archived)` suffix so answers stay traceable.
- **vektra-learn**: content-access audit entry (`learn_conversation_turns_read`) written on every successful turns fetch via the shared `vektra_shared.audit` interface (NFR-007).
- **widget**: white-label `data-*` attributes — `data-title`, `data-primary-color`, `data-icon` (emoji or URL), `data-welcome-message`, `data-powered-by`. All rendered via `textContent` / safe color whitelist to avoid XSS.
- **widget**: white-label `data-*` attributes — `data-title`, `data-primary-color`, `data-icon` (emoji or URL), `data-welcome-message`, `data-powered-by`, `data-powered-by-text`, `data-powered-by-url`. All rendered via `textContent` / safe color whitelist to avoid XSS.
- **widget**: tab-scoped conversation persistence via `sessionStorage` keyed by `course_id` (24h cutoff); history replay on load via the new turns endpoint; explicit "New chat" button in the header.
- **errors**: new codes `ERR-ADMIN-005/006/007` (namespace config) and `ERR-LEARN-005/006` (conversation turns).
- **docs**: consolidated [Widget integration guide](docs/guides/widget-integration.md) covering the LMS-agnostic embed flow (server-side JWT issuance, all `data-*` attributes, conversation persistence, token refresh, source citation visibility, error states, and end-to-end examples for PHP and Python backends). Linked from `docs/README.md` under Guides.

### Changed

- **User-facing branding**: README, top-level docs h1s, and external surfaces now use **Vektra RAG** as the product name. Coordinated with [vektralabs/vektra-moodle#14](https://github.com/vektralabs/vektra-moodle/pull/14) which renames its README h1 to "Vektra RAG for Moodle". Repo name (`vektra-stack`), Python package names (`vektra_*`), Docker images, container names, CLI commands, and internal code identifiers are unchanged.
- **widget footer label**: default "Powered by" link text changed from "Vektra" to "VektraLabs" — clearer maintainer-credit pattern (the link points to vektralabs.github.io, the org landing page) and avoids the residual short form. Integrators using the default branding will see the new label after upgrading the bundle; custom `data-powered-by-text` overrides are unaffected.
- **widget styles**: button and accent colors now use `var(--vektra-primary, …)` so `data-primary-color` takes effect without rebuilding the bundle. Hover states use `filter: brightness()` so custom colors still feel interactive.

## [0.4.0] - 2026-04-11

QueryTrace observability, eval harness, widget polish, and prompt hardening.

### Added

- **vektra-core**: configurable prompt grounding mode (FEAT-020, DEBT-016). `VEKTRA_PROMPT_GROUNDING_MODE=strict` (default) keeps the LLM tied to retrieved context + history; `hybrid` allows confident fallback to model knowledge. Foundation for the per-namespace override added in v0.5.0.
- **vektra-core**: multilingual reranker upgrade (BUG-016, DEBT-010). Default reranker switched to `BAAI/bge-reranker-v2-m3` with a lower score threshold so non-English queries are no longer over-filtered.
- **vektra-core**: `QueryTrace` persistence and an admin `GET /api/v1/admin/conversations/{id}/turns` endpoint (BUG-013, DEBT-011). Reranker scores, eval-mode metadata, and streaming model name are now captured in traces; gaps in the streaming path closed.
- **vektra-learn**: SSE streaming in the learn query endpoint (FEAT-010). The `done` event now carries `conversation_id` so the widget keeps multi-turn continuity across SSE responses (BUG-018).
- **widget**: Markdown rendering for assistant messages (FEAT-007), with code-block, list, and inline formatting; raw HTML stays sanitized.
- **widget**: token auto-refresh on 401 (FEAT-009) via either an `onTokenExpired` callback or a `data-token-refresh-url` endpoint.
- **widget**: API connectivity check and visible error feedback (FEAT-006) when the backend is unreachable.
- **eval**: retrieval and end-to-end evaluation harness (TECH-002), plus a curated 55-question bilingual EN/IT evaluation dataset.
- **config**: `VEKTRA_LLM_CONTEXT_WINDOW` propagation through `VektraSettings`, `VEKTRA_LLM_API_KEY` wired to litellm, and `VEKTRA_LLM_EXTRA_BODY` support for vLLM thinking-mode flags.

### Fixed

- **vektra-core**: warn (not crash) when `VEKTRA_LLM_CONTEXT_WINDOW` is unset for a model litellm does not know, falling back to 4096 (BUG-017).
- **vektra-core**: conversation row now created before the first turn is persisted (BUG-014).
- **vektra-core**: reranker scores propagated to `SearchResult` so observability and analytics see the post-rerank ranking (BUG-015).
- **vektra-core**: register the conversation store in `ProviderRegistry` so dependent endpoints can resolve it.
- **vektra-core**: distinguish "safeguard blocked" from "no relevant context" in the response so callers can tell apart a refusal from an empty result.
- **vektra-core**: system prompt iteratively hardened to stop the LLM from leaking RAG internals (chunk IDs, scores, role names) in any language.
- **vektra-learn**: audit logging on the conversation turns endpoint (NFR-007) was missing in the initial v0.4.0 release-review build.
- **config**: `grounding_mode` validator added to `VektraSettings` so invalid values fail fast at startup.
- **widget**: streaming token newlines and code-block formatting; centralized input-disabled state across sending and connection events; `<p>` no longer wraps inside `<code>` blocks.
- **build**: removed redundant `uv sync` when `INSTALL_UNSTRUCTURED=true`; eliminated all `uv pip install` invocations outside the lockfile to harden the supply chain.
- **CI**: latency measurement skipped on PR runs (post-merge only); perf-measurement queries reduced from 100 to 20 to keep workflow time bounded.

## [0.3.0] - 2026-03-21

E-learning vertical refinements, widget UX improvements, and Phase 2 stabilization.
Expand Down
28 changes: 27 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Contributing to Vektra
# Contributing to Vektra RAG

## Quick start

Expand Down Expand Up @@ -124,6 +124,31 @@ git log --show-signature -1 # should show "Good ssh signature"
**Remote servers without browser**: use `gh auth login --with-token` and add the
`admin:ssh_signing_key` scope upfront to avoid needing browser-based re-auth later.

## Changelog

We follow [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/). The
file must always have an `[Unreleased]` section at the top, even if empty.

**During development** — every PR with user-visible impact adds an entry under
`[Unreleased]` in `CHANGELOG.md`, using one of the six standard sections:

- **Added** — new features
- **Changed** — changes in existing functionality
- **Deprecated** — features that will be removed in upcoming releases
- **Removed** — features removed in this release
- **Fixed** — bug fixes
- **Security** — vulnerability fixes

Internal-only changes (refactors with no behavior change, CI tweaks, test-only
edits) do not require a changelog entry.

**At release time** — in the release-prep PR (`chore/vX.Y.Z-release`):

1. Rename `## [Unreleased]` to `## [X.Y.Z] - YYYY-MM-DD`
2. Add a fresh empty `## [Unreleased]` block above the new release entry
3. Bump versions in all `vektra-*/pyproject.toml` from `X.Y.Z-dev` to `X.Y.Z`
4. Refresh `uv.lock` to match the new versions

## PR workflow

1. Create a branch: `git checkout -b feat/core-streaming`
Expand All @@ -148,6 +173,7 @@ Before opening a PR:
- [ ] Unit tests pass: `uv run pytest <component>/tests/ -m "not integration"`
- [ ] New behavior has test coverage
- [ ] Commit messages follow Conventional Commits with `-s` sign-off
- [ ] `CHANGELOG.md` has an `[Unreleased]` entry for any user-visible change
- [ ] PR description explains the change and motivation

## CI pipeline
Expand Down
2 changes: 1 addition & 1 deletion GOVERNANCE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Vektra Governance
# Vektra RAG Governance

This document describes the governance model for the Vektra project.

Expand Down
Loading
Loading