Metadata-Version: 2.5
Name: yamcps-docs
Version: 0.2.0
Summary: Local document retrieval connector for yamcps
Project-URL: Repository, https://github.com/JoReci2/yamcps-docs
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: yamcps<0.4,>=0.3.0
Provides-Extra: all
Requires-Dist: openpyxl<4,>=3.1; extra == 'all'
Requires-Dist: pypdf<7,>=5; extra == 'all'
Requires-Dist: python-docx<2,>=1; extra == 'all'
Requires-Dist: python-pptx<2,>=1; extra == 'all'
Requires-Dist: xlrd<3,>=2; extra == 'all'
Provides-Extra: dev
Requires-Dist: bandit; extra == 'dev'
Requires-Dist: build; extra == 'dev'
Requires-Dist: coverage; extra == 'dev'
Requires-Dist: editables; extra == 'dev'
Requires-Dist: hatch-vcs<1,>=0.4; extra == 'dev'
Requires-Dist: hatchling; extra == 'dev'
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest-asyncio>=1; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: radon; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Provides-Extra: excel
Requires-Dist: openpyxl<4,>=3.1; extra == 'excel'
Requires-Dist: xlrd<3,>=2; extra == 'excel'
Provides-Extra: pdf
Requires-Dist: pypdf<7,>=5; extra == 'pdf'
Provides-Extra: powerpoint
Requires-Dist: python-pptx<2,>=1; extra == 'powerpoint'
Provides-Extra: words
Requires-Dist: python-docx<2,>=1; extra == 'words'
Description-Content-Type: text/markdown

# yamcps-docs

## Description

