Spaces:
Sleeping
Sleeping
yc1838 commited on
Commit ·
28a277f
1
Parent(s): bad5fbe
add deepseek compatibility
Browse files- .gitignore +4 -0
- CLAUDE.md +1 -1
- README.md +13 -0
- src/lilith_agent/app.py +7 -2
- src/lilith_agent/config.py +16 -0
- src/lilith_agent/models.py +40 -5
- tests/test_graph.py +12 -12
- tests/test_models.py +129 -0
.gitignore
CHANGED
|
@@ -18,3 +18,7 @@ gaia_eval_dashboard.html
|
|
| 18 |
.claude/
|
| 19 |
.codegraph/
|
| 20 |
.cursor/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
.claude/
|
| 19 |
.codegraph/
|
| 20 |
.cursor/
|
| 21 |
+
.pycache/
|
| 22 |
+
.__pycache__/
|
| 23 |
+
.ruff_cache/
|
| 24 |
+
.venv/
|
CLAUDE.md
CHANGED
|
@@ -49,7 +49,7 @@ Untrusted-input boundary: the user's first `HumanMessage` is wrapped in `<gaia_q
|
|
| 49 |
|
| 50 |
### Config (`src/lilith_agent/config.py`)
|
| 51 |
|
| 52 |
-
`Config.from_env()` reads `GAIA_*` env vars (note: `GAIA_` prefix, not `LILITH_`). Three model tiers: `cheap` / `strong` / `extra_strong`, each with independent provider+model. The agent's main model
|
| 53 |
|
| 54 |
### Tools (`src/lilith_agent/tools/`)
|
| 55 |
|
|
|
|
| 49 |
|
| 50 |
### Config (`src/lilith_agent/config.py`)
|
| 51 |
|
| 52 |
+
`Config.from_env()` reads `GAIA_*` env vars (note: `GAIA_` prefix, not `LILITH_`). Three model tiers: `cheap` / `strong` / `extra_strong`, each with independent provider+model. The agent's main model defaults to **`extra_strong`**, can select `cheap` or `strong` via `GAIA_AGENT_MODEL_TIER`, and can be directly overridden with `GAIA_AGENT_PROVIDER` / `GAIA_AGENT_MODEL`; `cheap` powers the tool-result summarizer. `extra_strong_*` defaults to `strong_*` if unset. DeepSeek is available as provider `deepseek` using `DEEPSEEK_API_KEY` or `GAIA_DEEPSEEK_API_KEY`. The `vision_*` pair is separate, used by `inspect_visual_content`. Behavior flags: `caveman`, `caveman_mode`, `recursion_limit` (50), `budget_hard_cap` (25), `budget_warn_at` (15), `semantic_dedup_threshold` (0.5), `compact_summarize`, `llm_formatter_enabled`.
|
| 53 |
|
| 54 |
### Tools (`src/lilith_agent/tools/`)
|
| 55 |
|
README.md
CHANGED
|
@@ -65,6 +65,19 @@ GAIA_STRONG_PROVIDER=anthropic
|
|
| 65 |
GAIA_STRONG_MODEL=claude-sonnet-4-6
|
| 66 |
GAIA_EXTRA_STRONG_PROVIDER=anthropic
|
| 67 |
GAIA_EXTRA_STRONG_MODEL=claude-sonnet-4-6
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
GAIA_VISION_PROVIDER=fal
|
| 69 |
GAIA_VISION_MODEL=gemini-3-flash-preview
|
| 70 |
GAIA_CAVEMAN=true
|
|
|
|
| 65 |
GAIA_STRONG_MODEL=claude-sonnet-4-6
|
| 66 |
GAIA_EXTRA_STRONG_PROVIDER=anthropic
|
| 67 |
GAIA_EXTRA_STRONG_MODEL=claude-sonnet-4-6
|
| 68 |
+
GAIA_AGENT_MODEL_TIER=extra_strong # cheap | strong | extra_strong
|
| 69 |
+
# Optional one-off override for the main agent model.
|
| 70 |
+
# GAIA_AGENT_PROVIDER=deepseek
|
| 71 |
+
# GAIA_AGENT_MODEL=deepseek-v4-pro
|
| 72 |
+
|
| 73 |
+
# DeepSeek uses an OpenAI-compatible API.
|
| 74 |
+
# DEEPSEEK_API_KEY=...
|
| 75 |
+
# GAIA_DEEPSEEK_BASE_URL=https://api.deepseek.com
|
| 76 |
+
# GAIA_CHEAP_PROVIDER=deepseek
|
| 77 |
+
# GAIA_CHEAP_MODEL=deepseek-v4-flash
|
| 78 |
+
# GAIA_STRONG_PROVIDER=deepseek
|
| 79 |
+
# GAIA_STRONG_MODEL=deepseek-v4-pro
|
| 80 |
+
|
| 81 |
GAIA_VISION_PROVIDER=fal
|
| 82 |
GAIA_VISION_MODEL=gemini-3-flash-preview
|
| 83 |
GAIA_CAVEMAN=true
|
src/lilith_agent/app.py
CHANGED
|
@@ -922,7 +922,10 @@ def build_react_agent(cfg: Config):
|
|
| 922 |
guidance = str(state.get("supervisor_guidance", "") or "").strip()
|
| 923 |
original_question = _initial_question_from_state(state)
|
| 924 |
compacted = _compact_old_tool_messages(state["messages"], summarize_fn=summarize_fn)
|
| 925 |
-
|
|
|
|
|
|
|
|
|
|
| 926 |
SystemMessage(content=(
|
| 927 |
"SUPERVISOR FINALIZER: Stop tool use. Answer the original question, not an intermediate hop. "
|
| 928 |
"Produce a bare final answer in 'Final Answer: ...' form using the existing evidence. "
|
|
@@ -1012,7 +1015,9 @@ def build_react_agent(cfg: Config):
|
|
| 1012 |
log.debug("[memory] skipping extraction: only %d messages", len(messages))
|
| 1013 |
return state
|
| 1014 |
try:
|
| 1015 |
-
|
|
|
|
|
|
|
| 1016 |
extract_and_compress_facts(messages, cheap_model)
|
| 1017 |
except Exception as e:
|
| 1018 |
log.warning("[memory] failed to run extraction: %s", e)
|
|
|
|
| 922 |
guidance = str(state.get("supervisor_guidance", "") or "").strip()
|
| 923 |
original_question = _initial_question_from_state(state)
|
| 924 |
compacted = _compact_old_tool_messages(state["messages"], summarize_fn=summarize_fn)
|
| 925 |
+
# Thinking mode (DeepSeek v4) emits tool-call markup as raw text here, where no
|
| 926 |
+
# tools are bound to parse it; disable it so the finalizer returns plain prose.
|
| 927 |
+
finalizer_model = get_extra_strong_model(cfg, thinking=False)
|
| 928 |
+
response = finalizer_model.invoke([
|
| 929 |
SystemMessage(content=(
|
| 930 |
"SUPERVISOR FINALIZER: Stop tool use. Answer the original question, not an intermediate hop. "
|
| 931 |
"Produce a bare final answer in 'Final Answer: ...' form using the existing evidence. "
|
|
|
|
| 1015 |
log.debug("[memory] skipping extraction: only %d messages", len(messages))
|
| 1016 |
return state
|
| 1017 |
try:
|
| 1018 |
+
# langmem/trustcall forces tool_choice for structured extraction, which
|
| 1019 |
+
# DeepSeek thinking mode rejects with a 400 it then retries forever.
|
| 1020 |
+
cheap_model = get_cheap_model(cfg, thinking=False)
|
| 1021 |
extract_and_compress_facts(messages, cheap_model)
|
| 1022 |
except Exception as e:
|
| 1023 |
log.warning("[memory] failed to run extraction: %s", e)
|
src/lilith_agent/config.py
CHANGED
|
@@ -41,6 +41,11 @@ class Config:
|
|
| 41 |
tavily_api_key: str
|
| 42 |
lmstudio_base_url: str
|
| 43 |
max_tokens: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
caveman: bool = False
|
| 45 |
caveman_mode: str = "full"
|
| 46 |
recursion_limit: int = 100
|
|
@@ -54,6 +59,12 @@ class Config:
|
|
| 54 |
|
| 55 |
@classmethod
|
| 56 |
def from_env(cls) -> "Config":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
return cls(
|
| 58 |
cheap_provider=os.getenv("GAIA_CHEAP_PROVIDER", "google"),
|
| 59 |
cheap_model=os.getenv("GAIA_CHEAP_MODEL", "gemini-3-flash-preview"),
|
|
@@ -72,6 +83,11 @@ class Config:
|
|
| 72 |
huggingface_api_key=os.getenv("GAIA_HUGGINGFACE_API_KEY", ""),
|
| 73 |
tavily_api_key=os.getenv("GAIA_TAVILY_API_KEY", ""),
|
| 74 |
lmstudio_base_url=os.getenv("GAIA_LMSTUDIO_BASE_URL", ""),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
max_tokens=_get_int_env("GAIA_MAX_TOKENS", "65536"),
|
| 76 |
caveman=os.getenv("GAIA_CAVEMAN", "true").lower() == "true",
|
| 77 |
caveman_mode=os.getenv("GAIA_CAVEMAN_MODE", "full"),
|
|
|
|
| 41 |
tavily_api_key: str
|
| 42 |
lmstudio_base_url: str
|
| 43 |
max_tokens: int
|
| 44 |
+
deepseek_api_key: str = ""
|
| 45 |
+
deepseek_base_url: str = "https://api.deepseek.com"
|
| 46 |
+
agent_model_tier: str = "extra_strong"
|
| 47 |
+
agent_provider: str = ""
|
| 48 |
+
agent_model: str = ""
|
| 49 |
caveman: bool = False
|
| 50 |
caveman_mode: str = "full"
|
| 51 |
recursion_limit: int = 100
|
|
|
|
| 59 |
|
| 60 |
@classmethod
|
| 61 |
def from_env(cls) -> "Config":
|
| 62 |
+
agent_model_tier = os.getenv("GAIA_AGENT_MODEL_TIER", "extra_strong").strip().lower()
|
| 63 |
+
if agent_model_tier not in {"cheap", "strong", "extra_strong"}:
|
| 64 |
+
raise ValueError(
|
| 65 |
+
"GAIA_AGENT_MODEL_TIER must be one of: cheap, strong, extra_strong"
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
return cls(
|
| 69 |
cheap_provider=os.getenv("GAIA_CHEAP_PROVIDER", "google"),
|
| 70 |
cheap_model=os.getenv("GAIA_CHEAP_MODEL", "gemini-3-flash-preview"),
|
|
|
|
| 83 |
huggingface_api_key=os.getenv("GAIA_HUGGINGFACE_API_KEY", ""),
|
| 84 |
tavily_api_key=os.getenv("GAIA_TAVILY_API_KEY", ""),
|
| 85 |
lmstudio_base_url=os.getenv("GAIA_LMSTUDIO_BASE_URL", ""),
|
| 86 |
+
deepseek_api_key=os.getenv("GAIA_DEEPSEEK_API_KEY", os.getenv("DEEPSEEK_API_KEY", "")),
|
| 87 |
+
deepseek_base_url=os.getenv("GAIA_DEEPSEEK_BASE_URL", "https://api.deepseek.com"),
|
| 88 |
+
agent_model_tier=agent_model_tier,
|
| 89 |
+
agent_provider=os.getenv("GAIA_AGENT_PROVIDER", "").strip(),
|
| 90 |
+
agent_model=os.getenv("GAIA_AGENT_MODEL", "").strip(),
|
| 91 |
max_tokens=_get_int_env("GAIA_MAX_TOKENS", "65536"),
|
| 92 |
caveman=os.getenv("GAIA_CAVEMAN", "true").lower() == "true",
|
| 93 |
caveman_mode=os.getenv("GAIA_CAVEMAN_MODE", "full"),
|
src/lilith_agent/models.py
CHANGED
|
@@ -109,6 +109,7 @@ if SQLiteCache:
|
|
| 109 |
log = logging.getLogger(__name__)
|
| 110 |
|
| 111 |
LMSTUDIO_DEFAULT_BASE_URL = "http://localhost:1234/v1"
|
|
|
|
| 112 |
_NO_THINK = "/no_think"
|
| 113 |
_GEMINI_COOLDOWN_MODELS = {"gemini-3-flash-preview", "gemini-3.1-pro"}
|
| 114 |
_COOLDOWN_LADDER_SECONDS = (60, 120, 300)
|
|
@@ -460,7 +461,27 @@ class _BoundRetryWrapper(Runnable):
|
|
| 460 |
return getattr(self._bound, name)
|
| 461 |
|
| 462 |
|
| 463 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
log.info("Building model for provider=%r, model=%r", provider, model)
|
| 465 |
max_tokens = cfg.max_tokens
|
| 466 |
|
|
@@ -493,6 +514,19 @@ def _build(provider: str, model: str, cfg: Config) -> BaseChatModel:
|
|
| 493 |
repo_id=model, huggingfacehub_api_token=cfg.huggingface_api_key, max_new_tokens=max_tokens
|
| 494 |
)
|
| 495 |
return _wrap(ChatHuggingFace(llm=endpoint))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 496 |
if provider == "lmstudio":
|
| 497 |
# LM Studio exposes an OpenAI-compatible API.
|
| 498 |
inner = ChatOpenAI(
|
|
@@ -509,13 +543,14 @@ def _build(provider: str, model: str, cfg: Config) -> BaseChatModel:
|
|
| 509 |
raise ValueError(f"Unknown provider: {provider}")
|
| 510 |
|
| 511 |
|
| 512 |
-
def get_cheap_model(cfg: Config) -> BaseChatModel:
|
| 513 |
-
return _build(cfg.cheap_provider, cfg.cheap_model, cfg)
|
| 514 |
|
| 515 |
|
| 516 |
def get_strong_model(cfg: Config) -> BaseChatModel:
|
| 517 |
return _build(cfg.strong_provider, cfg.strong_model, cfg)
|
| 518 |
|
| 519 |
|
| 520 |
-
def get_extra_strong_model(cfg: Config) -> BaseChatModel:
|
| 521 |
-
|
|
|
|
|
|
| 109 |
log = logging.getLogger(__name__)
|
| 110 |
|
| 111 |
LMSTUDIO_DEFAULT_BASE_URL = "http://localhost:1234/v1"
|
| 112 |
+
DEEPSEEK_DEFAULT_BASE_URL = "https://api.deepseek.com"
|
| 113 |
_NO_THINK = "/no_think"
|
| 114 |
_GEMINI_COOLDOWN_MODELS = {"gemini-3-flash-preview", "gemini-3.1-pro"}
|
| 115 |
_COOLDOWN_LADDER_SECONDS = (60, 120, 300)
|
|
|
|
| 461 |
return getattr(self._bound, name)
|
| 462 |
|
| 463 |
|
| 464 |
+
def _resolve_agent_model_choice(cfg: Config) -> tuple[str, str]:
|
| 465 |
+
tier = (cfg.agent_model_tier or "extra_strong").strip().lower()
|
| 466 |
+
tiers = {
|
| 467 |
+
"cheap": (cfg.cheap_provider, cfg.cheap_model),
|
| 468 |
+
"strong": (cfg.strong_provider, cfg.strong_model),
|
| 469 |
+
"extra_strong": (cfg.extra_strong_provider, cfg.extra_strong_model),
|
| 470 |
+
}
|
| 471 |
+
if tier not in tiers:
|
| 472 |
+
raise ValueError(
|
| 473 |
+
"GAIA_AGENT_MODEL_TIER must be one of: cheap, strong, extra_strong"
|
| 474 |
+
)
|
| 475 |
+
|
| 476 |
+
provider, model = tiers[tier]
|
| 477 |
+
return (
|
| 478 |
+
(cfg.agent_provider or provider).strip(),
|
| 479 |
+
(cfg.agent_model or model).strip(),
|
| 480 |
+
)
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def _build(provider: str, model: str, cfg: Config, thinking: bool = True) -> BaseChatModel:
|
| 484 |
+
provider = provider.strip().lower()
|
| 485 |
log.info("Building model for provider=%r, model=%r", provider, model)
|
| 486 |
max_tokens = cfg.max_tokens
|
| 487 |
|
|
|
|
| 514 |
repo_id=model, huggingfacehub_api_token=cfg.huggingface_api_key, max_new_tokens=max_tokens
|
| 515 |
)
|
| 516 |
return _wrap(ChatHuggingFace(llm=endpoint))
|
| 517 |
+
if provider == "deepseek":
|
| 518 |
+
ds_kwargs = dict(
|
| 519 |
+
model=model,
|
| 520 |
+
base_url=cfg.deepseek_base_url or DEEPSEEK_DEFAULT_BASE_URL,
|
| 521 |
+
api_key=cfg.deepseek_api_key,
|
| 522 |
+
max_tokens=max_tokens,
|
| 523 |
+
)
|
| 524 |
+
# DeepSeek v4 defaults to thinking mode, which rejects forced tool_choice
|
| 525 |
+
# (langmem extraction) and leaks raw tool-call markup in free-text calls
|
| 526 |
+
# (finalizer). Opt out explicitly when the caller needs a plain response.
|
| 527 |
+
if not thinking:
|
| 528 |
+
ds_kwargs["extra_body"] = {"thinking": {"type": "disabled"}}
|
| 529 |
+
return _wrap(ChatOpenAI(**ds_kwargs))
|
| 530 |
if provider == "lmstudio":
|
| 531 |
# LM Studio exposes an OpenAI-compatible API.
|
| 532 |
inner = ChatOpenAI(
|
|
|
|
| 543 |
raise ValueError(f"Unknown provider: {provider}")
|
| 544 |
|
| 545 |
|
| 546 |
+
def get_cheap_model(cfg: Config, thinking: bool = True) -> BaseChatModel:
|
| 547 |
+
return _build(cfg.cheap_provider, cfg.cheap_model, cfg, thinking=thinking)
|
| 548 |
|
| 549 |
|
| 550 |
def get_strong_model(cfg: Config) -> BaseChatModel:
|
| 551 |
return _build(cfg.strong_provider, cfg.strong_model, cfg)
|
| 552 |
|
| 553 |
|
| 554 |
+
def get_extra_strong_model(cfg: Config, thinking: bool = True) -> BaseChatModel:
|
| 555 |
+
provider, model = _resolve_agent_model_choice(cfg)
|
| 556 |
+
return _build(provider, model, cfg, thinking=thinking)
|
tests/test_graph.py
CHANGED
|
@@ -256,7 +256,7 @@ def test_supervisor_nudges_agent_to_answer_when_evidence_is_enough(monkeypatch,
|
|
| 256 |
cfg.compact_summarize = False
|
| 257 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 258 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 259 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 260 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 261 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 262 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -313,7 +313,7 @@ def test_supervisor_uses_extra_strong_model_not_cheap_model(monkeypatch, tmp_pat
|
|
| 313 |
cfg.compact_summarize = False
|
| 314 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 315 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 316 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 317 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", cheap_should_not_be_used)
|
| 318 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 319 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -362,7 +362,7 @@ def test_supervisor_finalizer_prompt_reinforces_original_question_contract(monke
|
|
| 362 |
cfg.compact_summarize = False
|
| 363 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 364 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 365 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 366 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 367 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 368 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -413,7 +413,7 @@ def test_supervisor_finalizer_rejects_unknown_best_answer_and_forces_best_guess(
|
|
| 413 |
cfg.compact_summarize = False
|
| 414 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 415 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 416 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 417 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 418 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 419 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -471,7 +471,7 @@ def test_supervisor_finalizes_even_with_placeholder_if_requested(monkeypatch, tm
|
|
| 471 |
cfg.compact_summarize = False
|
| 472 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 473 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 474 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 475 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 476 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 477 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -528,7 +528,7 @@ def test_supervisor_forces_finalize_after_max_nudges(monkeypatch, tmp_path):
|
|
| 528 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 529 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 530 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MAX_NUDGES", 5, raising=False)
|
| 531 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 532 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 533 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 534 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -580,7 +580,7 @@ def test_final_answer_gets_supervisor_review_and_can_be_returned_for_revision(mo
|
|
| 580 |
cfg.budget_warn_at = 99
|
| 581 |
cfg.compact_summarize = False
|
| 582 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 583 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 584 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 585 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [])
|
| 586 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -638,7 +638,7 @@ def test_fail_safe_falls_back_to_supervisor_best_answer_when_empty(monkeypatch,
|
|
| 638 |
cfg.compact_summarize = False
|
| 639 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 640 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 641 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 642 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 643 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 644 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -690,7 +690,7 @@ def test_fail_safe_never_returns_empty_answer_without_best_answer(monkeypatch, t
|
|
| 690 |
cfg.compact_summarize = False
|
| 691 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 692 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 99, raising=False)
|
| 693 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 694 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 695 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 696 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -749,7 +749,7 @@ def test_supervisor_review_auto_approves_after_fail_safe(monkeypatch, tmp_path):
|
|
| 749 |
cfg.compact_summarize = False
|
| 750 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 751 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 99, raising=False)
|
| 752 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 753 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 754 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 755 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -821,7 +821,7 @@ def test_supervisor_finalizes_when_agent_ignores_prior_nudge(monkeypatch, tmp_pa
|
|
| 821 |
cfg.compact_summarize = False
|
| 822 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 823 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 824 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 825 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 826 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 827 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
@@ -874,7 +874,7 @@ def test_supervisor_overhead_leaves_room_for_hard_cap_fail_safe(monkeypatch, tmp
|
|
| 874 |
cfg.compact_summarize = False
|
| 875 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 876 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 877 |
-
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg: strong)
|
| 878 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: FakeSupervisorModel())
|
| 879 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 880 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 256 |
cfg.compact_summarize = False
|
| 257 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 258 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 259 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 260 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 261 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 262 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 313 |
cfg.compact_summarize = False
|
| 314 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 315 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 316 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 317 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", cheap_should_not_be_used)
|
| 318 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 319 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 362 |
cfg.compact_summarize = False
|
| 363 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 364 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 365 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 366 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 367 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 368 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 413 |
cfg.compact_summarize = False
|
| 414 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 415 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 416 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 417 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 418 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 419 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 471 |
cfg.compact_summarize = False
|
| 472 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 473 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 474 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 475 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 476 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 477 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 528 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 529 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 530 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MAX_NUDGES", 5, raising=False)
|
| 531 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 532 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 533 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 534 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 580 |
cfg.budget_warn_at = 99
|
| 581 |
cfg.compact_summarize = False
|
| 582 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 583 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 584 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 585 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [])
|
| 586 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 638 |
cfg.compact_summarize = False
|
| 639 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 640 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 641 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 642 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 643 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 644 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 690 |
cfg.compact_summarize = False
|
| 691 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 692 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 99, raising=False)
|
| 693 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 694 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 695 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 696 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 749 |
cfg.compact_summarize = False
|
| 750 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 751 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 99, raising=False)
|
| 752 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 753 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 754 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 755 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 821 |
cfg.compact_summarize = False
|
| 822 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 823 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 824 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 825 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: object())
|
| 826 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 827 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
|
|
|
| 874 |
cfg.compact_summarize = False
|
| 875 |
monkeypatch.setenv("LILITH_HOME", str(tmp_path / ".lilith"))
|
| 876 |
monkeypatch.setattr("lilith_agent.app._SUPERVISOR_MIN_TOOL_CALLS", 1, raising=False)
|
| 877 |
+
monkeypatch.setattr("lilith_agent.app.get_extra_strong_model", lambda cfg, thinking=True: strong)
|
| 878 |
monkeypatch.setattr("lilith_agent.app.get_cheap_model", lambda cfg: FakeSupervisorModel())
|
| 879 |
monkeypatch.setattr("lilith_agent.tools.build_tools", lambda cfg: [echo_tool])
|
| 880 |
monkeypatch.setattr("lilith_agent.memory.extract_and_compress_facts", lambda messages, model: None)
|
tests/test_models.py
CHANGED
|
@@ -423,3 +423,132 @@ async def test_async_bound_retry_wrapper_raises_cooldown_for_gemini_lane(monkeyp
|
|
| 423 |
await bound.ainvoke([("user", "hi")])
|
| 424 |
|
| 425 |
assert raised.value.model == "gemini-3.1-pro"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
await bound.ainvoke([("user", "hi")])
|
| 424 |
|
| 425 |
assert raised.value.model == "gemini-3.1-pro"
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
def test_deepseek_config_defaults_and_env(monkeypatch):
|
| 429 |
+
from lilith_agent.config import Config
|
| 430 |
+
|
| 431 |
+
monkeypatch.delenv("GAIA_DEEPSEEK_API_KEY", raising=False)
|
| 432 |
+
monkeypatch.delenv("GAIA_DEEPSEEK_BASE_URL", raising=False)
|
| 433 |
+
monkeypatch.setenv("DEEPSEEK_API_KEY", "ds-env-key")
|
| 434 |
+
|
| 435 |
+
cfg = Config.from_env()
|
| 436 |
+
|
| 437 |
+
assert cfg.deepseek_api_key == "ds-env-key"
|
| 438 |
+
assert cfg.deepseek_base_url == "https://api.deepseek.com"
|
| 439 |
+
|
| 440 |
+
monkeypatch.setenv("GAIA_DEEPSEEK_API_KEY", "gaia-ds-key")
|
| 441 |
+
monkeypatch.setenv("GAIA_DEEPSEEK_BASE_URL", "https://deepseek.internal")
|
| 442 |
+
|
| 443 |
+
cfg = Config.from_env()
|
| 444 |
+
|
| 445 |
+
assert cfg.deepseek_api_key == "gaia-ds-key"
|
| 446 |
+
assert cfg.deepseek_base_url == "https://deepseek.internal"
|
| 447 |
+
|
| 448 |
+
|
| 449 |
+
def test_agent_model_tier_selects_configured_tier(monkeypatch):
|
| 450 |
+
from lilith_agent.config import Config
|
| 451 |
+
from lilith_agent.models import _resolve_agent_model_choice
|
| 452 |
+
|
| 453 |
+
monkeypatch.setenv("GAIA_CHEAP_PROVIDER", "deepseek")
|
| 454 |
+
monkeypatch.setenv("GAIA_CHEAP_MODEL", "deepseek-v4-flash")
|
| 455 |
+
monkeypatch.setenv("GAIA_STRONG_PROVIDER", "deepseek")
|
| 456 |
+
monkeypatch.setenv("GAIA_STRONG_MODEL", "deepseek-v4-pro")
|
| 457 |
+
monkeypatch.setenv("GAIA_AGENT_MODEL_TIER", "strong")
|
| 458 |
+
|
| 459 |
+
assert _resolve_agent_model_choice(Config.from_env()) == ("deepseek", "deepseek-v4-pro")
|
| 460 |
+
|
| 461 |
+
|
| 462 |
+
def test_agent_model_direct_override_wins_over_tier(monkeypatch):
|
| 463 |
+
from lilith_agent.config import Config
|
| 464 |
+
from lilith_agent.models import _resolve_agent_model_choice
|
| 465 |
+
|
| 466 |
+
monkeypatch.setenv("GAIA_AGENT_MODEL_TIER", "cheap")
|
| 467 |
+
monkeypatch.setenv("GAIA_AGENT_PROVIDER", "deepseek")
|
| 468 |
+
monkeypatch.setenv("GAIA_AGENT_MODEL", "deepseek-v4-pro")
|
| 469 |
+
|
| 470 |
+
assert _resolve_agent_model_choice(Config.from_env()) == ("deepseek", "deepseek-v4-pro")
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
def test_invalid_agent_model_tier_is_rejected(monkeypatch):
|
| 474 |
+
from lilith_agent.config import Config
|
| 475 |
+
|
| 476 |
+
monkeypatch.setenv("GAIA_AGENT_MODEL_TIER", "medium")
|
| 477 |
+
|
| 478 |
+
with pytest.raises(ValueError, match="GAIA_AGENT_MODEL_TIER"):
|
| 479 |
+
Config.from_env()
|
| 480 |
+
|
| 481 |
+
|
| 482 |
+
def test_build_deepseek_uses_openai_compatible_endpoint(monkeypatch):
|
| 483 |
+
from dataclasses import replace
|
| 484 |
+
|
| 485 |
+
from lilith_agent.config import Config
|
| 486 |
+
from lilith_agent.models import _build
|
| 487 |
+
|
| 488 |
+
class FakeChatOpenAI:
|
| 489 |
+
def __init__(self, **kwargs):
|
| 490 |
+
self.kwargs = kwargs
|
| 491 |
+
|
| 492 |
+
monkeypatch.setattr("lilith_agent.models.ChatOpenAI", FakeChatOpenAI)
|
| 493 |
+
monkeypatch.setattr(
|
| 494 |
+
"lilith_agent.models._RetryWrapper",
|
| 495 |
+
lambda inner, provider, model_name: inner,
|
| 496 |
+
)
|
| 497 |
+
|
| 498 |
+
cfg = replace(
|
| 499 |
+
Config.from_env(),
|
| 500 |
+
deepseek_api_key="ds-key",
|
| 501 |
+
deepseek_base_url="https://deepseek.internal",
|
| 502 |
+
max_tokens=123,
|
| 503 |
+
)
|
| 504 |
+
|
| 505 |
+
model = _build("deepseek", "deepseek-v4-flash", cfg)
|
| 506 |
+
|
| 507 |
+
assert model.kwargs == {
|
| 508 |
+
"model": "deepseek-v4-flash",
|
| 509 |
+
"api_key": "ds-key",
|
| 510 |
+
"base_url": "https://deepseek.internal",
|
| 511 |
+
"max_tokens": 123,
|
| 512 |
+
}
|
| 513 |
+
|
| 514 |
+
|
| 515 |
+
def _fake_deepseek_cfg(monkeypatch):
|
| 516 |
+
from dataclasses import replace
|
| 517 |
+
|
| 518 |
+
from lilith_agent.config import Config
|
| 519 |
+
|
| 520 |
+
class FakeChatOpenAI:
|
| 521 |
+
def __init__(self, **kwargs):
|
| 522 |
+
self.kwargs = kwargs
|
| 523 |
+
|
| 524 |
+
monkeypatch.setattr("lilith_agent.models.ChatOpenAI", FakeChatOpenAI)
|
| 525 |
+
monkeypatch.setattr(
|
| 526 |
+
"lilith_agent.models._RetryWrapper",
|
| 527 |
+
lambda inner, provider, model_name: inner,
|
| 528 |
+
)
|
| 529 |
+
return replace(
|
| 530 |
+
Config.from_env(),
|
| 531 |
+
deepseek_api_key="ds-key",
|
| 532 |
+
deepseek_base_url="https://deepseek.internal",
|
| 533 |
+
max_tokens=123,
|
| 534 |
+
)
|
| 535 |
+
|
| 536 |
+
|
| 537 |
+
def test_build_deepseek_thinking_disabled_sets_extra_body(monkeypatch):
|
| 538 |
+
from lilith_agent.models import _build
|
| 539 |
+
|
| 540 |
+
cfg = _fake_deepseek_cfg(monkeypatch)
|
| 541 |
+
|
| 542 |
+
model = _build("deepseek", "deepseek-v4-flash", cfg, thinking=False)
|
| 543 |
+
|
| 544 |
+
assert model.kwargs["extra_body"] == {"thinking": {"type": "disabled"}}
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
def test_build_deepseek_thinking_enabled_omits_extra_body(monkeypatch):
|
| 548 |
+
from lilith_agent.models import _build
|
| 549 |
+
|
| 550 |
+
cfg = _fake_deepseek_cfg(monkeypatch)
|
| 551 |
+
|
| 552 |
+
model = _build("deepseek", "deepseek-v4-flash", cfg)
|
| 553 |
+
|
| 554 |
+
assert "extra_body" not in model.kwargs
|