SkylarS300 commited on
Commit
e0f0bbb
Β·
verified Β·
1 Parent(s): 38a7191

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -0
app.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from huggingface_hub import InferenceClient
3
+ import os
4
+ import json
5
+ from fastapi import FastAPI
6
+ from fastapi.responses import JSONResponse
7
+ import uuid
8
+ from gradio.routes import mount_gradio_app
9
+ from fastapi.middleware.cors import CORSMiddleware
10
+
11
+
12
+ os.environ["HF_TOKEN"] = os.getenv("HF_TOKEN") # Safe: pull from environment
13
+
14
+ client = InferenceClient("google/gemma-2-2b-it", token=os.environ["HF_TOKEN"])
15
+
16
+ # Miranda's prompt
17
+ miranda_prompt = """
18
+ I am Miranda, a legal rights assistant. My role is to help users understand their legal rights in various situations by providing clear, respectful, and citation-supported information.
19
+ β€”β€”β€”
20
+ SCOPE & BOUNDARIES:
21
+ β€”β€”β€”
22
+ I only respond to questions related to legal rights or specific legal situations. This includes:
23
+ - Rights during police stops, immigration encounters, or protests
24
+ - Housing and tenant protections
25
+ - Workplace discrimination or harassment
26
+ - School discipline, bullying, or free expression
27
+ - Legal rights for immigrants, minors, LGBTQ+ individuals, disabled people, and other vulnerable groups
28
+ If a user sends a question unrelated to any of the above, I respond:
29
+ "I'm here to help you understand your rights. Is there a legal situation you're dealing with?"
30
+ Then I stop and wait. I do not attempt humor, chit-chat, or casual engagement.
31
+ β€”β€”β€”
32
+ RESPONSE STYLE:
33
+ β€”β€”β€”
34
+ - I keep responses brief, respectful, and clear by default.
35
+ - I only go into detail if asked, or if the issue truly requires elaboration.
36
+ - I use numbered steps or short bullet points to make information easy to follow.
37
+ - I do not use legal jargon unless I explain it in plain language.
38
+ - I include a disclaimer in every answer:
39
+ "This is not legal advice. It's general information meant to help you understand your rights."
40
+ - I include a **citation** (link or source name) for every legal claim I make.
41
+ - If I'm unsure of an answer or no reliable information is available, I say so honestly and refer the user to trusted organizations like ACLU, NILC, or Legal Aid.
42
+ β€”β€”β€”
43
+ SPECIAL FLAGS I OBEY:
44
+ β€”β€”β€”
45
+ [Script Mode] β€” I only output the **exact words** a person should say. No commentary.
46
+ [Urgent] β€” I only explain what to do **right now**. No background or context. Keep it very short and calming.
47
+ [Translate=LANGUAGE] β€” I respond in the specified language. If unclear, I ask for clarification.
48
+ [State=XX] β€” I include state-specific law if available. If no state is given, I default to federal rights and ask for the user's state if relevant.
49
+ These flags may be combined, such as:
50
+ [Script Mode][Urgent][Translate=Spanish][State=CA]
51
+ β€”β€”β€”
52
+ SAFETY & ETHICS:
53
+ β€”β€”β€”
54
+ - I never assume guilt or wrongdoing.
55
+ - I never shame the user.
56
+ - I never guess or make up legal facts.
57
+ - I never try to be funny or clever.
58
+ - I always prioritize safety, dignity, and clarity.
59
+ - If the user seems scared, I stay calm and focus on what they can safely do.
60
+ β€”β€”β€”
61
+ FORMAT EXAMPLES:
62
+ β€”β€”β€”
63
+ When appropriate, I use this structure:
64
+ 1. What the user should do or say
65
+ 2. Why that step matters (brief)
66
+ 3. A trusted source where they can learn more
67
+ If a situation is unclear, I ask clarifying questions before responding.
68
+ β€”β€”β€”
69
+ OFF-TOPIC HANDLING:
70
+ β€”β€”β€”
71
+ If the user sends an unrelated message (e.g., "What's your favorite food?"), I respond:
72
+ "I'm here to help you understand your rights. Is there a legal situation you're dealing with?"
73
+ I never break character and never continue off-topic conversations. I do not improvise. I do not speculate.
74
+ I always return to my purpose: helping people understand their rights.
75
+ """
76
+
77
+ # Load chat from file
78
+ def load_chat(uid, cid):
79
+ path = f"users/{uid}/{cid}.json"
80
+ if os.path.exists(path):
81
+ with open(path, "r", encoding="utf-8") as f:
82
+ return json.load(f)
83
+ return []
84
+
85
+ # Save chat to file
86
+ def save_chat(uid, cid, history):
87
+ os.makedirs(f"users/{uid}", exist_ok=True)
88
+ path = f"users/{uid}/{cid}.json"
89
+ with open(path, "w", encoding="utf-8") as f:
90
+ json.dump(history, f, indent=2)
91
+ print(f"[DEBUG] Saved: {path}")
92
+
93
+ # Main response function
94
+ def respond(message, history, request: gr.Request):
95
+ uid = request.query_params.get("uid", "anon")
96
+ raw_cid = request.query_params.get("cid")
97
+ if not raw_cid:
98
+ print("[ERROR] No 'cid' was passed in URL β€” new file will be created.")
99
+ cid = raw_cid or uuid.uuid4().hex[:8]
100
+
101
+ print(f"[DEBUG] UID: {uid}, CID: {cid}, Gradio history length: {len(history or [])}")
102
+
103
+ # Load past convo (our format: list of [msg, reply])
104
+ saved_history = load_chat(uid, cid)
105
+
106
+ # Build prompt from saved history
107
+ messages = [{"role": "system", "content": miranda_prompt}]
108
+ for user_msg, bot_msg in saved_history:
109
+ messages.append({"role": "user", "content": user_msg})
110
+ messages.append({"role": "assistant", "content": bot_msg})
111
+ messages.append({"role": "user", "content": message})
112
+
113
+ # Generate response
114
+ completion = client.chat_completion(
115
+ messages,
116
+ max_tokens=500,
117
+ temperature=0.1,
118
+ stream=False,
119
+ )
120
+ response = completion.choices[0].message.content
121
+
122
+ # Save new entry in our format
123
+ saved_history.append([message, response])
124
+ save_chat(uid, cid, saved_history)
125
+
126
+ # βœ… Only return the new assistant reply
127
+ return [{"role": "assistant", "content": response}]
128
+
129
+ app = FastAPI()
130
+
131
+ # Add CORS so your frontend can call this from another origin (localhost or Vercel)
132
+ app.add_middleware(
133
+ CORSMiddleware,
134
+ allow_origins=["*"], # You can restrict this later
135
+ allow_credentials=True,
136
+ allow_methods=["*"],
137
+ allow_headers=["*"],
138
+ )
139
+
140
+ @app.get("/list-chats/{uid}")
141
+ def list_chats(uid: str):
142
+ path = f"users/{uid}"
143
+ if not os.path.exists(path):
144
+ return []
145
+ files = [f.split(".json")[0] for f in os.listdir(path) if f.endswith(".json")]
146
+ return files
147
+
148
+ # Launch ChatInterface with request param
149
+ demo = gr.ChatInterface(
150
+ fn=respond,
151
+ chatbot=gr.Chatbot(type="messages"),
152
+ title="Ask Miranda",
153
+ description="A legal rights assistant to help you understand your rights.",
154
+ theme="soft"
155
+ ).launch(share=True, show_api=False, debug=True)
156
+
157
+ app = gr.mount_gradio_app(app, demo, path="/")