1313# limitations under the License.
1414import json
1515import os
16+ import time
1617import requests
1718from typing import Optional , List , Dict , Any , Union
1819from volcenginesdkllmshield .models .llm_shield_sign import request_sign
@@ -82,7 +83,27 @@ def __init__(self, timeout: int = 50) -> None:
8283 107 : "Computational Resource Consumption" ,
8384 }
8485
85- def _request_llm_shield (self , message : str , role : str ) -> Optional [str ]:
86+ def _get_session_info (self , tool_context : Any ) -> Dict [str , str ]:
87+ """Extract session id and run id from the tool context when available."""
88+ session_info = {"SessionID" : "" , "RunID" : "" }
89+ try :
90+ session = getattr (tool_context , "session" , None )
91+ session_id = getattr (session , "id" , "" ) if session else ""
92+ run_id = getattr (tool_context , "invocation_id" , "" ) or ""
93+ session_info = {"SessionID" : session_id or "" , "RunID" : run_id }
94+ except Exception :
95+ logger .debug (
96+ "Failed to extract session info from tool context" , exc_info = True
97+ )
98+ return session_info
99+
100+ def _request_llm_shield (
101+ self ,
102+ message : str ,
103+ role : str ,
104+ hook_name : Optional [str ] = None ,
105+ session_info : Optional [Dict [str , str ]] = None ,
106+ ) -> Optional [str ]:
86107 """
87108 Make a request to the LLM Shield service for content moderation.
88109
@@ -93,6 +114,8 @@ def _request_llm_shield(self, message: str, role: str) -> Optional[str]:
93114 Args:
94115 message (str): The content to be moderated
95116 role (str): The role of the message sender ("user" or "assistant")
117+ hook_name (str, optional): Hook name for the Lumen Moderate endpoint
118+ session_info (dict, optional): Session and run ids for the Lumen endpoint
96119
97120 Returns:
98121 Optional[str]: A blocking message if content violates policies,
@@ -102,64 +125,94 @@ def _request_llm_shield(self, message: str, role: str) -> Optional[str]:
102125 logger .error ("LLM Shield app ID not configured" )
103126 return None
104127
105- body = {
106- "Message" : {
107- "Role" : role ,
108- "Content" : message ,
109- "ContentType" : 1 ,
110- },
111- "Scene" : self .appid ,
112- }
113-
114- body_json = json .dumps (body ).encode ("utf-8" )
115-
116- path = "/v2/moderate"
117- action = "Moderate"
118- version = "2025-08-31"
119-
120- # Check if using API key authentication
121- logger .debug (f"API key configured: { bool (self .api_key )} " )
122- if self .api_key and self .api_key != "" :
123- logger .debug ("Using API key authentication (no AK/SK signature)" )
124- # Use API key authentication only - match curl command headers exactly
125- signed_header = {
128+ if "/OpenTOP/V1/Lumen/Moderate" in self .url :
129+ logger .debug ("Using Lumen Moderate endpoint" )
130+ body = {
131+ "Message" : {
132+ "Role" : role ,
133+ "MultiPart" : [
134+ {"Content" : message , "ContentType" : 1 },
135+ ],
136+ },
137+ "History" : [],
138+ "HookName" : hook_name or "before_tool_call" ,
139+ "IsGroup" : False ,
140+ "LocalTimestamp" : int (time .time ()),
141+ "LocalTimestampMS" : int (time .time () * 1000 ),
142+ "SessionInfo" : session_info or {"SessionID" : "" , "RunID" : "" },
143+ }
144+ headers = {
126145 "Content-Type" : "application/json" ,
127- "x-api-key" : self .api_key ,
146+ "X-Top-Account-Id" : "30000001" ,
147+ "X-Top-Region" : "cn-private" ,
148+ "X-endpoint-Id" : self .appid ,
149+ "X-endpoint-api-Key" : self .api_key ,
128150 }
151+ body_json = json .dumps (body ).encode ("utf-8" )
152+ request_url = self .url
153+ params = None
129154 else :
130- logger .debug ("Using AK/SK signature authentication" )
131- # Use AK/SK signature authentication
132- ak = os .getenv ("VOLCENGINE_ACCESS_KEY" )
133- sk = os .getenv ("VOLCENGINE_SECRET_KEY" )
134- session_token = ""
135- if not (ak and sk ):
136- logger .debug ("Get AK/SK from environment variables failed." )
137- credential = get_credential_from_vefaas_iam ()
138- ak = credential .access_key_id
139- sk = credential .secret_access_key
140- session_token = credential .session_token
155+ body = {
156+ "Message" : {
157+ "Role" : role ,
158+ "Content" : message ,
159+ "ContentType" : 1 ,
160+ },
161+ "Scene" : self .appid ,
162+ }
163+ body_json = json .dumps (body ).encode ("utf-8" )
164+
165+ path = "/v2/moderate"
166+ action = "Moderate"
167+ version = "2025-08-31"
168+
169+ # Check if using API key authentication
170+ logger .debug (f"API key configured: { bool (self .api_key )} " )
171+ if self .api_key and self .api_key != "" :
172+ logger .debug ("Using API key authentication (no AK/SK signature)" )
173+ # Use API key authentication only - match curl command headers exactly
174+ signed_header = {
175+ "Content-Type" : "application/json" ,
176+ "x-api-key" : self .api_key ,
177+ }
141178 else :
142- logger .debug ("Successfully get AK/SK from environment variables." )
179+ logger .debug ("Using AK/SK signature authentication" )
180+ # Use AK/SK signature authentication
181+ ak = os .getenv ("VOLCENGINE_ACCESS_KEY" )
182+ sk = os .getenv ("VOLCENGINE_SECRET_KEY" )
183+ session_token = ""
184+ if not (ak and sk ):
185+ logger .debug ("Get AK/SK from environment variables failed." )
186+ credential = get_credential_from_vefaas_iam ()
187+ ak = credential .access_key_id
188+ sk = credential .secret_access_key
189+ session_token = credential .session_token
190+ else :
191+ logger .debug ("Successfully get AK/SK from environment variables." )
192+
193+ header = {"X-Security-Token" : session_token }
194+ signed_header = request_sign (
195+ header , ak , sk , self .region , self .url , path , action , body_json
196+ )
143197
144- header = {"X-Security-Token" : session_token }
145- signed_header = request_sign (
146- header , ak , sk , self .region , self .url , path , action , body_json
147- )
198+ signed_header .update (
199+ {
200+ "Content-Type" : "application/json" ,
201+ "X-Top-Service" : "llmshield" ,
202+ "X-Top-Region" : self .region ,
203+ }
204+ )
148205
149- signed_header .update (
150- {
151- "Content-Type" : "application/json" ,
152- "X-Top-Service" : "llmshield" ,
153- "X-Top-Region" : self .region ,
154- }
155- )
206+ headers = signed_header
207+ request_url = self .url + path
208+ params = {"Action" : action , "Version" : version }
156209
157210 try :
158211 response = requests .post (
159- self . url + path ,
160- headers = signed_header ,
212+ request_url ,
213+ headers = headers ,
161214 data = body_json ,
162- params = { "Action" : action , "Version" : version } ,
215+ params = params ,
163216 timeout = self .timeout ,
164217 )
165218
@@ -261,7 +314,9 @@ def before_model_callback(
261314 if not last_user_message :
262315 return None
263316
264- response = self ._request_llm_shield (message = last_user_message , role = "user" )
317+ response = self ._request_llm_shield (
318+ message = last_user_message , role = "user" , hook_name = "before_model"
319+ )
265320 if response :
266321 logger .debug ("LLM Shield triggered in before_model_callback." )
267322 return LlmResponse (
@@ -308,7 +363,7 @@ def after_model_callback(
308363 return None
309364
310365 response = self ._request_llm_shield (
311- message = last_model_message , role = "assistant"
366+ message = last_model_message , role = "assistant" , hook_name = "after_model"
312367 )
313368 if response :
314369 logger .debug ("LLM Shield triggered in after_model_callback." )
@@ -347,7 +402,12 @@ def before_tool_callback(
347402 args_list .append (f"{ key } : { value } " )
348403
349404 message = "\n " .join (args_list )
350- response = self ._request_llm_shield (message = message , role = "user" )
405+ response = self ._request_llm_shield (
406+ message = message ,
407+ role = "user" ,
408+ hook_name = "before_tool_call" ,
409+ session_info = self ._get_session_info (tool_context ),
410+ )
351411 if response :
352412 logger .debug ("LLM Shield triggered in before_tool_callback." )
353413 return {"result" : response }
@@ -389,7 +449,12 @@ def after_tool_callback(
389449 for item in tool_response :
390450 message += f"{ item } \n "
391451
392- response = self ._request_llm_shield (message = message , role = "assistant" )
452+ response = self ._request_llm_shield (
453+ message = message ,
454+ role = "assistant" ,
455+ hook_name = "after_tool_call" ,
456+ session_info = self ._get_session_info (tool_context ),
457+ )
393458 if response :
394459 logger .debug ("LLM Shield triggered in after_tool_callback." )
395460 return {"result" : response }
0 commit comments