selectools 0.17.3__tar.gz → 0.17.4__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.
- {selectools-0.17.3/src/selectools.egg-info → selectools-0.17.4}/PKG-INFO +1 -1
- {selectools-0.17.3 → selectools-0.17.4}/pyproject.toml +1 -1
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/__init__.py +17 -2
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/agent/_provider_caller.py +25 -25
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/agent/config.py +2 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/agent/core.py +52 -0
- selectools-0.17.4/src/selectools/knowledge.py +655 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/observer.py +44 -0
- selectools-0.17.4/src/selectools/token_estimation.py +153 -0
- {selectools-0.17.3 → selectools-0.17.4/src/selectools.egg-info}/PKG-INFO +1 -1
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools.egg-info/SOURCES.txt +4 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_knowledge.py +6 -11
- selectools-0.17.4/tests/test_knowledge_stores.py +299 -0
- selectools-0.17.4/tests/test_model_switching.py +159 -0
- selectools-0.17.4/tests/test_token_estimation.py +135 -0
- selectools-0.17.3/src/selectools/knowledge.py +0 -198
- {selectools-0.17.3 → selectools-0.17.4}/LICENSE +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/README.md +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/setup.cfg +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/agent/__init__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/agent/_lifecycle.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/agent/_memory_manager.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/agent/_tool_executor.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/analytics.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/audit.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/cache.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/cache_redis.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/cancellation.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/coherence.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/embeddings/__init__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/embeddings/anthropic.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/embeddings/cohere.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/embeddings/gemini.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/embeddings/openai.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/embeddings/provider.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/entity_memory.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/env.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/__init__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/__main__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/badge.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/dataset.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/evaluators.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/generator.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/history.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/html.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/junit.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/llm_evaluators.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/pairwise.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/regression.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/report.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/serve.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/snapshot.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/suite.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/templates.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/evals/types.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/exceptions.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/guardrails/__init__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/guardrails/base.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/guardrails/format.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/guardrails/length.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/guardrails/pii.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/guardrails/pipeline.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/guardrails/topic.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/guardrails/toxicity.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/knowledge_graph.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/mcp/__init__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/mcp/_loop.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/mcp/bridge.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/mcp/client.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/mcp/config.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/mcp/multi.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/mcp/server.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/memory.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/models.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/parser.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/policy.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/pricing.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/prompt.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/providers/__init__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/providers/_openai_compat.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/providers/anthropic_provider.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/providers/base.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/providers/fallback.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/providers/gemini_provider.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/providers/ollama_provider.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/providers/openai_provider.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/providers/stubs.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/__init__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/bm25.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/chunking.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/hybrid.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/loaders.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/reranker.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/stores/__init__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/stores/chroma.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/stores/memory.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/stores/pinecone.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/stores/sqlite.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/tools.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/rag/vector_store.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/security.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/sessions.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/structured.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/toolbox/__init__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/toolbox/data_tools.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/toolbox/datetime_tools.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/toolbox/file_tools.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/toolbox/memory_tools.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/toolbox/text_tools.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/toolbox/web_tools.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/tools/__init__.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/tools/base.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/tools/decorators.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/tools/loader.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/tools/registry.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/trace.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/types.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools/usage.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools.egg-info/dependency_links.txt +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools.egg-info/requires.txt +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/src/selectools.egg-info/top_level.txt +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_approval_gate.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_architecture.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_async_observers.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_audit.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_budget.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_cache.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_cache_redis.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_cancellation.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_coherence.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_consolidation_regression.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_entity_memory.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_env.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_evals.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_evals_advanced.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_evals_e2e.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_evals_final.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_evals_hardening.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_evals_new_evaluators.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_evals_release.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_guardrails.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_knowledge_graph.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_mcp.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_memory.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_memory_async.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_memory_boundary.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_memory_integration.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_parser.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_phase1_design_patterns.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_policy.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_prompt.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_routing_mode.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_security.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_sessions.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_sessions_edge_cases.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_sessions_redis.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_simple_observer.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_structured.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_structured_tool_results.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_summarize_on_trim.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_terminal_actions.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_trace.py +0 -0
- {selectools-0.17.3 → selectools-0.17.4}/tests/test_v016_regression.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: selectools
|
|
3
|
-
Version: 0.17.
|
|
3
|
+
Version: 0.17.4
|
|
4
4
|
Summary: Production-ready AI agents with tool calling, structured output, execution traces, and RAG. Provider-agnostic (OpenAI, Anthropic, Gemini, Ollama) with fallback chains, batch processing, tool policies, streaming, caching, and cost tracking.
|
|
5
5
|
Author-email: John Nichev <johnnichev@gmail.com>
|
|
6
6
|
Maintainer-email: NichevLabs <support@nichevlabs.com>
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "selectools"
|
|
7
|
-
version = "0.17.
|
|
7
|
+
version = "0.17.4"
|
|
8
8
|
description = "Production-ready AI agents with tool calling, structured output, execution traces, and RAG. Provider-agnostic (OpenAI, Anthropic, Gemini, Ollama) with fallback chains, batch processing, tool policies, streaming, caching, and cost tracking."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Public exports for the selectools package."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.17.
|
|
3
|
+
__version__ = "0.17.4"
|
|
4
4
|
|
|
5
5
|
# Import submodules (lazy loading for optional dependencies)
|
|
6
6
|
from . import embeddings, evals, guardrails, models, rag, toolbox
|
|
@@ -34,7 +34,13 @@ from .guardrails import (
|
|
|
34
34
|
TopicGuardrail,
|
|
35
35
|
ToxicityGuardrail,
|
|
36
36
|
)
|
|
37
|
-
from .knowledge import
|
|
37
|
+
from .knowledge import (
|
|
38
|
+
FileKnowledgeStore,
|
|
39
|
+
KnowledgeEntry,
|
|
40
|
+
KnowledgeMemory,
|
|
41
|
+
KnowledgeStore,
|
|
42
|
+
SQLiteKnowledgeStore,
|
|
43
|
+
)
|
|
38
44
|
from .knowledge_graph import (
|
|
39
45
|
InMemoryTripleStore,
|
|
40
46
|
KnowledgeGraphMemory,
|
|
@@ -73,6 +79,7 @@ from .sessions import (
|
|
|
73
79
|
SQLiteSessionStore,
|
|
74
80
|
)
|
|
75
81
|
from .structured import ResponseFormat
|
|
82
|
+
from .token_estimation import TokenEstimate, estimate_run_tokens, estimate_tokens
|
|
76
83
|
from .tools import Tool, ToolParameter, ToolRegistry, tool
|
|
77
84
|
from .trace import AgentTrace, StepType, TraceStep
|
|
78
85
|
from .types import AgentResult, Message, Role, ToolCall
|
|
@@ -177,6 +184,14 @@ __all__ = [
|
|
|
177
184
|
"EntityMemory",
|
|
178
185
|
# Knowledge Memory
|
|
179
186
|
"KnowledgeMemory",
|
|
187
|
+
"KnowledgeEntry",
|
|
188
|
+
"KnowledgeStore",
|
|
189
|
+
"FileKnowledgeStore",
|
|
190
|
+
"SQLiteKnowledgeStore",
|
|
191
|
+
# Token estimation
|
|
192
|
+
"TokenEstimate",
|
|
193
|
+
"estimate_tokens",
|
|
194
|
+
"estimate_run_tokens",
|
|
180
195
|
# Knowledge Graph
|
|
181
196
|
"Triple",
|
|
182
197
|
"TripleStore",
|
|
@@ -37,7 +37,7 @@ class _ProviderCallerMixin:
|
|
|
37
37
|
self.config.stream and getattr(self.provider, "supports_streaming", False)
|
|
38
38
|
):
|
|
39
39
|
cache_key = CacheKeyBuilder.build(
|
|
40
|
-
model=self.
|
|
40
|
+
model=self._effective_model,
|
|
41
41
|
system_prompt=self._system_prompt,
|
|
42
42
|
messages=self._history,
|
|
43
43
|
tools=self.tools,
|
|
@@ -53,7 +53,7 @@ class _ProviderCallerMixin:
|
|
|
53
53
|
"on_llm_start",
|
|
54
54
|
run_id,
|
|
55
55
|
self._history,
|
|
56
|
-
self.
|
|
56
|
+
self._effective_model,
|
|
57
57
|
self._system_prompt,
|
|
58
58
|
)
|
|
59
59
|
self._notify_observers(
|
|
@@ -65,7 +65,7 @@ class _ProviderCallerMixin:
|
|
|
65
65
|
self._notify_observers(
|
|
66
66
|
"on_cache_hit",
|
|
67
67
|
run_id,
|
|
68
|
-
self.
|
|
68
|
+
self._effective_model,
|
|
69
69
|
cached_msg.content or "",
|
|
70
70
|
)
|
|
71
71
|
self._notify_observers("on_usage", run_id, cached_usage)
|
|
@@ -76,8 +76,8 @@ class _ProviderCallerMixin:
|
|
|
76
76
|
TraceStep(
|
|
77
77
|
type=StepType.CACHE_HIT,
|
|
78
78
|
duration_ms=(time.time() - call_start) * 1000,
|
|
79
|
-
model=self.
|
|
80
|
-
summary=f"Cache hit: {self.
|
|
79
|
+
model=self._effective_model,
|
|
80
|
+
summary=f"Cache hit: {self._effective_model}",
|
|
81
81
|
)
|
|
82
82
|
)
|
|
83
83
|
return cached_msg
|
|
@@ -93,7 +93,7 @@ class _ProviderCallerMixin:
|
|
|
93
93
|
"on_llm_start",
|
|
94
94
|
run_id,
|
|
95
95
|
self._history,
|
|
96
|
-
self.
|
|
96
|
+
self._effective_model,
|
|
97
97
|
self._system_prompt,
|
|
98
98
|
)
|
|
99
99
|
|
|
@@ -104,7 +104,7 @@ class _ProviderCallerMixin:
|
|
|
104
104
|
return Message(role=Role.ASSISTANT, content=response_text)
|
|
105
105
|
|
|
106
106
|
response_msg, usage_stats = self.provider.complete(
|
|
107
|
-
model=self.
|
|
107
|
+
model=self._effective_model,
|
|
108
108
|
system_prompt=self._system_prompt,
|
|
109
109
|
messages=self._history,
|
|
110
110
|
tools=self.tools,
|
|
@@ -144,11 +144,11 @@ class _ProviderCallerMixin:
|
|
|
144
144
|
TraceStep(
|
|
145
145
|
type=StepType.LLM_CALL,
|
|
146
146
|
duration_ms=(time.time() - call_start) * 1000,
|
|
147
|
-
model=self.
|
|
147
|
+
model=self._effective_model,
|
|
148
148
|
prompt_tokens=usage_stats.prompt_tokens,
|
|
149
149
|
completion_tokens=usage_stats.completion_tokens,
|
|
150
150
|
cost_usd=usage_stats.cost_usd,
|
|
151
|
-
summary=f"{self.
|
|
151
|
+
summary=f"{self._effective_model} → {len(response_text)} chars",
|
|
152
152
|
)
|
|
153
153
|
)
|
|
154
154
|
return response_msg
|
|
@@ -185,7 +185,7 @@ class _ProviderCallerMixin:
|
|
|
185
185
|
TraceStep(
|
|
186
186
|
type=StepType.LLM_CALL,
|
|
187
187
|
duration_ms=(time.time() - call_start) * 1000,
|
|
188
|
-
model=self.
|
|
188
|
+
model=self._effective_model,
|
|
189
189
|
error=last_error,
|
|
190
190
|
summary=f"Provider error: {last_error}",
|
|
191
191
|
)
|
|
@@ -200,7 +200,7 @@ class _ProviderCallerMixin:
|
|
|
200
200
|
|
|
201
201
|
aggregated: List[str] = []
|
|
202
202
|
for chunk in self.provider.stream(
|
|
203
|
-
model=self.
|
|
203
|
+
model=self._effective_model,
|
|
204
204
|
system_prompt=self._system_prompt,
|
|
205
205
|
messages=self._history,
|
|
206
206
|
tools=self.tools,
|
|
@@ -233,7 +233,7 @@ class _ProviderCallerMixin:
|
|
|
233
233
|
self.config.stream and getattr(self.provider, "supports_streaming", False)
|
|
234
234
|
):
|
|
235
235
|
cache_key = CacheKeyBuilder.build(
|
|
236
|
-
model=self.
|
|
236
|
+
model=self._effective_model,
|
|
237
237
|
system_prompt=self._system_prompt,
|
|
238
238
|
messages=self._history,
|
|
239
239
|
tools=self.tools,
|
|
@@ -249,7 +249,7 @@ class _ProviderCallerMixin:
|
|
|
249
249
|
"on_llm_start",
|
|
250
250
|
run_id,
|
|
251
251
|
self._history,
|
|
252
|
-
self.
|
|
252
|
+
self._effective_model,
|
|
253
253
|
self._system_prompt,
|
|
254
254
|
)
|
|
255
255
|
self._notify_observers(
|
|
@@ -261,7 +261,7 @@ class _ProviderCallerMixin:
|
|
|
261
261
|
self._notify_observers(
|
|
262
262
|
"on_cache_hit",
|
|
263
263
|
run_id,
|
|
264
|
-
self.
|
|
264
|
+
self._effective_model,
|
|
265
265
|
cached_msg.content or "",
|
|
266
266
|
)
|
|
267
267
|
self._notify_observers("on_usage", run_id, cached_usage)
|
|
@@ -272,8 +272,8 @@ class _ProviderCallerMixin:
|
|
|
272
272
|
TraceStep(
|
|
273
273
|
type=StepType.CACHE_HIT,
|
|
274
274
|
duration_ms=(time.time() - call_start) * 1000,
|
|
275
|
-
model=self.
|
|
276
|
-
summary=f"Cache hit: {self.
|
|
275
|
+
model=self._effective_model,
|
|
276
|
+
summary=f"Cache hit: {self._effective_model}",
|
|
277
277
|
)
|
|
278
278
|
)
|
|
279
279
|
return cached_msg
|
|
@@ -289,14 +289,14 @@ class _ProviderCallerMixin:
|
|
|
289
289
|
"on_llm_start",
|
|
290
290
|
run_id,
|
|
291
291
|
self._history,
|
|
292
|
-
self.
|
|
292
|
+
self._effective_model,
|
|
293
293
|
self._system_prompt,
|
|
294
294
|
)
|
|
295
295
|
await self._anotify_observers(
|
|
296
296
|
"on_llm_start",
|
|
297
297
|
run_id,
|
|
298
298
|
self._history,
|
|
299
|
-
self.
|
|
299
|
+
self._effective_model,
|
|
300
300
|
self._system_prompt,
|
|
301
301
|
)
|
|
302
302
|
|
|
@@ -312,7 +312,7 @@ class _ProviderCallerMixin:
|
|
|
312
312
|
self.provider, "supports_async", False
|
|
313
313
|
):
|
|
314
314
|
response_msg, usage_stats = await self.provider.acomplete(
|
|
315
|
-
model=self.
|
|
315
|
+
model=self._effective_model,
|
|
316
316
|
system_prompt=self._system_prompt,
|
|
317
317
|
messages=self._history,
|
|
318
318
|
tools=self.tools,
|
|
@@ -328,7 +328,7 @@ class _ProviderCallerMixin:
|
|
|
328
328
|
response_msg, usage_stats = await loop.run_in_executor(
|
|
329
329
|
executor,
|
|
330
330
|
lambda: self.provider.complete(
|
|
331
|
-
model=self.
|
|
331
|
+
model=self._effective_model,
|
|
332
332
|
system_prompt=self._system_prompt,
|
|
333
333
|
messages=self._history,
|
|
334
334
|
tools=self.tools,
|
|
@@ -371,11 +371,11 @@ class _ProviderCallerMixin:
|
|
|
371
371
|
TraceStep(
|
|
372
372
|
type=StepType.LLM_CALL,
|
|
373
373
|
duration_ms=(time.time() - call_start) * 1000,
|
|
374
|
-
model=self.
|
|
374
|
+
model=self._effective_model,
|
|
375
375
|
prompt_tokens=usage_stats.prompt_tokens,
|
|
376
376
|
completion_tokens=usage_stats.completion_tokens,
|
|
377
377
|
cost_usd=usage_stats.cost_usd,
|
|
378
|
-
summary=f"{self.
|
|
378
|
+
summary=f"{self._effective_model} → {len(response_text)} chars",
|
|
379
379
|
)
|
|
380
380
|
)
|
|
381
381
|
return response_msg
|
|
@@ -412,7 +412,7 @@ class _ProviderCallerMixin:
|
|
|
412
412
|
TraceStep(
|
|
413
413
|
type=StepType.LLM_CALL,
|
|
414
414
|
duration_ms=(time.time() - call_start) * 1000,
|
|
415
|
-
model=self.
|
|
415
|
+
model=self._effective_model,
|
|
416
416
|
error=last_error,
|
|
417
417
|
summary=f"Provider error: {last_error}",
|
|
418
418
|
)
|
|
@@ -430,7 +430,7 @@ class _ProviderCallerMixin:
|
|
|
430
430
|
|
|
431
431
|
if hasattr(self.provider, "astream") and getattr(self.provider, "supports_async", False):
|
|
432
432
|
stream = self.provider.astream( # type: ignore[attr-defined]
|
|
433
|
-
model=self.
|
|
433
|
+
model=self._effective_model,
|
|
434
434
|
system_prompt=self._system_prompt,
|
|
435
435
|
messages=self._history,
|
|
436
436
|
tools=self.tools,
|
|
@@ -445,7 +445,7 @@ class _ProviderCallerMixin:
|
|
|
445
445
|
stream_handler(chunk)
|
|
446
446
|
else:
|
|
447
447
|
for chunk in self.provider.stream(
|
|
448
|
-
model=self.
|
|
448
|
+
model=self._effective_model,
|
|
449
449
|
system_prompt=self._system_prompt,
|
|
450
450
|
messages=self._history,
|
|
451
451
|
tools=self.tools,
|
|
@@ -18,6 +18,7 @@ if TYPE_CHECKING:
|
|
|
18
18
|
from ..policy import ToolPolicy
|
|
19
19
|
from ..providers.base import Provider
|
|
20
20
|
from ..sessions import SessionStore
|
|
21
|
+
from ..usage import AgentUsage
|
|
21
22
|
|
|
22
23
|
# Hook type definitions
|
|
23
24
|
HookCallable = Callable[..., None]
|
|
@@ -157,6 +158,7 @@ class AgentConfig:
|
|
|
157
158
|
knowledge_graph: Optional[KnowledgeGraphMemory] = None
|
|
158
159
|
knowledge_memory: Optional[KnowledgeMemory] = None
|
|
159
160
|
stop_condition: Optional[Callable[[str, str], bool]] = None
|
|
161
|
+
model_selector: Optional[Callable[[int, List, "AgentUsage"], str]] = None
|
|
160
162
|
max_total_tokens: Optional[int] = None
|
|
161
163
|
max_cost_usd: Optional[float] = None
|
|
162
164
|
cancellation_token: Optional[CancellationToken] = None
|
|
@@ -189,6 +189,15 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
189
189
|
"""Return the agent's name from config."""
|
|
190
190
|
return self.config.name
|
|
191
191
|
|
|
192
|
+
@property
|
|
193
|
+
def _effective_model(self) -> str:
|
|
194
|
+
"""The model to use for the current iteration.
|
|
195
|
+
|
|
196
|
+
Returns the model set by ``model_selector`` if active, otherwise
|
|
197
|
+
falls back to ``self.config.model``.
|
|
198
|
+
"""
|
|
199
|
+
return getattr(self, "_current_model", None) or self.config.model
|
|
200
|
+
|
|
192
201
|
def __call__(
|
|
193
202
|
self,
|
|
194
203
|
messages: Union[str, List[Message]],
|
|
@@ -313,6 +322,7 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
313
322
|
wires observers, runs input guardrails, loads memory/session, injects
|
|
314
323
|
knowledge context, and returns a _RunContext carrying all state.
|
|
315
324
|
"""
|
|
325
|
+
self._current_model: Optional[str] = None
|
|
316
326
|
original_system_prompt = self._system_prompt
|
|
317
327
|
if response_format is not None:
|
|
318
328
|
schema = schema_from_response_format(response_format)
|
|
@@ -867,6 +877,18 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
867
877
|
if budget_msg:
|
|
868
878
|
return self._build_budget_exceeded_result(ctx, budget_msg)
|
|
869
879
|
|
|
880
|
+
# Model selection (R10)
|
|
881
|
+
if self.config.model_selector:
|
|
882
|
+
selected = self.config.model_selector(
|
|
883
|
+
ctx.iteration, ctx.all_tool_calls, self.usage
|
|
884
|
+
)
|
|
885
|
+
if selected != self._effective_model:
|
|
886
|
+
old_model = self._effective_model
|
|
887
|
+
self._current_model = selected
|
|
888
|
+
self._notify_observers(
|
|
889
|
+
"on_model_switch", ctx.run_id, ctx.iteration, old_model, selected
|
|
890
|
+
)
|
|
891
|
+
|
|
870
892
|
self._notify_observers(
|
|
871
893
|
"on_iteration_start", ctx.run_id, ctx.iteration, self._history
|
|
872
894
|
)
|
|
@@ -1053,6 +1075,21 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
1053
1075
|
yield StreamChunk(content=budget_msg)
|
|
1054
1076
|
return
|
|
1055
1077
|
|
|
1078
|
+
# Model selection (R10)
|
|
1079
|
+
if self.config.model_selector:
|
|
1080
|
+
selected = self.config.model_selector(
|
|
1081
|
+
ctx.iteration, ctx.all_tool_calls, self.usage
|
|
1082
|
+
)
|
|
1083
|
+
if selected != self._effective_model:
|
|
1084
|
+
old_model = self._effective_model
|
|
1085
|
+
self._current_model = selected
|
|
1086
|
+
self._notify_observers(
|
|
1087
|
+
"on_model_switch", ctx.run_id, ctx.iteration, old_model, selected
|
|
1088
|
+
)
|
|
1089
|
+
await self._anotify_observers(
|
|
1090
|
+
"on_model_switch", ctx.run_id, ctx.iteration, old_model, selected
|
|
1091
|
+
)
|
|
1092
|
+
|
|
1056
1093
|
self._notify_observers(
|
|
1057
1094
|
"on_iteration_start", ctx.run_id, ctx.iteration, self._history
|
|
1058
1095
|
)
|
|
@@ -1369,6 +1406,21 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
1369
1406
|
if budget_msg:
|
|
1370
1407
|
return self._build_budget_exceeded_result(ctx, budget_msg)
|
|
1371
1408
|
|
|
1409
|
+
# Model selection (R10)
|
|
1410
|
+
if self.config.model_selector:
|
|
1411
|
+
selected = self.config.model_selector(
|
|
1412
|
+
ctx.iteration, ctx.all_tool_calls, self.usage
|
|
1413
|
+
)
|
|
1414
|
+
if selected != self._effective_model:
|
|
1415
|
+
old_model = self._effective_model
|
|
1416
|
+
self._current_model = selected
|
|
1417
|
+
self._notify_observers(
|
|
1418
|
+
"on_model_switch", ctx.run_id, ctx.iteration, old_model, selected
|
|
1419
|
+
)
|
|
1420
|
+
await self._anotify_observers(
|
|
1421
|
+
"on_model_switch", ctx.run_id, ctx.iteration, old_model, selected
|
|
1422
|
+
)
|
|
1423
|
+
|
|
1372
1424
|
self._notify_observers(
|
|
1373
1425
|
"on_iteration_start", ctx.run_id, ctx.iteration, self._history
|
|
1374
1426
|
)
|