mim-ai 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. mim_ai-0.1.0/LICENSE +21 -0
  2. mim_ai-0.1.0/PKG-INFO +362 -0
  3. mim_ai-0.1.0/README.md +285 -0
  4. mim_ai-0.1.0/mim_ai/__init__.py +241 -0
  5. mim_ai-0.1.0/mim_ai/__main__.py +51 -0
  6. mim_ai-0.1.0/mim_ai/_http.py +194 -0
  7. mim_ai-0.1.0/mim_ai/anonymizer/__init__.py +16 -0
  8. mim_ai-0.1.0/mim_ai/anonymizer/anonymizer.py +142 -0
  9. mim_ai-0.1.0/mim_ai/anonymizer/config.py +81 -0
  10. mim_ai-0.1.0/mim_ai/anonymizer/operators.py +92 -0
  11. mim_ai-0.1.0/mim_ai/anonymizer/recognizers/__init__.py +4 -0
  12. mim_ai-0.1.0/mim_ai/anonymizer/recognizers/base.py +19 -0
  13. mim_ai-0.1.0/mim_ai/anonymizer/recognizers/custom.py +36 -0
  14. mim_ai-0.1.0/mim_ai/anonymizer/recognizers/morocco_patterns.py +41 -0
  15. mim_ai-0.1.0/mim_ai/audit/__init__.py +25 -0
  16. mim_ai-0.1.0/mim_ai/audit/audit_logger.py +249 -0
  17. mim_ai-0.1.0/mim_ai/audit/backends.py +665 -0
  18. mim_ai-0.1.0/mim_ai/audit/config.py +113 -0
  19. mim_ai-0.1.0/mim_ai/audit/normalizer_logger.py +137 -0
  20. mim_ai-0.1.0/mim_ai/audit/redaction.py +35 -0
  21. mim_ai-0.1.0/mim_ai/audit/schema.sql +41 -0
  22. mim_ai-0.1.0/mim_ai/audit/verify.py +101 -0
  23. mim_ai-0.1.0/mim_ai/chatbot/__init__.py +45 -0
  24. mim_ai-0.1.0/mim_ai/chatbot/chatbot.py +463 -0
  25. mim_ai-0.1.0/mim_ai/chatbot/config.py +106 -0
  26. mim_ai-0.1.0/mim_ai/chatbot/history.py +320 -0
  27. mim_ai-0.1.0/mim_ai/chatbot/llm.py +221 -0
  28. mim_ai-0.1.0/mim_ai/chatbot/prompt_templates.py +58 -0
  29. mim_ai-0.1.0/mim_ai/chatbot/tools.py +304 -0
  30. mim_ai-0.1.0/mim_ai/config.py +298 -0
  31. mim_ai-0.1.0/mim_ai/default_config.yaml +151 -0
  32. mim_ai-0.1.0/mim_ai/intents/__init__.py +13 -0
  33. mim_ai-0.1.0/mim_ai/intents/config.py +51 -0
  34. mim_ai-0.1.0/mim_ai/intents/example_intents.yaml +49 -0
  35. mim_ai-0.1.0/mim_ai/intents/registry.py +308 -0
  36. mim_ai-0.1.0/mim_ai/morocco_patterns.py +6 -0
  37. mim_ai-0.1.0/mim_ai/normalizer/__init__.py +69 -0
  38. mim_ai-0.1.0/mim_ai/normalizer/arabizi.py +142 -0
  39. mim_ai-0.1.0/mim_ai/normalizer/darija_rules.py +320 -0
  40. mim_ai-0.1.0/mim_ai/normalizer/data/__init__.py +1 -0
  41. mim_ai-0.1.0/mim_ai/normalizer/data/darija_variants.yaml +146 -0
  42. mim_ai-0.1.0/mim_ai/normalizer/data/skip_words.yaml +6 -0
  43. mim_ai-0.1.0/mim_ai/normalizer/dictionary.py +32 -0
  44. mim_ai-0.1.0/mim_ai/normalizer/layers/__init__.py +6 -0
  45. mim_ai-0.1.0/mim_ai/normalizer/layers/arabic_rules.py +313 -0
  46. mim_ai-0.1.0/mim_ai/normalizer/layers/base.py +22 -0
  47. mim_ai-0.1.0/mim_ai/normalizer/layers/cleaning.py +131 -0
  48. mim_ai-0.1.0/mim_ai/normalizer/layers/rules.py +28 -0
  49. mim_ai-0.1.0/mim_ai/normalizer/normalizer.py +104 -0
  50. mim_ai-0.1.0/mim_ai/normalizer/schema.py +90 -0
  51. mim_ai-0.1.0/mim_ai/utils/__init__.py +0 -0
  52. mim_ai-0.1.0/mim_ai/utils/unicode_helpers.py +44 -0
  53. mim_ai-0.1.0/mim_ai/voice/__init__.py +13 -0
  54. mim_ai-0.1.0/mim_ai/voice/audio.py +177 -0
  55. mim_ai-0.1.0/mim_ai/voice/pipeline.py +268 -0
  56. mim_ai-0.1.0/mim_ai/voice/stt.py +48 -0
  57. mim_ai-0.1.0/mim_ai/voice/tts.py +66 -0
  58. mim_ai-0.1.0/mim_ai/voice/voice_agent.py +16 -0
  59. mim_ai-0.1.0/mim_ai.egg-info/PKG-INFO +362 -0
  60. mim_ai-0.1.0/mim_ai.egg-info/SOURCES.txt +71 -0
  61. mim_ai-0.1.0/mim_ai.egg-info/dependency_links.txt +1 -0
  62. mim_ai-0.1.0/mim_ai.egg-info/entry_points.txt +2 -0
  63. mim_ai-0.1.0/mim_ai.egg-info/requires.txt +63 -0
  64. mim_ai-0.1.0/mim_ai.egg-info/top_level.txt +1 -0
  65. mim_ai-0.1.0/pyproject.toml +129 -0
  66. mim_ai-0.1.0/setup.cfg +4 -0
  67. mim_ai-0.1.0/tests/test_audit.py +856 -0
  68. mim_ai-0.1.0/tests/test_ci_report.py +76 -0
  69. mim_ai-0.1.0/tests/test_example_auth.py +59 -0
  70. mim_ai-0.1.0/tests/test_intents.py +804 -0
  71. mim_ai-0.1.0/tests/test_mim.py +306 -0
  72. mim_ai-0.1.0/tests/test_normalizer.py +395 -0
  73. mim_ai-0.1.0/tests/test_voice.py +748 -0
