Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions backend/app/crud/model_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,19 @@ def is_reasoning_model(session: Session, provider: Provider, model_name: str) ->
return "effort" in model.config


def is_summary_model(session: Session, provider: Provider, model_name: str) -> bool:
"""Return True if the model is configured with a summary `effort` control.

A model is considered summary-capable if its `config` JSON contains an
`summary` key; models that instead expose a `temperature` key are treated
as standard chat models.
"""
model = get_model_config(session=session, provider=provider, model_name=model_name)
if model is None or not isinstance(model.config, dict):
return False
return "summary" in model.config


def estimate_model_cost(
session: Session,
provider: Provider,
Expand Down
25 changes: 19 additions & 6 deletions backend/app/services/llm/mappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ def map_kaapi_to_openai_params(
- instructions → instructions
- knowledge_base_ids → tools[file_search].vector_store_ids
- max_num_results → tools[file_search].max_num_results (fallback default)
- reasoning → reasoning.effort (if reasoning supported by model else suppressed)
- effort → reasoning.effort (if reasoning supported by model else suppressed)
- reasoning → legacy alias for effort, used only when effort is absent
- summary → reasoning.summary (if reasoning supported by model else suppressed)
- temperature → temperature (if reasoning not supported by model else suppressed)

Returns:
Expand All @@ -83,6 +85,8 @@ def map_kaapi_to_openai_params(

model = kaapi_params.get("model")
reasoning = kaapi_params.get("reasoning")
effort = kaapi_params.get("effort")
summary = kaapi_params.get("summary")
temperature = kaapi_params.get("temperature")
instructions = kaapi_params.get("instructions")
knowledge_base_ids = kaapi_params.get("knowledge_base_ids")
Expand All @@ -92,21 +96,30 @@ def map_kaapi_to_openai_params(
session=session, provider="openai", model_name=model
)

# 'effort' is the canonical knob; 'reasoning' is a legacy alias kept for
# backward compatibility and only consulted when 'effort' is absent
effort_value = effort if effort is not None else reasoning

# Handle reasoning vs temperature mutual exclusivity
if support_reasoning:
if reasoning is not None:
openai_params["reasoning"] = {"effort": reasoning}
if effort_value is not None or summary is not None:
openai_params["reasoning"] = {}
if effort_value is not None:
openai_params["reasoning"] = {"effort": effort_value}

if summary is not None:
openai_params["reasoning"]["summary"] = summary

if temperature is not None:
warnings.append(
"Parameter 'temperature' was suppressed because the selected model "
"supports reasoning, and temperature is ignored when reasoning is enabled."
)
else:
if reasoning is not None:
if effort_value is not None:
warnings.append(
"Parameter 'reasoning' was suppressed because the selected model "
"does not support reasoning."
"Parameters 'reasoning'/'effort' were suppressed because the "
"selected model does not support reasoning."
)

if temperature is not None:
Expand Down
76 changes: 76 additions & 0 deletions backend/app/tests/crud/test_model_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,79 @@ def test_validate_blob_text_model_accepted_for_text_type(
_patch_validators(monkeypatch, row=row, supported=True)
blob = _make_blob("openai", "text", {"model": "gpt-4o"})
model_config_crud.validate_blob_model_or_raise(session=None, blob=blob) # type: ignore[arg-type]


def _patch_model_config(
monkeypatch: pytest.MonkeyPatch,
config: Any,
) -> None:
model = SimpleNamespace(config=config)
monkeypatch.setattr(
model_config_crud,
"get_model_config",
lambda session, provider, model_name: model,
)


def test_is_summary_model_true_when_summary_in_config(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_patch_model_config(monkeypatch, config={"summary": "auto"})

assert (
model_config_crud.is_summary_model(
session=None, # type: ignore[arg-type]
provider="openai",
model_name="gpt-5",
)
is True
)


def test_is_summary_model_false_when_summary_absent(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_patch_model_config(monkeypatch, config={"temperature": 0.7})

assert (
model_config_crud.is_summary_model(
session=None, # type: ignore[arg-type]
provider="openai",
model_name="gpt-4o",
)
is False
)


def test_is_summary_model_false_for_missing_model(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setattr(
model_config_crud,
"get_model_config",
lambda session, provider, model_name: None,
)

assert (
model_config_crud.is_summary_model(
session=None, # type: ignore[arg-type]
provider="openai",
model_name="does-not-exist",
)
is False
)


def test_is_summary_model_false_for_non_dict_config(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_patch_model_config(monkeypatch, config=["invalid"])

assert (
model_config_crud.is_summary_model(
session=None, # type: ignore[arg-type]
provider="openai",
model_name="gpt-4o",
)
is False
)
15 changes: 15 additions & 0 deletions backend/app/tests/services/llm/test_mappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,21 @@ def test_reasoning_suppressed_for_non_reasoning_models(self, db: Session):
assert "reasoning" in warnings[0].lower()
assert "does not support reasoning" in warnings[0]

def test_summary_mapping_for_reasoning_models(self, db: Session):
"""Test summary maps into reasoning block alongside effort for reasoning models."""
kaapi_params = TextLLMParams(
model="gpt-5",
effort="high",
summary="detailed",
)

result, warnings = map_kaapi_to_openai_params(
session=db, kaapi_params=kaapi_params.model_dump(exclude_none=True)
)

assert result["model"] == "gpt-5"
assert result["reasoning"] == {"effort": "high", "summary": "detailed"}


class TestMapKaapiToGoogleParams:
"""Test cases for map_kaapi_to_google_params function with completion_type."""
Expand Down
Loading