The Mannheim Intelligent Discovery System is an experimental web application that adds AI assisted search to discovery systems like Primo or VuFind®.
The installation is based on macOS, Linux, WSL, or a similar host system with Git and a sufficiently recent Python3.
Clone this repository and run these commands in your local working directory:
# Install required software.
python3 -m venv venv
source venv/bin/activate
pip install -U pip -r requirements.txtCopy the file sample.env to .env and provide your local settings
in the .env file.
Then, start the web application:
# Run web application (supports both VuFind and Primo).
./app.pyConnect to the running web application in your browser:
The application automatically detects which discovery system to use:
- VuFind is used by default if
VUFIND_SEARCH_ENDPOINTis configured - Primo is used if
PRIMO_SEARCH_ENDPOINTis configured and VuFind is not - Primo can be forced by including "primo" (case-insensitive) in the search query
The search page also supports a deep link / auto-search mode for embedding in
other tools (e.g. a button in a catalog UI). Pass the natural-language query as
the nl parameter and the search runs on page load:
The rendered page shows the AI summary, the normalized results, and a link to
the equivalent classic catalog search URL (the boolean lookfor query with all
filters applied).
A JSON API is available for programmatic access (e.g. phone applications).
Send a natural language query and receive a summary, follow-up suggestions, and results.
Request
| Field | Type | Required | Description |
|---|---|---|---|
query |
string | yes | Natural language search query |
model |
string | no | LLM model used for the result summary (defaults to first in LLM_MODELS). Query decomposition always uses the fixed DECOMPOSE_MODEL. |
Response
| Field | Type | Description |
|---|---|---|
summary |
string | Plain text summary of the search results |
follow_up_queries |
string[] | Suggested follow-up questions |
search_url |
string | Direct URL to the equivalent classic catalog search (VuFind) |
total_results |
integer | null | Total hit count reported by the catalog, or null if not available |
results |
object[] | Matching records (title, author, url, year) |
Rate limiting: 10 requests per minute per IP (configurable via API_RATE_LIMIT).
Example
curl -s -X POST http://127.0.0.1:5001/api/search \
-H "Content-Type: application/json" \
-d '{"query": "Bücher über Künstliche Intelligenz"}' | python3 -m json.tool{
"follow_up_queries": [
"Gibt es Neuerscheinungen zu diesem Thema?"
],
"results": [
{
"author": "Stuart Russell",
"title": "Artificial Intelligence: A Modern Approach",
"url": "https://example.com/...",
"year": "2021"
}
],
"summary": "Es wurden 3 Treffer gefunden. Das bekannteste Werk ist ..."
}Error responses use appropriate HTTP status codes (400, 429, 502, 503) with a JSON body {"error": "..."}.
For running as a system service (Linux), a systemd unit file is included.
# Create system user
sudo useradd -r -s /sbin/nologin ai
# Deploy application
sudo mkdir -p /opt/maidisco
sudo cp -r . /opt/maidisco/
sudo chown -R ai:ai /opt/maidisco
# Set up virtual environment
sudo -u ai python3 -m venv /opt/maidisco/venv
sudo -u ai /opt/maidisco/venv/bin/pip install -U pip -r /opt/maidisco/requirements.txt
# Configure
sudo -u ai cp /opt/maidisco/sample.env /opt/maidisco/.env
sudo nano /opt/maidisco/.env # edit settings
# Install and start service
sudo cp /opt/maidisco/maidisco.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now maidiscoTo serve under a subpath (e.g. /maidisco), set APPLICATION_ROOT=/maidisco in .env
and configure the WSGI server accordingly. For gunicorn, the systemd service already
passes SCRIPT_NAME=/maidisco. An Apache reverse proxy configuration:
ProxyPass /maidisco/ http://127.0.0.1:5001/
ProxyPassReverse /maidisco/ http://127.0.0.1:5001/Key environment variables in .env:
| Variable | Description |
|---|---|
OPENAI_API_KEY |
API key for OpenAI-compatible LLM (required) |
OPENAI_API_URL |
Base URL for the LLM API (e.g., http://localhost:11434/v1 for Ollama) |
LLM_MODELS |
Comma-separated list of models offered in the UI dropdown; the selected model is used for the result summary (first is default, e.g. gpt-4,llama3,mistral) |
DECOMPOSE_MODEL |
Fixed model for query decomposition (optional, defaults to first in LLM_MODELS) |
VUFIND_SEARCH_ENDPOINT |
VuFind API search endpoint URL |
VUFIND_WEB_URL |
Public base URL of the VuFind web UI for user-facing catalog links (optional, defaults to the API endpoint's base) |
PRIMO_SEARCH_ENDPOINT |
Primo API search endpoint URL |
PRIMO_APIKEY |
Primo API key (optional) |
PRIMO_SCOPE |
Primo scope parameter |
PRIMO_TAB |
Primo tab parameter |
PRIMO_VID |
Primo view ID |
HOST |
Server host (default: 127.0.0.1) |
PORT |
Server port (default: 5001) |
APPLICATION_ROOT |
URL prefix for subpath deployment (default: /) |
MAX_RESULTS |
Maximum number of search results (default: 10) |
API_RATE_LIMIT |
API rate limit: requests per minute per IP (default: 10) |
DEBUGMODE |
Enable Flask debug mode (default: False) |
MATOMO_URL |
Matomo tracking URL (optional, e.g. https://analytics.example.com/) |
MATOMO_SITE_ID |
Matomo site ID (optional) |
LEGAL_NOTICE_URL |
URL to legal notice/Impressum page (optional) |
PRIVACY_URL |
URL to privacy policy/Datenschutz page (optional) |
ACCESSIBILITY_URL |
URL to accessibility statement/Barrierefreiheit page (optional) |
SIGN_LANGUAGE_URL |
URL to sign language/Gebärdensprache page (optional) |
EASY_LANGUAGE_URL |
URL to easy language/Leichte Sprache page (optional) |
- CSRF protection via Sec-Fetch-Site header validation
- Rate limiting: 30 POST requests per minute per IP
- HTML sanitization in LLM summaries (XSS prevention)
- URL validation (blocks
javascript:and other dangerous schemes) - SSRF prevention (validates endpoint URLs at startup)
- Structured LLM prompts to mitigate prompt injection attacks
The structured concept-group query generation — synonym groups combined with AND/OR, boolean exclusions, and deterministic query building in Python — is inspired by the smart search service at Katholische Universität Eichstätt-Ingolstadt.
This is an experimental proof of concept. It is not intended for production use. Many features are missing, and the software may have bugs and security issues.
maidisco – Mannheim Intelligent Discovery System for AI assisted search
Copyright (C) 2025–2026 Universitätsbibliothek Mannheim
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.