evidor 1.10.6.dev1__tar.gz → 1.10.7.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/CHANGELOG.md +11 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/PKG-INFO +12 -7
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/README.md +11 -6
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/pyproject.toml +1 -1
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/gemini/builder.py +33 -13
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/gemini/provider.py +1 -13
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/models/thinking.py +13 -8
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_providers.py +33 -9
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_thinking.py +27 -23
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/.github/workflows/release.yml +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/.gitignore +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/AGENTS.md +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/CODE_OF_CONDUCT.md +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/LICENSE +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/requirements.txt +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/core/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/core/agent.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/core/context.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/core/runner.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/core/telemetry_bridge.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/anthropic/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/anthropic/builder.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/anthropic/parser.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/anthropic/provider.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/anthropic/stream.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/base.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/client_pool.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/gemini/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/gemini/parser.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/gemini/stream.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/openai/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/openai/builder.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/openai/parser.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/openai/provider.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/agent/providers/openai/stream.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/constants.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/errors.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/mcp/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/mcp/client.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/mcp/config.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/mcp/runner.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/mcp/session.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/models/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/models/generation.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/models/streaming.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/adapters/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/adapters/langfuse.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/adapters/opentelemetry.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/adapters/phoenix.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/adapters/prometheus.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/context.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/events.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/runtime.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/semconv.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/telemetry/sink.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/core/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/core/tool_executor.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/core/tools.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/core/utils.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/calculator.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/evidence_store.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/filesystem/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/filesystem/_common.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/filesystem/list_files.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/filesystem/mutate_files.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/filesystem/read_file.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/filesystem/search_files.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/get_current_time.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/read_webpage.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/_base.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/http.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/providers/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/providers/brave.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/providers/exa.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/providers/tavily.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/tool.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/utils/__init__.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/utils/execution.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/utils/schema.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_agent.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_builtin_tools.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_conversation.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_filesystem_tools.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_langfuse_adapter.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_mcp.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_models.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_native_tools.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_opentelemetry_adapter.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_phoenix_adapter.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_prometheus_adapter.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_streaming.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_telemetry_events.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_telemetry_integration.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_tools.py +0 -0
- {evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/tests/test_web_search.py +0 -0
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v1.10.7-dev.1 (2026-11-02)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- **gemini**: Fixing thinking related issues
|
|
10
|
+
([`4451ea1`](https://github.com/vedangiitb/evidor-core/commit/4451ea1d3d5039fd0cc276b9f6b0f6ceb88c5a92))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
## v1.10.6 (2026-10-31)
|
|
14
|
+
|
|
15
|
+
|
|
5
16
|
## v1.10.6-dev.1 (2026-10-30)
|
|
6
17
|
|
|
7
18
|
### Bug Fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: evidor
|
|
3
|
-
Version: 1.10.
|
|
3
|
+
Version: 1.10.7-dev.1
|
|
4
4
|
Summary: A small, provider-agnostic harness for LLM input and output.
|
|
5
5
|
Project-URL: Homepage, https://github.com/evidor/evidor-core
|
|
6
6
|
Project-URL: Repository, https://github.com/evidor/evidor-core
|
|
@@ -547,16 +547,21 @@ provider = AnthropicProvider(
|
|
|
547
547
|
thinking=AnthropicThinkingConfig(budget_tokens=4096),
|
|
548
548
|
)
|
|
549
549
|
|
|
550
|
-
# 3. Gemini:
|
|
551
|
-
#
|
|
550
|
+
# 3. Gemini: 2 modes for thinking configuration
|
|
551
|
+
# Mode 1: Thinking level string ('minimal', 'low', 'medium', 'high'):
|
|
552
552
|
provider = GeminiProvider(model="gemini-3.8-flash", thinking="high")
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
# Or via typed config:
|
|
553
|
+
|
|
554
|
+
# Mode 2: Object supporting thinking_level, thinking_summaries ('auto', 'none'), or thinking_budget:
|
|
556
555
|
provider = GeminiProvider(
|
|
557
556
|
model="gemini-3.8-flash",
|
|
558
557
|
thinking=GeminiThinkingConfig(thinking_level="high", thinking_summaries="auto"),
|
|
559
558
|
)
|
|
559
|
+
|
|
560
|
+
# Earlier models supporting numeric thinking_budget can also be configured via GeminiThinkingConfig:
|
|
561
|
+
provider = GeminiProvider(
|
|
562
|
+
model="gemini-2.5-flash",
|
|
563
|
+
thinking=GeminiThinkingConfig(thinking_budget=2048),
|
|
564
|
+
)
|
|
560
565
|
```
|
|
561
566
|
|
|
562
567
|
#### Inspecting Thinking Steps & Reasoning Tokens
|
|
@@ -1286,7 +1291,7 @@ Supported providers, example models, and reasoning/thinking support:
|
|
|
1286
1291
|
| --- | --- | --- | --- | --- |
|
|
1287
1292
|
| `OpenAIProvider` | `gpt-6-astra`, `gpt-6.1-sol`, `gpt-6-luna`, `gpt-4.1-mini` | `reasoning_effort` (`"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"`) | `OPENAI_API_KEY` | `evidor[openai]` |
|
|
1288
1293
|
| `AnthropicProvider` | `claude-3-7-sonnet` | Extended budget (`budget_tokens >= 1024`) or adaptive | `ANTHROPIC_API_KEY` | `evidor[anthropic]` |
|
|
1289
|
-
| `GeminiProvider` | `gemini-3.8-flash`, `gemini-3.5-flash-lite`, `gemini-3.1-pro-preview` | `thinking_level` (`"minimal"`, `"low"`, `"medium"`, `"high"`), `thinking_summaries` (`"auto"`, `"none"`) | `GEMINI_API_KEY` | `evidor[gemini]` |
|
|
1294
|
+
| `GeminiProvider` | `gemini-3.8-flash`, `gemini-3.5-flash-lite`, `gemini-3.1-pro-preview` | `thinking_level` (`"minimal"`, `"low"`, `"medium"`, `"high"`), `thinking_summaries` (`"auto"`, `"none"`), or `thinking_budget` | `GEMINI_API_KEY` | `evidor[gemini]` |
|
|
1290
1295
|
|
|
1291
1296
|
API keys can be supplied explicitly via the `api_key` constructor argument or read automatically from environment variables. Custom base URLs for OpenAI-compatible providers (such as DeepSeek) can be supplied via the `base_url` constructor argument or `OPENAI_BASE_URL` environment variable:
|
|
1292
1297
|
|
|
@@ -460,16 +460,21 @@ provider = AnthropicProvider(
|
|
|
460
460
|
thinking=AnthropicThinkingConfig(budget_tokens=4096),
|
|
461
461
|
)
|
|
462
462
|
|
|
463
|
-
# 3. Gemini:
|
|
464
|
-
#
|
|
463
|
+
# 3. Gemini: 2 modes for thinking configuration
|
|
464
|
+
# Mode 1: Thinking level string ('minimal', 'low', 'medium', 'high'):
|
|
465
465
|
provider = GeminiProvider(model="gemini-3.8-flash", thinking="high")
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
# Or via typed config:
|
|
466
|
+
|
|
467
|
+
# Mode 2: Object supporting thinking_level, thinking_summaries ('auto', 'none'), or thinking_budget:
|
|
469
468
|
provider = GeminiProvider(
|
|
470
469
|
model="gemini-3.8-flash",
|
|
471
470
|
thinking=GeminiThinkingConfig(thinking_level="high", thinking_summaries="auto"),
|
|
472
471
|
)
|
|
472
|
+
|
|
473
|
+
# Earlier models supporting numeric thinking_budget can also be configured via GeminiThinkingConfig:
|
|
474
|
+
provider = GeminiProvider(
|
|
475
|
+
model="gemini-2.5-flash",
|
|
476
|
+
thinking=GeminiThinkingConfig(thinking_budget=2048),
|
|
477
|
+
)
|
|
473
478
|
```
|
|
474
479
|
|
|
475
480
|
#### Inspecting Thinking Steps & Reasoning Tokens
|
|
@@ -1199,7 +1204,7 @@ Supported providers, example models, and reasoning/thinking support:
|
|
|
1199
1204
|
| --- | --- | --- | --- | --- |
|
|
1200
1205
|
| `OpenAIProvider` | `gpt-6-astra`, `gpt-6.1-sol`, `gpt-6-luna`, `gpt-4.1-mini` | `reasoning_effort` (`"low"`, `"medium"`, `"high"`, `"xhigh"`, `"max"`) | `OPENAI_API_KEY` | `evidor[openai]` |
|
|
1201
1206
|
| `AnthropicProvider` | `claude-3-7-sonnet` | Extended budget (`budget_tokens >= 1024`) or adaptive | `ANTHROPIC_API_KEY` | `evidor[anthropic]` |
|
|
1202
|
-
| `GeminiProvider` | `gemini-3.8-flash`, `gemini-3.5-flash-lite`, `gemini-3.1-pro-preview` | `thinking_level` (`"minimal"`, `"low"`, `"medium"`, `"high"`), `thinking_summaries` (`"auto"`, `"none"`) | `GEMINI_API_KEY` | `evidor[gemini]` |
|
|
1207
|
+
| `GeminiProvider` | `gemini-3.8-flash`, `gemini-3.5-flash-lite`, `gemini-3.1-pro-preview` | `thinking_level` (`"minimal"`, `"low"`, `"medium"`, `"high"`), `thinking_summaries` (`"auto"`, `"none"`), or `thinking_budget` | `GEMINI_API_KEY` | `evidor[gemini]` |
|
|
1203
1208
|
|
|
1204
1209
|
API keys can be supplied explicitly via the `api_key` constructor argument or read automatically from environment variables. Custom base URLs for OpenAI-compatible providers (such as DeepSeek) can be supplied via the `base_url` constructor argument or `OPENAI_BASE_URL` environment variable:
|
|
1205
1210
|
|
|
@@ -138,7 +138,12 @@ class GeminiPayloadBuilder:
|
|
|
138
138
|
|
|
139
139
|
return cleaned
|
|
140
140
|
|
|
141
|
-
def build(
|
|
141
|
+
def build(
|
|
142
|
+
self,
|
|
143
|
+
request: GenerationRequest,
|
|
144
|
+
types: Any = None,
|
|
145
|
+
thinking: Any = None,
|
|
146
|
+
) -> dict[str, Any]:
|
|
142
147
|
"""Construct the full request dictionary for Gemini Interactions API."""
|
|
143
148
|
system_prompt = extract_system_prompt(request)
|
|
144
149
|
input_steps = self.format_input_steps(request.messages)
|
|
@@ -167,19 +172,34 @@ class GeminiPayloadBuilder:
|
|
|
167
172
|
for t in sorted_tools
|
|
168
173
|
]
|
|
169
174
|
|
|
170
|
-
if
|
|
175
|
+
active_thinking = thinking if thinking is not None else self.thinking
|
|
176
|
+
if active_thinking and isinstance(active_thinking, str):
|
|
177
|
+
payload["generation_config"] = {"thinking_level": active_thinking}
|
|
178
|
+
elif active_thinking and not isinstance(active_thinking, bool):
|
|
171
179
|
gen_config: dict[str, Any] = {}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
180
|
+
thinking_level = (
|
|
181
|
+
active_thinking.get("thinking_level")
|
|
182
|
+
if isinstance(active_thinking, dict)
|
|
183
|
+
else getattr(active_thinking, "thinking_level", None)
|
|
184
|
+
)
|
|
185
|
+
if isinstance(thinking_level, str):
|
|
186
|
+
gen_config["thinking_level"] = thinking_level
|
|
187
|
+
|
|
188
|
+
thinking_summaries = (
|
|
189
|
+
active_thinking.get("thinking_summaries")
|
|
190
|
+
if isinstance(active_thinking, dict)
|
|
191
|
+
else getattr(active_thinking, "thinking_summaries", None)
|
|
192
|
+
)
|
|
193
|
+
if isinstance(thinking_summaries, str):
|
|
194
|
+
gen_config["thinking_summaries"] = thinking_summaries
|
|
195
|
+
|
|
196
|
+
thinking_budget = (
|
|
197
|
+
active_thinking.get("thinking_budget")
|
|
198
|
+
if isinstance(active_thinking, dict)
|
|
199
|
+
else getattr(active_thinking, "thinking_budget", None)
|
|
200
|
+
)
|
|
201
|
+
if thinking_budget is not None and not isinstance(thinking_budget, bool):
|
|
202
|
+
gen_config["thinking_budget"] = thinking_budget
|
|
183
203
|
|
|
184
204
|
if gen_config:
|
|
185
205
|
payload["generation_config"] = gen_config
|
|
@@ -32,19 +32,7 @@ class GeminiProvider(BaseProvider):
|
|
|
32
32
|
cached_content: str | None = None,
|
|
33
33
|
thinking: Any = None,
|
|
34
34
|
) -> None:
|
|
35
|
-
|
|
36
|
-
from evidor.models import GeminiThinkingConfig
|
|
37
|
-
|
|
38
|
-
thinking_cfg: GeminiThinkingConfig | None = (
|
|
39
|
-
thinking
|
|
40
|
-
if isinstance(thinking, GeminiThinkingConfig)
|
|
41
|
-
else GeminiThinkingConfig.from_value(thinking)
|
|
42
|
-
)
|
|
43
|
-
thinking_cfg.validate()
|
|
44
|
-
else:
|
|
45
|
-
thinking_cfg = None
|
|
46
|
-
|
|
47
|
-
super().__init__(model=model, api_key=api_key, thinking=thinking_cfg)
|
|
35
|
+
super().__init__(model=model, api_key=api_key, thinking=thinking)
|
|
48
36
|
self._cached_content = cached_content
|
|
49
37
|
self._client: Any = None
|
|
50
38
|
self._types: Any = None
|
|
@@ -67,11 +67,12 @@ class AnthropicThinkingConfig(ThinkingConfig):
|
|
|
67
67
|
|
|
68
68
|
|
|
69
69
|
@dataclass(frozen=True, slots=True)
|
|
70
|
-
class GeminiThinkingConfig
|
|
70
|
+
class GeminiThinkingConfig:
|
|
71
71
|
"""Configuration for Google Gemini thinking models in the Interactions API."""
|
|
72
72
|
|
|
73
73
|
thinking_level: str | None = None # Supported in Gemini 3.x Interactions API: 'minimal', 'low', 'medium', 'high'
|
|
74
|
-
thinking_summaries: str | None =
|
|
74
|
+
thinking_summaries: str | None = None # Supported in Gemini Interactions API: 'auto', 'none'
|
|
75
|
+
thinking_budget: int | None = None # Supported in earlier Gemini models (e.g. Gemini 2.5)
|
|
75
76
|
|
|
76
77
|
def validate(self) -> None:
|
|
77
78
|
pass
|
|
@@ -81,15 +82,19 @@ class GeminiThinkingConfig(ThinkingConfig):
|
|
|
81
82
|
if isinstance(value, GeminiThinkingConfig):
|
|
82
83
|
return value
|
|
83
84
|
if isinstance(value, bool):
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
raise TypeError(
|
|
86
|
+
"Cannot coerce bool to GeminiThinkingConfig. "
|
|
87
|
+
"Gemini Interactions API supports thinking_level (e.g. 'minimal', 'low', 'medium', 'high'), "
|
|
88
|
+
"thinking_summaries ('auto', 'none'), or thinking_budget (int)."
|
|
89
|
+
)
|
|
90
|
+
if isinstance(value, int):
|
|
91
|
+
return cls(thinking_budget=value)
|
|
87
92
|
if isinstance(value, str):
|
|
88
|
-
return cls(
|
|
93
|
+
return cls(thinking_level=value)
|
|
89
94
|
raise TypeError(
|
|
90
95
|
f"Cannot coerce {type(value).__name__} to GeminiThinkingConfig. "
|
|
91
|
-
"Gemini Interactions API supports thinking_level (e.g. 'minimal', 'low', 'medium', 'high') "
|
|
92
|
-
"
|
|
96
|
+
"Gemini Interactions API supports thinking_level (e.g. 'minimal', 'low', 'medium', 'high'), "
|
|
97
|
+
"thinking_summaries ('auto', 'none'), or thinking_budget (int)."
|
|
93
98
|
)
|
|
94
99
|
|
|
95
100
|
|
|
@@ -1414,7 +1414,17 @@ def test_gemini_payload_builder_thinking_config() -> None:
|
|
|
1414
1414
|
|
|
1415
1415
|
req = GenerationRequest(prompt="hello")
|
|
1416
1416
|
|
|
1417
|
-
# 1
|
|
1417
|
+
# Mode 1: thinking="..." (user configures thinking as a string)
|
|
1418
|
+
b_str = GeminiPayloadBuilder(
|
|
1419
|
+
model="gemini-3.8-flash",
|
|
1420
|
+
thinking="high",
|
|
1421
|
+
)
|
|
1422
|
+
p_str = b_str.build(req)
|
|
1423
|
+
assert p_str["generation_config"] == {
|
|
1424
|
+
"thinking_level": "high",
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
# Mode 2: Arbitrary / future thinking_level with thinking_summaries
|
|
1418
1428
|
b_custom = GeminiPayloadBuilder(
|
|
1419
1429
|
model="gemini-3.8-flash",
|
|
1420
1430
|
thinking=GeminiThinkingConfig(thinking_level="experimental_deep", thinking_summaries="auto"),
|
|
@@ -1425,7 +1435,7 @@ def test_gemini_payload_builder_thinking_config() -> None:
|
|
|
1425
1435
|
"thinking_summaries": "auto",
|
|
1426
1436
|
}
|
|
1427
1437
|
|
|
1428
|
-
# 2
|
|
1438
|
+
# Mode 2: thinking_summaries="none"
|
|
1429
1439
|
b_no_thoughts = GeminiPayloadBuilder(
|
|
1430
1440
|
model="gemini-3.8-flash",
|
|
1431
1441
|
thinking=GeminiThinkingConfig(thinking_level="high", thinking_summaries="none"),
|
|
@@ -1436,17 +1446,31 @@ def test_gemini_payload_builder_thinking_config() -> None:
|
|
|
1436
1446
|
"thinking_summaries": "none",
|
|
1437
1447
|
}
|
|
1438
1448
|
|
|
1439
|
-
#
|
|
1440
|
-
|
|
1449
|
+
# Mode 2: thinking_budget supported without error on client side
|
|
1450
|
+
b_budget = GeminiPayloadBuilder(
|
|
1441
1451
|
model="gemini-3.8-flash",
|
|
1442
|
-
thinking=GeminiThinkingConfig(
|
|
1452
|
+
thinking=GeminiThinkingConfig(thinking_budget=2048),
|
|
1443
1453
|
)
|
|
1444
|
-
|
|
1445
|
-
assert
|
|
1446
|
-
"
|
|
1447
|
-
"thinking_summaries": "none",
|
|
1454
|
+
p_budget = b_budget.build(req)
|
|
1455
|
+
assert p_budget["generation_config"] == {
|
|
1456
|
+
"thinking_budget": 2048,
|
|
1448
1457
|
}
|
|
1449
1458
|
|
|
1459
|
+
# Don't handle for thinking=False
|
|
1460
|
+
b_false = GeminiPayloadBuilder(
|
|
1461
|
+
model="gemini-3.8-flash",
|
|
1462
|
+
thinking=False,
|
|
1463
|
+
)
|
|
1464
|
+
p_false = b_false.build(req)
|
|
1465
|
+
assert "generation_config" not in p_false
|
|
1466
|
+
|
|
1467
|
+
# build() handles thinking passed directly without needing it at init
|
|
1468
|
+
b_dynamic = GeminiPayloadBuilder(model="gemini-3.8-flash")
|
|
1469
|
+
p_dyn_str = b_dynamic.build(req, thinking="low")
|
|
1470
|
+
assert p_dyn_str["generation_config"] == {"thinking_level": "low"}
|
|
1471
|
+
p_dyn_obj = b_dynamic.build(req, thinking=GeminiThinkingConfig(thinking_budget=4096))
|
|
1472
|
+
assert p_dyn_obj["generation_config"] == {"thinking_budget": 4096}
|
|
1473
|
+
|
|
1450
1474
|
|
|
1451
1475
|
def test_gemini_payload_builder_types_optional() -> None:
|
|
1452
1476
|
from evidor.agent.providers.gemini.builder import GeminiPayloadBuilder
|
|
@@ -104,34 +104,30 @@ def test_gemini_thinking_config() -> None:
|
|
|
104
104
|
cfg = GeminiThinkingConfig(thinking_level="high", thinking_summaries="auto")
|
|
105
105
|
assert cfg.thinking_level == "high"
|
|
106
106
|
assert cfg.thinking_summaries == "auto"
|
|
107
|
+
assert not hasattr(cfg, "enabled")
|
|
107
108
|
cfg.validate()
|
|
108
109
|
|
|
109
110
|
# Dynamic / None
|
|
110
111
|
cfg_dyn = GeminiThinkingConfig(thinking_level=None)
|
|
111
112
|
cfg_dyn.validate()
|
|
112
113
|
|
|
113
|
-
#
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
# Coercion from int -> raises TypeError because Interactions API does not support numeric budgets
|
|
118
|
-
with pytest.raises(TypeError, match="Numeric budgets are not supported"):
|
|
119
|
-
GeminiThinkingConfig.from_value(1024)
|
|
120
|
-
|
|
121
|
-
# Coercion from bool False -> minimal + none
|
|
122
|
-
c_false = GeminiThinkingConfig.from_value(False)
|
|
123
|
-
assert c_false.enabled is False
|
|
124
|
-
assert c_false.thinking_level == "minimal"
|
|
125
|
-
assert c_false.thinking_summaries == "none"
|
|
126
|
-
|
|
127
|
-
# Coercion from bool True
|
|
128
|
-
c_true = GeminiThinkingConfig.from_value(True)
|
|
129
|
-
assert c_true.enabled is True
|
|
114
|
+
# Coercion from int -> thinking_budget supported
|
|
115
|
+
c_int = GeminiThinkingConfig.from_value(1024)
|
|
116
|
+
assert c_int.thinking_budget == 1024
|
|
117
|
+
assert not hasattr(c_int, "enabled")
|
|
130
118
|
|
|
131
119
|
# Coercion from str -> thinking_level (Gemini 3.x)
|
|
132
120
|
c_str = GeminiThinkingConfig.from_value("high")
|
|
133
|
-
assert c_str.enabled is True
|
|
134
121
|
assert c_str.thinking_level == "high"
|
|
122
|
+
assert not hasattr(c_str, "enabled")
|
|
123
|
+
|
|
124
|
+
# Enabled is not a parameter on GeminiThinkingConfig
|
|
125
|
+
with pytest.raises(TypeError):
|
|
126
|
+
GeminiThinkingConfig(enabled=True) # type: ignore[call-arg]
|
|
127
|
+
|
|
128
|
+
# Bool coercion is not supported on GeminiThinkingConfig
|
|
129
|
+
with pytest.raises(TypeError, match="Cannot coerce bool to GeminiThinkingConfig"):
|
|
130
|
+
GeminiThinkingConfig.from_value(False)
|
|
135
131
|
|
|
136
132
|
|
|
137
133
|
# ==============================================================================
|
|
@@ -335,13 +331,20 @@ def test_gemini_provider_thinking_extraction_and_config(monkeypatch: pytest.Monk
|
|
|
335
331
|
assert call_config["thinking_level"] == "medium"
|
|
336
332
|
assert call_config["thinking_summaries"] == "auto"
|
|
337
333
|
|
|
338
|
-
# Test Gemini 3.x thinking_level parameter
|
|
334
|
+
# Test Gemini 3.x thinking_level parameter (Mode 1: thinking="high")
|
|
339
335
|
p_gemini_3 = GeminiProvider(model="gemini-3.8-flash", thinking="high")
|
|
340
|
-
assert p_gemini_3.thinking
|
|
336
|
+
assert p_gemini_3.thinking == "high"
|
|
341
337
|
res_3 = p_gemini_3.generate(GenerationRequest(prompt="Explain logic"))
|
|
342
338
|
call_config_3 = mock_client.interactions.create.call_args.kwargs["generation_config"]
|
|
343
339
|
assert call_config_3["thinking_level"] == "high"
|
|
344
|
-
assert
|
|
340
|
+
assert "thinking_summaries" not in call_config_3
|
|
341
|
+
|
|
342
|
+
# Test Mode 2 with thinking_budget (earlier models)
|
|
343
|
+
p_gemini_budget = GeminiProvider(model="gemini-2.5-flash", thinking=GeminiThinkingConfig(thinking_budget=2048))
|
|
344
|
+
assert p_gemini_budget.thinking.thinking_budget == 2048
|
|
345
|
+
p_gemini_budget.generate(GenerationRequest(prompt="Explain logic"))
|
|
346
|
+
call_config_budget = mock_client.interactions.create.call_args.kwargs["generation_config"]
|
|
347
|
+
assert call_config_budget["thinking_budget"] == 2048
|
|
345
348
|
|
|
346
349
|
|
|
347
350
|
def test_gemini_provider_disable_thinking(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
@@ -358,8 +361,9 @@ def test_gemini_provider_disable_thinking(monkeypatch: pytest.MonkeyPatch) -> No
|
|
|
358
361
|
provider = GeminiProvider(model="gemini-3.8-flash", thinking=False)
|
|
359
362
|
provider.generate(GenerationRequest(prompt="Fast query"))
|
|
360
363
|
|
|
361
|
-
|
|
362
|
-
|
|
364
|
+
# When thinking=False, it is not handled, so no generation_config is passed
|
|
365
|
+
call_kwargs = mock_client.interactions.create.call_args.kwargs
|
|
366
|
+
assert "generation_config" not in call_kwargs
|
|
363
367
|
|
|
364
368
|
|
|
365
369
|
# ==============================================================================
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/filesystem/mutate_files.py
RENAMED
|
File without changes
|
|
File without changes
|
{evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/filesystem/search_files.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/providers/__init__.py
RENAMED
|
File without changes
|
{evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/providers/brave.py
RENAMED
|
File without changes
|
{evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/providers/exa.py
RENAMED
|
File without changes
|
{evidor-1.10.6.dev1 → evidor-1.10.7.dev1}/src/evidor/tools/native/web_search/providers/tavily.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|