fixes infomaniak download

This commit is contained in:
ValueOn AG 2026-04-29 01:03:40 +02:00
parent 9816f13ae9
commit 49f3660d89
5 changed files with 68 additions and 10 deletions

View file

@ -13,6 +13,35 @@ from modules.datamodels.datamodelAi import AiModel, PriorityEnum, ProcessingMode
# Configure logger
logger = logging.getLogger(__name__)
def _supportsCustomTemperature(modelName: str) -> bool:
"""Check whether an Anthropic model accepts a custom ``temperature``.
Anthropic's Extended-Thinking models (Claude 4.7 Opus and the
upcoming 4.7 Sonnet/Haiku, plus all 5.x and beyond) reject every
``temperature`` value with HTTP 400
``{"error": "`temperature` is deprecated for this model."}`` --
only the model's internal default is accepted. Older Claude 4.5 /
4.6 models still accept any value in [0, 1].
Returns:
True if ``temperature`` may be sent; False if it must be omitted.
"""
if not modelName:
return True
name = modelName.lower()
if name.startswith("claude-opus-4-7"):
return False
if name.startswith("claude-sonnet-4-7"):
return False
if name.startswith("claude-haiku-4-7"):
return False
# 5.x and beyond: same Extended-Thinking family, no custom temperature.
if name.startswith("claude-opus-5") or name.startswith("claude-sonnet-5") or name.startswith("claude-haiku-5"):
return False
return True
def loadConfigData():
"""Load configuration data for Anthropic connector"""
return {
@ -276,8 +305,11 @@ class AiAnthropic(BaseConnectorAi):
payload: Dict[str, Any] = {
"model": model.name,
"messages": converted_messages,
"temperature": temperature,
}
# Extended-Thinking models (claude-opus-4-7 etc.) reject any
# `temperature` value -- only the model default is accepted.
if _supportsCustomTemperature(model.name):
payload["temperature"] = temperature
# Anthropic requires max_tokens - use provided value or throw error
if maxTokens is None:
@ -381,10 +413,11 @@ class AiAnthropic(BaseConnectorAi):
payload: Dict[str, Any] = {
"model": model.name,
"messages": converted,
"temperature": temperature,
"max_tokens": model.maxTokens,
"stream": True,
}
if _supportsCustomTemperature(model.name):
payload["temperature"] = temperature
if system_prompt:
payload["system"] = system_prompt
if modelCall.tools:
@ -609,7 +642,7 @@ class AiAnthropic(BaseConnectorAi):
if systemPrompt:
payload["system"] = systemPrompt
# Set temperature from model
if _supportsCustomTemperature(model.name):
payload["temperature"] = temperature
# Make API call with headers from httpClient (which includes anthropic-version)

View file

@ -101,13 +101,22 @@ async def _infomaniakDownload(
endpoint: str,
baseUrl: str = _API_BASE,
) -> Optional[bytes]:
"""Binary download from an Infomaniak host. Returns bytes or ``None``."""
"""Binary download from an Infomaniak host. Returns bytes or ``None``.
Unlike :func:`_infomaniakGet`, this follows redirects: kDrive's
``/2/drive/{driveId}/files/{fileId}/download`` answers with
``302 -> presigned CDN URL`` (standard for bandwidth-heavy
transfers), and the same pattern shows up on Calendar/Contacts
export endpoints. Refusing to follow would lose every download.
The Authorization header is preserved across the redirect by
aiohttp because the host is the same Infomaniak property.
"""
url = f"{baseUrl.rstrip('/')}/{endpoint.lstrip('/')}"
headers = {"Authorization": f"Bearer {token}"}
timeout = aiohttp.ClientTimeout(total=120)
try:
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.get(url, headers=headers, allow_redirects=False) as resp:
async with session.get(url, headers=headers, allow_redirects=True) as resp:
if resp.status == 200:
return await resp.read()
logger.warning(

View file

@ -26,7 +26,12 @@ class DataSource(PowerOnModel):
json_schema_extra={"label": "Verbindungs-ID", "fk_target": {"db": "poweron_app", "table": "UserConnection", "labelField": "externalUsername"}},
)
sourceType: str = Field(
description="sharepointFolder, googleDriveFolder, outlookFolder, ftpFolder, clickupList (path under /team/...)",
description=(
"sharepointFolder, onedriveFolder, googleDriveFolder, "
"outlookFolder, gmailFolder, ftpFolder, clickupList "
"(path under /team/...), kdriveFolder, calendarFolder, "
"contactFolder"
),
json_schema_extra={"label": "Quellentyp"},
)
path: str = Field(

View file

@ -188,6 +188,9 @@ _SOURCE_TYPE_TO_SERVICE = {
"gmailFolder": "gmail",
"ftpFolder": "files",
"clickupList": "clickup",
"kdriveFolder": "kdrive",
"calendarFolder": "calendar",
"contactFolder": "contact",
}

View file

@ -37,6 +37,11 @@ def _registerDataSourceTools(registry: ToolRegistry, services):
return getattr(chatService, "interfaceDbComponent", None)
# ---- DataSource convenience tools ----
# Maps the FE-side `sourceType` literal (see SourcesTab.tsx
# `_SERVICE_TO_SOURCE_TYPE`) to the Connector's `service` key in
# `_SERVICE_MAP`. Keep this table in sync with both the FE and the
# Connector `_SERVICE_MAP` entries -- a missing row produces
# "Service '<sourceType>' not available" in the agent tools.
_SOURCE_TYPE_TO_SERVICE = {
"sharepointFolder": "sharepoint",
"onedriveFolder": "onedrive",
@ -45,6 +50,9 @@ def _registerDataSourceTools(registry: ToolRegistry, services):
"gmailFolder": "gmail",
"ftpFolder": "files",
"clickupList": "clickup",
"kdriveFolder": "kdrive",
"calendarFolder": "calendar",
"contactFolder": "contact",
}
async def _resolveDataSource(dsId: str):