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.
Files changed (52) hide show
  1. mentalsaathi_ai-0.1.0/PKG-INFO +136 -0
  2. mentalsaathi_ai-0.1.0/README.md +121 -0
  3. mentalsaathi_ai-0.1.0/mentalsaathi_ai/__init__.py +128 -0
  4. mentalsaathi_ai-0.1.0/mentalsaathi_ai/agent/__init__.py +15 -0
  5. mentalsaathi_ai-0.1.0/mentalsaathi_ai/agent/risk_detection.py +3 -0
  6. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/__init__.py +81 -0
  7. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/__main__.py +109 -0
  8. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/base_chain.py +41 -0
  9. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/calculate_risk.py +17 -0
  10. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/crisis_detection_chain.py +34 -0
  11. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/depth_perception_chain.py +32 -0
  12. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/emotional_tone_detection.py +22 -0
  13. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/full_analysis_chain.py +38 -0
  14. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/mentalsaathi_chain.py +87 -0
  15. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/reframe_scenario_chain.py +17 -0
  16. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/relief_response_chain.py +30 -0
  17. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/response_generation_chain.py +35 -0
  18. mentalsaathi_ai-0.1.0/mentalsaathi_ai/chains/root_cause_extraction_chain.py +26 -0
  19. mentalsaathi_ai-0.1.0/mentalsaathi_ai/core/__init__.py +23 -0
  20. mentalsaathi_ai-0.1.0/mentalsaathi_ai/core/settings.py +59 -0
  21. mentalsaathi_ai-0.1.0/mentalsaathi_ai/llms/__init__.py +24 -0
  22. mentalsaathi_ai-0.1.0/mentalsaathi_ai/llms/llm.py +55 -0
  23. mentalsaathi_ai-0.1.0/mentalsaathi_ai/memory/__init__.py +9 -0
  24. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/__init__.py +74 -0
  25. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/base_prompt.py +68 -0
  26. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/crisis_detection.py +10 -0
  27. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/depth_perception.py +14 -0
  28. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/emotion_detection.py +14 -0
  29. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/full_analysis.py +8 -0
  30. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/loader.py +8 -0
  31. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/reframe_scenario.py +10 -0
  32. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/relief_response.py +10 -0
  33. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/response_generation.py +8 -0
  34. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/risk.py +68 -0
  35. mentalsaathi_ai-0.1.0/mentalsaathi_ai/prompts/root_cause_extraction.py +6 -0
  36. mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/__init__.py +52 -0
  37. mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/base_schema.py +40 -0
  38. mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/crisis_detection.py +82 -0
  39. mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/depth_perception.py +43 -0
  40. mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/emotion.py +50 -0
  41. mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/full_analysis.py +49 -0
  42. mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/reframe_scenario.py +40 -0
  43. mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/relief_response.py +64 -0
  44. mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/risk.py +35 -0
  45. mentalsaathi_ai-0.1.0/mentalsaathi_ai/schemas/root_cause.py +36 -0
  46. mentalsaathi_ai-0.1.0/mentalsaathi_ai.egg-info/PKG-INFO +136 -0
  47. mentalsaathi_ai-0.1.0/mentalsaathi_ai.egg-info/SOURCES.txt +50 -0
  48. mentalsaathi_ai-0.1.0/mentalsaathi_ai.egg-info/dependency_links.txt +1 -0
  49. mentalsaathi_ai-0.1.0/mentalsaathi_ai.egg-info/requires.txt +7 -0
  50. mentalsaathi_ai-0.1.0/mentalsaathi_ai.egg-info/top_level.txt +1 -0
  51. mentalsaathi_ai-0.1.0/pyproject.toml +30 -0
  52. 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,3 @@
1
+ from mentalsaathi_ai.chains.calculate_risk import risk_calculation_chains as risk_agent
2
+
3
+ __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()