fix: Enhance tool binding in LangChainXAIService to support web search and update API handler for new parameters

This commit is contained in:
bsiggel
2026-03-15 16:37:57 +00:00
parent 59fdd7d9ec
commit 8e53fd6345
2 changed files with 97 additions and 26 deletions

View File

@@ -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,

View File

@@ -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
) )