mentalsaathi-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.
- mentalsaathi_ai-0.1.0/PKG-INFO +136 -0
- mentalsaathi_ai-0.1.0/README.md +121 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/__init__.py +128 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/agent/__init__.py +15 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/agent/risk_detection.py +3 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/__init__.py +81 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/__main__.py +109 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/base_chain.py +41 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/calculate_risk.py +17 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/crisis_detection_chain.py +34 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/depth_perception_chain.py +32 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/emotional_tone_detection.py +22 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/full_analysis_chain.py +38 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/mentalsaathi_chain.py +87 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/reframe_scenario_chain.py +17 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/relief_response_chain.py +30 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/response_generation_chain.py +35 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/root_cause_extraction_chain.py +26 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/core/__init__.py +23 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/core/settings.py +59 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/llms/__init__.py +24 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/llms/llm.py +55 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/memory/__init__.py +9 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/__init__.py +74 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/base_prompt.py +68 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/crisis_detection.py +10 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/depth_perception.py +14 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/emotion_detection.py +14 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/full_analysis.py +8 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/loader.py +8 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/reframe_scenario.py +10 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/relief_response.py +10 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/response_generation.py +8 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/risk.py +68 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/root_cause_extraction.py +6 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/__init__.py +52 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/base_schema.py +40 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/crisis_detection.py +82 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/depth_perception.py +43 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/emotion.py +50 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/full_analysis.py +49 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/reframe_scenario.py +40 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/relief_response.py +64 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/risk.py +35 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/root_cause.py +36 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai.egg-info/PKG-INFO +136 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai.egg-info/SOURCES.txt +50 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai.egg-info/dependency_links.txt +1 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai.egg-info/requires.txt +7 -0
- mentalsaathi_ai-0.1.0/mentalsaathi_ai.egg-info/top_level.txt +1 -0
- mentalsaathi_ai-0.1.0/pyproject.toml +30 -0
- mentalsaathi_ai-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mentalsaathi-ai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: LangChain-based AI engine for mental health support conversations
|
|
5
|
+
Author-email: Rhydham Mittal <manjeetsinghdangi786@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: langchain-core
|
|
10
|
+
Requires-Dist: langchain-google-genai
|
|
11
|
+
Requires-Dist: python-dotenv
|
|
12
|
+
Requires-Dist: pydantic
|
|
13
|
+
Provides-Extra: groq
|
|
14
|
+
Requires-Dist: langchain-groq; extra == "groq"
|
|
15
|
+
|
|
16
|
+
# 🌿 MentalSaathi AI Engine
|
|
17
|
+
|
|
18
|
+
The AI backbone of [Mental Saathi](https://www.mentalsaathi.in) — a mental health platform built for Indian college students. This package handles risk detection, LLM chains, prompt management, and agent orchestration.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- **Risk Detection Agent** — analyses user-submitted text and returns a structured risk assessment (`risk_score`, `risk_level`, `summary`)
|
|
25
|
+
- **LangChain-powered chains** — modular, composable chains built on `langchain-core`
|
|
26
|
+
- **Groq LLM integration** — fast inference via `langchain-groq`
|
|
27
|
+
- **Pydantic output schemas** — structured, validated responses from every chain
|
|
28
|
+
- **Pluggable prompt system** — prompts and parsers are decoupled and easy to swap
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Project Structure
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
mentalsaathi.backend/ai/
|
|
36
|
+
├── agent/ # High-level agents (entry points)
|
|
37
|
+
│ └── risk_detection.py
|
|
38
|
+
├── chains/ # LangChain chains
|
|
39
|
+
│ └── calculate_risk.py
|
|
40
|
+
├── core/ # App settings & config
|
|
41
|
+
│ └── settings.py
|
|
42
|
+
├── llms/ # LLM instantiation
|
|
43
|
+
│ └── llm.py
|
|
44
|
+
├── prompts/ # Prompt templates & parsers
|
|
45
|
+
│ └── risk.py
|
|
46
|
+
└── schemas/ # Pydantic output schemas
|
|
47
|
+
└── risk.py
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
Requires Python 3.11+.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Clone the repo
|
|
58
|
+
git clone https://github.com/your-org/mentalsaathi.git
|
|
59
|
+
cd mentalsaathi.backend
|
|
60
|
+
|
|
61
|
+
# Install the AI package in editable mode
|
|
62
|
+
pip install -e ".[dev]"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
Create a `.env` file in `mentalsaathi.backend/`:
|
|
70
|
+
|
|
71
|
+
```env
|
|
72
|
+
GROQ_API_KEY=your_groq_api_key
|
|
73
|
+
GROQ_MODEL=llama3-8b-8192
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
All settings are loaded via `ai.core.Settings` (Pydantic `BaseSettings`).
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Usage
|
|
81
|
+
|
|
82
|
+
### Risk Detection
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from ai.agent import risk_agent
|
|
86
|
+
|
|
87
|
+
result = risk_agent.invoke({"user_input": "I've been feeling really hopeless lately..."})
|
|
88
|
+
|
|
89
|
+
print(result.risk_level) # e.g. "HIGH"
|
|
90
|
+
print(result.risk_score) # e.g. 78
|
|
91
|
+
print(result.summary) # e.g. "User expresses signs of hopelessness..."
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Risk Levels
|
|
95
|
+
|
|
96
|
+
| Level | Description |
|
|
97
|
+
|------------|------------------------------------------|
|
|
98
|
+
| `MINIMAL` | No significant distress detected |
|
|
99
|
+
| `LOW` | Mild emotional difficulty |
|
|
100
|
+
| `MODERATE` | Noticeable distress, worth monitoring |
|
|
101
|
+
| `HIGH` | Significant risk, prompt attention needed|
|
|
102
|
+
| `CRITICAL` | Immediate intervention recommended |
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Dependencies
|
|
107
|
+
|
|
108
|
+
Key packages used:
|
|
109
|
+
|
|
110
|
+
| Package | Purpose |
|
|
111
|
+
|----------------------|--------------------------------|
|
|
112
|
+
| `langchain-core` | Chain & prompt abstractions |
|
|
113
|
+
| `langchain-groq` | Groq LLM integration |
|
|
114
|
+
| `pydantic` | Output schema validation |
|
|
115
|
+
| `python-dotenv` | Environment variable loading |
|
|
116
|
+
| `rich` | Pretty console output |
|
|
117
|
+
| `langsmith` | Tracing & observability |
|
|
118
|
+
|
|
119
|
+
Full list in [`pyproject.toml`](./pyproject.toml).
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Development
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Run a quick smoke test on the risk agent
|
|
127
|
+
python -c "from ai.agent import risk_agent; print(risk_agent.invoke({'user_input': 'test'}))"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Make sure the package is installed from the repo root so absolute imports (`from ai.x import ...`) resolve correctly.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT © 2026 Mental Saathi
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# 🌿 MentalSaathi AI Engine
|
|
2
|
+
|
|
3
|
+
The AI backbone of [Mental Saathi](https://www.mentalsaathi.in) — a mental health platform built for Indian college students. This package handles risk detection, LLM chains, prompt management, and agent orchestration.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Risk Detection Agent** — analyses user-submitted text and returns a structured risk assessment (`risk_score`, `risk_level`, `summary`)
|
|
10
|
+
- **LangChain-powered chains** — modular, composable chains built on `langchain-core`
|
|
11
|
+
- **Groq LLM integration** — fast inference via `langchain-groq`
|
|
12
|
+
- **Pydantic output schemas** — structured, validated responses from every chain
|
|
13
|
+
- **Pluggable prompt system** — prompts and parsers are decoupled and easy to swap
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Project Structure
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
mentalsaathi.backend/ai/
|
|
21
|
+
├── agent/ # High-level agents (entry points)
|
|
22
|
+
│ └── risk_detection.py
|
|
23
|
+
├── chains/ # LangChain chains
|
|
24
|
+
│ └── calculate_risk.py
|
|
25
|
+
├── core/ # App settings & config
|
|
26
|
+
│ └── settings.py
|
|
27
|
+
├── llms/ # LLM instantiation
|
|
28
|
+
│ └── llm.py
|
|
29
|
+
├── prompts/ # Prompt templates & parsers
|
|
30
|
+
│ └── risk.py
|
|
31
|
+
└── schemas/ # Pydantic output schemas
|
|
32
|
+
└── risk.py
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
Requires Python 3.11+.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Clone the repo
|
|
43
|
+
git clone https://github.com/your-org/mentalsaathi.git
|
|
44
|
+
cd mentalsaathi.backend
|
|
45
|
+
|
|
46
|
+
# Install the AI package in editable mode
|
|
47
|
+
pip install -e ".[dev]"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
Create a `.env` file in `mentalsaathi.backend/`:
|
|
55
|
+
|
|
56
|
+
```env
|
|
57
|
+
GROQ_API_KEY=your_groq_api_key
|
|
58
|
+
GROQ_MODEL=llama3-8b-8192
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
All settings are loaded via `ai.core.Settings` (Pydantic `BaseSettings`).
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
### Risk Detection
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from ai.agent import risk_agent
|
|
71
|
+
|
|
72
|
+
result = risk_agent.invoke({"user_input": "I've been feeling really hopeless lately..."})
|
|
73
|
+
|
|
74
|
+
print(result.risk_level) # e.g. "HIGH"
|
|
75
|
+
print(result.risk_score) # e.g. 78
|
|
76
|
+
print(result.summary) # e.g. "User expresses signs of hopelessness..."
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Risk Levels
|
|
80
|
+
|
|
81
|
+
| Level | Description |
|
|
82
|
+
|------------|------------------------------------------|
|
|
83
|
+
| `MINIMAL` | No significant distress detected |
|
|
84
|
+
| `LOW` | Mild emotional difficulty |
|
|
85
|
+
| `MODERATE` | Noticeable distress, worth monitoring |
|
|
86
|
+
| `HIGH` | Significant risk, prompt attention needed|
|
|
87
|
+
| `CRITICAL` | Immediate intervention recommended |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Dependencies
|
|
92
|
+
|
|
93
|
+
Key packages used:
|
|
94
|
+
|
|
95
|
+
| Package | Purpose |
|
|
96
|
+
|----------------------|--------------------------------|
|
|
97
|
+
| `langchain-core` | Chain & prompt abstractions |
|
|
98
|
+
| `langchain-groq` | Groq LLM integration |
|
|
99
|
+
| `pydantic` | Output schema validation |
|
|
100
|
+
| `python-dotenv` | Environment variable loading |
|
|
101
|
+
| `rich` | Pretty console output |
|
|
102
|
+
| `langsmith` | Tracing & observability |
|
|
103
|
+
|
|
104
|
+
Full list in [`pyproject.toml`](./pyproject.toml).
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Development
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Run a quick smoke test on the risk agent
|
|
112
|
+
python -c "from ai.agent import risk_agent; print(risk_agent.invoke({'user_input': 'test'}))"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Make sure the package is installed from the repo root so absolute imports (`from ai.x import ...`) resolve correctly.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT © 2026 Mental Saathi
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""MentalSaathi AI — emotional support pipeline for Indian university students.
|
|
2
|
+
|
|
3
|
+
3-stage pipeline (3 LLM calls total):
|
|
4
|
+
|
|
5
|
+
Stage 1 FullAnalysisChain emotion + depth perception + root cause
|
|
6
|
+
Stage 2 CrisisDetectionChain safety gate — routes to crisis or response
|
|
7
|
+
Stage 3 ResponseGenerationChain reframe + relief (non-crisis path only)
|
|
8
|
+
|
|
9
|
+
Package layout::
|
|
10
|
+
|
|
11
|
+
mentalsaathi_ai/
|
|
12
|
+
├── __init__.py ← you are here — top-level API
|
|
13
|
+
├── core/ ← Settings (env vars)
|
|
14
|
+
├── llms/ ← LLMFactory + brain singleton
|
|
15
|
+
├── schemas/ ← Pydantic output schemas (BasePipelineSchema hierarchy)
|
|
16
|
+
├── prompts/ ← BasePrompt + loader + templates/*.txt
|
|
17
|
+
├── chains/ ← BaseChain + all LCEL chain classes
|
|
18
|
+
├── agent/ ← risk detection agent
|
|
19
|
+
└── memory/ ← conversation memory (in progress)
|
|
20
|
+
|
|
21
|
+
Quickstart — one function::
|
|
22
|
+
|
|
23
|
+
from mentalsaathi_ai import run
|
|
24
|
+
|
|
25
|
+
result = run("mujhe neend nahi aa rahi aur ghar pe sab chup hain.")
|
|
26
|
+
print(result) # the message the student will read (or crisis response)
|
|
27
|
+
print(bool(result)) # False if crisis (CrisisCheck._is_truthy), True otherwise
|
|
28
|
+
|
|
29
|
+
Quickstart — pipeline object::
|
|
30
|
+
|
|
31
|
+
from mentalsaathi_ai import mentalsaathi_chain
|
|
32
|
+
|
|
33
|
+
result = mentalsaathi_chain("yaar bahut stressed hoon")
|
|
34
|
+
result = mentalsaathi_chain(
|
|
35
|
+
student_message="I don't see the point anymore",
|
|
36
|
+
conversation_context="earlier: student mentioned failing exams",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
print(len(mentalsaathi_chain)) # 3 (number of LLM calls)
|
|
40
|
+
print(mentalsaathi_chain) # MentalSaathiPipeline(3 LLM calls)
|
|
41
|
+
|
|
42
|
+
Return types::
|
|
43
|
+
|
|
44
|
+
ReliefResponse — normal path: .response (str), .anchor (str)
|
|
45
|
+
CrisisCheck — crisis path: .crisis_response (str), .resources (list)
|
|
46
|
+
|
|
47
|
+
bool(result) → True for ReliefResponse, False for CrisisCheck
|
|
48
|
+
str(result) → the student-facing message text in both cases
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
from mentalsaathi_ai.chains.mentalsaathi_chain import (
|
|
52
|
+
MentalSaathiPipeline,
|
|
53
|
+
mentalsaathi_chain,
|
|
54
|
+
)
|
|
55
|
+
from mentalsaathi_ai.schemas import (
|
|
56
|
+
BasePipelineSchema,
|
|
57
|
+
EmotionalDetection,
|
|
58
|
+
SurfaceVsDeep,
|
|
59
|
+
DeepPainEntry,
|
|
60
|
+
RootCauseExtraction,
|
|
61
|
+
FullAnalysis,
|
|
62
|
+
CrisisCheck,
|
|
63
|
+
CrisisResource,
|
|
64
|
+
Reframe,
|
|
65
|
+
ReliefResponse,
|
|
66
|
+
RiskToolOutput,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
__version__ = "0.2.0"
|
|
70
|
+
__author__ = "MentalSaathi"
|
|
71
|
+
|
|
72
|
+
__all__ = [
|
|
73
|
+
# Version
|
|
74
|
+
"__version__",
|
|
75
|
+
"__author__",
|
|
76
|
+
# Top-level API
|
|
77
|
+
"run",
|
|
78
|
+
# Pipeline
|
|
79
|
+
"MentalSaathiPipeline",
|
|
80
|
+
"mentalsaathi_chain",
|
|
81
|
+
# Output schemas — normal path
|
|
82
|
+
"ReliefResponse",
|
|
83
|
+
# Output schemas — crisis path
|
|
84
|
+
"CrisisCheck",
|
|
85
|
+
"CrisisResource",
|
|
86
|
+
# Output schemas — intermediate (for type hints / testing)
|
|
87
|
+
"BasePipelineSchema",
|
|
88
|
+
"FullAnalysis",
|
|
89
|
+
"EmotionalDetection",
|
|
90
|
+
"SurfaceVsDeep",
|
|
91
|
+
"DeepPainEntry",
|
|
92
|
+
"RootCauseExtraction",
|
|
93
|
+
"Reframe",
|
|
94
|
+
"RiskToolOutput",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def run(
|
|
99
|
+
student_message: str,
|
|
100
|
+
conversation_context: str = "",
|
|
101
|
+
) -> ReliefResponse | CrisisCheck:
|
|
102
|
+
"""Run the full 3-stage pipeline on a student message.
|
|
103
|
+
|
|
104
|
+
Convenience wrapper around :data:`mentalsaathi_chain`. Synchronous.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
student_message: Raw text from the student (Hindi/English/Hinglish).
|
|
108
|
+
conversation_context: Recent conversation history as a plain string.
|
|
109
|
+
Pass ``""`` or omit when there is no prior context.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
:class:`ReliefResponse` on the normal path — contains ``.response``
|
|
113
|
+
(the message to show the student) and ``.anchor`` (a grounded action).
|
|
114
|
+
|
|
115
|
+
:class:`CrisisCheck` when crisis signals are detected — contains
|
|
116
|
+
``.crisis_response`` and ``.resources`` (helpline list). Always
|
|
117
|
+
evaluates to ``False`` in a boolean context so callers can branch::
|
|
118
|
+
|
|
119
|
+
result = run(msg)
|
|
120
|
+
if not result: # crisis path
|
|
121
|
+
show_crisis_ui(result.crisis_response, result.resources)
|
|
122
|
+
else: # normal path
|
|
123
|
+
show_response(result.response)
|
|
124
|
+
"""
|
|
125
|
+
return mentalsaathi_chain(
|
|
126
|
+
student_message=student_message,
|
|
127
|
+
conversation_context=conversation_context,
|
|
128
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Agent utilities for the MentalSaathi AI package.
|
|
2
|
+
|
|
3
|
+
Currently exposes the risk detection agent — a standalone chain that
|
|
4
|
+
scores a student message for risk level independent of the main pipeline.
|
|
5
|
+
|
|
6
|
+
Usage::
|
|
7
|
+
|
|
8
|
+
from mentalsaathi_ai.agent import risk_agent
|
|
9
|
+
|
|
10
|
+
result = risk_agent.invoke({"student_message": "..."})
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from mentalsaathi_ai.agent.risk_detection import risk_agent
|
|
14
|
+
|
|
15
|
+
__all__ = ["risk_agent"]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""LCEL chain classes and singleton instances for the MentalSaathi pipeline.
|
|
2
|
+
|
|
3
|
+
Active 3-stage pipeline::
|
|
4
|
+
|
|
5
|
+
MentalSaathiPipeline
|
|
6
|
+
├── Stage 1: FullAnalysisChain (1 LLM call — emotion + depth + root cause)
|
|
7
|
+
├── Stage 2: CrisisDetectionChain (1 LLM call — safety gate)
|
|
8
|
+
└── Stage 3: ResponseGenerationChain (1 LLM call — reframe + relief, non-crisis)
|
|
9
|
+
|
|
10
|
+
All chain classes inherit :class:`BaseChain` which provides ``__call__``,
|
|
11
|
+
``invoke``, ``__or__``, and ``__repr__`` uniformly.
|
|
12
|
+
|
|
13
|
+
Quick usage::
|
|
14
|
+
|
|
15
|
+
from mentalsaathi_ai.chains import mentalsaathi_chain, MentalSaathiPipeline
|
|
16
|
+
|
|
17
|
+
result = mentalsaathi_chain("yaar bahut stressed hoon")
|
|
18
|
+
print(len(mentalsaathi_chain)) # 3
|
|
19
|
+
print(mentalsaathi_chain) # MentalSaathiPipeline(3 LLM calls)
|
|
20
|
+
|
|
21
|
+
Individual stage access (for testing or custom pipelines)::
|
|
22
|
+
|
|
23
|
+
from mentalsaathi_ai.chains import (
|
|
24
|
+
full_analysis_chain,
|
|
25
|
+
crisis_detection_chain,
|
|
26
|
+
response_generation_chain,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
analysis = full_analysis_chain.invoke({"student_message": "...", "conversation_context_block": ""})
|
|
30
|
+
|
|
31
|
+
Legacy chains (kept for reference — not used by the active pipeline)::
|
|
32
|
+
|
|
33
|
+
from mentalsaathi_ai.chains import (
|
|
34
|
+
emotional_tone_detection_chain,
|
|
35
|
+
depth_perception_chain,
|
|
36
|
+
root_cause_extraction_chain,
|
|
37
|
+
reframing_chain,
|
|
38
|
+
relief_response_chain,
|
|
39
|
+
risk_agent,
|
|
40
|
+
)
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
from mentalsaathi_ai.chains.base_chain import BaseChain
|
|
44
|
+
|
|
45
|
+
# Active pipeline — primary entry points
|
|
46
|
+
from mentalsaathi_ai.chains.mentalsaathi_chain import MentalSaathiPipeline, mentalsaathi_chain
|
|
47
|
+
|
|
48
|
+
# Active individual stages
|
|
49
|
+
from mentalsaathi_ai.chains.full_analysis_chain import FullAnalysisChain, full_analysis_chain
|
|
50
|
+
from mentalsaathi_ai.chains.crisis_detection_chain import CrisisDetectionChain, crisis_detection_chain
|
|
51
|
+
from mentalsaathi_ai.chains.response_generation_chain import ResponseGenerationChain, response_generation_chain
|
|
52
|
+
|
|
53
|
+
# Legacy individual stages (pre-merge, not active in pipeline)
|
|
54
|
+
from mentalsaathi_ai.chains.emotional_tone_detection import emotional_tone_detection_chain
|
|
55
|
+
from mentalsaathi_ai.chains.depth_perception_chain import depth_perception_chain
|
|
56
|
+
from mentalsaathi_ai.chains.root_cause_extraction_chain import root_cause_extraction_chain
|
|
57
|
+
from mentalsaathi_ai.chains.reframe_scenario_chain import reframing_chain
|
|
58
|
+
from mentalsaathi_ai.chains.relief_response_chain import relief_response_chain
|
|
59
|
+
from mentalsaathi_ai.chains.calculate_risk import risk_calculation_chains as risk_agent
|
|
60
|
+
|
|
61
|
+
__all__ = [
|
|
62
|
+
# Base class
|
|
63
|
+
"BaseChain",
|
|
64
|
+
# Active pipeline
|
|
65
|
+
"MentalSaathiPipeline",
|
|
66
|
+
"mentalsaathi_chain",
|
|
67
|
+
# Active stages
|
|
68
|
+
"FullAnalysisChain",
|
|
69
|
+
"full_analysis_chain",
|
|
70
|
+
"CrisisDetectionChain",
|
|
71
|
+
"crisis_detection_chain",
|
|
72
|
+
"ResponseGenerationChain",
|
|
73
|
+
"response_generation_chain",
|
|
74
|
+
# Legacy stages
|
|
75
|
+
"emotional_tone_detection_chain",
|
|
76
|
+
"depth_perception_chain",
|
|
77
|
+
"root_cause_extraction_chain",
|
|
78
|
+
"reframing_chain",
|
|
79
|
+
"relief_response_chain",
|
|
80
|
+
"risk_agent",
|
|
81
|
+
]
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from mentalsaathi_ai import run
|
|
3
|
+
from mentalsaathi_ai.schemas import CrisisCheck
|
|
4
|
+
|
|
5
|
+
RESET = "\033[0m"
|
|
6
|
+
BOLD = "\033[1m"
|
|
7
|
+
DIM = "\033[2m"
|
|
8
|
+
CYAN = "\033[96m"
|
|
9
|
+
GREEN = "\033[92m"
|
|
10
|
+
YELLOW = "\033[93m"
|
|
11
|
+
RED = "\033[91m"
|
|
12
|
+
GREY = "\033[90m"
|
|
13
|
+
WHITE = "\033[97m"
|
|
14
|
+
|
|
15
|
+
def header():
|
|
16
|
+
print(f"""
|
|
17
|
+
{CYAN}{BOLD}╔══════════════════════════════════════════════╗
|
|
18
|
+
║ TalkSaathi — Dev Chat ║
|
|
19
|
+
║ type 'quit' or Ctrl+C to exit ║
|
|
20
|
+
╚══════════════════════════════════════════════╝{RESET}
|
|
21
|
+
""")
|
|
22
|
+
|
|
23
|
+
def divider():
|
|
24
|
+
print(f"{GREY}{'─' * 48}{RESET}")
|
|
25
|
+
|
|
26
|
+
def format_bot(text: str, anchor: str = ""):
|
|
27
|
+
print(f"\n{GREEN}{BOLD}TalkSaathi{RESET}")
|
|
28
|
+
# word-wrap at 48 chars
|
|
29
|
+
words = text.split()
|
|
30
|
+
line, lines = [], []
|
|
31
|
+
for w in words:
|
|
32
|
+
if sum(len(x) + 1 for x in line) + len(w) > 48:
|
|
33
|
+
lines.append(" ".join(line))
|
|
34
|
+
line = [w]
|
|
35
|
+
else:
|
|
36
|
+
line.append(w)
|
|
37
|
+
if line:
|
|
38
|
+
lines.append(" ".join(line))
|
|
39
|
+
for l in lines:
|
|
40
|
+
print(f" {WHITE}{l}{RESET}")
|
|
41
|
+
if anchor:
|
|
42
|
+
print(f"\n {YELLOW}[anchor] {anchor}{RESET}")
|
|
43
|
+
|
|
44
|
+
def format_crisis(crisis_response: str, resources):
|
|
45
|
+
print(f"\n{RED}{BOLD}[CRISIS DETECTED]{RESET}")
|
|
46
|
+
print(f" {WHITE}{crisis_response}{RESET}")
|
|
47
|
+
print(f"\n{RED}Helplines:{RESET}")
|
|
48
|
+
for r in resources:
|
|
49
|
+
print(f" {YELLOW}{r.name}{RESET} — {r.contact} ({r.available})")
|
|
50
|
+
|
|
51
|
+
def format_thinking():
|
|
52
|
+
print(f"\n {DIM}thinking...{RESET}", end="\r")
|
|
53
|
+
|
|
54
|
+
def build_context(history: list[dict]) -> str:
|
|
55
|
+
if not history:
|
|
56
|
+
return ""
|
|
57
|
+
lines = []
|
|
58
|
+
for turn in history[-6:]: # last 6 turns max
|
|
59
|
+
lines.append(f"Student: {turn['student']}")
|
|
60
|
+
lines.append(f"TalkSaathi: {turn['bot']}")
|
|
61
|
+
return "\n".join(lines)
|
|
62
|
+
|
|
63
|
+
def main():
|
|
64
|
+
header()
|
|
65
|
+
history: list[dict] = []
|
|
66
|
+
|
|
67
|
+
while True:
|
|
68
|
+
try:
|
|
69
|
+
print(f"\n{CYAN}{BOLD}You{RESET}")
|
|
70
|
+
user_input = input(f" {WHITE}> {RESET}").strip()
|
|
71
|
+
except (KeyboardInterrupt, EOFError):
|
|
72
|
+
print(f"\n\n{DIM}Take care.{RESET}\n")
|
|
73
|
+
sys.exit(0)
|
|
74
|
+
|
|
75
|
+
if not user_input:
|
|
76
|
+
continue
|
|
77
|
+
if user_input.lower() in ("quit", "exit", "bye"):
|
|
78
|
+
print(f"\n{DIM}Take care.{RESET}\n")
|
|
79
|
+
sys.exit(0)
|
|
80
|
+
if user_input.lower() == "clear":
|
|
81
|
+
history.clear()
|
|
82
|
+
print(f"{DIM}[conversation cleared]{RESET}")
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
context = build_context(history)
|
|
86
|
+
format_thinking()
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
result = run(
|
|
90
|
+
student_message=user_input,
|
|
91
|
+
conversation_context=context,
|
|
92
|
+
)
|
|
93
|
+
except Exception as e:
|
|
94
|
+
print(f"\n {RED}[error] {e}{RESET}")
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
print(" " * 20, end="\r") # clear "thinking..."
|
|
98
|
+
|
|
99
|
+
if isinstance(result, CrisisCheck):
|
|
100
|
+
format_crisis(result.crisis_response, result.resources)
|
|
101
|
+
history.append({"student": user_input, "bot": result.crisis_response or ""})
|
|
102
|
+
else:
|
|
103
|
+
format_bot(result.response, result.anchor)
|
|
104
|
+
history.append({"student": user_input, "bot": result.response})
|
|
105
|
+
|
|
106
|
+
divider()
|
|
107
|
+
|
|
108
|
+
if __name__ == "__main__":
|
|
109
|
+
main()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from langchain_core.runnables import Runnable
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BaseChain(ABC):
|
|
7
|
+
"""Abstract base class for all MentalSaathi pipeline chain stages.
|
|
8
|
+
|
|
9
|
+
Subclasses must implement:
|
|
10
|
+
_build() -> Runnable — assemble the LCEL chain
|
|
11
|
+
__str__() -> str — human-readable one-liner description
|
|
12
|
+
|
|
13
|
+
Provides automatically:
|
|
14
|
+
__call__ — invoke the chain with a dict
|
|
15
|
+
invoke — alias for __call__ (LangChain convention)
|
|
16
|
+
__or__ — pipe this chain into another runnable (chain | next)
|
|
17
|
+
__repr__ — class name + chain repr
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self) -> None:
|
|
21
|
+
self._chain: Runnable = self._build()
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def _build(self) -> Runnable:
|
|
25
|
+
"""Assemble and return the LCEL chain for this stage."""
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def __str__(self) -> str:
|
|
29
|
+
"""One-line human description of what this stage does."""
|
|
30
|
+
|
|
31
|
+
def __call__(self, inputs: dict):
|
|
32
|
+
return self._chain.invoke(inputs)
|
|
33
|
+
|
|
34
|
+
def invoke(self, inputs: dict):
|
|
35
|
+
return self._chain.invoke(inputs)
|
|
36
|
+
|
|
37
|
+
def __or__(self, other) -> Runnable:
|
|
38
|
+
return self._chain | other
|
|
39
|
+
|
|
40
|
+
def __repr__(self) -> str:
|
|
41
|
+
return f"{self.__class__.__name__}(chain={self._chain!r})"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import mentalsaathi_ai.prompts
|
|
2
|
+
import langchain_core.prompts
|
|
3
|
+
import mentalsaathi_ai.llms
|
|
4
|
+
|
|
5
|
+
prompt = langchain_core.prompts.ChatPromptTemplate.from_messages(
|
|
6
|
+
[
|
|
7
|
+
("system", mentalsaathi_ai.prompts.risk_prompt["system"]),
|
|
8
|
+
("human", mentalsaathi_ai.prompts.risk_prompt["human"]),
|
|
9
|
+
]
|
|
10
|
+
)
|
|
11
|
+
prompt = prompt.partial(
|
|
12
|
+
format_instructions=mentalsaathi_ai.prompts.risk_parser["instructions"]
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
risk_calculation_chains = (
|
|
16
|
+
prompt | mentalsaathi_ai.llms.brain | mentalsaathi_ai.prompts.risk_parser["body"]
|
|
17
|
+
)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from langchain_core.prompts import ChatPromptTemplate
|
|
3
|
+
from langchain_core.runnables import Runnable, RunnablePassthrough
|
|
4
|
+
from mentalsaathi_ai.chains.base_chain import BaseChain
|
|
5
|
+
from mentalsaathi_ai.prompts import crisis_detection_tools
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CrisisDetectionChain(BaseChain):
|
|
9
|
+
"""Stage 2 — the safety gate. Never merged with other stages.
|
|
10
|
+
|
|
11
|
+
Uses RunnablePassthrough.assign so all upstream analysis keys are
|
|
12
|
+
preserved alongside the new crisis_check key.
|
|
13
|
+
|
|
14
|
+
A false negative here can cost a life. When in doubt, flag as crisis.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def _build(self) -> Runnable:
|
|
18
|
+
import mentalsaathi_ai.llms
|
|
19
|
+
template = ChatPromptTemplate.from_messages(
|
|
20
|
+
[
|
|
21
|
+
("system", crisis_detection_tools.system_prompt),
|
|
22
|
+
("human", crisis_detection_tools.human_prompt),
|
|
23
|
+
]
|
|
24
|
+
).partial(format_instructions=crisis_detection_tools.parser.get_format_instructions())
|
|
25
|
+
|
|
26
|
+
return RunnablePassthrough.assign(
|
|
27
|
+
crisis_check=(template | mentalsaathi_ai.llms.brain | crisis_detection_tools.parser)
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def __str__(self) -> str:
|
|
31
|
+
return "CrisisDetectionChain(safety gate → adds crisis_check key)"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
crisis_detection_chain = CrisisDetectionChain()
|