selectools 0.27.2__tar.gz → 0.29.0__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.27.2/src/selectools.egg-info → selectools-0.29.0}/PKG-INFO +39 -7
- {selectools-0.27.2 → selectools-0.29.0}/README.md +33 -5
- {selectools-0.27.2 → selectools-0.29.0}/pyproject.toml +7 -2
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/__init__.py +1 -1
- selectools-0.29.0/src/selectools/_ssrf.py +88 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/agent/_provider_caller.py +43 -21
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/agent/_tool_executor.py +3 -2
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/agent/config.py +24 -11
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/agent/core.py +18 -8
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/embeddings/anthropic.py +12 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/embeddings/cohere.py +12 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/embeddings/openai.py +8 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/evaluators.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/mcp/multi.py +7 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/orchestration/__init__.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/orchestration/checkpoint.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/orchestration/graph.py +3 -5
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/orchestration/node.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/orchestration/state.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/orchestration/supervisor.py +4 -2
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/patterns/__init__.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/patterns/debate.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/patterns/plan_and_execute.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/patterns/reflective.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/patterns/team_lead.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/policy.py +1 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/anthropic_provider.py +4 -4
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/loaders.py +1 -54
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/stores/chroma.py +2 -2
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/stores/memory.py +10 -11
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/vector_store.py +7 -5
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/api.py +7 -5
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/app.py +30 -10
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/sessions.py +61 -35
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/templates/__init__.py +2 -9
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/browser_tools.py +7 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/calculator_tools.py +3 -3
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/code_tools.py +56 -19
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/db_tools.py +3 -3
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/email_tools.py +3 -3
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/github_tools.py +4 -4
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/image_tools.py +3 -1
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/linear_tools.py +4 -4
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/notion_tools.py +4 -4
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/pdf_tools.py +3 -3
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/search_tools.py +4 -54
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/slack_tools.py +4 -4
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/types.py +8 -4
- {selectools-0.27.2 → selectools-0.29.0/src/selectools.egg-info}/PKG-INFO +39 -7
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools.egg-info/SOURCES.txt +2 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools.egg-info/requires.txt +6 -1
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_architecture.py +12 -12
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_conversation_branching.py +40 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_guardrails_injection.py +18 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_scheduler.py +31 -6
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_sessions_dynamodb.py +32 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_sessions_supabase.py +31 -0
- selectools-0.29.0/tests/test_ssrf.py +63 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_yaml_config.py +4 -2
- {selectools-0.27.2 → selectools-0.29.0}/LICENSE +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/setup.cfg +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/_async_utils.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/_json_utils.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/a2a/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/a2a/client.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/a2a/server.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/a2a/types.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/agent/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/agent/_lifecycle.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/agent/_memory_manager.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/agent/_planning.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/agent/config_groups.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/analytics.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/audit.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/cache.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/cache_redis.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/cache_semantic.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/cancellation.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/checkpoint_postgres.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/coherence.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/compose.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/embeddings/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/embeddings/gemini.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/embeddings/provider.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/entity_memory.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/env.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/__main__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/badge.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/dataset.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/generator.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/history.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/html.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/junit.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/llm_evaluators.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/pairwise.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/regression.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/report.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/serve.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/snapshot.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/suite.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/templates.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/evals/types.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/exceptions.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/guardrails/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/guardrails/base.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/guardrails/format.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/guardrails/injection.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/guardrails/length.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/guardrails/pii.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/guardrails/pipeline.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/guardrails/topic.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/guardrails/toxicity.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/knowledge.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/knowledge_backends.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/knowledge_graph.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/knowledge_sanitizers.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/knowledge_store_redis.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/knowledge_store_supabase.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/loop_detection.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/mcp/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/mcp/_loop.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/mcp/bridge.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/mcp/client.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/mcp/config.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/mcp/server.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/memory.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/models.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/observe/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/observe/langfuse.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/observe/otel.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/observe/trace_store.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/observer.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/parser.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/pending.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/pipeline.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/pricing.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/prompt.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/_openai_compat.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/azure_openai_provider.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/base.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/fallback.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/gemini_provider.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/litellm_provider.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/ollama_provider.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/openai_provider.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/router.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/providers/stubs.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/bm25.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/chunking.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/hybrid.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/reranker.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/stores/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/stores/faiss.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/stores/pgvector.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/stores/pinecone.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/stores/qdrant.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/stores/sqlite.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/rag/tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/results.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/scheduler.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/security.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/_starlette_app.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/_static/builder.css +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/_static/builder.html +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/_static/builder.js +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/builder.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/cli.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/models.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/serve/playground.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/stability.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/structured.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/templates/code_reviewer.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/templates/customer_support.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/templates/data_analyst.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/templates/rag_chatbot.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/templates/research_assistant.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/token_estimation.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/data_tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/datetime_tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/discord_tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/file_tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/memory_tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/reasoning_tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/s3_tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/text_tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/toolbox/web_tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/tools/__init__.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/tools/base.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/tools/decorators.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/tools/loader.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/tools/registry.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/trace.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/unified_memory.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools/usage.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools.egg-info/dependency_links.txt +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools.egg-info/entry_points.txt +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/src/selectools.egg-info/top_level.txt +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_a2a.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_agent_api.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_agent_hitl.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_approval_gate.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_async_observers.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_audit.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_bake_window_hunt.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_budget.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_bug_hunt_batch1_core.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_bug_hunt_batch1_security.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_bug_hunt_batch1_tools.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_bug_hunt_regression.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_cache.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_cache_redis.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_cancellation.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_checkpoint_postgres.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_coherence.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_concurrency_smoke.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_consolidation_regression.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_coverage_orchestration.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_document_loaders_extended.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_e2e_langfuse_observer.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_e2e_multimodal.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_e2e_otel_observer.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_e2e_v0_21_0_apps.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_e2e_v0_21_0_simulations.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_entity_memory.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_env.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_advanced.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_cli.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_e2e.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_final.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_hardening.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_html_report.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_new_evaluators.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_ralph_bugs.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_release.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_serve.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_v017_features.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_evals_v0191.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_features_in_graph.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_final_coverage_a.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_final_coverage_b.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_guardrails.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_hardening.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_knowledge.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_knowledge_backend.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_knowledge_backend_redis.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_knowledge_backend_supabase.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_knowledge_graph.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_knowledge_sanitizers.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_knowledge_store_redis.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_knowledge_store_supabase.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_knowledge_stores.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_knowledge_stores_error_handling.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_langfuse_observer.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_loop_detection.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_mcp.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_mcp_coverage.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_memory.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_memory_async.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_memory_boundary.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_memory_integration.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_model_switching.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_multi_agent_edge_cases.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_multimodal.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_orchestration_checkpoint.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_orchestration_e2e.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_orchestration_evals.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_orchestration_graph.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_orchestration_integration.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_orchestration_primitives.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_orchestration_supervisor.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_otel_observer.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_parser.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_patterns.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_pending.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_pending_redis_smoke.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_phase1_design_patterns.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_pipeline.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_pipeline_coverage.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_policy.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_prompt.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_prompt_compression.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_property_based.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_reasoning_strategy.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_remaining_coverage.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_results.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_routing_mode.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_security.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_semantic_cache.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_serve_app_coverage.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_serve_cli.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_sessions.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_sessions_edge_cases.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_sessions_mongo.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_sessions_redis.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_simple_observer.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_simulation_evals.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_stability.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_starlette_app.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_structured.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_structured_config.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_structured_tool_results.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_summarize_on_trim.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_templates_coverage.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_terminal_actions.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_token_estimation.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_tool_caching.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_tool_result_compression.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_trace.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_trace_html.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_trace_store.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_unified_memory.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_unified_memory_config.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_v016_regression.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_v019_features.py +0 -0
- {selectools-0.27.2 → selectools-0.29.0}/tests/test_visual_builder.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: selectools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.29.0
|
|
4
4
|
Summary: Production-ready Python framework for AI agents with multi-agent graphs, hybrid RAG, guardrails, audit logging, 50 evaluators, and a visual builder. Supports OpenAI, Anthropic, Gemini, Ollama. By NichevLabs.
|
|
5
5
|
Author-email: John Nichev <johnnichev@gmail.com>
|
|
6
6
|
Maintainer-email: NichevLabs <support@nichevlabs.com>
|
|
@@ -59,14 +59,18 @@ Provides-Extra: toolbox
|
|
|
59
59
|
Requires-Dist: requests>=2.28.0; extra == "toolbox"
|
|
60
60
|
Requires-Dist: slack-sdk>=3.27.0; extra == "toolbox"
|
|
61
61
|
Requires-Dist: pdfplumber>=0.11.0; extra == "toolbox"
|
|
62
|
+
Requires-Dist: pytz>=2023.3; extra == "toolbox"
|
|
62
63
|
Provides-Extra: aws
|
|
63
64
|
Requires-Dist: boto3>=1.34.0; extra == "aws"
|
|
64
65
|
Provides-Extra: browser
|
|
65
66
|
Requires-Dist: playwright>=1.40.0; extra == "browser"
|
|
66
67
|
Provides-Extra: mongo
|
|
67
68
|
Requires-Dist: pymongo>=4.0.0; extra == "mongo"
|
|
69
|
+
Provides-Extra: cache
|
|
70
|
+
Requires-Dist: redis>=4.0.0; extra == "cache"
|
|
68
71
|
Provides-Extra: evals
|
|
69
72
|
Requires-Dist: pyyaml>=6.0.0; extra == "evals"
|
|
73
|
+
Requires-Dist: jsonschema>=4.0.0; extra == "evals"
|
|
70
74
|
Provides-Extra: mcp
|
|
71
75
|
Requires-Dist: mcp<2.0.0,>=1.0.0; extra == "mcp"
|
|
72
76
|
Provides-Extra: litellm
|
|
@@ -77,7 +81,7 @@ Provides-Extra: supabase
|
|
|
77
81
|
Requires-Dist: supabase>=2.0.0; extra == "supabase"
|
|
78
82
|
Provides-Extra: serve
|
|
79
83
|
Requires-Dist: pyyaml>=6.0.0; extra == "serve"
|
|
80
|
-
Requires-Dist: starlette
|
|
84
|
+
Requires-Dist: starlette<1.0,>=0.27.0; extra == "serve"
|
|
81
85
|
Requires-Dist: uvicorn[standard]>=0.24.0; extra == "serve"
|
|
82
86
|
Requires-Dist: httpx>=0.24.0; extra == "serve"
|
|
83
87
|
Dynamic: license-file
|
|
@@ -114,6 +118,34 @@ result = AgentGraph.chain(planner, writer, reviewer).run("Write a blog post")
|
|
|
114
118
|
# selectools serve agent.yaml
|
|
115
119
|
```
|
|
116
120
|
|
|
121
|
+
## What's New in v0.29
|
|
122
|
+
|
|
123
|
+
### v0.29.0 — Stability Promotions (v1.0 Freeze Prep)
|
|
124
|
+
|
|
125
|
+
The first beta → stable promotion pass toward the 1.0 API freeze. Additive only — promotions strengthen guarantees, nothing is removed or renamed.
|
|
126
|
+
|
|
127
|
+
- **24 mature toolbox tools promoted to `@stable`** — calculator, code (incl. `execute_shell`), db, email, GitHub, Linear, Notion, PDF, web search, and Slack tools. They're 4–15 releases old with frozen signatures.
|
|
128
|
+
- **`orchestration`, `patterns`, and `policy` are now stable modules** — their public surfaces (AgentGraph, the checkpoint stores, all five pattern agents, ToolPolicy) were already frozen in v0.25; this aligns the module-level promise. Also promoted `SessionSearchResult` and `PlanningConfig`.
|
|
129
|
+
- **Held `@beta` on purpose** — the v0.27.0 additions (recall tool, injection guardrail, cache-rate cost, Mongo/Dynamo session stores) get one more cycle, and `evals` stays a beta module while its 50 evaluator classes keep growing (its 7 core types are `@stable`).
|
|
130
|
+
- **New `scripts/stability_audit.py`** reports any symbol whose marker disagrees with its module promise, so promotion passes are reproducible.
|
|
131
|
+
|
|
132
|
+
See `CHANGELOG.md` for the full entry.
|
|
133
|
+
|
|
134
|
+
## What's New in v0.28
|
|
135
|
+
|
|
136
|
+
### v0.28.0 — Hardening Sweep: Shell Safety, Session Namespaces & Typed Public API
|
|
137
|
+
|
|
138
|
+
A full audit-driven tech-debt sweep — mostly additive and bugfix, with one called-out behavior change.
|
|
139
|
+
|
|
140
|
+
- **`execute_shell` is now a real boundary, not a best-effort filter** — it parses with `shlex` and runs with `shell=False`, so pipes, chaining (`;`, `&&`), redirection, subshells, globbing, and backgrounding can never be interpreted (closes the previously-bypassable `\n` and bare-`&` holes). **Behavior change:** commands relying on shell features now fail fast instead of running through `/bin/sh`.
|
|
141
|
+
- **SSRF guard extended to the headless-browser tools and the serve webhook** — `browser_scrape_page`/`browser_screenshot` and the eval-alert webhook now reject loopback/private/link-local targets; SSRF logic is consolidated into one shared validator.
|
|
142
|
+
- **Session namespaces fixed across all backends** — `SessionStore.branch()` gained an optional `namespace` parameter, and `list()` now returns a round-trippable storage key consistently (JSON/Redis/Mongo/DynamoDB previously returned a bare id that couldn't be reloaded for namespaced sessions).
|
|
143
|
+
- **Embedding providers accept `timeout` + `max_retries`** (OpenAI/Voyage/Cohere, default 60s/2) so a hung or rate-limited call can't block ingestion.
|
|
144
|
+
- **Typed public API** — `AgentResult.trace`/`.usage` and the ten `AgentConfig` nested-group fields are now their concrete types instead of `Any` (full autocomplete + type-checking); tightening surfaced and fixed two real bugs the `Any` had masked.
|
|
145
|
+
- **Packaging** — added the missing `[cache]` extra (`redis`), `pytz` to `[toolbox]`, and `jsonschema` to `[evals]`.
|
|
146
|
+
|
|
147
|
+
See `CHANGELOG.md` for the full entry (7,796 tests, 115 examples, 115 models).
|
|
148
|
+
|
|
117
149
|
## What's New in v0.27
|
|
118
150
|
|
|
119
151
|
### v0.27.2 — Provider Model Fix & Tool-less Agents
|
|
@@ -298,7 +330,7 @@ The first AI agent framework to ship a visual graph builder in a single `pip ins
|
|
|
298
330
|
|
|
299
331
|
**[Try the builder in your browser →](https://selectools.dev/builder/)** — no install required.
|
|
300
332
|
|
|
301
|
-
[](https://colab.research.google.com/github/johnnichev/selectools/blob/main/notebooks/getting_started.ipynb) [](https://colab.research.google.com/github/johnnichev/selectools/blob/main/notebooks/getting_started.ipynb) [](https://selectools.dev/examples/)
|
|
302
334
|
|
|
303
335
|
```bash
|
|
304
336
|
pip install selectools
|
|
@@ -600,7 +632,7 @@ report = suite.run()
|
|
|
600
632
|
report.to_html("report.html")
|
|
601
633
|
```
|
|
602
634
|
|
|
603
|
-
- **50 Evaluators** —
|
|
635
|
+
- **50 Evaluators** — 29 deterministic + 21 LLM-as-judge
|
|
604
636
|
- **A/B Testing**, regression detection, snapshot testing
|
|
605
637
|
- **HTML reports**, JUnit XML, CLI, GitHub Action integration
|
|
606
638
|
|
|
@@ -682,7 +714,7 @@ report.to_html("report.html")
|
|
|
682
714
|
| **Knowledge Graph** | Relationship triple extraction with in-memory and SQLite storage and keyword-based querying. |
|
|
683
715
|
| **Cross-Session Knowledge** | Daily logs + persistent facts with auto-registered `remember` + `recall` tools. |
|
|
684
716
|
| **MCP Integration** | Connect to any MCP tool server (stdio + HTTP). MCPClient, MultiMCPClient, MCPServer. Circuit breaker, retry, graceful degradation. |
|
|
685
|
-
| **Eval Framework** | 50 built-in evaluators (
|
|
717
|
+
| **Eval Framework** | 50 built-in evaluators (29 deterministic + 21 LLM-as-judge). A/B testing, regression detection, snapshot testing, HTML reports, JUnit XML, CI integration. |
|
|
686
718
|
| **Multi-Agent Orchestration** | `AgentGraph` for directed agent graphs, `SupervisorAgent` with 4 strategies, HITL via generator nodes, parallel execution, checkpointing, subgraph composition. |
|
|
687
719
|
| **Composable Pipelines** | `Pipeline` + `@step` + `|` operator + `parallel()` + `branch()` — chain agents, tools, and transforms with plain Python. |
|
|
688
720
|
| **AgentObserver Protocol** | 46-event lifecycle observer with `run_id`/`call_id` correlation. Built-in `LoggingObserver` + `SimpleStepObserver`. |
|
|
@@ -729,7 +761,7 @@ report.to_html("report.html")
|
|
|
729
761
|
- **Multi-Agent Orchestration**: `AgentGraph` with routing, parallel execution, HITL, checkpointing; `SupervisorAgent` with 4 strategies (plan_and_execute, round_robin, dynamic, magentic)
|
|
730
762
|
- **Composable Pipelines**: `Pipeline` + `@step` + `|` operator + `parallel()` + `branch()` — chain agents, tools, and transforms
|
|
731
763
|
- **115 Examples**: Multi-agent graphs, RAG, hybrid search, streaming, structured output, traces, batch, policy, observer, guardrails, audit, sessions (incl. Supabase), entity memory, knowledge graph, eval framework, advanced agent patterns, stability markers, HTML trace viewer, agent-as-API, A2A, routing, unified memory, scheduled agents, reasoning tools, and more
|
|
732
|
-
- **Built-in Eval Framework**: 50 evaluators (
|
|
764
|
+
- **Built-in Eval Framework**: 50 evaluators (29 deterministic + 21 LLM-as-judge), A/B testing, regression detection, HTML reports, JUnit XML, snapshot testing
|
|
733
765
|
- **AgentObserver Protocol**: 46 lifecycle events with `run_id` correlation, `LoggingObserver`, `SimpleStepObserver`, OTel export
|
|
734
766
|
- **7700+ Tests**: Unit, integration, regression, and E2E with real API calls
|
|
735
767
|
|
|
@@ -1357,7 +1389,7 @@ pytest tests/ -x -q # All tests
|
|
|
1357
1389
|
pytest tests/ -k "not e2e" # Skip E2E (no API keys needed)
|
|
1358
1390
|
```
|
|
1359
1391
|
|
|
1360
|
-
|
|
1392
|
+
7,796 tests covering parsing, agent loop, providers, RAG pipeline, hybrid search, advanced chunking, dynamic tools, caching, streaming, guardrails, sessions, memory, eval framework, budget/cancellation, knowledge stores, orchestration, pipelines, agent patterns, stability markers, trace viewer, serve API, A2A, routing, and E2E integration with real API calls.
|
|
1361
1393
|
|
|
1362
1394
|
## License
|
|
1363
1395
|
|
|
@@ -30,6 +30,34 @@ result = AgentGraph.chain(planner, writer, reviewer).run("Write a blog post")
|
|
|
30
30
|
# selectools serve agent.yaml
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
## What's New in v0.29
|
|
34
|
+
|
|
35
|
+
### v0.29.0 — Stability Promotions (v1.0 Freeze Prep)
|
|
36
|
+
|
|
37
|
+
The first beta → stable promotion pass toward the 1.0 API freeze. Additive only — promotions strengthen guarantees, nothing is removed or renamed.
|
|
38
|
+
|
|
39
|
+
- **24 mature toolbox tools promoted to `@stable`** — calculator, code (incl. `execute_shell`), db, email, GitHub, Linear, Notion, PDF, web search, and Slack tools. They're 4–15 releases old with frozen signatures.
|
|
40
|
+
- **`orchestration`, `patterns`, and `policy` are now stable modules** — their public surfaces (AgentGraph, the checkpoint stores, all five pattern agents, ToolPolicy) were already frozen in v0.25; this aligns the module-level promise. Also promoted `SessionSearchResult` and `PlanningConfig`.
|
|
41
|
+
- **Held `@beta` on purpose** — the v0.27.0 additions (recall tool, injection guardrail, cache-rate cost, Mongo/Dynamo session stores) get one more cycle, and `evals` stays a beta module while its 50 evaluator classes keep growing (its 7 core types are `@stable`).
|
|
42
|
+
- **New `scripts/stability_audit.py`** reports any symbol whose marker disagrees with its module promise, so promotion passes are reproducible.
|
|
43
|
+
|
|
44
|
+
See `CHANGELOG.md` for the full entry.
|
|
45
|
+
|
|
46
|
+
## What's New in v0.28
|
|
47
|
+
|
|
48
|
+
### v0.28.0 — Hardening Sweep: Shell Safety, Session Namespaces & Typed Public API
|
|
49
|
+
|
|
50
|
+
A full audit-driven tech-debt sweep — mostly additive and bugfix, with one called-out behavior change.
|
|
51
|
+
|
|
52
|
+
- **`execute_shell` is now a real boundary, not a best-effort filter** — it parses with `shlex` and runs with `shell=False`, so pipes, chaining (`;`, `&&`), redirection, subshells, globbing, and backgrounding can never be interpreted (closes the previously-bypassable `\n` and bare-`&` holes). **Behavior change:** commands relying on shell features now fail fast instead of running through `/bin/sh`.
|
|
53
|
+
- **SSRF guard extended to the headless-browser tools and the serve webhook** — `browser_scrape_page`/`browser_screenshot` and the eval-alert webhook now reject loopback/private/link-local targets; SSRF logic is consolidated into one shared validator.
|
|
54
|
+
- **Session namespaces fixed across all backends** — `SessionStore.branch()` gained an optional `namespace` parameter, and `list()` now returns a round-trippable storage key consistently (JSON/Redis/Mongo/DynamoDB previously returned a bare id that couldn't be reloaded for namespaced sessions).
|
|
55
|
+
- **Embedding providers accept `timeout` + `max_retries`** (OpenAI/Voyage/Cohere, default 60s/2) so a hung or rate-limited call can't block ingestion.
|
|
56
|
+
- **Typed public API** — `AgentResult.trace`/`.usage` and the ten `AgentConfig` nested-group fields are now their concrete types instead of `Any` (full autocomplete + type-checking); tightening surfaced and fixed two real bugs the `Any` had masked.
|
|
57
|
+
- **Packaging** — added the missing `[cache]` extra (`redis`), `pytz` to `[toolbox]`, and `jsonschema` to `[evals]`.
|
|
58
|
+
|
|
59
|
+
See `CHANGELOG.md` for the full entry (7,796 tests, 115 examples, 115 models).
|
|
60
|
+
|
|
33
61
|
## What's New in v0.27
|
|
34
62
|
|
|
35
63
|
### v0.27.2 — Provider Model Fix & Tool-less Agents
|
|
@@ -214,7 +242,7 @@ The first AI agent framework to ship a visual graph builder in a single `pip ins
|
|
|
214
242
|
|
|
215
243
|
**[Try the builder in your browser →](https://selectools.dev/builder/)** — no install required.
|
|
216
244
|
|
|
217
|
-
[](https://colab.research.google.com/github/johnnichev/selectools/blob/main/notebooks/getting_started.ipynb) [](https://colab.research.google.com/github/johnnichev/selectools/blob/main/notebooks/getting_started.ipynb) [](https://selectools.dev/examples/)
|
|
218
246
|
|
|
219
247
|
```bash
|
|
220
248
|
pip install selectools
|
|
@@ -516,7 +544,7 @@ report = suite.run()
|
|
|
516
544
|
report.to_html("report.html")
|
|
517
545
|
```
|
|
518
546
|
|
|
519
|
-
- **50 Evaluators** —
|
|
547
|
+
- **50 Evaluators** — 29 deterministic + 21 LLM-as-judge
|
|
520
548
|
- **A/B Testing**, regression detection, snapshot testing
|
|
521
549
|
- **HTML reports**, JUnit XML, CLI, GitHub Action integration
|
|
522
550
|
|
|
@@ -598,7 +626,7 @@ report.to_html("report.html")
|
|
|
598
626
|
| **Knowledge Graph** | Relationship triple extraction with in-memory and SQLite storage and keyword-based querying. |
|
|
599
627
|
| **Cross-Session Knowledge** | Daily logs + persistent facts with auto-registered `remember` + `recall` tools. |
|
|
600
628
|
| **MCP Integration** | Connect to any MCP tool server (stdio + HTTP). MCPClient, MultiMCPClient, MCPServer. Circuit breaker, retry, graceful degradation. |
|
|
601
|
-
| **Eval Framework** | 50 built-in evaluators (
|
|
629
|
+
| **Eval Framework** | 50 built-in evaluators (29 deterministic + 21 LLM-as-judge). A/B testing, regression detection, snapshot testing, HTML reports, JUnit XML, CI integration. |
|
|
602
630
|
| **Multi-Agent Orchestration** | `AgentGraph` for directed agent graphs, `SupervisorAgent` with 4 strategies, HITL via generator nodes, parallel execution, checkpointing, subgraph composition. |
|
|
603
631
|
| **Composable Pipelines** | `Pipeline` + `@step` + `|` operator + `parallel()` + `branch()` — chain agents, tools, and transforms with plain Python. |
|
|
604
632
|
| **AgentObserver Protocol** | 46-event lifecycle observer with `run_id`/`call_id` correlation. Built-in `LoggingObserver` + `SimpleStepObserver`. |
|
|
@@ -645,7 +673,7 @@ report.to_html("report.html")
|
|
|
645
673
|
- **Multi-Agent Orchestration**: `AgentGraph` with routing, parallel execution, HITL, checkpointing; `SupervisorAgent` with 4 strategies (plan_and_execute, round_robin, dynamic, magentic)
|
|
646
674
|
- **Composable Pipelines**: `Pipeline` + `@step` + `|` operator + `parallel()` + `branch()` — chain agents, tools, and transforms
|
|
647
675
|
- **115 Examples**: Multi-agent graphs, RAG, hybrid search, streaming, structured output, traces, batch, policy, observer, guardrails, audit, sessions (incl. Supabase), entity memory, knowledge graph, eval framework, advanced agent patterns, stability markers, HTML trace viewer, agent-as-API, A2A, routing, unified memory, scheduled agents, reasoning tools, and more
|
|
648
|
-
- **Built-in Eval Framework**: 50 evaluators (
|
|
676
|
+
- **Built-in Eval Framework**: 50 evaluators (29 deterministic + 21 LLM-as-judge), A/B testing, regression detection, HTML reports, JUnit XML, snapshot testing
|
|
649
677
|
- **AgentObserver Protocol**: 46 lifecycle events with `run_id` correlation, `LoggingObserver`, `SimpleStepObserver`, OTel export
|
|
650
678
|
- **7700+ Tests**: Unit, integration, regression, and E2E with real API calls
|
|
651
679
|
|
|
@@ -1273,7 +1301,7 @@ pytest tests/ -x -q # All tests
|
|
|
1273
1301
|
pytest tests/ -k "not e2e" # Skip E2E (no API keys needed)
|
|
1274
1302
|
```
|
|
1275
1303
|
|
|
1276
|
-
|
|
1304
|
+
7,796 tests covering parsing, agent loop, providers, RAG pipeline, hybrid search, advanced chunking, dynamic tools, caching, streaming, guardrails, sessions, memory, eval framework, budget/cancellation, knowledge stores, orchestration, pipelines, agent patterns, stability markers, trace viewer, serve API, A2A, routing, and E2E integration with real API calls.
|
|
1277
1305
|
|
|
1278
1306
|
## License
|
|
1279
1307
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "selectools"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.29.0"
|
|
8
8
|
description = "Production-ready Python framework for AI agents with multi-agent graphs, hybrid RAG, guardrails, audit logging, 50 evaluators, and a visual builder. Supports OpenAI, Anthropic, Gemini, Ollama. By NichevLabs."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -79,6 +79,7 @@ toolbox = [
|
|
|
79
79
|
"requests>=2.28.0",
|
|
80
80
|
"slack-sdk>=3.27.0",
|
|
81
81
|
"pdfplumber>=0.11.0",
|
|
82
|
+
"pytz>=2023.3",
|
|
82
83
|
]
|
|
83
84
|
aws = [
|
|
84
85
|
"boto3>=1.34.0",
|
|
@@ -89,8 +90,12 @@ browser = [
|
|
|
89
90
|
mongo = [
|
|
90
91
|
"pymongo>=4.0.0",
|
|
91
92
|
]
|
|
93
|
+
cache = [
|
|
94
|
+
"redis>=4.0.0",
|
|
95
|
+
]
|
|
92
96
|
evals = [
|
|
93
97
|
"pyyaml>=6.0.0",
|
|
98
|
+
"jsonschema>=4.0.0",
|
|
94
99
|
]
|
|
95
100
|
mcp = [
|
|
96
101
|
"mcp>=1.0.0,<2.0.0",
|
|
@@ -106,7 +111,7 @@ supabase = [
|
|
|
106
111
|
]
|
|
107
112
|
serve = [
|
|
108
113
|
"pyyaml>=6.0.0",
|
|
109
|
-
"starlette>=0.27.0",
|
|
114
|
+
"starlette>=0.27.0,<1.0",
|
|
110
115
|
"uvicorn[standard]>=0.24.0",
|
|
111
116
|
"httpx>=0.24.0",
|
|
112
117
|
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared SSRF (server-side request forgery) URL validation.
|
|
3
|
+
|
|
4
|
+
A single source of truth for the blocked-network list and the
|
|
5
|
+
hostname-resolution check used by every outbound-request surface in the
|
|
6
|
+
library (web search/scrape tools, headless-browser tools, RAG document
|
|
7
|
+
loaders, and the serve webhook dispatcher).
|
|
8
|
+
|
|
9
|
+
Two entry points expose the same logic under the two contracts callers
|
|
10
|
+
need:
|
|
11
|
+
|
|
12
|
+
- :func:`validate_url` returns an error string (or ``None`` when safe), for
|
|
13
|
+
tool functions that report errors as their return value.
|
|
14
|
+
- :func:`check_url` raises :class:`ValueError`, for internal callers that
|
|
15
|
+
prefer exceptions.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import ipaddress
|
|
21
|
+
import socket
|
|
22
|
+
from typing import List, Optional, Union
|
|
23
|
+
from urllib.parse import urlparse
|
|
24
|
+
|
|
25
|
+
# Private / loopback / link-local networks that must never be reached from a
|
|
26
|
+
# user- or model-supplied URL.
|
|
27
|
+
_BLOCKED_NETWORKS: List[Union[ipaddress.IPv4Network, ipaddress.IPv6Network]] = [
|
|
28
|
+
ipaddress.ip_network("127.0.0.0/8"),
|
|
29
|
+
ipaddress.ip_network("10.0.0.0/8"),
|
|
30
|
+
ipaddress.ip_network("172.16.0.0/12"),
|
|
31
|
+
ipaddress.ip_network("192.168.0.0/16"),
|
|
32
|
+
ipaddress.ip_network("169.254.0.0/16"),
|
|
33
|
+
ipaddress.ip_network("::1/128"),
|
|
34
|
+
ipaddress.ip_network("fc00::/7"),
|
|
35
|
+
ipaddress.ip_network("fe80::/10"),
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def validate_url(url: str) -> Optional[str]:
|
|
40
|
+
"""Validate *url* against SSRF rules.
|
|
41
|
+
|
|
42
|
+
Returns a human-readable error message if the URL uses a non-HTTP
|
|
43
|
+
scheme, has no hostname, targets a loopback/internal name, or resolves
|
|
44
|
+
to a private/reserved IP range. Returns ``None`` when the URL is safe to
|
|
45
|
+
request.
|
|
46
|
+
"""
|
|
47
|
+
parsed = urlparse(url)
|
|
48
|
+
|
|
49
|
+
if parsed.scheme not in ("http", "https"):
|
|
50
|
+
return f"Error: URL scheme {parsed.scheme!r} is not allowed."
|
|
51
|
+
|
|
52
|
+
hostname = parsed.hostname
|
|
53
|
+
if not hostname:
|
|
54
|
+
return "Error: URL has no hostname."
|
|
55
|
+
|
|
56
|
+
lower_host = hostname.lower()
|
|
57
|
+
if lower_host in ("localhost", "0.0.0.0"): # nosec B104 — comparison, not a bind
|
|
58
|
+
return f"Error: Requests to {hostname!r} are blocked (loopback/internal address)."
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
addr_infos = socket.getaddrinfo(hostname, None)
|
|
62
|
+
except socket.gaierror as e:
|
|
63
|
+
return f"Error: Could not resolve hostname {hostname!r}: {e}"
|
|
64
|
+
|
|
65
|
+
for _family, _type, _proto, _canonname, sockaddr in addr_infos:
|
|
66
|
+
ip = ipaddress.ip_address(sockaddr[0])
|
|
67
|
+
for network in _BLOCKED_NETWORKS:
|
|
68
|
+
if ip in network:
|
|
69
|
+
return (
|
|
70
|
+
f"Error: URL resolves to private/reserved address {ip} "
|
|
71
|
+
f"(network {network}). Requests to internal networks are blocked."
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def check_url(url: str) -> None:
|
|
78
|
+
"""Raise :class:`ValueError` if *url* fails SSRF validation.
|
|
79
|
+
|
|
80
|
+
Equivalent to :func:`validate_url` but raises instead of returning the
|
|
81
|
+
message (with the ``"Error: "`` prefix stripped).
|
|
82
|
+
"""
|
|
83
|
+
message = validate_url(url)
|
|
84
|
+
if message is not None:
|
|
85
|
+
raise ValueError(message.removeprefix("Error: "))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
__all__ = ["validate_url", "check_url"]
|
|
@@ -6,7 +6,7 @@ import asyncio
|
|
|
6
6
|
import threading
|
|
7
7
|
import time
|
|
8
8
|
from concurrent.futures import ThreadPoolExecutor
|
|
9
|
-
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple
|
|
10
10
|
|
|
11
11
|
# Module-level singleton for running sync provider calls in an async context.
|
|
12
12
|
# Creating a new ThreadPoolExecutor per call (inside a retry loop) wastes
|
|
@@ -65,8 +65,15 @@ class _ProviderCallerMixin:
|
|
|
65
65
|
temperature=self.config.temperature,
|
|
66
66
|
)
|
|
67
67
|
cached = self.config.cache.get(cache_key)
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
# Only treat a well-formed (Message, usage) pair as a cache hit;
|
|
69
|
+
# anything else (key collision, schema drift) falls through to a
|
|
70
|
+
# real provider call instead of crashing on a bad cast.
|
|
71
|
+
if (
|
|
72
|
+
isinstance(cached, (tuple, list))
|
|
73
|
+
and len(cached) >= 2
|
|
74
|
+
and isinstance(cached[0], Message)
|
|
75
|
+
):
|
|
76
|
+
cached_msg = cached[0]
|
|
70
77
|
cached_usage = cached[1]
|
|
71
78
|
self.usage.add_usage(cached_usage, tool_name=None)
|
|
72
79
|
if run_id:
|
|
@@ -273,8 +280,15 @@ class _ProviderCallerMixin:
|
|
|
273
280
|
temperature=self.config.temperature,
|
|
274
281
|
)
|
|
275
282
|
cached = self.config.cache.get(cache_key)
|
|
276
|
-
|
|
277
|
-
|
|
283
|
+
# Only treat a well-formed (Message, usage) pair as a cache hit;
|
|
284
|
+
# anything else (key collision, schema drift) falls through to a
|
|
285
|
+
# real provider call instead of crashing on a bad cast.
|
|
286
|
+
if (
|
|
287
|
+
isinstance(cached, (tuple, list))
|
|
288
|
+
and len(cached) >= 2
|
|
289
|
+
and isinstance(cached[0], Message)
|
|
290
|
+
):
|
|
291
|
+
cached_msg = cached[0]
|
|
278
292
|
cached_usage = cached[1]
|
|
279
293
|
self.usage.add_usage(cached_usage, tool_name=None)
|
|
280
294
|
if run_id:
|
|
@@ -524,21 +538,29 @@ class _ProviderCallerMixin:
|
|
|
524
538
|
elif isinstance(chunk, ToolCall):
|
|
525
539
|
tool_calls.append(chunk)
|
|
526
540
|
else:
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
541
|
+
# Sync-generator fallback: wrap in closing() so a stream_handler
|
|
542
|
+
# exception or caller disconnect deterministically closes the
|
|
543
|
+
# provider generator (the sync analog of the aclosing path above).
|
|
544
|
+
from contextlib import closing
|
|
545
|
+
|
|
546
|
+
with closing(
|
|
547
|
+
self.provider.stream(
|
|
548
|
+
model=self._effective_model,
|
|
549
|
+
system_prompt=self._system_prompt,
|
|
550
|
+
messages=self._history,
|
|
551
|
+
tools=self.tools,
|
|
552
|
+
temperature=self.config.temperature,
|
|
553
|
+
max_tokens=self.config.max_tokens,
|
|
554
|
+
timeout=self.config.request_timeout,
|
|
555
|
+
)
|
|
556
|
+
) as stream:
|
|
557
|
+
for chunk in stream:
|
|
558
|
+
if isinstance(chunk, str):
|
|
559
|
+
if chunk:
|
|
560
|
+
aggregated.append(chunk)
|
|
561
|
+
if stream_handler:
|
|
562
|
+
stream_handler(chunk)
|
|
563
|
+
elif isinstance(chunk, ToolCall):
|
|
564
|
+
tool_calls.append(chunk)
|
|
543
565
|
|
|
544
566
|
return "".join(aggregated), tool_calls
|
|
@@ -107,7 +107,7 @@ class _ToolExecutorMixin:
|
|
|
107
107
|
return False
|
|
108
108
|
if tool is None or getattr(tool, "terminal", False):
|
|
109
109
|
return False
|
|
110
|
-
return len(result) > tcfg.compress_threshold
|
|
110
|
+
return bool(len(result) > tcfg.compress_threshold)
|
|
111
111
|
|
|
112
112
|
def _stop_condition_hit(self, tool_name: str, result: str) -> bool:
|
|
113
113
|
"""True if config.stop_condition fires for this result (terminal path)."""
|
|
@@ -138,6 +138,7 @@ class _ToolExecutorMixin:
|
|
|
138
138
|
tool_name,
|
|
139
139
|
type(exc).__name__,
|
|
140
140
|
exc,
|
|
141
|
+
exc_info=exc,
|
|
141
142
|
)
|
|
142
143
|
|
|
143
144
|
def _build_compression_request(self, tool_name: str, result: str) -> Message:
|
|
@@ -203,7 +204,7 @@ class _ToolExecutorMixin:
|
|
|
203
204
|
def _compression_max_tokens(self) -> int:
|
|
204
205
|
"""Token budget for the summary: comfortably under the char threshold."""
|
|
205
206
|
threshold = self.config.tool.compress_threshold
|
|
206
|
-
return max(128, min(1000, threshold // 4))
|
|
207
|
+
return int(max(128, min(1000, threshold // 4)))
|
|
207
208
|
|
|
208
209
|
def _compress_tool_result(
|
|
209
210
|
self,
|
|
@@ -22,6 +22,18 @@ if TYPE_CHECKING:
|
|
|
22
22
|
from ..providers.base import Provider
|
|
23
23
|
from ..sessions import SessionStore
|
|
24
24
|
from ..usage import AgentUsage
|
|
25
|
+
from .config_groups import (
|
|
26
|
+
BudgetConfig,
|
|
27
|
+
CoherenceConfig,
|
|
28
|
+
CompressConfig,
|
|
29
|
+
GuardrailsConfig,
|
|
30
|
+
MemoryConfig,
|
|
31
|
+
RetryConfig,
|
|
32
|
+
SessionConfig,
|
|
33
|
+
SummarizeConfig,
|
|
34
|
+
ToolConfig,
|
|
35
|
+
TraceConfig,
|
|
36
|
+
)
|
|
25
37
|
|
|
26
38
|
ConfirmAction = Union[
|
|
27
39
|
Callable[[str, Dict[str, Any], str], bool],
|
|
@@ -179,16 +191,17 @@ class AgentConfig:
|
|
|
179
191
|
|
|
180
192
|
# -- Nested config groups (optional, for YAML / clean APIs) --
|
|
181
193
|
# When provided, nested values take precedence over flat fields.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
194
|
+
# (CoherenceConfig's attribute name collides with the flat coherence_check.)
|
|
195
|
+
retry: Optional["RetryConfig"] = None
|
|
196
|
+
tool: Optional["ToolConfig"] = None
|
|
197
|
+
coherence: Optional["CoherenceConfig"] = None
|
|
198
|
+
guardrail: Optional["GuardrailsConfig"] = None
|
|
199
|
+
session: Optional["SessionConfig"] = None
|
|
200
|
+
summarize: Optional["SummarizeConfig"] = None
|
|
201
|
+
memory: Optional["MemoryConfig"] = None
|
|
202
|
+
budget: Optional["BudgetConfig"] = None
|
|
203
|
+
trace: Optional["TraceConfig"] = None
|
|
204
|
+
compress: Optional["CompressConfig"] = None
|
|
192
205
|
|
|
193
206
|
# -- Planning-as-config (ROADMAP P2). See PlanningConfig below and
|
|
194
207
|
# agent/_planning.py for the adapter. Self-contained: default None means
|
|
@@ -391,7 +404,7 @@ step executes and must return one of:
|
|
|
391
404
|
"""
|
|
392
405
|
|
|
393
406
|
|
|
394
|
-
@
|
|
407
|
+
@stable
|
|
395
408
|
@dataclass
|
|
396
409
|
class PlanningConfig:
|
|
397
410
|
"""Opt-in planning for any Agent: plan -> (approve) -> execute -> synthesize.
|
|
@@ -142,16 +142,14 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
142
142
|
Initialize a new Agent instance.
|
|
143
143
|
|
|
144
144
|
Args:
|
|
145
|
-
tools: List of Tool instances the agent can use
|
|
145
|
+
tools: List of Tool instances the agent can use. May be empty for a
|
|
146
|
+
pure conversational agent (the provider is called with no tools).
|
|
146
147
|
provider: LLM provider adapter. Defaults to OpenAIProvider().
|
|
147
148
|
prompt_builder: Custom prompt builder. Defaults to PromptBuilder().
|
|
148
149
|
parser: Custom tool call parser. Defaults to ToolCallParser().
|
|
149
150
|
config: Agent configuration options. Defaults to AgentConfig().
|
|
150
151
|
memory: Optional conversation memory for maintaining history across turns.
|
|
151
152
|
|
|
152
|
-
Raises:
|
|
153
|
-
ValueError: If tools list is empty.
|
|
154
|
-
|
|
155
153
|
Example:
|
|
156
154
|
>>> agent = Agent(
|
|
157
155
|
... tools=[search, calculator],
|
|
@@ -1078,6 +1076,10 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
1078
1076
|
result = AgentResult(
|
|
1079
1077
|
message=Message(role=Role.ASSISTANT, content=f"Batch error: {exc}"),
|
|
1080
1078
|
)
|
|
1079
|
+
# No lock needed here (unlike sync batch(), which uses real OS
|
|
1080
|
+
# threads): merge() and the increment are synchronous with no
|
|
1081
|
+
# await, so under asyncio's single-threaded event loop they run
|
|
1082
|
+
# atomically relative to the other gathered coroutines.
|
|
1081
1083
|
self.usage.merge(clone.usage)
|
|
1082
1084
|
completed += 1
|
|
1083
1085
|
if on_progress:
|
|
@@ -1123,6 +1125,14 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
1123
1125
|
Returns:
|
|
1124
1126
|
AgentResult with the final response and tool call metadata.
|
|
1125
1127
|
|
|
1128
|
+
Error handling:
|
|
1129
|
+
If the provider keeps failing after ``config.max_retries`` attempts,
|
|
1130
|
+
the agent does NOT raise — it returns an ``AgentResult`` whose
|
|
1131
|
+
``content`` is ``"Provider error: <detail>"`` (graceful degradation),
|
|
1132
|
+
and the failure is recorded on ``result.trace`` with the error.
|
|
1133
|
+
Check ``result.trace`` (or test for the ``"Provider error:"`` prefix)
|
|
1134
|
+
if you need to distinguish a provider failure from a real answer.
|
|
1135
|
+
|
|
1126
1136
|
Examples:
|
|
1127
1137
|
>>> result = agent.run("What is Python?")
|
|
1128
1138
|
>>> result = agent.run([Message(role=Role.USER, content="Hello")])
|
|
@@ -1214,7 +1224,7 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
1214
1224
|
# agent with a tight tool-iteration budget should
|
|
1215
1225
|
# still allow the full RetryConfig.max_retries
|
|
1216
1226
|
# worth of structured-validation retries.
|
|
1217
|
-
retry_budget = self.config.
|
|
1227
|
+
retry_budget = self.config.max_retries
|
|
1218
1228
|
if ctx.structured_retries < retry_budget:
|
|
1219
1229
|
ctx.structured_retries += 1
|
|
1220
1230
|
ctx.trace.add(
|
|
@@ -1372,7 +1382,7 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
1372
1382
|
clone.analytics = None
|
|
1373
1383
|
return clone
|
|
1374
1384
|
|
|
1375
|
-
@deprecated(since="
|
|
1385
|
+
@deprecated(since="0.25.0", replacement="clone_for_isolation")
|
|
1376
1386
|
def _clone_for_isolation(self) -> "Agent":
|
|
1377
1387
|
"""Deprecated alias for :meth:`clone_for_isolation`."""
|
|
1378
1388
|
return self.clone_for_isolation()
|
|
@@ -1651,7 +1661,7 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
1651
1661
|
# agent with a tight tool-iteration budget should
|
|
1652
1662
|
# still allow the full RetryConfig.max_retries
|
|
1653
1663
|
# worth of structured-validation retries.
|
|
1654
|
-
retry_budget = self.config.
|
|
1664
|
+
retry_budget = self.config.max_retries
|
|
1655
1665
|
if ctx.structured_retries < retry_budget:
|
|
1656
1666
|
ctx.structured_retries += 1
|
|
1657
1667
|
ctx.trace.add(
|
|
@@ -1970,7 +1980,7 @@ class Agent(_ToolExecutorMixin, _ProviderCallerMixin, _LifecycleMixin, _MemoryMa
|
|
|
1970
1980
|
# agent with a tight tool-iteration budget should
|
|
1971
1981
|
# still allow the full RetryConfig.max_retries
|
|
1972
1982
|
# worth of structured-validation retries.
|
|
1973
|
-
retry_budget = self.config.
|
|
1983
|
+
retry_budget = self.config.max_retries
|
|
1974
1984
|
if ctx.structured_retries < retry_budget:
|
|
1975
1985
|
ctx.structured_retries += 1
|
|
1976
1986
|
ctx.trace.add(
|
|
@@ -35,6 +35,8 @@ class AnthropicEmbeddingProvider(EmbeddingProvider):
|
|
|
35
35
|
model: str = "voyage-3-lite",
|
|
36
36
|
api_key: Optional[str] = None,
|
|
37
37
|
truncate: bool = True,
|
|
38
|
+
timeout: float = 60.0,
|
|
39
|
+
max_retries: int = 2,
|
|
38
40
|
):
|
|
39
41
|
"""
|
|
40
42
|
Initialize Anthropic/Voyage embedding provider.
|
|
@@ -43,6 +45,10 @@ class AnthropicEmbeddingProvider(EmbeddingProvider):
|
|
|
43
45
|
model: Model name (default: voyage-3-lite)
|
|
44
46
|
api_key: Voyage API key (defaults to VOYAGE_API_KEY env var)
|
|
45
47
|
truncate: Whether to truncate inputs longer than context window
|
|
48
|
+
timeout: Per-request timeout in seconds (default: 60). Prevents a
|
|
49
|
+
hung embeddings call from blocking ingestion indefinitely.
|
|
50
|
+
max_retries: Number of automatic retries on transient errors,
|
|
51
|
+
handled by the Voyage SDK (default: 2).
|
|
46
52
|
"""
|
|
47
53
|
try:
|
|
48
54
|
import voyageai
|
|
@@ -52,7 +58,12 @@ class AnthropicEmbeddingProvider(EmbeddingProvider):
|
|
|
52
58
|
"Install with: pip install voyageai"
|
|
53
59
|
) from e
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
try:
|
|
62
|
+
self.client = voyageai.Client(api_key=api_key, max_retries=max_retries, timeout=timeout)
|
|
63
|
+
except TypeError:
|
|
64
|
+
# Older voyageai versions lack the timeout/max_retries kwargs.
|
|
65
|
+
logger.debug("voyageai.Client does not accept timeout/max_retries; using defaults")
|
|
66
|
+
self.client = voyageai.Client(api_key=api_key)
|
|
56
67
|
self.model = model
|
|
57
68
|
self.truncate = truncate
|
|
58
69
|
self._dimension = self._get_model_dimension()
|