Recherche documentaire locale via le RAG natif du moteur
[yamcps](https://github.com/JoReci2/yamcps). Le plugin expose les documents indexés
en lecture seule dans MCP ; l'ingestion se fait séparément en ligne de commande.
Aucun secret n'est requis.

TXT et Markdown UTF-8 sont pris en charge de base. Les autres formats
s’installent séparément avec les extras suivants :

| Extra | Formats pris en charge |
| --- | --- |
| `pdf` | PDF texte |
| `words` | Word (`.docx`) |
| `excel` | Excel (`.xlsx`, `.xls`) |
| `powerpoint` | PowerPoint (`.pptx`) |
| `all` | Tous les formats ci-dessus |

L'extraction ne fait ni OCR, ni extraction de figures, ni
reconstruction de la mise en page.

## Quickstart

Python ≥ 3.12 et yamcps ≥ 0.3.0, < 0.4 sont requis. La compatibilité est
vérifiée avec yamcps 0.3.0. Installer le plugin dans le même environnement que le moteur :

```sh
python -m pip install 'yamcps-docs[all]'
yamcps configs set --connector docs
```

Pour installer uniquement un lecteur, utiliser par exemple
`python -m pip install 'yamcps-docs[pdf]'`, `'yamcps-docs[words]'` ou
`'yamcps-docs[excel]'`. Les extras se combinent aussi :
`python -m pip install 'yamcps-docs[pdf,excel]'`.


Laisser les champs `index` et `model` vides pour utiliser l'index par défaut du
moteur et la recherche lexicale. Depuis la racine de ce dépôt, essayer le devis de travaux
fictif fourni :

```sh
yamcps-docs ingest examples/manifest.json
yamcps serve --connector docs
```

Les fichiers d'exemple sont disponibles dans [examples](examples) ; hors du dépôt,
les copier ensemble dans un dossier local. Depuis le client MCP, demander de
rechercher `DEV-2026-0042` dans le corpus `demo/travaux`.

Le serveur n'ingère rien au démarrage. Utiliser le même compte et la même
configuration pour l'ingestion et le serveur. Le manifeste remplace le contenu du
corpus désigné : ne pas utiliser l'exemple pour remplacer un corpus existant.

Par exemple, demander ensuite : « Quel est le montant TTC du devis et quel acompte
est prévu ? » ou « La peinture est-elle comprise dans le poste TRV-003 ? », avec
les passages sources à l'appui.

### Recherche sémantique locale optionnelle

Sans modèle, la recherche est lexicale et ne comprend pas les synonymes. Installer
l'extra `rag`, puis télécharger explicitement un modèle Sentence Transformers
avec yamcps depuis Hugging Face. Par exemple, pour un modèle multilingue :

```sh
python -m pip install 'yamcps[rag]'
yamcps models download sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
```

La commande affiche le chemin absolu du dossier téléchargé. Par défaut, il se
trouve sous `~/.local/share/yamcps/models/<organisation>--<modele>/<commit>`.
L'option `--revision` permet de choisir une branche, un tag ou un commit ;
`--destination /chemin/absolu/vers/un-nouveau-dossier` fixe le dossier local.
Le téléchargement nécessite une connexion réseau et n'active pas le modèle.

Pour activer la recherche hybride, renseigner le chemin affiché dans le champ
`model`, puis ingérer le corpus dans un nouvel index. Un modèle compatible déjà
présent sur la machine peut aussi être utilisé directement :

```sh
yamcps configs set --connector docs --name model --value /chemin/absolu/vers/modele-local
yamcps configs set --connector docs --name index --value /chemin/absolu/docs-hybrid.sqlite3
yamcps-docs ingest /chemin/vers/manifest.json
yamcps serve --connector docs
```

Utiliser le même modèle pour l'ingestion et la recherche. Un changement de modèle
exige un nouvel index et une réingestion. Aucun modèle n'est fourni ni téléchargé
automatiquement, et aucun code distant n'est autorisé. Côté serveur, le modèle est
chargé au premier appel MCP, dans un thread, jamais à l'import ou à l'enregistrement
du plugin.

Pour un serveur hors ligne, télécharger le modèle sur une machine connectée,
puis copier son dossier sur le serveur, y installer aussi l'extra `rag` et
configurer `model` avec le chemin local de cette copie.
Évaluer sa qualité sur vos questions métier ; aucune qualification IVVQ n'est
revendiquée.

### Documents PowerPoint

Installer `yamcps-docs[powerpoint]` ou `yamcps-docs[all]` pour lire les `.pptx`
avec `python-pptx`. Ajouter la présentation au manifeste comme les autres sources.
Une révision de présentation devient un document indexé.

L'extraction conserve le texte des formes (y compris les groupes imbriqués) et des
tableaux, avec un repère `Slide: 1` pour chaque diapositive contenant du texte.
Les diapositives vides sont ignorées, mais leur numéro reste compté. Les formes
suivent l'ordre du fichier, qui peut différer de l'ordre visuel de lecture.
Les diapositives masquées sont incluses. Les cellules fusionnées des tableaux
ne répètent pas le texte de leur cellule d'origine.

Les notes du présentateur, commentaires, masques, graphiques, SmartArt, images,
objets incorporés et animations ne sont pas extraits. Aucun OCR ni rendu visuel
n'est effectué. Les anciens `.ppt` et les `.pptm` ne sont pas pris en charge.
Les limites de 20 MiB et de 4 millions de caractères s'appliquent ; un fichier vide,
invalide ou trop grand fait échouer le snapshot sans remplacer le corpus précédent.
Les offsets de citation concernent le texte extrait, pas les coordonnées visuelles.

### Documents Excel

L'extra `excel` (également inclus dans `all`) installe `openpyxl` pour `.xlsx` et `xlrd` pour `.xls`.
Ajouter le classeur au manifeste comme les autres sources ; une révision du
classeur devient un document indexé. Les feuilles, y compris masquées, sont lues
dans l'ordre du classeur. Les feuilles et cellules vides sont ignorées.
Le texte extrait contient le nom de chaque feuille et les coordonnées des cellules,
par exemple `Sheet: Budget` puis `A2: TRV-003 | B2: 1500`.

Les formules `.xlsx` sont conservées comme expressions, sans calcul. Pour `.xls`,
seuls leurs résultats enregistrés sont disponibles ; ils peuvent être anciens.
Les dates sont converties en ISO, les nombres et booléens en texte brut. Les formats
d'affichage (monnaie, pourcentage, zéros initiaux), graphiques, images, commentaires
et la mise en page ne sont pas reproduits. Les cellules fusionnées ne répètent pas
la valeur de leur cellule d'origine. Aucun lien externe n'est actualisé et aucune
macro n'est exécutée. Les formats `.xlsm` et `.xlsb` ne sont pas pris en charge.

La lecture est limitée à un million de cellules parcourues par classeur, y compris
les cellules vides des zones parcourues, et à 4 millions de caractères extraits.
La limite de 20 MiB par fichier s'applique aussi. Un classeur vide, invalide ou trop
grand fait échouer le snapshot entier sans remplacer le corpus précédent.
Les offsets de citation désignent ce texte extrait, pas une plage Excel native.

### Ingestion d'un corpus

Créer un manifeste JSON à côté des fichiers à indexer. Par exemple, avec `devis-travaux.md`
dans le même dossier que `manifest.json` :

```json
{
  "corpus": "demo/travaux",
  "sources": [
    {
      "path": "devis-travaux.md",
      "document_id": "DEV-2026-0042",
      "revision": "1.0",
      "metadata": {"project": "renovation-salon", "status": "example", "type": "devis"}
    }
  ]
}
```

Puis lancer :

```sh
yamcps-docs ingest /chemin/vers/manifest.json
```

Le manifeste est un **snapshot complet d'un seul corpus** : retirer une source
supprime cette révision de l'index. Garder explicitement les anciennes révisions
souhaitées. Les métadonnées sont facultatives et associent des clés à des valeurs
textuelles ; elles servent aux filtres de recherche. L'extraction de toutes les
sources se termine avant la synchronisation atomique du corpus. Une ingestion
identique réutilise les passages existants.

Les chemins des sources sont relatifs au dossier du manifeste. Les chemins
absolus, les traversées `..` et les liens symboliques sont refusés. Chaque source
est limitée à 20 MiB et à 4 millions de caractères extraits ; un texte vide est
refusé. N'ingérer que des fichiers de confiance.

Le découpage utilise au plus 1200 caractères par passage, sans chevauchement.
L'option `--chunk-size` permet une valeur de 64 à 16000. Un passage peut couper une
phrase ou un tableau. Les contenus sont stockés en clair dans SQLite : protéger
le dossier avec les droits du système et sauvegarder l'index si nécessaire.

## Outils

| Outil | Paramètres | Description |
| --- | --- | --- |
| `docs_search` | `query`, `corpus`, `filters=None`, `limit=5` | Recherche de passages sourcés ; limite de 1 à 20 |
| `docs_get_document` | `corpus`, `document_id`, `revision` | Texte extrait d'une révision précise |
| `docs_get_passage` | `passage_id` | Passage cité et provenance exacte |
| `docs_index_status` | `corpus` | Mode, compteurs et date du dernier passage modifié |

Toujours préciser le corpus lors d'une recherche. Les filtres correspondent
exactement aux métadonnées du manifeste, par exemple
`{"project": "renovation-salon", "type": "devis"}` ; ils ne constituent pas des droits
d'accès.

Les ressources `docs://source/{corpus}/{document_id}/{revision}` (segments encodés)
et `docs://passage/{passage_id}` exposent aussi les références citées. Pour une
citation vérifiable, conserver `document_id`, `revision`, `source_uri`,
`passage_id` et les offsets `start`/`end` renvoyés. Ces offsets désignent des
caractères dans le texte extrait, début inclus et fin exclue ; ils ne garantissent
pas une pagination dans le document original.

L'index est une copie des sources au moment de l'ingestion. La date indiquée par
`docs_index_status` concerne les passages modifiés et ne prouve pas la fraîcheur
des fichiers sources. Un index absent produit une erreur explicative au premier
appel, sans empêcher les autres plugins de démarrer. Aucune mutation de l'index
n'est exposée au modèle via MCP.

## Skills

| Skill | Description |
| --- | --- |
| [docs-research](src/yamcps_docs/skills/docs-research/SKILL.md) | Rechercher des preuves dans les corpus documentaires indexés et citer les versions exactes. |

Le skill est accessible via `skill://docs-research/SKILL.md`.
Redémarrer le serveur après une modification du skill embarqué.

## Variable de configuration

Configurer les chemins avec `yamcps configs set --connector docs` ; aucun export
de variable d'environnement n'est nécessaire. Les valeurs sont enregistrées dans
un fichier JSON local. Préférer des chemins absolus pour éviter les différences
de répertoire courant.

| Variable | Nom du champ | Description | Obligatoire | Valeur par défaut |
| --- | --- | --- | --- | --- |
| `YAMCPS_DOCS_INDEX` | `index` | Chemin de l'index SQLite | Non | Chaîne vide : index du moteur pour `docs` |
| `YAMCPS_DOCS_MODEL` | `model` | Dossier d'un modèle sémantique local | Non | Chaîne vide : recherche lexicale |

Les clés `YAMCPS_*` identifient la configuration non secrète. Les anciennes
variables d'environnement servent de repli si la clé est absente du fichier ;
une valeur vide enregistrée prévaut sur ce repli.

Les deux champs sont facultatifs. Voir le
[guide de configuration du moteur](https://github.com/JoReci2/yamcps/blob/main/docs/configuration.md)
pour leur priorité, `--config-file` et le mode non interactif. Pour un fichier de
configuration personnalisé, le transmettre aux deux commandes :

```sh
yamcps-docs ingest /chemin/vers/manifest.json --config-file /chemin/vers/config.json
yamcps serve --connector docs --config-file /chemin/vers/config.json
```

### Recherche sémantique locale optionnelle

Sans modèle, la recherche est lexicale et ne comprend pas les synonymes. Installer
l'extra `rag`, puis télécharger explicitement un modèle Sentence Transformers
avec yamcps depuis Hugging Face. Par exemple, pour un modèle multilingue :

```sh
python -m pip install 'yamcps[rag]'
yamcps models download sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2
```

La commande affiche le chemin absolu du dossier téléchargé. Par défaut, il se
trouve sous `~/.local/share/yamcps/models/<organisation>--<modele>/<commit>`.
L'option `--revision` permet de choisir une branche, un tag ou un commit ;
`--destination /chemin/absolu/vers/un-nouveau-dossier` fixe le dossier local.
Le téléchargement nécessite une connexion réseau et n'active pas le modèle.

Pour activer la recherche hybride, renseigner le chemin affiché dans le champ
`model`, puis ingérer le corpus dans un nouvel index. Un modèle compatible déjà
présent sur la machine peut aussi être utilisé directement :

```sh
yamcps configs set --connector docs --name model --value /chemin/absolu/vers/modele-local
yamcps configs set --connector docs --name index --value /chemin/absolu/docs-hybrid.sqlite3
yamcps-docs ingest /chemin/vers/manifest.json
yamcps serve --connector docs
```

Utiliser le même modèle pour l'ingestion et la recherche. Un changement de modèle
exige un nouvel index et une réingestion. Aucun modèle n'est fourni ni téléchargé
automatiquement, et aucun code distant n'est autorisé. Côté serveur, le modèle est
chargé au premier appel MCP, dans un thread, jamais à l'import ou à l'enregistrement
du plugin.

Pour un serveur hors ligne, télécharger le modèle sur une machine connectée,
puis copier son dossier sur le serveur, y installer aussi l'extra `rag` et
configurer `model` avec le chemin local de cette copie.
Évaluer sa qualité sur vos questions métier ; aucune qualification IVVQ n'est
revendiquée.

## Secret

| Secret | Nom du champ | Description | Obligatoire |
| --- | --- | --- | --- |
| Aucun | — | — | — |

### Contribution

Voir [contributing.md](contributing.md) pour l'installation de développement,
l'intégration au moteur et les validations.
