commit 88099d461b0fb2d94783e5103a50adc95fc5ab2d
parent 6df7fd9acbfba3fdbe70bd2a76cf1c59b3ddaf94
Author: andrewlaack-collab <andrew.laack@imbue.com>
Date: Fri, 20 Feb 2026 08:26:02 +0000
Update models.json specification to add temperature boolean (#112)
* Update models.json specification to add an additional temperature boolean for models that do and don't support the param
* Updated support for temp
* Updated readme to make temperature explicit
* Make supports_temperature a required field in models.json
* Formatting
* Removed useless tests
---------
Co-authored-by: Andrew Laack <andrew@laack.co>
Co-authored-by: OpenCode <opencode@users.noreply.github.com>
Diffstat:
11 files changed, 149 insertions(+), 29 deletions(-)
diff --git a/.vet/models.json b/.vet/models.json
@@ -1,5 +1,25 @@
{
"providers": {
+ "zen": {
+ "name": "Zen",
+ "api_type": "openai_compatible",
+ "base_url": "https://opencode.ai/zen/v1",
+ "api_key_env": "ZEN_API_KEY",
+ "models": {
+ "big-pickle": {
+ "model_id": "big-pickle",
+ "context_window": 200000,
+ "max_output_tokens": 128000,
+ "supports_temperature": true
+ },
+ "gpt-5.2-codex": {
+ "model_id": "gpt-5.2-codex",
+ "context_window": 272000,
+ "max_output_tokens": 128000,
+ "supports_temperature": false
+ }
+ }
+ },
"anthropic": {
"name": "Anthropic",
"api_type": "openai_compatible",
@@ -9,17 +29,20 @@
"sonnet": {
"model_id": "claude-sonnet-4-5-20250929",
"context_window": 200000,
- "max_output_tokens": 16384
+ "max_output_tokens": 16384,
+ "supports_temperature": true
},
"haiku": {
"model_id": "claude-haiku-4-5-20251001",
"context_window": 200000,
- "max_output_tokens": 16384
+ "max_output_tokens": 16384,
+ "supports_temperature": true
},
"opus": {
"model_id": "claude-opus-4-6",
"context_window": 200000,
- "max_output_tokens": 16384
+ "max_output_tokens": 16384,
+ "supports_temperature": true
}
}
},
@@ -32,7 +55,8 @@
"gpt-5.2": {
"model_id": "gpt-5.2-2025-12-11",
"context_window": 128000,
- "max_output_tokens": 16384
+ "max_output_tokens": 16384,
+ "supports_temperature": true
}
}
},
@@ -45,12 +69,14 @@
"gpt-oss-120b": {
"model_id": "openai/gpt-oss-120b",
"context_window": 131072,
- "max_output_tokens": 65536
+ "max_output_tokens": 65536,
+ "supports_temperature": true
},
"kimi-k2": {
"model_id": "moonshotai/kimi-k2-instruct-0905",
"context_window": 262144,
- "max_output_tokens": 16384
+ "max_output_tokens": 16384,
+ "supports_temperature": true
}
}
}
diff --git a/README.md b/README.md
@@ -167,12 +167,14 @@ Vet supports custom model definitions using OpenAI-compatible endpoints via JSON
"gpt-5.2": {
"model_id": "openai/gpt-5.2",
"context_window": 400000,
- "max_output_tokens": 128000
+ "max_output_tokens": 128000,
+ "supports_temperature": true
},
"kimi-k2": {
"model_id": "moonshotai/kimi-k2",
"context_window": 131072,
- "max_output_tokens": 32768
+ "max_output_tokens": 32768,
+ "supports_temperature": true
}
}
}
diff --git a/dev/build.sh b/dev/build.sh
@@ -8,7 +8,7 @@ if [ "$1" = "claude" ]; then
IMAGE_NAME="vet-claude"
fi
-sudo docker build \
+docker build \
--build-arg INSTALL_CLAUDE=$INSTALL_CLAUDE \
-t $IMAGE_NAME \
dev/.
diff --git a/dev/run.sh b/dev/run.sh
@@ -8,7 +8,7 @@ fi
[ -f .env ] || { echo '.env file not found, please create one before proceeding'; exit 1; }
-sudo docker run -it \
+docker run -it \
--mount type=bind,source="$(pwd)",target=/app \
--env-file .env \
"$IMAGE_NAME" bash
diff --git a/vet/cli/config/loader.py b/vet/cli/config/loader.py
@@ -161,6 +161,7 @@ def build_language_model_config(model_id: str, user_config: ModelsConfig):
custom_api_key_env=provider.api_key_env or "",
custom_context_window=model_config.context_window,
custom_max_output_tokens=model_config.max_output_tokens,
+ custom_supports_temperature=model_config.supports_temperature,
)
diff --git a/vet/cli/config/loader_test.py b/vet/cli/config/loader_test.py
@@ -90,6 +90,7 @@ def test_load_single_config_file_loads_valid_config(tmp_path: Path) -> None:
"model_id": "test-model-v1",
"context_window": 128000,
"max_output_tokens": 16384,
+ "supports_temperature": True,
}
},
}
@@ -174,6 +175,7 @@ def test_load_models_config_loads_project_config(tmp_path: Path) -> None:
"project-model": {
"context_window": 128000,
"max_output_tokens": 16384,
+ "supports_temperature": True,
}
},
}
@@ -203,6 +205,7 @@ def test_load_models_config_project_overrides_global(tmp_path: Path) -> None:
"global-model": {
"context_window": 128000,
"max_output_tokens": 16384,
+ "supports_temperature": True,
}
},
}
@@ -227,6 +230,7 @@ def test_load_models_config_project_overrides_global(tmp_path: Path) -> None:
"project-model": {
"context_window": 128000,
"max_output_tokens": 16384,
+ "supports_temperature": True,
}
},
}
@@ -249,15 +253,27 @@ def test_get_user_defined_model_ids_extracts_all_ids() -> None:
base_url="http://localhost:8080/v1",
api_key_env="KEY1",
models={
- "model-a": ModelConfig(context_window=128000, max_output_tokens=16384),
- "model-b": ModelConfig(context_window=128000, max_output_tokens=16384),
+ "model-a": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ ),
+ "model-b": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ ),
},
),
"provider2": ProviderConfig(
base_url="http://localhost:8081/v1",
api_key_env="KEY2",
models={
- "model-c": ModelConfig(context_window=128000, max_output_tokens=16384),
+ "model-c": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ ),
},
),
}
@@ -274,12 +290,24 @@ def test_get_provider_for_model_finds_provider() -> None:
"provider1": ProviderConfig(
base_url="http://localhost:8080/v1",
api_key_env="KEY1",
- models={"model-a": ModelConfig(context_window=128000, max_output_tokens=16384)},
+ models={
+ "model-a": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ )
+ },
),
"provider2": ProviderConfig(
base_url="http://localhost:8081/v1",
api_key_env="KEY2",
- models={"model-b": ModelConfig(context_window=128000, max_output_tokens=16384)},
+ models={
+ "model-b": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ )
+ },
),
}
)
@@ -296,7 +324,13 @@ def test_get_provider_for_model_returns_none_for_unknown() -> None:
"provider1": ProviderConfig(
base_url="http://localhost:8080/v1",
api_key_env="KEY1",
- models={"model-a": ModelConfig(context_window=128000, max_output_tokens=16384)},
+ models={
+ "model-a": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ )
+ },
),
}
)
@@ -313,7 +347,13 @@ def test_validate_api_key_passes_when_key_is_set() -> None:
name="Test Provider",
base_url="http://localhost:8080/v1",
api_key_env="TEST_API_KEY",
- models={"model-a": ModelConfig(context_window=128000, max_output_tokens=16384)},
+ models={
+ "model-a": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ )
+ },
),
}
)
@@ -329,7 +369,13 @@ def test_validate_api_key_raises_when_key_not_set() -> None:
name="Test Provider",
base_url="http://localhost:8080/v1",
api_key_env="MISSING_KEY",
- models={"model-a": ModelConfig(context_window=128000, max_output_tokens=16384)},
+ models={
+ "model-a": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ )
+ },
),
}
)
@@ -357,15 +403,27 @@ def test_get_models_by_provider_groups_models() -> None:
base_url="http://localhost:11434/v1",
api_key_env="OLLAMA_KEY",
models={
- "llama3.2:latest": ModelConfig(context_window=128000, max_output_tokens=16384),
- "qwen:7b": ModelConfig(context_window=32768, max_output_tokens=8192),
+ "llama3.2:latest": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ ),
+ "qwen:7b": ModelConfig(
+ context_window=32768,
+ max_output_tokens=8192,
+ supports_temperature=True,
+ ),
},
),
"openrouter": ProviderConfig(
base_url="https://openrouter.ai/api/v1",
api_key_env="OPENROUTER_KEY",
models={
- "anthropic/claude-3": ModelConfig(context_window=200000, max_output_tokens=16384),
+ "anthropic/claude-3": ModelConfig(
+ context_window=200000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ ),
},
),
}
diff --git a/vet/cli/config/schema.py b/vet/cli/config/schema.py
@@ -13,6 +13,7 @@ class ModelConfig(BaseModel):
model_id: str | None = None
context_window: int
max_output_tokens: int
+ supports_temperature: bool
class ProviderConfig(BaseModel):
diff --git a/vet/cli/models_test.py b/vet/cli/models_test.py
@@ -20,8 +20,16 @@ SAMPLE_USER_CONFIG = ModelsConfig(
base_url="http://localhost:8080/v1",
api_key_env="CUSTOM_KEY",
models={
- "my-custom-model": ModelConfig(context_window=128000, max_output_tokens=16384),
- "another-model": ModelConfig(context_window=128000, max_output_tokens=16384),
+ "my-custom-model": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ ),
+ "another-model": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ ),
},
)
}
@@ -94,7 +102,13 @@ def test_validate_model_id_validates_user_defined_model() -> None:
"custom": ProviderConfig(
base_url="http://localhost:8080/v1",
api_key_env="CUSTOM_KEY",
- models={"my-custom-model": ModelConfig(context_window=128000, max_output_tokens=16384)},
+ models={
+ "my-custom-model": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ )
+ },
)
}
)
@@ -135,8 +149,16 @@ def test_get_models_by_provider_includes_user_defined_providers() -> None:
base_url="http://localhost:11434/v1",
api_key_env="OLLAMA_KEY",
models={
- "llama3.2:latest": ModelConfig(context_window=128000, max_output_tokens=16384),
- "qwen:7b": ModelConfig(context_window=32768, max_output_tokens=8192),
+ "llama3.2:latest": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ ),
+ "qwen:7b": ModelConfig(
+ context_window=32768,
+ max_output_tokens=8192,
+ supports_temperature=True,
+ ),
},
)
}
@@ -157,7 +179,13 @@ def test_get_models_by_provider_user_provider_overrides_builtin_with_same_name()
name="anthropic",
base_url="http://localhost:8080/v1",
api_key_env="CUSTOM_KEY",
- models={"custom-model": ModelConfig(context_window=128000, max_output_tokens=16384)},
+ models={
+ "custom-model": ModelConfig(
+ context_window=128000,
+ max_output_tokens=16384,
+ supports_temperature=True,
+ )
+ },
)
}
)
diff --git a/vet/imbue_core/agents/configs.py b/vet/imbue_core/agents/configs.py
@@ -67,6 +67,7 @@ class OpenAICompatibleModelConfig(LanguageModelGenerationConfig):
custom_api_key_env: str
custom_context_window: int
custom_max_output_tokens: int
+ custom_supports_temperature: bool = True
def count_tokens(self, text: str) -> int:
"""Count tokens using approximation since we don't have access to the model's tokenizer."""
diff --git a/vet/imbue_core/agents/llm_apis/build_apis.py b/vet/imbue_core/agents/llm_apis/build_apis.py
@@ -31,6 +31,7 @@ def build_language_model_from_config(
api_key_env=config.custom_api_key_env,
context_window=config.custom_context_window,
max_output_tokens=config.custom_max_output_tokens,
+ supports_temperature=config.custom_supports_temperature,
cache_path=config.cache_path,
is_caching_inputs=config.is_caching_inputs,
is_running_offline=config.is_running_offline,
diff --git a/vet/imbue_core/agents/llm_apis/openai_compatible_api.py b/vet/imbue_core/agents/llm_apis/openai_compatible_api.py
@@ -8,6 +8,7 @@ from loguru import logger
from openai import AsyncOpenAI
from openai import AsyncStream
from openai import InternalServerError
+from openai import NOT_GIVEN
from openai import NotGiven
from openai._exceptions import APIConnectionError
from openai._exceptions import BadRequestError
@@ -58,6 +59,7 @@ class OpenAICompatibleAPI(LanguageModelAPI):
max_output_tokens: int | None = None
is_conversational: bool = True
presence_penalty: float = 0.0
+ supports_temperature: bool = True
# this shouldn't really ever even be used, but just in case
stop_token_log_probability: float = math.log(0.9999)
@@ -127,7 +129,7 @@ class OpenAICompatibleAPI(LanguageModelAPI):
with self._exception_handler(prompt):
client = self._get_client()
- temperature: NotGiven | float = params.temperature
+ temperature: NotGiven | float = params.temperature if self.supports_temperature else NOT_GIVEN
api_result = await client.chat.completions.create(
model=self.model_name,
@@ -190,7 +192,7 @@ class OpenAICompatibleAPI(LanguageModelAPI):
with self._exception_handler(prompt):
client = self._get_client()
- temperature: NotGiven | float = params.temperature
+ temperature: NotGiven | float = params.temperature if self.supports_temperature else NOT_GIVEN
api_result = await client.chat.completions.create(
model=self.model_name,