fix: Enhance tool binding in LangChainXAIService to support web search and update API handler for new parameters
This commit is contained in:
@@ -84,6 +84,72 @@ class LangChainXAIService:
|
|||||||
|
|
||||||
return ChatXAI(**kwargs)
|
return ChatXAI(**kwargs)
|
||||||
|
|
||||||
|
def bind_tools(
|
||||||
|
self,
|
||||||
|
model,
|
||||||
|
collection_id: Optional[str] = None,
|
||||||
|
enable_web_search: bool = False,
|
||||||
|
web_search_config: Optional[Dict[str, Any]] = None,
|
||||||
|
max_num_results: int = 10
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Bindet xAI Tools (file_search und/oder web_search) an Model.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model: ChatXAI model instance
|
||||||
|
collection_id: Optional xAI Collection ID für file_search
|
||||||
|
enable_web_search: Enable web search tool (default: False)
|
||||||
|
web_search_config: Optional web search configuration:
|
||||||
|
{
|
||||||
|
'allowed_domains': ['example.com'], # Max 5 domains
|
||||||
|
'excluded_domains': ['spam.com'], # Max 5 domains
|
||||||
|
'enable_image_understanding': True
|
||||||
|
}
|
||||||
|
max_num_results: Max results from file search (default: 10)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Model with requested tools bound (file_search and/or web_search)
|
||||||
|
"""
|
||||||
|
tools = []
|
||||||
|
|
||||||
|
# Add file_search tool if collection_id provided
|
||||||
|
if collection_id:
|
||||||
|
self._log(f"🔍 Binding file_search: collection={collection_id}")
|
||||||
|
tools.append({
|
||||||
|
"type": "file_search",
|
||||||
|
"vector_store_ids": [collection_id],
|
||||||
|
"max_num_results": max_num_results
|
||||||
|
})
|
||||||
|
|
||||||
|
# Add web_search tool if enabled
|
||||||
|
if enable_web_search:
|
||||||
|
self._log("🌐 Binding web_search")
|
||||||
|
web_search_tool = {"type": "web_search"}
|
||||||
|
|
||||||
|
# Add optional web search filters
|
||||||
|
if web_search_config:
|
||||||
|
if 'allowed_domains' in web_search_config:
|
||||||
|
domains = web_search_config['allowed_domains'][:5] # Max 5
|
||||||
|
web_search_tool['filters'] = {'allowed_domains': domains}
|
||||||
|
self._log(f" Allowed domains: {domains}")
|
||||||
|
elif 'excluded_domains' in web_search_config:
|
||||||
|
domains = web_search_config['excluded_domains'][:5] # Max 5
|
||||||
|
web_search_tool['filters'] = {'excluded_domains': domains}
|
||||||
|
self._log(f" Excluded domains: {domains}")
|
||||||
|
|
||||||
|
if web_search_config.get('enable_image_understanding'):
|
||||||
|
web_search_tool['enable_image_understanding'] = True
|
||||||
|
self._log(" Image understanding: enabled")
|
||||||
|
|
||||||
|
tools.append(web_search_tool)
|
||||||
|
|
||||||
|
if not tools:
|
||||||
|
self._log("⚠️ No tools to bind (no collection_id and web_search disabled)", level='warn')
|
||||||
|
return model
|
||||||
|
|
||||||
|
self._log(f"🔧 Binding {len(tools)} tool(s) to model")
|
||||||
|
return model.bind_tools(tools)
|
||||||
|
|
||||||
def bind_file_search(
|
def bind_file_search(
|
||||||
self,
|
self,
|
||||||
model,
|
model,
|
||||||
@@ -91,25 +157,15 @@ class LangChainXAIService:
|
|||||||
max_num_results: int = 10
|
max_num_results: int = 10
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Bindet xAI file_search Tool an Model.
|
Legacy method: Bindet nur file_search Tool an Model.
|
||||||
|
|
||||||
Args:
|
Use bind_tools() for more flexibility.
|
||||||
model: ChatXAI model instance
|
|
||||||
collection_id: xAI Collection ID (vector store)
|
|
||||||
max_num_results: Max results from file search (default: 10)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Model with bound file_search tool
|
|
||||||
"""
|
"""
|
||||||
self._log(f"🔍 Binding file_search: collection={collection_id}, max_results={max_num_results}")
|
return self.bind_tools(
|
||||||
|
model=model,
|
||||||
tools = [{
|
collection_id=collection_id,
|
||||||
"type": "file_search",
|
max_num_results=max_num_results
|
||||||
"vector_store_ids": [collection_id],
|
)
|
||||||
"max_num_results": max_num_results
|
|
||||||
}]
|
|
||||||
|
|
||||||
return model.bind_tools(tools)
|
|
||||||
|
|
||||||
async def invoke_chat(
|
async def invoke_chat(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -34,7 +34,13 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
"max_tokens": 2000,
|
"max_tokens": 2000,
|
||||||
"stream": false,
|
"stream": false,
|
||||||
"extra_body": {
|
"extra_body": {
|
||||||
"collection_id": "col_abc123" // Optional: override auto-detection
|
"collection_id": "col_abc123", // Optional: override auto-detection
|
||||||
|
"enable_web_search": true, // Optional: enable web search (default: false)
|
||||||
|
"web_search_config": { // Optional: web search configuration
|
||||||
|
"allowed_domains": ["example.com"],
|
||||||
|
"excluded_domains": ["spam.com"],
|
||||||
|
"enable_image_understanding": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,9 +97,16 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
stream = body.get('stream', False)
|
stream = body.get('stream', False)
|
||||||
extra_body = body.get('extra_body', {})
|
extra_body = body.get('extra_body', {})
|
||||||
|
|
||||||
|
# Web Search parameters (default: disabled)
|
||||||
|
enable_web_search = extra_body.get('enable_web_search', False)
|
||||||
|
web_search_config = extra_body.get('web_search_config', {})
|
||||||
|
|
||||||
ctx.logger.info(f"📋 Model: {model_name}")
|
ctx.logger.info(f"📋 Model: {model_name}")
|
||||||
ctx.logger.info(f"📋 Messages: {len(messages)}")
|
ctx.logger.info(f"📋 Messages: {len(messages)}")
|
||||||
ctx.logger.info(f"📋 Stream: {stream}")
|
ctx.logger.info(f"📋 Stream: {stream}")
|
||||||
|
ctx.logger.info(f"📋 Web Search: {'enabled' if enable_web_search else 'disabled'}")
|
||||||
|
if enable_web_search and web_search_config:
|
||||||
|
ctx.logger.debug(f"Web Search Config: {json.dumps(web_search_config, indent=2)}")
|
||||||
ctx.logger.debug(f"Messages: {json.dumps(messages, indent=2, ensure_ascii=False)}")
|
ctx.logger.debug(f"Messages: {json.dumps(messages, indent=2, ensure_ascii=False)}")
|
||||||
|
|
||||||
# Validate messages
|
# Validate messages
|
||||||
@@ -141,15 +154,15 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
|
|
||||||
break # Only check first user message
|
break # Only check first user message
|
||||||
|
|
||||||
# Priority 3: Error if no collection_id (strict mode)
|
# Priority 3: Error if no collection_id AND web_search disabled
|
||||||
if not collection_id:
|
if not collection_id and not enable_web_search:
|
||||||
ctx.logger.error("❌ No collection_id found (neither extra_body nor Aktenzeichen)")
|
ctx.logger.error("❌ No collection_id found and web_search disabled")
|
||||||
ctx.logger.error(" Provide collection_id in extra_body or start message with Aktenzeichen")
|
ctx.logger.error(" Provide collection_id, enable web_search, or both")
|
||||||
return ApiResponse(
|
return ApiResponse(
|
||||||
status=400,
|
status=400,
|
||||||
body={
|
body={
|
||||||
'error': 'collection_id required',
|
'error': 'collection_id or web_search required',
|
||||||
'message': 'Provide collection_id in extra_body or start message with Aktenzeichen (e.g., "1234/56 question")'
|
'message': 'Provide collection_id in extra_body, enable web_search, or start message with Aktenzeichen (e.g., "1234/56 question")'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -170,10 +183,12 @@ async def handler(request: ApiRequest, ctx: FlowContext[Any]) -> ApiResponse:
|
|||||||
max_tokens=max_tokens
|
max_tokens=max_tokens
|
||||||
)
|
)
|
||||||
|
|
||||||
# Bind file_search tool
|
# Bind tools (file_search and/or web_search)
|
||||||
model_with_tools = langchain_service.bind_file_search(
|
model_with_tools = langchain_service.bind_tools(
|
||||||
model=model,
|
model=model,
|
||||||
collection_id=collection_id,
|
collection_id=collection_id,
|
||||||
|
enable_web_search=enable_web_search,
|
||||||
|
web_search_config=web_search_config,
|
||||||
max_num_results=10
|
max_num_results=10
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user