sap - add additional parameters for grounding

- additional parameter for grounding added for the sap provider
This commit is contained in:
Alperen Kömürcü 2026-02-12 12:14:43 +01:00 committed by Sameer Kankute
parent c6c8087488
commit 5211e7a121
No known key found for this signature in database
2 changed files with 130 additions and 15 deletions

View file

@ -1,6 +1,6 @@
from typing import Union, Literal
from pydantic import BaseModel, Field, field_validator
from pydantic import BaseModel, Field, field_validator, model_validator, ValidationError
def validate_different_content(v: Union[str, dict, list]) -> str:
@ -113,6 +113,8 @@ class SAPToolChatMessage(BaseModel):
validate_different_content
)
ChatMessage = Union[SAPUserMessage, SAPAssistantMessage, SAPToolChatMessage, SAPMessage]
class ResponseFormat(BaseModel):
type_: Literal["text", "json_object"] = Field(default="text", alias="type")
@ -128,3 +130,110 @@ class JSONResponseSchema(BaseModel):
class ResponseFormatJSONSchema(BaseModel):
type_: Literal["json_schema"] = Field(default="json_schema", alias="type")
json_schema: JSONResponseSchema
class KeyValueListPair(BaseModel):
key: str
value: list[str]
class DocumentMetadataKeyValueListPairs(KeyValueListPair):
select_mode: list[Literal['ignoreIfKeyAbsent']] = None
class GroundingSearchConfig(BaseModel):
max_chunk_count: int = Field(default=None, ge=0)
max_document_count: int = Field(default=None, ge=0)
@model_validator(mode='after')
def validate_max_chunk_count_and_max_document_count(self):
if self.max_chunk_count and self.max_document_count:
raise ValidationError("Cannot specify both maxChunkCount and maxDocumentCount.")
return self
class DocumentGroundingFilter(BaseModel):
id_: str = Field(default=None, alias="id")
data_repository_type: Literal["vector", "help.sap.com"]
search_config: GroundingSearchConfig = None
data_repositories: list[str] = None
data_repository_metadata: list[KeyValueListPair] = None
document_metadata: list[DocumentMetadataKeyValueListPairs] = None
chunk_metadata: list[KeyValueListPair] = None
class DocumentGroundingPlaceholders(BaseModel):
input: list[str] = Field(min_length=1)
output: str
class DocumentGroundingConfig(BaseModel):
filters: list[DocumentGroundingFilter] = None
placeholders: DocumentGroundingPlaceholders
metadata_params: list[str] = None
class GroundingModuleConfig(BaseModel):
type_: Literal["document_grounding_service"] = Field(default="document_grounding_service", alias="type")
config: DocumentGroundingConfig
class Template(BaseModel):
template: list[ChatMessage]
defaults: dict = None
response_format: ResponseFormat | ResponseFormatJSONSchema = None
tools: list[FunctionTool] = None
class LLMModelDetails(BaseModel):
name: str
version: str = "latest"
params: dict = None
class PromptTemplatingModuleConfig(BaseModel):
prompt: Template
model: LLMModelDetails
class ModuleConfig(BaseModel):
prompt_templating: PromptTemplatingModuleConfig
# filtering: Optional[FilteringModuleConfig] = None
# masking: Optional[MaskingModuleConfig] = None
grounding: GroundingModuleConfig = None
# translation: Optional[TranslationModuleConfig] = None
class GlobalStreamOptions(BaseModel):
enabled: bool = False
chunk_size: int = 100
delimiters: list[str] = None
@model_validator(mode='after')
def validate_streaming_params(self):
"""Validate that chunk_size and delimiters are not set when enabled is False."""
if not self.enabled:
if self.chunk_size != 100: # Check if chunk_size was explicitly set
raise ValueError("chunk_size cannot be set when enabled is False")
if self.delimiters is not None:
raise ValueError("delimiters cannot be set when enabled is False")
return self
def model_dump(self, **kwargs):
"""Override model_dump to exclude chunk_size and delimiters when enabled is False."""
data = super().model_dump(**kwargs)
if not self.enabled:
# Remove chunk_size and delimiters from output when streaming is disabled
data.pop('chunk_size', None)
data.pop('delimiters', None)
return data
class OrchestrationConfig(BaseModel):
modules: ModuleConfig
stream: GlobalStreamOptions = None
class OrchestrationRequest(BaseModel):
config: OrchestrationConfig
placeholder_values: dict = None

View file

@ -38,6 +38,8 @@ from .models import (
ResponseFormatJSONSchema,
ResponseFormat,
SAPUserMessage,
GroundingModuleConfig,
OrchestrationRequest
)
from .handler import (
GenAIHubOrchestrationError,
@ -227,23 +229,13 @@ class GenAIHubOrchestrationConfig(OpenAIGPTConfig):
excluded_params.add("strict")
model_params = {
k: v for k, v in optional_params.items() if k not in excluded_params
k: v for k, v in optional_params.items() if k not in {"tools", "model_version", "deployment_url", "grounding", "placeholder_values"}
}
model_version = optional_params.pop("model_version", "latest")
template = []
for message in messages:
if message["role"] == "user":
template.append(validate_dict(message, SAPUserMessage))
elif message["role"] == "assistant":
template.append(validate_dict(message, SAPAssistantMessage))
elif message["role"] == "tool":
template.append(validate_dict(message, SAPToolChatMessage))
else:
template.append(validate_dict(message, SAPMessage))
template = messages
tools_ = optional_params.pop("tools", [])
tools_ = [validate_dict(tool, ChatCompletionTool) for tool in tools_]
if tools_ != []:
tools = {"tools": tools_}
else:
@ -269,7 +261,7 @@ class GenAIHubOrchestrationConfig(OpenAIGPTConfig):
stream_config["delimiters"] = stream_options.get("delimiters")
# else:
# stream_config["enabled"] = False
config = {
request_body = {
"config": {
"modules": {
"prompt_templating": {
@ -285,7 +277,21 @@ class GenAIHubOrchestrationConfig(OpenAIGPTConfig):
}
}
return config
placeholder_defaults = optional_params.pop("placeholder_defaults", {})
if placeholder_defaults:
request_body["config"]["modules"]["prompt_templating"]["prompt"]["defaults"] = placeholder_defaults
placeholder_values = optional_params.pop("placeholder_values", {})
if placeholder_values:
request_body["placeholder_values"] = placeholder_values
grounding_config = optional_params.pop("grounding", {})
if grounding_config:
request_body["config"]["modules"]["grounding"] = grounding_config
validate_dict(request_body, OrchestrationRequest)
return request_body
def transform_response(
self,