Repository navigation
Expand file tree
/
Copy pathsafe_update.py
More file actions
184 lines (146 loc) · 6.2 KB
/
Copy pathsafe_update.py
File metadata and controls
184 lines (146 loc) · 6.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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
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
"""
SAFE UPDATE SCRIPT — Append-only approach.
Adds the FORBIDDEN PHRASES rule to the END of each agent's system prompt.
Does NOT modify any existing prompt text — only appends a new HTML block.
Features:
- Creates .bak backups before modifying
- Skips files that already have the rule
- Skips ConditionAgent nodes (not relevant)
- Two modes: --dry-run (default) and --apply
- Validates JSON integrity after writing
"""
import json
import os
import sys
import shutil
FOLDER = r"c:\Users\Guillermo\Downloads\Chatbots"
FORBIDDEN_RULE = (
'<p><strong>⛔ STRICTLY FORBIDDEN PHRASES (ALL LANGUAGES):</strong></p>'
'<ul>'
'<li><p>NEVER use phrases like: "según el documento", "el documento menciona", '
'"de acuerdo con el documento", "no se menciona en el documento", '
'"no viene en el documento", "el documento no incluye", '
'"according to the document", "the document states", '
'"based on the provided document", "not mentioned in the document".</p></li>'
'<li><p>NEVER reveal you are consulting a document, file, or external data source.</p></li>'
'<li><p>Instead, respond naturally as if you have personal expert knowledge of the project. '
'Example: Instead of "Según el documento, el precio es..." say "El precio es..."</p></li>'
'</ul>'
)
ALREADY_HAS_MARKERS = [
"STRICTLY FORBIDDEN PHRASES",
"FRASES ESTRICTAMENTE PROHIBIDAS",
"NUNCA menciones que consultaste",
"NUNCA uses frases como",
]
def process_file(filepath, dry_run=True):
"""Process a single JSON agent file. Returns (status, details)."""
filename = os.path.basename(filepath)
with open(filepath, 'r', encoding='utf-8') as f:
data = json.load(f)
nodes = data.get("nodes", [])
updated_agents = []
skipped_agents = []
for node in nodes:
nd = node.get("data", {})
# Skip non-agent nodes
if nd.get("type") not in ("Agent",):
continue
label = nd.get("label", "Unknown")
label_lower = label.lower()
inputs = nd.get("inputs", {})
messages = inputs.get("agentMessages", [])
# Skip Sales/Lead agents and Condition agents — they don't consult documents
is_sales = any(w in label_lower for w in ["sales", "lead", "ventas"])
is_condition = "condition" in label_lower
is_availability = "availability" in label_lower or "disponibilidad" in label_lower
if is_sales:
skipped_agents.append(f"{label} (Sales agent — skipped)")
continue
if is_condition:
skipped_agents.append(f"{label} (Condition agent — skipped)")
continue
if is_availability:
skipped_agents.append(f"{label} (Availability agent — skipped)")
continue
# Find system message
sys_msg = None
for msg in messages:
if msg.get("role") == "system":
sys_msg = msg
break
if not sys_msg or not sys_msg.get("content"):
skipped_agents.append(f"{label} (no system prompt)")
continue
content = sys_msg["content"]
# Check if rule already exists
if any(marker in content for marker in ALREADY_HAS_MARKERS):
skipped_agents.append(f"{label} (already has rule)")
continue
# SAFE APPEND: Just add the rule at the end
new_content = content + FORBIDDEN_RULE
if not dry_run:
sys_msg["content"] = new_content
updated_agents.append(label)
if not updated_agents:
return "SKIPPED", skipped_agents
if not dry_run and updated_agents:
# Create backup
bak_path = filepath + ".bak"
if not os.path.exists(bak_path):
shutil.copy2(filepath, bak_path)
# Write updated JSON
output = json.dumps(data, ensure_ascii=False, indent=2)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(output)
# Validate JSON integrity
with open(filepath, 'r', encoding='utf-8') as f:
json.load(f) # Will raise if invalid
return "UPDATED", updated_agents, skipped_agents
def main():
dry_run = "--apply" not in sys.argv
mode = "DRY-RUN" if dry_run else "APPLYING CHANGES"
files = sorted([f for f in os.listdir(FOLDER)
if f.endswith('.json') and not f.endswith('.bak')])
# Exclude our own scripts
files = [f for f in files if 'update_prompts' not in f and 'analyze_prompts' not in f]
print(f"\n{'='*80}")
print(f" MODE: {mode}")
print(f" Files to process: {len(files)}")
print(f"{'='*80}\n")
stats = {"updated": 0, "skipped": 0, "errors": 0}
all_updated = []
all_skipped = []
for filename in files:
filepath = os.path.join(FOLDER, filename)
try:
result = process_file(filepath, dry_run=dry_run)
status = result[0]
if status == "UPDATED":
_, updated, skipped = result
stats["updated"] += 1
print(f" {'WOULD UPDATE' if dry_run else 'UPDATED'}: {filename}")
for a in updated:
print(f" + {a}")
all_updated.append(f"{filename} → {a}")
for s in skipped:
print(f" - skip: {s}")
else:
_, skipped = result
stats["skipped"] += 1
reason = ", ".join(skipped) if skipped else "no agents"
print(f" SKIP: {filename} ({reason})")
except Exception as e:
stats["errors"] += 1
print(f" ERROR: {filename} — {e}")
print(f"\n{'='*80}")
print(f" SUMMARY ({mode}):")
print(f" Files updated: {stats['updated']}")
print(f" Files skipped: {stats['skipped']}")
print(f" Errors: {stats['errors']}")
print(f" Total agents that {'would get' if dry_run else 'got'} the rule: {len(all_updated)}")
print(f"{'='*80}")
if dry_run:
print(f"\n To apply changes, run: python safe_update.py --apply\n")
if __name__ == "__main__":
main()