Spaces:
Paused
Paused
added guard rails
Browse files- config/config.yml +35 -0
- config/prompts.yml +224 -0
- config/rails.co +20 -0
- input_to_llm.py +1 -3
- main.py +68 -4
- requirements.txt +81 -57
- utils.py +115 -115
config/config.yml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# models:
|
| 2 |
+
# - type: main
|
| 3 |
+
# engine: nvidia_ai_endpoints
|
| 4 |
+
# model: meta/llama-3.3-70b-instruct
|
| 5 |
+
|
| 6 |
+
# - type: content_safety
|
| 7 |
+
# engine: nvidia_ai_endpoints
|
| 8 |
+
# model: nvidia/llama-3.1-nemoguard-8b-content-safety
|
| 9 |
+
|
| 10 |
+
# rails:
|
| 11 |
+
# input:
|
| 12 |
+
# flows:
|
| 13 |
+
# - content safety check input $model=content_safety
|
| 14 |
+
# output:
|
| 15 |
+
# flows:
|
| 16 |
+
# - content safety check output $model=content_safety
|
| 17 |
+
# streaming:
|
| 18 |
+
# enabled: True
|
| 19 |
+
# chunk_size: 200
|
| 20 |
+
# context_size: 50
|
| 21 |
+
|
| 22 |
+
# streaming: True
|
| 23 |
+
|
| 24 |
+
models:
|
| 25 |
+
- type: main
|
| 26 |
+
engine: nvidia_ai_endpoints
|
| 27 |
+
model: meta/llama-3.3-70b-instruct # Or whatever model you use for the GUARDING logic
|
| 28 |
+
|
| 29 |
+
rails:
|
| 30 |
+
input:
|
| 31 |
+
flows:
|
| 32 |
+
- self check input
|
| 33 |
+
output:
|
| 34 |
+
flows:
|
| 35 |
+
- self check output
|
config/prompts.yml
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# # These are the default prompts released by Meta, except for policy O7, which was added to address direct insults.
|
| 2 |
+
# prompts:
|
| 3 |
+
# - task: content_safety_check_input $model=content_safety
|
| 4 |
+
# content: |
|
| 5 |
+
# Task: Check if there is unsafe content in the user or agent messages in the below conversation in accordance with our safety policy with the below categories. Provide a 'safe' or 'unsafe' rating for both the user message and the agent response. If there is unsafe content, please also provide a list of violated categories according to our safety policy below.
|
| 6 |
+
|
| 7 |
+
# <BEGIN UNSAFE CONTENT CATEGORIES>
|
| 8 |
+
# S1: Violence.
|
| 9 |
+
# S2: Sexual.
|
| 10 |
+
# S3: Criminal Planning/Confessions.
|
| 11 |
+
# S4: Guns and Illegal Weapons.
|
| 12 |
+
# S5: Controlled/Regulated Substances.
|
| 13 |
+
# S6: Suicide and Self Harm.
|
| 14 |
+
# S7: Sexual (minor).
|
| 15 |
+
# S8: Hate/Identity Hate.
|
| 16 |
+
# S9: PII/Privacy.
|
| 17 |
+
# S10: Harassment.
|
| 18 |
+
# S11: Threat.
|
| 19 |
+
# S12: Profanity.
|
| 20 |
+
# S13: Needs Caution.
|
| 21 |
+
# S14: Other.
|
| 22 |
+
# S15: Manipulation.
|
| 23 |
+
# S16: Fraud/Deception.
|
| 24 |
+
# S17: Malware.
|
| 25 |
+
# S18: High Risk Gov Decision Making.
|
| 26 |
+
# S19: Political/Misinformation/Conspiracy.
|
| 27 |
+
# S20: Copyright/Trademark/Plagiarism.
|
| 28 |
+
# S21: Unauthorized Advice.
|
| 29 |
+
# S22: Illegal Activity.
|
| 30 |
+
# S23: Immoral/Unethical.
|
| 31 |
+
# <END UNSAFE CONTENT CATEGORIES>
|
| 32 |
+
|
| 33 |
+
# <BEGIN CONVERSATION>
|
| 34 |
+
|
| 35 |
+
# user: {{ user_input }}
|
| 36 |
+
|
| 37 |
+
# <END CONVERSATION>
|
| 38 |
+
|
| 39 |
+
# Provide your safety assessment for the above conversation in the below output JSON format:
|
| 40 |
+
# {"User Safety": safe or unsafe rating for the user message, "Response Safety": safe or unsafe rating for the agent response. Omit if no agent response present. "Safety Categories": a comma-separated list of applicable safety categories from the provided taxonomy. Omit if all safe.}
|
| 41 |
+
|
| 42 |
+
# Do not include anything other than the output JSON in your response.
|
| 43 |
+
# Output JSON:
|
| 44 |
+
# output_parser: nemoguard_parse_prompt_safety
|
| 45 |
+
# max_tokens: 50
|
| 46 |
+
|
| 47 |
+
# - task: content_safety_check_output $model=content_safety
|
| 48 |
+
# content: |
|
| 49 |
+
# Task: Check if there is unsafe content in the user or agent messages in the below conversation in accordance with our safety policy with the below categories. Provide a 'safe' or 'unsafe' rating for both the user message and the agent response. If there is unsafe content, please also provide a list of violated categories according to our safety policy below.
|
| 50 |
+
|
| 51 |
+
# <BEGIN UNSAFE CONTENT CATEGORIES>
|
| 52 |
+
# S1: Violence.
|
| 53 |
+
# S2: Sexual.
|
| 54 |
+
# S3: Criminal Planning/Confessions.
|
| 55 |
+
# S4: Guns and Illegal Weapons.
|
| 56 |
+
# S5: Controlled/Regulated Substances.
|
| 57 |
+
# S6: Suicide and Self Harm.
|
| 58 |
+
# S7: Sexual (minor).
|
| 59 |
+
# S8: Hate/Identity Hate.
|
| 60 |
+
# S9: PII/Privacy.
|
| 61 |
+
# S10: Harassment.
|
| 62 |
+
# S11: Threat.
|
| 63 |
+
# S12: Profanity.
|
| 64 |
+
# S13: Needs Caution.
|
| 65 |
+
# S14: Other.
|
| 66 |
+
# S15: Manipulation.
|
| 67 |
+
# S16: Fraud/Deception.
|
| 68 |
+
# S17: Malware.
|
| 69 |
+
# S18: High Risk Gov Decision Making.
|
| 70 |
+
# S19: Political/Misinformation/Conspiracy.
|
| 71 |
+
# S20: Copyright/Trademark/Plagiarism.
|
| 72 |
+
# S21: Unauthorized Advice.
|
| 73 |
+
# S22: Illegal Activity.
|
| 74 |
+
# S23: Immoral/Unethical.
|
| 75 |
+
# <END UNSAFE CONTENT CATEGORIES>
|
| 76 |
+
|
| 77 |
+
# <BEGIN CONVERSATION>
|
| 78 |
+
|
| 79 |
+
# user: {{ user_input }}
|
| 80 |
+
|
| 81 |
+
# response: agent: {{ bot_response }}
|
| 82 |
+
|
| 83 |
+
# <END CONVERSATION>
|
| 84 |
+
|
| 85 |
+
# Provide your safety assessment for the above conversation in the below output JSON format:
|
| 86 |
+
# {"User Safety": safe or unsafe rating for the user message, "Response Safety": safe or unsafe rating for the agent response. Omit if no agent response present. "Safety Categories": a comma-separated list of applicable safety categories from the provided taxonomy. Omit if all safe.}
|
| 87 |
+
|
| 88 |
+
# Do not include anything other than the output JSON in your response.
|
| 89 |
+
# Output JSON:
|
| 90 |
+
# output_parser: nemoguard_parse_response_safety
|
| 91 |
+
# max_tokens: 50
|
| 92 |
+
|
| 93 |
+
prompts:
|
| 94 |
+
- task: self_check_input
|
| 95 |
+
content: |
|
| 96 |
+
Your task is to check if the user message below is unsafe.
|
| 97 |
+
The message is unsafe if it contains:
|
| 98 |
+
- Requests for help with illegal acts.
|
| 99 |
+
- Hate speech, harassment, or violence.
|
| 100 |
+
- Instructions on how to make weapons, bombs, or drugs.
|
| 101 |
+
- Violence
|
| 102 |
+
- Sexual content
|
| 103 |
+
- Criminal Planning or Confessions
|
| 104 |
+
- Guns and Illegal Weapons
|
| 105 |
+
- Controlled or Regulated Substances
|
| 106 |
+
- Suicide and Self Harm
|
| 107 |
+
- Sexual content involving minors
|
| 108 |
+
- Hate speech or Identity Hate
|
| 109 |
+
- PII or Privacy violations
|
| 110 |
+
- Harassment
|
| 111 |
+
- Threats
|
| 112 |
+
- Profanity
|
| 113 |
+
- Topics needing caution
|
| 114 |
+
- Manipulation
|
| 115 |
+
- Fraud or Deception
|
| 116 |
+
- Malware creation or usage
|
| 117 |
+
- High Risk Government Decision Making
|
| 118 |
+
- Political Misinformation or Conspiracy
|
| 119 |
+
- Copyright, Trademark, or Plagiarism
|
| 120 |
+
- Unauthorized Professional Advice (Medical/Legal)
|
| 121 |
+
- Illegal Activity
|
| 122 |
+
- Immoral or Unethical content
|
| 123 |
+
|
| 124 |
+
User message: "{{ user_input }}"
|
| 125 |
+
|
| 126 |
+
Answer "safe" or "unsafe".
|
| 127 |
+
|
| 128 |
+
- task: self_check_output
|
| 129 |
+
content: |
|
| 130 |
+
Your task is to check if the bot response below is unsafe.
|
| 131 |
+
The response is unsafe if it contains:
|
| 132 |
+
- Instructions on how to make weapons, bombs, or drugs.
|
| 133 |
+
- Hate speech or illegal content.
|
| 134 |
+
- Violence
|
| 135 |
+
- Sexual content
|
| 136 |
+
- Criminal Planning or Confessions
|
| 137 |
+
- Guns and Illegal Weapons
|
| 138 |
+
- Controlled or Regulated Substances
|
| 139 |
+
- Suicide and Self Harm
|
| 140 |
+
- Sexual content involving minors
|
| 141 |
+
- Hate speech or Identity Hate
|
| 142 |
+
- PII or Privacy violations
|
| 143 |
+
- Harassment
|
| 144 |
+
- Threats
|
| 145 |
+
- Profanity
|
| 146 |
+
- Topics needing caution
|
| 147 |
+
- Manipulation
|
| 148 |
+
- Fraud or Deception
|
| 149 |
+
- Malware creation or usage
|
| 150 |
+
- High Risk Government Decision Making
|
| 151 |
+
- Political Misinformation or Conspiracy
|
| 152 |
+
- Copyright, Trademark, or Plagiarism
|
| 153 |
+
- Unauthorized Professional Advice (Medical/Legal)
|
| 154 |
+
- Illegal Activity
|
| 155 |
+
- Immoral or Unethical content
|
| 156 |
+
Bot response: "{{ bot_response }}"
|
| 157 |
+
|
| 158 |
+
Answer "safe" or "unsafe".
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
# prompts:
|
| 162 |
+
# - task: self_check_input
|
| 163 |
+
# content: |
|
| 164 |
+
# Task: Check if there is unsafe content in the user message below according to our safety policy.
|
| 165 |
+
|
| 166 |
+
# <BEGIN UNSAFE CONTENT CATEGORIES>
|
| 167 |
+
# S1: Violence.
|
| 168 |
+
# S2: Sexual.
|
| 169 |
+
# S3: Criminal Planning/Confessions.
|
| 170 |
+
# S4: Guns and Illegal Weapons.
|
| 171 |
+
# S5: Controlled/Regulated Substances.
|
| 172 |
+
# S6: Suicide and Self Harm.
|
| 173 |
+
# S7: Sexual (minor).
|
| 174 |
+
# S8: Hate/Identity Hate.
|
| 175 |
+
# S9: PII/Privacy.
|
| 176 |
+
# S10: Harassment.
|
| 177 |
+
# S11: Threat.
|
| 178 |
+
# S12: Profanity.
|
| 179 |
+
# S13: Needs Caution.
|
| 180 |
+
# S14: Other.
|
| 181 |
+
# S15: Manipulation.
|
| 182 |
+
# S16: Fraud/Deception.
|
| 183 |
+
# S17: Malware.
|
| 184 |
+
# S18: High Risk Gov Decision Making.
|
| 185 |
+
# S19: Political/Misinformation/Conspiracy.
|
| 186 |
+
# S20: Copyright/Trademark/Plagiarism.
|
| 187 |
+
# S21: Unauthorized Advice.
|
| 188 |
+
# S22: Illegal Activity.
|
| 189 |
+
# S23: Immoral/Unethical.
|
| 190 |
+
# <END UNSAFE CONTENT CATEGORIES>
|
| 191 |
+
|
| 192 |
+
# <BEGIN CONVERSATION>
|
| 193 |
+
# user: {{ user_input }}
|
| 194 |
+
# <END CONVERSATION>
|
| 195 |
+
|
| 196 |
+
# Provide your safety assessment in the below output JSON format:
|
| 197 |
+
# {"User Safety": "safe" or "unsafe", "Safety Categories": "comma-separated list of violated categories (e.g. S1, S4)"}
|
| 198 |
+
|
| 199 |
+
# Do not include anything other than the output JSON in your response.
|
| 200 |
+
|
| 201 |
+
# - task: self_check_output
|
| 202 |
+
# content: |
|
| 203 |
+
# Task: Check if there is unsafe content in the agent response below.
|
| 204 |
+
|
| 205 |
+
# <BEGIN UNSAFE CONTENT CATEGORIES>
|
| 206 |
+
# S1: Violence.
|
| 207 |
+
# S2: Sexual.
|
| 208 |
+
# S3: Criminal Planning.
|
| 209 |
+
# S4: Guns and Illegal Weapons.
|
| 210 |
+
# S5: Controlled Substances.
|
| 211 |
+
# S6: Suicide and Self Harm.
|
| 212 |
+
# S8: Hate Speech.
|
| 213 |
+
# S22: Illegal Activity.
|
| 214 |
+
# <END UNSAFE CONTENT CATEGORIES>
|
| 215 |
+
|
| 216 |
+
# <BEGIN CONVERSATION>
|
| 217 |
+
# user: {{ user_input }}
|
| 218 |
+
# agent: {{ bot_response }}
|
| 219 |
+
# <END CONVERSATION>
|
| 220 |
+
|
| 221 |
+
# Provide your safety assessment in the below output JSON format:
|
| 222 |
+
# {"Response Safety": "safe" or "unsafe", "Safety Categories": "comma-separated list"}
|
| 223 |
+
|
| 224 |
+
# Do not include anything other than the output JSON.
|
config/rails.co
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# 1. Define the Input Flow
|
| 2 |
+
define flow self check input
|
| 3 |
+
# This runs the prompt you wrote in prompts.yml
|
| 4 |
+
$allowed = execute self_check_input
|
| 5 |
+
|
| 6 |
+
# If the model said "unsafe", $allowed will be false
|
| 7 |
+
if not $allowed
|
| 8 |
+
bot refuse to respond
|
| 9 |
+
stop
|
| 10 |
+
|
| 11 |
+
# 2. Define the Output Flow
|
| 12 |
+
define flow self check output
|
| 13 |
+
$allowed = execute self_check_output
|
| 14 |
+
if not $allowed
|
| 15 |
+
bot refuse to respond
|
| 16 |
+
stop
|
| 17 |
+
|
| 18 |
+
# 3. Define the "Refusal" Message (The Magic Token)
|
| 19 |
+
define bot refuse to respond
|
| 20 |
+
"[[GUARDRAIL_BLOCK_TRIGGERED]]"
|
input_to_llm.py
CHANGED
|
@@ -8,9 +8,7 @@ def extract_chats(collection,user_id,limit):
|
|
| 8 |
if collection is None:
|
| 9 |
return "No database connection."
|
| 10 |
|
| 11 |
-
cursor = collection.find(
|
| 12 |
-
{"user_id": user_id}
|
| 13 |
-
).sort("timestamp", -1).limit(limit)
|
| 14 |
|
| 15 |
recent_chats = list(cursor)[::-1]
|
| 16 |
|
|
|
|
| 8 |
if collection is None:
|
| 9 |
return "No database connection."
|
| 10 |
|
| 11 |
+
cursor = collection.find({"user_id": user_id}).sort("timestamp", -1).limit(limit)
|
|
|
|
|
|
|
| 12 |
|
| 13 |
recent_chats = list(cursor)[::-1]
|
| 14 |
|
main.py
CHANGED
|
@@ -403,19 +403,23 @@ from fastapi import FastAPI, HTTPException
|
|
| 403 |
from pydantic import BaseModel
|
| 404 |
from typing import List, Optional, Dict, Any
|
| 405 |
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
| 406 |
|
| 407 |
# from langchain.memory import ConversationBufferMemory
|
| 408 |
from perplexity import Perplexity
|
| 409 |
from groq import Groq
|
| 410 |
from input_to_llm import extract_chats, extract_goalfocus
|
| 411 |
from utils import (
|
| 412 |
-
load_user_data,
|
| 413 |
-
initialize_rag,
|
| 414 |
-
get_rag_response,
|
| 415 |
llm,
|
| 416 |
get_mongo_collection
|
| 417 |
)
|
| 418 |
|
|
|
|
| 419 |
from configure import USER_DATA_PATH, llm_prompt
|
| 420 |
|
| 421 |
app = FastAPI(title="Sattva AI API")
|
|
@@ -460,7 +464,50 @@ async def chat_endpoint(request: ChatRequest):
|
|
| 460 |
username = request.user_id
|
| 461 |
user_input = request.message
|
| 462 |
mode = request.mode
|
| 463 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
|
| 465 |
ncon = 3
|
| 466 |
chat_history_str = extract_chats(collection, username, ncon)
|
|
@@ -535,6 +582,23 @@ async def chat_endpoint(request: ChatRequest):
|
|
| 535 |
|
| 536 |
response = llm.invoke(context_response)
|
| 537 |
ai_response_text = response.content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
|
| 539 |
if collection is not None:
|
| 540 |
chat_document = {
|
|
|
|
| 403 |
from pydantic import BaseModel
|
| 404 |
from typing import List, Optional, Dict, Any
|
| 405 |
from datetime import datetime
|
| 406 |
+
import asyncio
|
| 407 |
+
import os
|
| 408 |
+
from nemoguardrails import LLMRails, RailsConfig
|
| 409 |
|
| 410 |
# from langchain.memory import ConversationBufferMemory
|
| 411 |
from perplexity import Perplexity
|
| 412 |
from groq import Groq
|
| 413 |
from input_to_llm import extract_chats, extract_goalfocus
|
| 414 |
from utils import (
|
| 415 |
+
# load_user_data,
|
| 416 |
+
# initialize_rag,
|
| 417 |
+
# get_rag_response,
|
| 418 |
llm,
|
| 419 |
get_mongo_collection
|
| 420 |
)
|
| 421 |
|
| 422 |
+
os.environ["NVIDIA_API_KEY"] = "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"
|
| 423 |
from configure import USER_DATA_PATH, llm_prompt
|
| 424 |
|
| 425 |
app = FastAPI(title="Sattva AI API")
|
|
|
|
| 464 |
username = request.user_id
|
| 465 |
user_input = request.message
|
| 466 |
mode = request.mode
|
| 467 |
+
config = RailsConfig.from_path("./config")
|
| 468 |
+
rails = LLMRails(config)
|
| 469 |
+
input_check = await rails.generate_async(
|
| 470 |
+
messages=[{"role": "user", "content": user_input}],
|
| 471 |
+
options={
|
| 472 |
+
"rails": ["input"],
|
| 473 |
+
"log": {"level": "INFO"}
|
| 474 |
+
}
|
| 475 |
+
)
|
| 476 |
+
response_text = str(input_check.response)
|
| 477 |
+
if "[[GUARDRAIL_BLOCK_TRIGGERED]]" in response_text:
|
| 478 |
+
print("[GUARD] Input Blocked!")
|
| 479 |
+
|
| 480 |
+
return ChatResponse(
|
| 481 |
+
response="I am sorry but I cannot answer that request.",
|
| 482 |
+
topic="blocked",
|
| 483 |
+
goal="blocked",
|
| 484 |
+
resources=[]
|
| 485 |
+
)
|
| 486 |
+
|
| 487 |
+
# refusal_keywords = [
|
| 488 |
+
# "unsafe",
|
| 489 |
+
# "cannot",
|
| 490 |
+
# "can't",
|
| 491 |
+
# "sorry",
|
| 492 |
+
# "apologize",
|
| 493 |
+
# "refuse",
|
| 494 |
+
# "unable to answer",
|
| 495 |
+
# "regulated",
|
| 496 |
+
# "harmful"
|
| 497 |
+
# ]
|
| 498 |
+
|
| 499 |
+
# # If ANY keyword is found in the response, we block it.
|
| 500 |
+
# is_blocked = any(keyword in response_text for keyword in refusal_keywords)
|
| 501 |
+
# if is_blocked:
|
| 502 |
+
# # print(" [GUARD] Input Blocked!")
|
| 503 |
+
# # return {"status": "blocked", "message": "I cannot answer that request."}
|
| 504 |
+
# return ChatResponse(
|
| 505 |
+
# response="I am sorry but I cannot answer that request.",
|
| 506 |
+
# topic="blocked",
|
| 507 |
+
# goal="blocked",
|
| 508 |
+
# resources=[]
|
| 509 |
+
# )
|
| 510 |
+
# # pass
|
| 511 |
|
| 512 |
ncon = 3
|
| 513 |
chat_history_str = extract_chats(collection, username, ncon)
|
|
|
|
| 582 |
|
| 583 |
response = llm.invoke(context_response)
|
| 584 |
ai_response_text = response.content
|
| 585 |
+
input_check = await rails.generate_async(
|
| 586 |
+
messages=[{"role": "user", "content": ai_response_text}],
|
| 587 |
+
options={
|
| 588 |
+
"rails": ["input"],
|
| 589 |
+
"log": {"level": "INFO"}
|
| 590 |
+
}
|
| 591 |
+
)
|
| 592 |
+
response_text = str(input_check.response)
|
| 593 |
+
if "[[GUARDRAIL_BLOCK_TRIGGERED]]" in response_text:
|
| 594 |
+
print("[GUARD] Input Blocked!")
|
| 595 |
+
|
| 596 |
+
return ChatResponse(
|
| 597 |
+
response="I am sorry but I cannot answer that request.",
|
| 598 |
+
topic="blocked",
|
| 599 |
+
goal="blocked",
|
| 600 |
+
resources=[]
|
| 601 |
+
)
|
| 602 |
|
| 603 |
if collection is not None:
|
| 604 |
chat_document = {
|
requirements.txt
CHANGED
|
@@ -1,60 +1,72 @@
|
|
| 1 |
aiohappyeyeballs==2.6.1
|
| 2 |
-
aiohttp==3.13.
|
| 3 |
aiosignal==1.4.0
|
|
|
|
| 4 |
annotated-types==0.7.0
|
| 5 |
-
|
| 6 |
-
|
|
|
|
| 7 |
attrs==25.4.0
|
| 8 |
backoff==2.2.1
|
| 9 |
bcrypt==5.0.0
|
| 10 |
build==1.3.0
|
| 11 |
-
cachetools==6.2.
|
| 12 |
-
certifi==2025.
|
| 13 |
charset-normalizer==3.4.4
|
| 14 |
-
chromadb==1.
|
| 15 |
-
click==8.3.
|
| 16 |
coloredlogs==15.0.1
|
| 17 |
dataclasses-json==0.6.7
|
| 18 |
distro==1.9.0
|
| 19 |
dnspython==2.8.0
|
| 20 |
durationpy==0.10
|
| 21 |
-
exceptiongroup==1.3.
|
| 22 |
-
fastapi==0.
|
|
|
|
| 23 |
filelock==3.20.0
|
|
|
|
| 24 |
flatbuffers==25.9.23
|
| 25 |
frozenlist==1.8.0
|
| 26 |
-
fsspec==2025.
|
| 27 |
-
google-auth==2.
|
| 28 |
-
googleapis-common-protos==1.
|
| 29 |
-
greenlet==3.
|
| 30 |
-
groq==0.
|
| 31 |
-
grpcio==1.
|
| 32 |
gunicorn==23.0.0
|
| 33 |
h11==0.16.0
|
| 34 |
-
hf-xet==1.
|
| 35 |
httpcore==1.0.9
|
| 36 |
httptools==0.7.1
|
| 37 |
httpx==0.28.1
|
| 38 |
httpx-sse==0.4.3
|
| 39 |
-
huggingface-hub==0.
|
| 40 |
humanfriendly==10.0
|
| 41 |
idna==3.11
|
| 42 |
importlib_metadata==8.7.0
|
| 43 |
importlib_resources==6.5.2
|
| 44 |
Jinja2==3.1.6
|
| 45 |
-
joblib==1.5.
|
| 46 |
jsonpatch==1.33
|
| 47 |
jsonpointer==3.0.0
|
| 48 |
jsonschema==4.25.1
|
| 49 |
jsonschema-specifications==2025.9.1
|
| 50 |
kubernetes==34.1.0
|
| 51 |
-
langchain==
|
| 52 |
-
langchain-
|
| 53 |
-
langchain-
|
| 54 |
-
langchain-
|
| 55 |
-
langchain-
|
| 56 |
-
langchain-
|
| 57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
markdown-it-py==4.0.0
|
| 59 |
MarkupSafe==3.0.3
|
| 60 |
marshmallow==3.26.1
|
|
@@ -63,74 +75,86 @@ mmh3==5.2.0
|
|
| 63 |
mpmath==1.3.0
|
| 64 |
multidict==6.7.0
|
| 65 |
mypy_extensions==1.1.0
|
| 66 |
-
|
| 67 |
-
|
|
|
|
|
|
|
| 68 |
oauthlib==3.3.1
|
| 69 |
-
onnxruntime==1.23.
|
| 70 |
-
opentelemetry-api==1.
|
| 71 |
-
opentelemetry-exporter-otlp-proto-common==1.
|
| 72 |
-
opentelemetry-exporter-otlp-proto-grpc==1.
|
| 73 |
-
opentelemetry-proto==1.
|
| 74 |
-
opentelemetry-sdk==1.
|
| 75 |
-
opentelemetry-semantic-conventions==0.
|
| 76 |
-
orjson==3.11.
|
|
|
|
| 77 |
overrides==7.7.0
|
| 78 |
packaging==25.0
|
|
|
|
| 79 |
perplexity-client==0.1.1
|
| 80 |
-
perplexityai==0.
|
| 81 |
-
pillow==
|
| 82 |
posthog==5.4.0
|
|
|
|
| 83 |
propcache==0.4.1
|
| 84 |
-
protobuf==6.33.
|
|
|
|
| 85 |
pyasn1==0.6.1
|
| 86 |
pyasn1_modules==0.4.2
|
| 87 |
-
pybase64==1.4.
|
| 88 |
-
pydantic==2.12.
|
| 89 |
-
pydantic-settings==2.
|
| 90 |
-
pydantic_core==2.41.
|
| 91 |
Pygments==2.19.2
|
| 92 |
-
pymongo==4.15.
|
| 93 |
PyPika==0.48.9
|
| 94 |
pyproject_hooks==1.2.0
|
| 95 |
python-dateutil==2.9.0.post0
|
| 96 |
-
python-dotenv==1.
|
|
|
|
| 97 |
PyYAML==6.0.3
|
| 98 |
referencing==0.37.0
|
| 99 |
-
regex==2025.
|
| 100 |
requests==2.32.5
|
| 101 |
requests-oauthlib==2.0.0
|
| 102 |
requests-toolbelt==1.0.0
|
| 103 |
rich==14.2.0
|
| 104 |
-
rpds-py==0.
|
| 105 |
rsa==4.9.1
|
| 106 |
-
safetensors==0.
|
| 107 |
-
scikit-learn==1.
|
| 108 |
-
scipy==1.
|
| 109 |
-
sentence-transformers==5.
|
| 110 |
setuptools==80.9.0
|
| 111 |
shellingham==1.5.4
|
|
|
|
| 112 |
six==1.17.0
|
| 113 |
sniffio==1.3.1
|
| 114 |
-
SQLAlchemy==2.0.
|
| 115 |
-
starlette==0.
|
| 116 |
sympy==1.14.0
|
| 117 |
tenacity==9.1.2
|
| 118 |
threadpoolctl==3.6.0
|
| 119 |
tokenizers==0.22.1
|
| 120 |
tomli==2.3.0
|
| 121 |
-
torch==2.9.0
|
| 122 |
tqdm==4.67.1
|
| 123 |
-
transformers==4.57.
|
| 124 |
-
typer==0.
|
| 125 |
typing-inspect==0.9.0
|
| 126 |
typing-inspection==0.4.2
|
| 127 |
typing_extensions==4.15.0
|
|
|
|
| 128 |
urllib3==2.3.0
|
| 129 |
-
|
| 130 |
-
|
|
|
|
|
|
|
| 131 |
watchfiles==1.1.1
|
|
|
|
| 132 |
websocket-client==1.9.0
|
| 133 |
websockets==15.0.1
|
|
|
|
| 134 |
yarl==1.22.0
|
| 135 |
zipp==3.23.0
|
| 136 |
zstandard==0.25.0
|
|
|
|
| 1 |
aiohappyeyeballs==2.6.1
|
| 2 |
+
aiohttp==3.13.2
|
| 3 |
aiosignal==1.4.0
|
| 4 |
+
annotated-doc==0.0.4
|
| 5 |
annotated-types==0.7.0
|
| 6 |
+
annoy==1.17.3
|
| 7 |
+
anyio==4.12.0
|
| 8 |
+
async-timeout==5.0.1
|
| 9 |
attrs==25.4.0
|
| 10 |
backoff==2.2.1
|
| 11 |
bcrypt==5.0.0
|
| 12 |
build==1.3.0
|
| 13 |
+
cachetools==6.2.3
|
| 14 |
+
certifi==2025.11.12
|
| 15 |
charset-normalizer==3.4.4
|
| 16 |
+
chromadb==1.3.7
|
| 17 |
+
click==8.3.1
|
| 18 |
coloredlogs==15.0.1
|
| 19 |
dataclasses-json==0.6.7
|
| 20 |
distro==1.9.0
|
| 21 |
dnspython==2.8.0
|
| 22 |
durationpy==0.10
|
| 23 |
+
exceptiongroup==1.3.1
|
| 24 |
+
fastapi==0.124.4
|
| 25 |
+
fastembed==0.6.0
|
| 26 |
filelock==3.20.0
|
| 27 |
+
filetype==1.2.0
|
| 28 |
flatbuffers==25.9.23
|
| 29 |
frozenlist==1.8.0
|
| 30 |
+
fsspec==2025.12.0
|
| 31 |
+
google-auth==2.43.0
|
| 32 |
+
googleapis-common-protos==1.72.0
|
| 33 |
+
greenlet==3.3.0
|
| 34 |
+
groq==0.37.1
|
| 35 |
+
grpcio==1.76.0
|
| 36 |
gunicorn==23.0.0
|
| 37 |
h11==0.16.0
|
| 38 |
+
hf-xet==1.2.0
|
| 39 |
httpcore==1.0.9
|
| 40 |
httptools==0.7.1
|
| 41 |
httpx==0.28.1
|
| 42 |
httpx-sse==0.4.3
|
| 43 |
+
huggingface-hub==0.36.0
|
| 44 |
humanfriendly==10.0
|
| 45 |
idna==3.11
|
| 46 |
importlib_metadata==8.7.0
|
| 47 |
importlib_resources==6.5.2
|
| 48 |
Jinja2==3.1.6
|
| 49 |
+
joblib==1.5.3
|
| 50 |
jsonpatch==1.33
|
| 51 |
jsonpointer==3.0.0
|
| 52 |
jsonschema==4.25.1
|
| 53 |
jsonschema-specifications==2025.9.1
|
| 54 |
kubernetes==34.1.0
|
| 55 |
+
langchain==1.1.3
|
| 56 |
+
langchain-classic==1.0.0
|
| 57 |
+
langchain-community==0.4.1
|
| 58 |
+
langchain-core==1.2.0
|
| 59 |
+
langchain-groq==1.1.1
|
| 60 |
+
langchain-huggingface==1.2.0
|
| 61 |
+
langchain-nvidia-ai-endpoints==1.0.0
|
| 62 |
+
langchain-text-splitters==1.1.0
|
| 63 |
+
langgraph==1.0.5
|
| 64 |
+
langgraph-checkpoint==3.0.1
|
| 65 |
+
langgraph-prebuilt==1.0.5
|
| 66 |
+
langgraph-sdk==0.3.0
|
| 67 |
+
langsmith==0.4.59
|
| 68 |
+
lark==1.3.1
|
| 69 |
+
loguru==0.7.3
|
| 70 |
markdown-it-py==4.0.0
|
| 71 |
MarkupSafe==3.0.3
|
| 72 |
marshmallow==3.26.1
|
|
|
|
| 75 |
mpmath==1.3.0
|
| 76 |
multidict==6.7.0
|
| 77 |
mypy_extensions==1.1.0
|
| 78 |
+
nemoguardrails==0.19.0
|
| 79 |
+
nest-asyncio==1.6.0
|
| 80 |
+
networkx==3.6.1
|
| 81 |
+
numpy==2.3.5
|
| 82 |
oauthlib==3.3.1
|
| 83 |
+
onnxruntime==1.23.2
|
| 84 |
+
opentelemetry-api==1.39.1
|
| 85 |
+
opentelemetry-exporter-otlp-proto-common==1.39.1
|
| 86 |
+
opentelemetry-exporter-otlp-proto-grpc==1.39.1
|
| 87 |
+
opentelemetry-proto==1.39.1
|
| 88 |
+
opentelemetry-sdk==1.39.1
|
| 89 |
+
opentelemetry-semantic-conventions==0.60b1
|
| 90 |
+
orjson==3.11.5
|
| 91 |
+
ormsgpack==1.12.1
|
| 92 |
overrides==7.7.0
|
| 93 |
packaging==25.0
|
| 94 |
+
pandas==2.3.3
|
| 95 |
perplexity-client==0.1.1
|
| 96 |
+
perplexityai==0.22.0
|
| 97 |
+
pillow==11.3.0
|
| 98 |
posthog==5.4.0
|
| 99 |
+
prompt_toolkit==3.0.52
|
| 100 |
propcache==0.4.1
|
| 101 |
+
protobuf==6.33.2
|
| 102 |
+
py_rust_stemmers==0.1.5
|
| 103 |
pyasn1==0.6.1
|
| 104 |
pyasn1_modules==0.4.2
|
| 105 |
+
pybase64==1.4.3
|
| 106 |
+
pydantic==2.12.5
|
| 107 |
+
pydantic-settings==2.12.0
|
| 108 |
+
pydantic_core==2.41.5
|
| 109 |
Pygments==2.19.2
|
| 110 |
+
pymongo==4.15.5
|
| 111 |
PyPika==0.48.9
|
| 112 |
pyproject_hooks==1.2.0
|
| 113 |
python-dateutil==2.9.0.post0
|
| 114 |
+
python-dotenv==1.2.1
|
| 115 |
+
pytz==2025.2
|
| 116 |
PyYAML==6.0.3
|
| 117 |
referencing==0.37.0
|
| 118 |
+
regex==2025.11.3
|
| 119 |
requests==2.32.5
|
| 120 |
requests-oauthlib==2.0.0
|
| 121 |
requests-toolbelt==1.0.0
|
| 122 |
rich==14.2.0
|
| 123 |
+
rpds-py==0.30.0
|
| 124 |
rsa==4.9.1
|
| 125 |
+
safetensors==0.7.0
|
| 126 |
+
scikit-learn==1.8.0
|
| 127 |
+
scipy==1.16.3
|
| 128 |
+
sentence-transformers==5.2.0
|
| 129 |
setuptools==80.9.0
|
| 130 |
shellingham==1.5.4
|
| 131 |
+
simpleeval==1.0.3
|
| 132 |
six==1.17.0
|
| 133 |
sniffio==1.3.1
|
| 134 |
+
SQLAlchemy==2.0.45
|
| 135 |
+
starlette==0.50.0
|
| 136 |
sympy==1.14.0
|
| 137 |
tenacity==9.1.2
|
| 138 |
threadpoolctl==3.6.0
|
| 139 |
tokenizers==0.22.1
|
| 140 |
tomli==2.3.0
|
|
|
|
| 141 |
tqdm==4.67.1
|
| 142 |
+
transformers==4.57.3
|
| 143 |
+
typer==0.20.0
|
| 144 |
typing-inspect==0.9.0
|
| 145 |
typing-inspection==0.4.2
|
| 146 |
typing_extensions==4.15.0
|
| 147 |
+
tzdata==2025.3
|
| 148 |
urllib3==2.3.0
|
| 149 |
+
uuid_utils==0.12.0
|
| 150 |
+
uvicorn==0.38.0
|
| 151 |
+
uvloop==0.22.1
|
| 152 |
+
watchdog==6.0.0
|
| 153 |
watchfiles==1.1.1
|
| 154 |
+
wcwidth==0.2.14
|
| 155 |
websocket-client==1.9.0
|
| 156 |
websockets==15.0.1
|
| 157 |
+
xxhash==3.6.0
|
| 158 |
yarl==1.22.0
|
| 159 |
zipp==3.23.0
|
| 160 |
zstandard==0.25.0
|
utils.py
CHANGED
|
@@ -2,12 +2,12 @@
|
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
from langchain_groq import ChatGroq
|
| 5 |
-
from
|
| 6 |
-
from langchain.schema import Document
|
| 7 |
-
from langchain.
|
| 8 |
-
from langchain_huggingface import HuggingFaceEmbeddings
|
| 9 |
-
from
|
| 10 |
-
from langchain.prompts import PromptTemplate
|
| 11 |
from configure import USER_DATA_PATH, RAG_BASE_DIRECTORY, RAG_CATEGORIES
|
| 12 |
import shutil
|
| 13 |
from dotenv import load_dotenv
|
|
@@ -75,114 +75,114 @@ def save_user_data(data):
|
|
| 75 |
json.dump(data, f, indent=2)
|
| 76 |
|
| 77 |
# Initialize RAG with per-category vector stores and QA chains
|
| 78 |
-
def initialize_rag():
|
| 79 |
-
|
| 80 |
-
|
| 81 |
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
|
| 96 |
-
|
| 97 |
-
|
| 98 |
|
| 99 |
-
|
| 100 |
-
|
| 101 |
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
# are defined above this block.
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
# The rest of your code
|
| 185 |
-
|
| 186 |
|
| 187 |
# Classify question to category
|
| 188 |
def classify_question_category(question):
|
|
@@ -195,18 +195,18 @@ def classify_question_category(question):
|
|
| 195 |
print(response)
|
| 196 |
return response.content.strip()
|
| 197 |
|
| 198 |
-
# Get RAG response using category-specific QA chain
|
| 199 |
-
def get_rag_response(question, qa_chains):
|
| 200 |
-
|
| 201 |
-
|
| 202 |
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
|
| 211 |
|
| 212 |
# Classify user input
|
|
|
|
| 2 |
import os
|
| 3 |
import json
|
| 4 |
from langchain_groq import ChatGroq
|
| 5 |
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
| 6 |
+
# from langchain.schema import Document
|
| 7 |
+
# from langchain.chains import RetrievalQA
|
| 8 |
+
# from langchain_huggingface import HuggingFaceEmbeddings
|
| 9 |
+
# from langchain_community.vectorstores import Chroma
|
| 10 |
+
# from langchain.prompts import PromptTemplate
|
| 11 |
from configure import USER_DATA_PATH, RAG_BASE_DIRECTORY, RAG_CATEGORIES
|
| 12 |
import shutil
|
| 13 |
from dotenv import load_dotenv
|
|
|
|
| 75 |
json.dump(data, f, indent=2)
|
| 76 |
|
| 77 |
# Initialize RAG with per-category vector stores and QA chains
|
| 78 |
+
# def initialize_rag():
|
| 79 |
+
# vector_stores = {}
|
| 80 |
+
# qa_chains = {}
|
| 81 |
|
| 82 |
+
# # Updated prompt template for advice-focused responses
|
| 83 |
+
# custom_prompt_template = """
|
| 84 |
+
# You are a {category} wellness expert. Provide helpful advice with specific actions:
|
| 85 |
|
| 86 |
+
# 1. Start with a brief empathetic response to the user's concern
|
| 87 |
+
# 2. Offer 1-3 actionable suggestions with brief explanations
|
| 88 |
+
# 3. End with an open-ended question to continue conversation
|
| 89 |
|
| 90 |
+
# Guidelines:
|
| 91 |
+
# - Keep responses conversational and supportive
|
| 92 |
+
# - Avoid clinical jargon
|
| 93 |
+
# - Focus on practical, implementable advice
|
| 94 |
+
# - Maintain hopeful and encouraging tone
|
| 95 |
|
| 96 |
+
# Context:
|
| 97 |
+
# {context}
|
| 98 |
|
| 99 |
+
# Question: {question}
|
| 100 |
+
# """
|
| 101 |
|
| 102 |
+
# # Create vector stores and QA chains for each category
|
| 103 |
+
# # Assume all your variables like RAG_CATEGORIES, embeddings, llm, etc.
|
| 104 |
+
# # are defined above this block.
|
| 105 |
+
|
| 106 |
+
# for category in RAG_CATEGORIES:
|
| 107 |
+
# persist_dir = f"./chroma_db_{category}"
|
| 108 |
+
# vector_store = None # Initialize vector_store to None
|
| 109 |
+
|
| 110 |
+
# # --- 1. Check if Vector Store Already Exists ---
|
| 111 |
+
# if os.path.exists(persist_dir):
|
| 112 |
+
# print(f"Found existing vector store for {category}. Attempting to load...")
|
| 113 |
+
# try:
|
| 114 |
+
# # Load the existing vector store from disk
|
| 115 |
+
# vector_store = Chroma(
|
| 116 |
+
# persist_directory=persist_dir,
|
| 117 |
+
# embedding_function=embeddings # Must provide the same embedding function
|
| 118 |
+
# )
|
| 119 |
+
# vector_stores[category] = vector_store
|
| 120 |
+
# except Exception as e:
|
| 121 |
+
# print(f"Error loading existing store {persist_dir}: {e}")
|
| 122 |
+
# print("Will delete and attempt to re-build.")
|
| 123 |
+
# shutil.rmtree(persist_dir)
|
| 124 |
|
| 125 |
+
# # --- 2. Create Vector Store if it Doesn't Exist (or failed to load) ---
|
| 126 |
+
# if vector_store is None:
|
| 127 |
+
# print(f"No valid vector store for {category} found. Creating new one...")
|
| 128 |
|
| 129 |
+
# # Process documents (Your original logic)
|
| 130 |
+
# dir_path = os.path.join(RAG_BASE_DIRECTORY, category)
|
| 131 |
+
# docs = []
|
| 132 |
+
# if os.path.exists(dir_path):
|
| 133 |
+
# for filename in os.listdir(dir_path):
|
| 134 |
+
# if filename.endswith('.txt'):
|
| 135 |
+
# file_path = os.path.join(dir_path, filename)
|
| 136 |
+
# try:
|
| 137 |
+
# with open(file_path, 'r', encoding='utf-8') as f:
|
| 138 |
+
# text = f.read()
|
| 139 |
+
# chunks = text_splitter.split_text(text)
|
| 140 |
+
# for chunk in chunks:
|
| 141 |
+
# if chunk.strip():
|
| 142 |
+
# metadata = {
|
| 143 |
+
# "source": filename,
|
| 144 |
+
# "category": category
|
| 145 |
+
# }
|
| 146 |
+
# docs.append(Document(
|
| 147 |
+
# page_content=chunk.strip(),
|
| 148 |
+
# metadata=metadata
|
| 149 |
+
# ))
|
| 150 |
+
# except Exception as e:
|
| 151 |
+
# print(f"Error processing {file_path}: {e}")
|
| 152 |
|
| 153 |
+
# # Create vector store (Your original logic)
|
| 154 |
+
# if docs:
|
| 155 |
+
# vector_store = Chroma.from_documents(
|
| 156 |
+
# documents=docs,
|
| 157 |
+
# embedding=embeddings,
|
| 158 |
+
# persist_directory=persist_dir
|
| 159 |
+
# )
|
| 160 |
+
# vector_store.persist()
|
| 161 |
+
# vector_stores[category] = vector_store
|
| 162 |
+
# print(f"Created and persisted new vector store for {category} with {len(docs)} documents.")
|
| 163 |
+
# else:
|
| 164 |
+
# print(f"No documents found for {category}. Skipping QA chain setup.")
|
| 165 |
+
# continue # Skip to the next category
|
| 166 |
+
|
| 167 |
+
# # --- 3. Create QA Chain (if vector_store was loaded or created) ---
|
| 168 |
+
# if vector_store:
|
| 169 |
+
# # Create QA chain with updated prompt
|
| 170 |
+
# prompt = PromptTemplate(
|
| 171 |
+
# template=custom_prompt_template.replace("{category}", category),
|
| 172 |
+
# input_variables=["context", "question"]
|
| 173 |
+
# )
|
| 174 |
+
# qa_chain = RetrievalQA.from_chain_type(
|
| 175 |
+
# llm=llm,
|
| 176 |
+
# chain_type="stuff",
|
| 177 |
+
# retriever=vector_store.as_retriever(search_kwargs={"k": 5}),
|
| 178 |
+
# chain_type_kwargs={"prompt": prompt},
|
| 179 |
+
# return_source_documents=True
|
| 180 |
+
# )
|
| 181 |
+
# qa_chains[category] = qa_chain
|
| 182 |
+
# print(f"Initialized {category} QA chain.")
|
| 183 |
+
|
| 184 |
+
# # The rest of your code
|
| 185 |
+
# return qa_chains
|
| 186 |
|
| 187 |
# Classify question to category
|
| 188 |
def classify_question_category(question):
|
|
|
|
| 195 |
print(response)
|
| 196 |
return response.content.strip()
|
| 197 |
|
| 198 |
+
# # Get RAG response using category-specific QA chain
|
| 199 |
+
# def get_rag_response(question, qa_chains):
|
| 200 |
+
# # Classify question
|
| 201 |
+
# category = classify_question_category(question)
|
| 202 |
|
| 203 |
+
# if category not in qa_chains:
|
| 204 |
+
# # Fallback to first available chain
|
| 205 |
+
# category = list(qa_chains.keys())[0]
|
| 206 |
|
| 207 |
+
# # Get response
|
| 208 |
+
# result = qa_chains[category].invoke({"query": question})
|
| 209 |
+
# return result['result']
|
| 210 |
|
| 211 |
|
| 212 |
# Classify user input
|