mim_ai-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Salman KHATIB
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
mim_ai-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,362 @@
1
+ Metadata-Version: 2.4
2
+ Name: mim-ai
3
+ Version: 0.1.0
4
+ Summary: A configuration-first Python SDK for building production-grade Moroccan Darija AI applications β€” normalizer, anonymizer, chatbot, voice pipeline, and Law 09-08 audit trail.
5
+ Author: Salman KHATIB
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Salmankhatib/mim-ai
8
+ Project-URL: Repository, https://github.com/Salmankhatib/mim-ai
9
+ Project-URL: Documentation, https://github.com/Salmankhatib/mim-ai/tree/main/docs/EN
10
+ Project-URL: Bug Tracker, https://github.com/Salmankhatib/mim-ai/issues
11
+ Project-URL: Changelog, https://github.com/Salmankhatib/mim-ai/blob/main/CHANGELOG.md
12
+ Keywords: darija,arabic,moroccan,chatbot,voice-agent,stt,tts,anonymizer,audit,arabizi,normalizer
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Natural Language :: Arabic
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: Text Processing :: Linguistic
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: pyyaml>=6.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.0; extra == "dev"
31
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
32
+ Requires-Dist: black>=23.0; extra == "dev"
33
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
34
+ Requires-Dist: build>=1.2.0; extra == "dev"
35
+ Requires-Dist: twine>=5.0.0; extra == "dev"
36
+ Requires-Dist: fastapi<1.0.0,>=0.115.0; extra == "dev"
37
+ Requires-Dist: pydub<1.0.0,>=0.25.1; extra == "dev"
38
+ Provides-Extra: voice
39
+ Requires-Dist: numpy>=1.26; extra == "voice"
40
+ Requires-Dist: sounddevice>=0.4.6; extra == "voice"
41
+ Requires-Dist: soundfile>=0.12.1; extra == "voice"
42
+ Provides-Extra: anonymizer
43
+ Requires-Dist: presidio-analyzer>=2.2.0; extra == "anonymizer"
44
+ Requires-Dist: presidio-anonymizer>=2.2.0; extra == "anonymizer"
45
+ Requires-Dist: spacy<3.8; python_version < "3.10" and extra == "anonymizer"
46
+ Provides-Extra: redis
47
+ Requires-Dist: redis>=5.0.0; extra == "redis"
48
+ Provides-Extra: postgresql
49
+ Requires-Dist: psycopg[binary]>=3.1.0; extra == "postgresql"
50
+ Provides-Extra: mysql
51
+ Requires-Dist: PyMySQL>=1.1.0; extra == "mysql"
52
+ Provides-Extra: mongodb
53
+ Requires-Dist: pymongo>=4.8.0; extra == "mongodb"
54
+ Provides-Extra: production
55
+ Requires-Dist: psycopg[binary]>=3.1.0; extra == "production"
56
+ Requires-Dist: PyMySQL>=1.1.0; extra == "production"
57
+ Requires-Dist: pymongo>=4.8.0; extra == "production"
58
+ Requires-Dist: numpy>=1.26; extra == "production"
59
+ Requires-Dist: sounddevice>=0.4.6; extra == "production"
60
+ Requires-Dist: soundfile>=0.12.1; extra == "production"
61
+ Requires-Dist: presidio-analyzer>=2.2.0; extra == "production"
62
+ Requires-Dist: presidio-anonymizer>=2.2.0; extra == "production"
63
+ Requires-Dist: spacy<3.8; python_version < "3.10" and extra == "production"
64
+ Requires-Dist: redis>=5.0.0; extra == "production"
65
+ Provides-Extra: all
66
+ Requires-Dist: psycopg[binary]>=3.1.0; extra == "all"
67
+ Requires-Dist: PyMySQL>=1.1.0; extra == "all"
68
+ Requires-Dist: pymongo>=4.8.0; extra == "all"
69
+ Requires-Dist: numpy>=1.26; extra == "all"
70
+ Requires-Dist: sounddevice>=0.4.6; extra == "all"
71
+ Requires-Dist: soundfile>=0.12.1; extra == "all"
72
+ Requires-Dist: presidio-analyzer>=2.2.0; extra == "all"
73
+ Requires-Dist: presidio-anonymizer>=2.2.0; extra == "all"
74
+ Requires-Dist: spacy<3.8; python_version < "3.10" and extra == "all"
75
+ Requires-Dist: redis>=5.0.0; extra == "all"
76
+ Dynamic: license-file
77
+
78
+ # MIM β€” Mim AI
79
+
80
+ [![PyPI version](https://img.shields.io/pypi/v/mim-ai.svg?color=brightgreen)](https://pypi.org/project/mim-ai/)
81
+ [![Python versions](https://img.shields.io/pypi/pyversions/mim-ai.svg)](https://pypi.org/project/mim-ai/)
82
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
83
+ [![Tests](https://github.com/Salmankhatib/mim-ai/actions/workflows/tests.yml/badge.svg)](https://github.com/Salmankhatib/mim-ai/actions/workflows/tests.yml)
84
+ [![Made in Morocco](https://img.shields.io/badge/Made%20in-Morocco%20πŸ‡²πŸ‡¦-cc0000)](https://github.com/Salmankhatib/mim-ai)
85
+
86
+ > **A config-first Python SDK for Moroccan Darija AI.**
87
+ > Built by a Moroccan developer, for Moroccan developers.
88
+ > The last-mile bridge between Morocco's open-source AI models and the apps that use them.
89
+
90
+ ---
91
+
92
+ ## πŸ‡²πŸ‡¦ The Gap
93
+
94
+ Morocco is building its AI foundation fast. The **MTNRA–Mistral AI convention** (signed September 2025) has already delivered open-source bricks: a Darija language-identification classifier and a Voxtral-based speech recognition model. The **Sovereign AI Marketplace** and **Idarati AI** are rolling out. The **Maroc IA 2030** roadmap targets 100 billion MAD in GDP contribution and 50,000 new jobs.
95
+
96
+ **The models are arriving. What's missing is the last mile.**
97
+
98
+ A developer who wants to build a voice-enabled Darija app today still has to:
99
+
100
+ 1. Find the right model.
101
+ 2. Figure out how to run it.
102
+ 3. Handle audio I/O.
103
+ 4. Clean up messy Darija/Arabizi text before it reaches an LLM.
104
+ 5. Anonymize PII before logging.
105
+ 6. Log everything so it survives a Law 09-08 compliance review.
106
+
107
+ That's a week of work before the first feature ships. **MIM is an opinionated answer to that gap.**
108
+
109
+ ---
110
+
111
+ ## ✨ What MIM Is
112
+
113
+ A small Python SDK that gives Moroccan developers a **drop-in pipeline** for Darija text and voice:
114
+
115
+ - 🧹 **Normalizer** β€” Cleans real-world Darija (Arabizi, code-switching, inconsistent orthography) before it hits a model. Runs in **under 5 ms**, zero GPU.
116
+ - πŸ›‘οΈ **Anonymizer** β€” Config-first PII redaction using Presidio, with Morocco-specific CIN and phone patterns.
117
+ - πŸ€– **Chatbot** β€” Wraps any LLM with tools, sessions, and intent classification. Pre-wired for MNTRA/Mistral endpoints.
118
+ - πŸŽ™οΈ **Voice** β€” STT β†’ Normalizer β†’ Chatbot β†’ TTS full pipeline. Three methods, one config, zero bloat.
119
+ - πŸ“œ **Audit** β€” Hash-chained audit trail aligned with **Law 09-08** and **ISO 42001**. Out-of-the-box compliance.
120
+
121
+ *One YAML file. Any model. Any endpoint.*
122
+
123
+ ---
124
+
125
+ ## 🚫 What MIM Is Not
126
+
127
+ - **Not a model:** We don't train anything.
128
+ - **Not a framework:** We don't want you to learn a new abstraction.
129
+ - **Not the final word:** It's a proposition β€” open for community contribution.
130
+
131
+ ---
132
+
133
+ ## πŸš€ Quick Start
134
+
135
+ ### 1. Install
136
+
137
+ ```bash
138
+ pip install mim-ai
139
+ ```
140
+
141
+ The base install supports the normalizer and config-driven text workflows. Install optional extras only for the features you use:
142
+
143
+ ```bash
144
+ # Microphone/speaker I/O and optional non-WAV audio decoding
145
+ pip install "mim-ai[voice]"
146
+
147
+ # PII anonymization (also requires the spaCy model described below)
148
+ pip install "mim-ai[anonymizer]"
149
+ ```
150
+
151
+ ### 2. Generate Your Config
152
+
153
+ ```bash
154
+ mim-init
155
+ ```
156
+
157
+ This creates the canonical `mim_config.yaml` in the current project directory. Review its endpoint, model, and enable/disable settings before using remote or optional features.
158
+
159
+ ### 3. Use It
160
+
161
+ ```python
162
+ from mim_ai import Mim
163
+
164
+ # Load the config generated above. Components initialize only when used.
165
+ mim = Mim.from_config("mim_config.yaml")
166
+
167
+ # Normalization works with the base install; it does not call a model endpoint.
168
+ normalized = mim.normalizer.normalize("salam wa7ed 3la ekher")
169
+ print(normalized) # salam wa7ed 3la ekher
170
+
171
+ mim.shutdown()
172
+ ```
173
+
174
+ `load_config()` defaults to `mim_config.yaml`, reads only that exact file, and never creates config files or searches legacy filenames. Run `mim-init` (or call `write_default_config()`) explicitly before loading a generated config.
175
+
176
+ To use chat, set the API key named by `chatbot.llm_api_key_env` in your environment and configure `chatbot.llm_endpoint` and `chatbot.llm_model` in `mim_config.yaml`. The default chatbot endpoint is MNTRA's OpenAI-compatible API and expects `MNTRA_API_KEY`; compatible endpoints that do not require authentication can leave the key unset.
177
+
178
+ ### CI and test reports
179
+
180
+ GitHub Actions runs Ruff and the test suites separately for each module. It also uploads a PDF test-results summary with each run. Optional SMTP delivery can be enabled using GitHub Actions secrets; see [CI setup](.github/CI.md) for the secret names and configuration.
181
+
182
+ The generated config starts with chatbot tools and chatbot intent classification disabled, so chat is a regular text-completion flow by default. Voice intent classification is separately enabled in the generated config and uses the configured registry (empty unless you provide an intent file). Set `chatbot.system_prompt` to your full custom prompt; `system_prompt_extra` and `system_prompt_file` let you add reusable instructions:
183
+
184
+ ```yaml
185
+ chatbot:
186
+ enabled: true
187
+ llm_endpoint: https://api.mntra.ma/v1
188
+ llm_model: mntra/mistral-darija
189
+ llm_api_key_env: MNTRA_API_KEY
190
+ enable_tools: false
191
+ use_intents: false
192
+ system_prompt: |
193
+ You are a helpful customer support assistant for Acme.
194
+ Answer in the language used by the customer.
195
+ Be clear and concise. Do not invent company policies.
196
+ system_prompt_extra: |
197
+ If you cannot answer from the information provided, say so and offer a human handoff.
198
+ ```
199
+
200
+ ```python
201
+ from mim_ai import Mim
202
+
203
+ mim = Mim.from_config("mim_config.yaml")
204
+ message = input("You: ").strip()
205
+ if message:
206
+ reply = mim.chat(message, session_id="user-42")
207
+ if reply.error:
208
+ raise RuntimeError(reply.error)
209
+ print(f"Assistant: {reply.text}")
210
+ mim.shutdown()
211
+ ```
212
+
213
+ Voice requires the `voice` extra, an available microphone/speaker (and any OS audio libraries required by `sounddevice`), plus reachable STT/TTS endpoints. The default STT/TTS configs use `OPENAI_API_KEY`; set the environment variables configured by `stt.api_key_env` and `tts.api_key_env` before calling `listen_and_reply()`. If voice also uses chat, configure that endpoint's key as well. A TTS endpoint can be disabled when only a transcript or text reply is needed.
214
+
215
+ Anonymization requires the `anonymizer` extra and its spaCy model; it is not available from the base install alone. See the [anonymizer setup guide](docs/anonymizer/README.md).
216
+
217
+ ---
218
+
219
+ ## ⚑ The Normalizer β€” Built for Production
220
+
221
+ Real-time applications don't have 300 ms to spare on text cleaning.
222
+
223
+ The MIM normalizer runs in **under 5 ms on a modern CPU** with **zero model inference**. It is pure Python: Unicode normalization, a hand-curated Arabizi dictionary, and Arabic-script rules. No neural network, no GPU, no warmup.
224
+
225
+ For a voice pipeline, that's the difference between a live conversation and a laggy one.
226
+
227
+ ### Pipeline Layers (Cheapest First)
228
+
229
+ | Layer | Speed / Cost | What it does |
230
+ | --- | --- | --- |
231
+ | **1 β€” Universal Cleaning** | `~0 ms` | Whitespace, control characters, URL protection |
232
+ | **2 β€” Rule-based Darija** | `< 5 ms` | Alef/Ya unification, Arabizi dictionary lookup *(Default)* |
233
+
234
+ > **Dictionary Customization:** The dictionary is yours to grow. Ship a base of common Darija, add your app's jargon in a side file. Every skipped token is logged so you can refine from real usage, not guesses.
235
+
236
+ ---
237
+
238
+ ## πŸ›‘οΈ Anonymizer β€” Morocco-Specific PII Redaction
239
+
240
+ ```python
241
+ from mim_ai import anonymize
242
+
243
+ result = anonymize("Mon CIN est AB58075 et mon tel est 0612345678")
244
+ print(result.text) # Mon CIN est [CIN_MA] et mon tel est ****345678
245
+ print(result.entities_found) # [{'type': 'CIN_MA', 'start': 12, 'end': 19, 'score': 0.85}, ...]
246
+ ```
247
+
248
+ Built on top of Microsoft Presidio with native Moroccan recognizers:
249
+ - **CIN patterns** (Carte d'IdentitΓ© Nationale) β€” `AB123456`, `A123456`, etc.
250
+ - **Moroccan phone numbers** β€” supported mobile and fixed-line prefixes
251
+ - Fully configurable per-entity operators: `replace`, `mask`, `hash`, `redact`
252
+
253
+ Presidio requires the English spaCy model. Install it after `mim-ai` and before using the anonymizer:
254
+
255
+ ```bash
256
+ python -m spacy download en_core_web_lg
257
+ ```
258
+
259
+ There is no automatic regex-only fallback. See the [anonymizer setup guide](docs/anonymizer/README.md) for details.
260
+
261
+ ---
262
+
263
+ ## πŸ“œ Compliance β€” Law 09-08 & ISO 42001
264
+
265
+ MIM provides audit-log features intended to support compliance workflows:
266
+
267
+ ```yaml
268
+ audit:
269
+ enabled: true
270
+ backend: sqlite
271
+ hash_chain: true # tamper-evident chain
272
+ retention_days: 90 # auto-purge on schedule
273
+ ```
274
+
275
+ When enabled, chat turns are logged with a hash chain and retention is applied automatically. A retention purge appends a hash-chained checkpoint that starts a verifiable new chain segment. SQLite supports access and erasure helpers; erasing records outside the retention purge workflow breaks the chain, so retain and review verification evidence according to your organization's legal and compliance requirements. MIM is a technical component, not a compliance certification or legal guarantee.
276
+
277
+ Verify a configured audit chain with the same backend settings:
278
+
279
+ ```python
280
+ from mim_ai import load_config
281
+ from mim_ai.audit import verify_chain
282
+
283
+ audit_config = load_config("mim_config.yaml").audit
284
+ ok, message = verify_chain(config=audit_config)
285
+ ```
286
+
287
+ SQLite, JSONL, PostgreSQL, MySQL, and MongoDB have built-in ordered readers. A custom audit adapter must implement `iter_chain_records()` to enable verification; multi-writer custom adapters must also implement the existing atomic `append_chained()` contract.
288
+
289
+ ---
290
+
291
+ ## πŸ‡²πŸ‡¦ Made in Morocco, for Morocco
292
+
293
+ This is a **Made in Morocco** tool, built in step with the national AI strategy:
294
+
295
+ * **Aligned with Maroc IA 2030** β€” Contributing toward the 100B MAD GDP target, 50,000 jobs, and 200,000 trained talents.
296
+
297
+ * **MTNRA–Mistral Partnership** β€” Pre-configured for the MNTRA/Mistral Darija API endpoint. When Morocco's sovereign models ship, MIM is already wired for them.
298
+
299
+ * **Ecosystem Integration** β€” Built to plug directly into the Sovereign AI Marketplace and to serve Idarati AI and public-facing government services.
300
+
301
+ * **Compliant by Design** β€” Aligned with **Law 09-08** (audit logs hash user text, honor erasure requests, and expire on schedule) and **ISO 42001**.
302
+
303
+ *MIM is not infrastructure. It's the glue between the infrastructure Morocco is building and the applications Morocco's developers want to ship.*
304
+
305
+ ---
306
+
307
+ ## πŸ“‚ What's Inside
308
+
309
+ ```text
310
+ mim_ai/
311
+ β”œβ”€β”€ anonymizer/ # Config-first PII redaction with Presidio + Morocco-specific rules
312
+ β”œβ”€β”€ normalizer/ # Fast, config-first Darija text normalization (< 5 ms, zero GPU)
313
+ β”œβ”€β”€ chatbot/ # LLM wrapper with tools, sessions, intents
314
+ β”œβ”€β”€ voice/ # STT, TTS, VAD, full listen-and-reply pipeline
315
+ β”œβ”€β”€ intents/ # English-named, multilingual-matched intent registry
316
+ β”œβ”€β”€ audit/ # Law 09-08 + ISO 42001 aligned logging
317
+ └── config.py # One YAML, one loader, one Mim object
318
+ ```
319
+
320
+ > **Note:** Every subsystem is optional. If your configuration only enables the normalizer, nothing else loads. No torch, no sounddevice, no HTTP client.
321
+
322
+ ---
323
+
324
+ ## πŸ“š Documentation
325
+
326
+ - English: [docs/EN/tutorial.md](docs/EN/tutorial.md)
327
+ - FranΓ§ais: [docs/FR/tutorial.md](docs/FR/tutorial.md)
328
+ - Architecture: [docs/EN/architecture.md](docs/EN/architecture.md) / [docs/FR/architecture.md](docs/FR/architecture.md)
329
+ - Manifesto: [docs/EN/manifesto.md](docs/EN/manifesto.md) / [docs/FR/manifesto.md](docs/FR/manifesto.md)
330
+ - Feature guides: [Normalizer](docs/EN/features/normalizer.md) Β· [Chatbot](docs/EN/features/chatbot.md) Β· [Voice](docs/EN/features/voice.md) Β· [Audit](docs/EN/features/audit.md)
331
+ - Contributing: [docs/EN/contributing.md](docs/EN/contributing.md)
332
+ - Changelog: [CHANGELOG.md](CHANGELOG.md)
333
+
334
+ ---
335
+
336
+ ## πŸ§ͺ Minimal Project Example
337
+
338
+ A FastAPI example is included in [examples/project_example.py](examples/project_example.py). It demonstrates how to expose MIM through a simple HTTP service.
339
+
340
+ ---
341
+
342
+ ## πŸ”– PyPI Release
343
+
344
+ Before publishing a release candidate:
345
+
346
+ ```bash
347
+ python -m pip install --upgrade build twine
348
+ python -m build
349
+ python -m twine check dist/*
350
+ ```
351
+
352
+ Upload to TestPyPI first:
353
+
354
+ ```bash
355
+ python -m twine upload --repository testpypi dist/*
356
+ ```
357
+
358
+ Then to PyPI:
359
+
360
+ ```bash
361
+ python -m twine upload dist/*
362
+ ```
mim_ai-0.1.0/README.md ADDED
@@ -0,0 +1,285 @@
1
+ # MIM β€” Mim AI
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/mim-ai.svg?color=brightgreen)](https://pypi.org/project/mim-ai/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/mim-ai.svg)](https://pypi.org/project/mim-ai/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Tests](https://github.com/Salmankhatib/mim-ai/actions/workflows/tests.yml/badge.svg)](https://github.com/Salmankhatib/mim-ai/actions/workflows/tests.yml)
7
+ [![Made in Morocco](https://img.shields.io/badge/Made%20in-Morocco%20πŸ‡²πŸ‡¦-cc0000)](https://github.com/Salmankhatib/mim-ai)
8
+
9
+ > **A config-first Python SDK for Moroccan Darija AI.**
10
+ > Built by a Moroccan developer, for Moroccan developers.
11
+ > The last-mile bridge between Morocco's open-source AI models and the apps that use them.
12
+
13
+ ---
14
+
15
+ ## πŸ‡²πŸ‡¦ The Gap
16
+
17
+ Morocco is building its AI foundation fast. The **MTNRA–Mistral AI convention** (signed September 2025) has already delivered open-source bricks: a Darija language-identification classifier and a Voxtral-based speech recognition model. The **Sovereign AI Marketplace** and **Idarati AI** are rolling out. The **Maroc IA 2030** roadmap targets 100 billion MAD in GDP contribution and 50,000 new jobs.
18
+
19
+ **The models are arriving. What's missing is the last mile.**
20
+
21
+ A developer who wants to build a voice-enabled Darija app today still has to:
22
+
23
+ 1. Find the right model.
24
+ 2. Figure out how to run it.
25
+ 3. Handle audio I/O.
26
+ 4. Clean up messy Darija/Arabizi text before it reaches an LLM.
27
+ 5. Anonymize PII before logging.
28
+ 6. Log everything so it survives a Law 09-08 compliance review.
29
+
30
+ That's a week of work before the first feature ships. **MIM is an opinionated answer to that gap.**
31
+
32
+ ---
33
+
34
+ ## ✨ What MIM Is
35
+
36
+ A small Python SDK that gives Moroccan developers a **drop-in pipeline** for Darija text and voice:
37
+
38
+ - 🧹 **Normalizer** β€” Cleans real-world Darija (Arabizi, code-switching, inconsistent orthography) before it hits a model. Runs in **under 5 ms**, zero GPU.
39
+ - πŸ›‘οΈ **Anonymizer** β€” Config-first PII redaction using Presidio, with Morocco-specific CIN and phone patterns.
40
+ - πŸ€– **Chatbot** β€” Wraps any LLM with tools, sessions, and intent classification. Pre-wired for MNTRA/Mistral endpoints.
41
+ - πŸŽ™οΈ **Voice** β€” STT β†’ Normalizer β†’ Chatbot β†’ TTS full pipeline. Three methods, one config, zero bloat.
42
+ - πŸ“œ **Audit** β€” Hash-chained audit trail aligned with **Law 09-08** and **ISO 42001**. Out-of-the-box compliance.
43
+
44
+ *One YAML file. Any model. Any endpoint.*
45
+
46
+ ---
47
+
48
+ ## 🚫 What MIM Is Not
49
+
50
+ - **Not a model:** We don't train anything.
51
+ - **Not a framework:** We don't want you to learn a new abstraction.
52
+ - **Not the final word:** It's a proposition β€” open for community contribution.
53
+
54
+ ---
55
+
56
+ ## πŸš€ Quick Start
57
+
58
+ ### 1. Install
59
+
60
+ ```bash
61
+ pip install mim-ai
62
+ ```
63
+
64
+ The base install supports the normalizer and config-driven text workflows. Install optional extras only for the features you use:
65
+
66
+ ```bash
67
+ # Microphone/speaker I/O and optional non-WAV audio decoding
68
+ pip install "mim-ai[voice]"
69
+
70
+ # PII anonymization (also requires the spaCy model described below)
71
+ pip install "mim-ai[anonymizer]"
72
+ ```
73
+
74
+ ### 2. Generate Your Config
75
+
76
+ ```bash
77
+ mim-init
78
+ ```
79
+
80
+ This creates the canonical `mim_config.yaml` in the current project directory. Review its endpoint, model, and enable/disable settings before using remote or optional features.
81
+
82
+ ### 3. Use It
83
+
84
+ ```python
85
+ from mim_ai import Mim
86
+
87
+ # Load the config generated above. Components initialize only when used.
88
+ mim = Mim.from_config("mim_config.yaml")
89
+
90
+ # Normalization works with the base install; it does not call a model endpoint.
91
+ normalized = mim.normalizer.normalize("salam wa7ed 3la ekher")
92
+ print(normalized) # salam wa7ed 3la ekher
93
+
94
+ mim.shutdown()
95
+ ```
96
+
97
+ `load_config()` defaults to `mim_config.yaml`, reads only that exact file, and never creates config files or searches legacy filenames. Run `mim-init` (or call `write_default_config()`) explicitly before loading a generated config.
98
+
99
+ To use chat, set the API key named by `chatbot.llm_api_key_env` in your environment and configure `chatbot.llm_endpoint` and `chatbot.llm_model` in `mim_config.yaml`. The default chatbot endpoint is MNTRA's OpenAI-compatible API and expects `MNTRA_API_KEY`; compatible endpoints that do not require authentication can leave the key unset.
100
+
101
+ ### CI and test reports
102
+
103
+ GitHub Actions runs Ruff and the test suites separately for each module. It also uploads a PDF test-results summary with each run. Optional SMTP delivery can be enabled using GitHub Actions secrets; see [CI setup](.github/CI.md) for the secret names and configuration.
104
+
105
+ The generated config starts with chatbot tools and chatbot intent classification disabled, so chat is a regular text-completion flow by default. Voice intent classification is separately enabled in the generated config and uses the configured registry (empty unless you provide an intent file). Set `chatbot.system_prompt` to your full custom prompt; `system_prompt_extra` and `system_prompt_file` let you add reusable instructions:
106
+
107
+ ```yaml
108
+ chatbot:
109
+ enabled: true
110
+ llm_endpoint: https://api.mntra.ma/v1
111
+ llm_model: mntra/mistral-darija
112
+ llm_api_key_env: MNTRA_API_KEY
113
+ enable_tools: false
114
+ use_intents: false
115
+ system_prompt: |
116
+ You are a helpful customer support assistant for Acme.
117
+ Answer in the language used by the customer.
118
+ Be clear and concise. Do not invent company policies.
119
+ system_prompt_extra: |
120
+ If you cannot answer from the information provided, say so and offer a human handoff.
121
+ ```
122
+
123
+ ```python
124
+ from mim_ai import Mim
125
+
126
+ mim = Mim.from_config("mim_config.yaml")
127
+ message = input("You: ").strip()
128
+ if message:
129
+ reply = mim.chat(message, session_id="user-42")
130
+ if reply.error:
131
+ raise RuntimeError(reply.error)
132
+ print(f"Assistant: {reply.text}")
133
+ mim.shutdown()
134
+ ```
135
+
136
+ Voice requires the `voice` extra, an available microphone/speaker (and any OS audio libraries required by `sounddevice`), plus reachable STT/TTS endpoints. The default STT/TTS configs use `OPENAI_API_KEY`; set the environment variables configured by `stt.api_key_env` and `tts.api_key_env` before calling `listen_and_reply()`. If voice also uses chat, configure that endpoint's key as well. A TTS endpoint can be disabled when only a transcript or text reply is needed.
137
+
138
+ Anonymization requires the `anonymizer` extra and its spaCy model; it is not available from the base install alone. See the [anonymizer setup guide](docs/anonymizer/README.md).
139
+
140
+ ---
141
+
142
+ ## ⚑ The Normalizer β€” Built for Production
143
+
144
+ Real-time applications don't have 300 ms to spare on text cleaning.
145
+
146
+ The MIM normalizer runs in **under 5 ms on a modern CPU** with **zero model inference**. It is pure Python: Unicode normalization, a hand-curated Arabizi dictionary, and Arabic-script rules. No neural network, no GPU, no warmup.
147
+
148
+ For a voice pipeline, that's the difference between a live conversation and a laggy one.
149
+
150
+ ### Pipeline Layers (Cheapest First)
151
+
152
+ | Layer | Speed / Cost | What it does |
153
+ | --- | --- | --- |
154
+ | **1 β€” Universal Cleaning** | `~0 ms` | Whitespace, control characters, URL protection |
155
+ | **2 β€” Rule-based Darija** | `< 5 ms` | Alef/Ya unification, Arabizi dictionary lookup *(Default)* |
156
+
157
+ > **Dictionary Customization:** The dictionary is yours to grow. Ship a base of common Darija, add your app's jargon in a side file. Every skipped token is logged so you can refine from real usage, not guesses.
158
+
159
+ ---
160
+
161
+ ## πŸ›‘οΈ Anonymizer β€” Morocco-Specific PII Redaction
162
+
163
+ ```python
164
+ from mim_ai import anonymize
165
+
166
+ result = anonymize("Mon CIN est AB58075 et mon tel est 0612345678")
167
+ print(result.text) # Mon CIN est [CIN_MA] et mon tel est ****345678
168
+ print(result.entities_found) # [{'type': 'CIN_MA', 'start': 12, 'end': 19, 'score': 0.85}, ...]
169
+ ```
170
+
171
+ Built on top of Microsoft Presidio with native Moroccan recognizers:
172
+ - **CIN patterns** (Carte d'IdentitΓ© Nationale) β€” `AB123456`, `A123456`, etc.
173
+ - **Moroccan phone numbers** β€” supported mobile and fixed-line prefixes
174
+ - Fully configurable per-entity operators: `replace`, `mask`, `hash`, `redact`
175
+
176
+ Presidio requires the English spaCy model. Install it after `mim-ai` and before using the anonymizer:
177
+
178
+ ```bash
179
+ python -m spacy download en_core_web_lg
180
+ ```
181
+
182
+ There is no automatic regex-only fallback. See the [anonymizer setup guide](docs/anonymizer/README.md) for details.
183
+
184
+ ---
185
+
186
+ ## πŸ“œ Compliance β€” Law 09-08 & ISO 42001
187
+
188
+ MIM provides audit-log features intended to support compliance workflows:
189
+
190
+ ```yaml
191
+ audit:
192
+ enabled: true
193
+ backend: sqlite
194
+ hash_chain: true # tamper-evident chain
195
+ retention_days: 90 # auto-purge on schedule
196
+ ```
197
+
198
+ When enabled, chat turns are logged with a hash chain and retention is applied automatically. A retention purge appends a hash-chained checkpoint that starts a verifiable new chain segment. SQLite supports access and erasure helpers; erasing records outside the retention purge workflow breaks the chain, so retain and review verification evidence according to your organization's legal and compliance requirements. MIM is a technical component, not a compliance certification or legal guarantee.
199
+
200
+ Verify a configured audit chain with the same backend settings:
201
+
202
+ ```python
203
+ from mim_ai import load_config
204
+ from mim_ai.audit import verify_chain
205
+
206
+ audit_config = load_config("mim_config.yaml").audit
207
+ ok, message = verify_chain(config=audit_config)
208
+ ```
209
+
210
+ SQLite, JSONL, PostgreSQL, MySQL, and MongoDB have built-in ordered readers. A custom audit adapter must implement `iter_chain_records()` to enable verification; multi-writer custom adapters must also implement the existing atomic `append_chained()` contract.
211
+
212
+ ---
213
+
214
+ ## πŸ‡²πŸ‡¦ Made in Morocco, for Morocco
215
+
216
+ This is a **Made in Morocco** tool, built in step with the national AI strategy:
217
+
218
+ * **Aligned with Maroc IA 2030** β€” Contributing toward the 100B MAD GDP target, 50,000 jobs, and 200,000 trained talents.
219
+
220
+ * **MTNRA–Mistral Partnership** β€” Pre-configured for the MNTRA/Mistral Darija API endpoint. When Morocco's sovereign models ship, MIM is already wired for them.
221
+
222
+ * **Ecosystem Integration** β€” Built to plug directly into the Sovereign AI Marketplace and to serve Idarati AI and public-facing government services.
223
+
224
+ * **Compliant by Design** β€” Aligned with **Law 09-08** (audit logs hash user text, honor erasure requests, and expire on schedule) and **ISO 42001**.
225
+
226
+ *MIM is not infrastructure. It's the glue between the infrastructure Morocco is building and the applications Morocco's developers want to ship.*
227
+
228
+ ---
229
+
230
+ ## πŸ“‚ What's Inside
231
+
232
+ ```text
233
+ mim_ai/
234
+ β”œβ”€β”€ anonymizer/ # Config-first PII redaction with Presidio + Morocco-specific rules
235
+ β”œβ”€β”€ normalizer/ # Fast, config-first Darija text normalization (< 5 ms, zero GPU)
236
+ β”œβ”€β”€ chatbot/ # LLM wrapper with tools, sessions, intents
237
+ β”œβ”€β”€ voice/ # STT, TTS, VAD, full listen-and-reply pipeline
238
+ β”œβ”€β”€ intents/ # English-named, multilingual-matched intent registry
239
+ β”œβ”€β”€ audit/ # Law 09-08 + ISO 42001 aligned logging
240
+ └── config.py # One YAML, one loader, one Mim object
241
+ ```
242
+
243
+ > **Note:** Every subsystem is optional. If your configuration only enables the normalizer, nothing else loads. No torch, no sounddevice, no HTTP client.
244
+
245
+ ---
246
+
247
+ ## πŸ“š Documentation
248
+
249
+ - English: [docs/EN/tutorial.md](docs/EN/tutorial.md)
250
+ - FranΓ§ais: [docs/FR/tutorial.md](docs/FR/tutorial.md)
251
+ - Architecture: [docs/EN/architecture.md](docs/EN/architecture.md) / [docs/FR/architecture.md](docs/FR/architecture.md)
252
+ - Manifesto: [docs/EN/manifesto.md](docs/EN/manifesto.md) / [docs/FR/manifesto.md](docs/FR/manifesto.md)
253
+ - Feature guides: [Normalizer](docs/EN/features/normalizer.md) Β· [Chatbot](docs/EN/features/chatbot.md) Β· [Voice](docs/EN/features/voice.md) Β· [Audit](docs/EN/features/audit.md)
254
+ - Contributing: [docs/EN/contributing.md](docs/EN/contributing.md)
255
+ - Changelog: [CHANGELOG.md](CHANGELOG.md)
256
+
257
+ ---
258
+
259
+ ## πŸ§ͺ Minimal Project Example
260
+
261
+ A FastAPI example is included in [examples/project_example.py](examples/project_example.py). It demonstrates how to expose MIM through a simple HTTP service.
262
+
263
+ ---
264
+
265
+ ## πŸ”– PyPI Release
266
+
267
+ Before publishing a release candidate:
268
+
269
+ ```bash
270
+ python -m pip install --upgrade build twine
271
+ python -m build
272
+ python -m twine check dist/*
273
+ ```
274
+
275
+ Upload to TestPyPI first:
276
+
277
+ ```bash
278
+ python -m twine upload --repository testpypi dist/*
279
+ ```
280
+
281
+ Then to PyPI:
282
+
283
+ ```bash
284
+ python -m twine upload dist/*
285
+ ```