Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
chatbot_canifa_test_conservation_tools
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
chatbot_canifa_test_conservation_tools
Commits
b4329aaa
Commit
b4329aaa
authored
Jan 21, 2026
by
Vũ Hoàng Anh
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update server config, docker-compose and add test scripts
parent
79dfe7d5
Changes
6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
187 additions
and
19 deletions
+187
-19
system_prompt.txt
backend/agent/system_prompt.txt
+5
-14
prompt_route.py
backend/api/prompt_route.py
+42
-2
middleware.py
backend/common/middleware.py
+21
-2
docker-compose.yml
backend/docker-compose.yml
+11
-1
manual_test_chat.py
backend/manual_test_chat.py
+38
-0
test_auth_verify.py
backend/test_auth_verify.py
+70
-0
No files found.
backend/agent/system_prompt.txt
View file @
b4329aaa
...
...
@@ -7,22 +7,13 @@ Bạn là CiCi - Chuyên viên tư vấn thời trang CANIFA.
---
# QUY TẮC TRUNG THỰC - BẮT BUỘC
KHÔNG BAO GIỜ BỊA ĐẶT - CHỈ NÓI THEO DỮ LIỆU
ĐÚNG:
**ĐÚNG:**
- Tool trả về áo thun → Giới thiệu áo thun
- Tool trả về 0 sản phẩm → Nói "Shop chưa có sản phẩm này"
- Tool trả về quần nỉ mà khách hỏi bikini → Nói "Shop chưa có bikini"
**CẤM:**
- Tool trả về quần nỉ → Gọi là "đồ bơi"
- Tool trả về 0 kết quả → Nói "shop có sản phẩm X"
- Tự bịa mã sản phẩm, giá tiền, chính sách
Không có trong data = Không nói = Không tư vấn láo
Tool trả về áo thun → Giới thiệu áo thun
Tool trả về 0 sản phẩm → Nói "Shop chưa có sản phẩm này"
Tool trả về quần nỉ mà khách hỏi bikini → Nói "Shop chưa có bikini"
Khách hỏi giá online vs offline mà không có data → "Mình không rõ chi tiết so sánh giá, bạn có thể xem trực tiếp trên web hoặc liên hệ hotline nhé"
---
# NGÔN NGỮ & XƯNG HÔ
...
...
backend/api/prompt_route.py
View file @
b4329aaa
from
fastapi
import
APIRouter
,
HTTPException
from
pydantic
import
BaseModel
import
os
import
re
from
agent.graph
import
reset_graph
router
=
APIRouter
()
PROMPT_FILE_PATH
=
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
"../agent/system_prompt.txt"
)
# Allowed variables in prompt (single braces OK for these)
ALLOWED_VARIABLES
=
{
"date_str"
}
class
PromptUpdateRequest
(
BaseModel
):
content
:
str
def
validate_prompt_braces
(
content
:
str
)
->
tuple
[
bool
,
list
[
str
]]:
"""
Validate that all braces in prompt are properly escaped.
Returns (is_valid, list of problematic patterns)
"""
# Find all {word} patterns
single_brace_pattern
=
re
.
findall
(
r'\{([^{}]+)\}'
,
content
)
# Filter out allowed variables
problematic
=
[
var
for
var
in
single_brace_pattern
if
var
.
strip
()
not
in
ALLOWED_VARIABLES
and
not
var
.
startswith
(
'{'
)
]
return
len
(
problematic
)
==
0
,
problematic
@
router
.
get
(
"/api/agent/system-prompt"
)
async
def
get_system_prompt_content
():
"""Get current system prompt content"""
# ... existing code ...
try
:
if
os
.
path
.
exists
(
PROMPT_FILE_PATH
):
with
open
(
PROMPT_FILE_PATH
,
"r"
,
encoding
=
"utf-8"
)
as
f
:
...
...
@@ -28,6 +47,19 @@ async def get_system_prompt_content():
async
def
update_system_prompt_content
(
request
:
PromptUpdateRequest
):
"""Update system prompt content"""
try
:
# Validate braces
is_valid
,
problematic
=
validate_prompt_braces
(
request
.
content
)
if
not
is_valid
:
# Return warning but still allow save
warning
=
(
f
"⚠️ Phát hiện {{...}} chưa escape: {problematic[:3]}... "
f
"Nếu đây là JSON, hãy dùng {{{{ }}}} thay vì {{ }}. "
f
"Prompt vẫn được lưu nhưng có thể gây lỗi khi chat."
)
else
:
warning
=
None
# 1. Update file
with
open
(
PROMPT_FILE_PATH
,
"w"
,
encoding
=
"utf-8"
)
as
f
:
f
.
write
(
request
.
content
)
...
...
@@ -35,6 +67,14 @@ async def update_system_prompt_content(request: PromptUpdateRequest):
# 2. Reset Graph Singleton to force reload prompt
reset_graph
()
return
{
"status"
:
"success"
,
"message"
:
"System prompt updated successfully. Graph reloaded."
}
response
=
{
"status"
:
"success"
,
"message"
:
"System prompt updated successfully. Graph reloaded."
}
if
warning
:
response
[
"warning"
]
=
warning
return
response
except
Exception
as
e
:
raise
HTTPException
(
status_code
=
500
,
detail
=
str
(
e
))
backend/common/middleware.py
View file @
b4329aaa
...
...
@@ -4,6 +4,7 @@ Singleton Pattern cho cả 2 services
"""
from
__future__
import
annotations
import
json
import
logging
from
collections.abc
import
Callable
from
typing
import
TYPE_CHECKING
...
...
@@ -79,7 +80,25 @@ class CanifaAuthMiddleware(BaseHTTPMiddleware):
# =====================================================================
try
:
auth_header
=
request
.
headers
.
get
(
"Authorization"
)
device_id
=
request
.
headers
.
get
(
"device_id"
,
""
)
# --- Device ID from Body ---
device_id
=
""
if
method
in
[
"POST"
,
"PUT"
,
"PATCH"
]:
try
:
body_bytes
=
await
request
.
body
()
async
def
receive_wrapper
():
return
{
"type"
:
"http.request"
,
"body"
:
body_bytes
}
request
.
_receive
=
receive_wrapper
if
body_bytes
:
try
:
body_json
=
json
.
loads
(
body_bytes
)
device_id
=
body_json
.
get
(
"device_id"
,
""
)
except
json
.
JSONDecodeError
:
pass
except
Exception
as
e
:
logger
.
warning
(
f
"Error reading device_id from body: {e}"
)
# ========== DEV MODE: Bypass auth ==========
dev_user_id
=
request
.
headers
.
get
(
"X-Dev-User-Id"
)
...
...
@@ -133,7 +152,7 @@ class CanifaAuthMiddleware(BaseHTTPMiddleware):
request
.
state
.
user
=
None
request
.
state
.
user_id
=
None
request
.
state
.
is_authenticated
=
False
request
.
state
.
device_id
=
request
.
headers
.
get
(
"device_id"
,
""
)
request
.
state
.
device_id
=
""
# =====================================================================
# STEP 2: RATE LIMIT CHECK (Chỉ cho các path cần limit)
...
...
backend/docker-compose.yml
View file @
b4329aaa
...
...
@@ -15,8 +15,18 @@ services:
resources
:
limits
:
memory
:
8g
networks
:
-
backend_network
logging
:
driver
:
"
json-file"
options
:
tag
:
"
{{.Name}}"
networks
:
backend_network
:
driver
:
bridge
ipam
:
driver
:
default
config
:
-
subnet
:
"
172.24.0.0/16"
gateway
:
"
172.24.0.1"
backend/manual_test_chat.py
0 → 100644
View file @
b4329aaa
import
requests
import
json
import
time
url
=
"http://localhost:5000/api/agent/chat"
token
=
"071w198x23ict4hs1i6bl889fit5p3f7"
headers
=
{
"Content-Type"
:
"application/json"
,
"Authorization"
:
f
"Bearer {token}"
}
payload
=
{
"user_query"
:
"tư vấn cho mình áo hoodie"
}
print
(
f
"Sending AUTHENTICATED POST request to {url}..."
)
print
(
f
"Token: {token}"
)
start
=
time
.
time
()
try
:
response
=
requests
.
post
(
url
,
json
=
payload
,
headers
=
headers
,
timeout
=
120
)
print
(
f
"Status Code: {response.status_code}"
)
print
(
f
"Time taken: {time.time() - start:.2f}s"
)
if
response
.
status_code
==
200
:
data
=
response
.
json
()
print
(
"Response JSON:"
)
# Print limit info specifically to check if limit increased to USER level (100)
if
"limit_info"
in
data
:
print
(
"Limit Info:"
,
json
.
dumps
(
data
[
"limit_info"
],
indent
=
2
))
else
:
print
(
json
.
dumps
(
data
,
indent
=
2
,
ensure_ascii
=
False
))
else
:
print
(
"Error Response:"
)
print
(
response
.
text
)
except
Exception
as
e
:
print
(
f
"Error: {e}"
)
backend/test_auth_verify.py
0 → 100644
View file @
b4329aaa
import
httpx
import
asyncio
import
logging
# Configure logging
logging
.
basicConfig
(
level
=
logging
.
INFO
)
logger
=
logging
.
getLogger
(
__name__
)
async
def
test_auth
():
url
=
"http://localhost:5000/api/agent/chat"
# 1. Test GUEST Mode (No Token)
logger
.
info
(
"--- TEST 1: GUEST MODE (No Token) ---"
)
payload_guest
=
{
"device_id"
:
"device_guest_123"
,
"user_query"
:
"hello guest"
}
try
:
async
with
httpx
.
AsyncClient
(
timeout
=
10.0
)
as
client
:
resp
=
await
client
.
post
(
url
,
json
=
payload_guest
)
if
resp
.
status_code
==
200
:
data
=
resp
.
json
()
limit
=
data
.
get
(
"limit_info"
,
{})
.
get
(
"limit"
)
used
=
data
.
get
(
"limit_info"
,
{})
.
get
(
"used"
)
logger
.
info
(
f
"✅ Guest Response Limit: {limit} (Expected 10)"
)
if
limit
==
10
:
logger
.
info
(
"=> Logic Guest OK"
)
else
:
logger
.
error
(
f
"=> Logic Guest FAILED (Limit is {limit})"
)
else
:
logger
.
error
(
f
"Request failed: {resp.text}"
)
except
Exception
as
e
:
logger
.
error
(
f
"Error Test 1: {e}"
)
# 2. Test USER Mode (With Token)
logger
.
info
(
"
\n
--- TEST 2: USER MODE (With Access Token) ---"
)
token
=
"071w198x23ict4hs1i6bl889fit5p3f7"
payload_user
=
{
"device_id"
:
"device_user_123"
,
# device_id này sẽ bị ignore nếu token valid
"user_query"
:
"hello user"
}
headers
=
{
"Authorization"
:
f
"Bearer {token}"
}
try
:
async
with
httpx
.
AsyncClient
(
timeout
=
20.0
)
as
client
:
resp
=
await
client
.
post
(
url
,
json
=
payload_user
,
headers
=
headers
)
if
resp
.
status_code
==
200
:
data
=
resp
.
json
()
limit
=
data
.
get
(
"limit_info"
,
{})
.
get
(
"limit"
)
used
=
data
.
get
(
"limit_info"
,
{})
.
get
(
"used"
)
logger
.
info
(
f
"✅ User Response Limit: {limit} (Expected 100)"
)
if
limit
==
100
:
logger
.
info
(
"=> Logic User OK (Prioritized Token over DeviceID)"
)
elif
limit
==
10
:
logger
.
warning
(
"=> Logic User FAILED (Still Guest Mode). Token might be invalid or Canifa API unreachable."
)
else
:
logger
.
info
(
f
"=> Unexpected Limit: {limit}"
)
elif
resp
.
status_code
==
429
:
logger
.
warning
(
"Rate limit exceeded for this user/device."
)
else
:
logger
.
error
(
f
"Request failed: {resp.status_code} - {resp.text}"
)
except
Exception
as
e
:
logger
.
error
(
f
"Error Test 2: {e}"
)
if
__name__
==
"__main__"
:
asyncio
.
run
(
test_auth
())
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment