selectools 1.0.1__tar.gz → 1.2.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-1.0.1/src/selectools.egg-info → selectools-1.2.0}/PKG-INFO +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/pyproject.toml +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/__init__.py +2 -1
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/agent/_provider_caller.py +77 -27
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/agent/_tool_executor.py +44 -6
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/agent/config.py +8 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/agent/config_groups.py +61 -2
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/agent/core.py +734 -123
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/audit.py +19 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/cache.py +4 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/embeddings/anthropic.py +0 -10
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/embeddings/cohere.py +0 -10
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/embeddings/gemini.py +0 -10
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/embeddings/openai.py +0 -10
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/embeddings/provider.py +6 -2
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/guardrails/base.py +32 -6
- selectools-1.2.0/src/selectools/guardrails/pipeline.py +260 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/observer.py +33 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/_openai_compat.py +45 -8
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/anthropic_provider.py +131 -108
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/gemini_provider.py +103 -127
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/openai_provider.py +2 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/_starlette_app.py +7 -1
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/_static/builder.js +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/app.py +41 -5
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/sessions.py +37 -104
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/structured.py +19 -1
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/types.py +13 -0
- {selectools-1.0.1 → selectools-1.2.0/src/selectools.egg-info}/PKG-INFO +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_bug_hunt_batch1_core.py +0 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_bug_hunt_batch1_security.py +0 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_bug_hunt_batch1_tools.py +1 -5
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_bug_hunt_regression.py +1 -10
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_checkpoint_postgres.py +3 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_coherence.py +1 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_concurrency_smoke.py +1 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_consolidation_regression.py +2 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_coverage_orchestration.py +7 -14
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_document_loaders_extended.py +0 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_e2e_multimodal.py +0 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_entity_memory.py +1 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals.py +0 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_advanced.py +2 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_cli.py +5 -7
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_e2e.py +3 -11
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_final.py +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_hardening.py +0 -8
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_html_report.py +0 -6
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_new_evaluators.py +2 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_ralph_bugs.py +2 -7
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_release.py +1 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_serve.py +0 -5
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_v017_features.py +1 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_evals_v0191.py +1 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_features_in_graph.py +7 -12
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_final_coverage_a.py +1 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_final_coverage_b.py +10 -14
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_guardrails.py +159 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_hardening.py +1 -7
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_knowledge.py +1 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_knowledge_backend.py +0 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_knowledge_backend_redis.py +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_knowledge_backend_supabase.py +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_knowledge_graph.py +1 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_knowledge_stores.py +0 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_knowledge_stores_error_handling.py +0 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_mcp.py +2 -5
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_mcp_coverage.py +1 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_memory_boundary.py +0 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_memory_integration.py +0 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_multi_agent_edge_cases.py +2 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_multimodal.py +0 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_orchestration_checkpoint.py +4 -5
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_orchestration_evals.py +0 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_orchestration_graph.py +3 -9
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_orchestration_integration.py +0 -9
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_orchestration_supervisor.py +5 -6
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_patterns.py +5 -11
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_phase1_design_patterns.py +0 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_pipeline.py +0 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_pipeline_coverage.py +1 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_prompt_compression.py +0 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_property_based.py +0 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_remaining_coverage.py +4 -9
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_results.py +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_security.py +1 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_semantic_cache.py +0 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_serve_app_coverage.py +17 -11
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_serve_cli.py +0 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_sessions_dynamodb.py +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_sessions_edge_cases.py +1 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_sessions_redis.py +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_sessions_supabase.py +4 -5
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_starlette_app.py +0 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_structured_config.py +0 -10
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_structured_tool_results.py +1 -1
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_summarize_on_trim.py +2 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_terminal_actions.py +1 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_token_estimation.py +0 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_trace.py +0 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_trace_html.py +0 -4
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_trace_store.py +1 -5
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_v016_regression.py +0 -2
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_v019_features.py +2 -3
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_visual_builder.py +51 -26
- selectools-1.0.1/src/selectools/guardrails/pipeline.py +0 -124
- {selectools-1.0.1 → selectools-1.2.0}/LICENSE +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/README.md +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/setup.cfg +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/_async_utils.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/_json_utils.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/_ssrf.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/_time.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/a2a/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/a2a/client.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/a2a/server.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/a2a/types.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/agent/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/agent/_lifecycle.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/agent/_memory_manager.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/agent/_planning.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/analytics.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/cache_redis.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/cache_semantic.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/cancellation.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/checkpoint_postgres.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/coherence.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/compose.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/embeddings/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/entity_memory.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/env.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/__main__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/badge.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/dataset.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/evaluators.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/generator.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/history.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/html.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/junit.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/llm_evaluators.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/pairwise.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/regression.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/report.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/serve.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/snapshot.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/suite.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/templates.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/evals/types.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/exceptions.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/guardrails/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/guardrails/format.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/guardrails/injection.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/guardrails/length.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/guardrails/pii.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/guardrails/topic.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/guardrails/toxicity.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/knowledge.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/knowledge_backends.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/knowledge_graph.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/knowledge_sanitizers.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/knowledge_store_redis.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/knowledge_store_supabase.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/loop_detection.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/mcp/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/mcp/_loop.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/mcp/bridge.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/mcp/client.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/mcp/config.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/mcp/multi.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/mcp/server.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/memory.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/models.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/observe/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/observe/langfuse.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/observe/otel.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/observe/trace_store.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/orchestration/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/orchestration/checkpoint.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/orchestration/graph.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/orchestration/node.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/orchestration/state.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/orchestration/supervisor.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/parser.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/patterns/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/patterns/debate.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/patterns/plan_and_execute.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/patterns/reflective.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/patterns/team_lead.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/pending.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/pipeline.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/policy.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/pricing.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/prompt.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/azure_openai_provider.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/base.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/fallback.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/litellm_provider.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/ollama_provider.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/router.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/providers/stubs.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/bm25.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/chunking.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/hybrid.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/loaders.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/reranker.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/stores/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/stores/chroma.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/stores/faiss.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/stores/memory.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/stores/pgvector.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/stores/pinecone.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/stores/qdrant.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/stores/sqlite.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/rag/vector_store.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/results.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/scheduler.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/security.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/_static/builder.css +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/_static/builder.html +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/api.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/builder.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/cli.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/models.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/serve/playground.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/stability.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/templates/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/templates/code_reviewer.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/templates/customer_support.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/templates/data_analyst.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/templates/rag_chatbot.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/templates/research_assistant.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/token_estimation.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/browser_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/calculator_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/code_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/data_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/datetime_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/db_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/discord_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/email_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/file_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/github_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/image_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/linear_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/memory_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/notion_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/pdf_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/reasoning_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/s3_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/search_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/slack_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/text_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/toolbox/web_tools.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/tools/__init__.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/tools/base.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/tools/decorators.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/tools/loader.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/tools/registry.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/trace.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/unified_memory.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools/usage.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools.egg-info/SOURCES.txt +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools.egg-info/dependency_links.txt +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools.egg-info/entry_points.txt +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools.egg-info/requires.txt +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/src/selectools.egg-info/top_level.txt +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_a2a.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_agent_api.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_agent_hitl.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_approval_gate.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_architecture.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_async_observers.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_audit.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_bake_window_hunt.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_budget.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_cache.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_cache_redis.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_cancellation.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_conversation_branching.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_e2e_langfuse_observer.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_e2e_otel_observer.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_e2e_v0_21_0_apps.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_e2e_v0_21_0_simulations.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_env.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_guardrails_injection.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_knowledge_sanitizers.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_knowledge_store_redis.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_knowledge_store_supabase.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_langfuse_observer.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_loop_detection.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_memory.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_memory_async.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_model_switching.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_orchestration_e2e.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_orchestration_primitives.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_otel_observer.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_parser.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_pending.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_pending_redis_smoke.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_policy.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_prompt.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_reasoning_strategy.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_routing_mode.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_scheduler.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_sessions.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_sessions_mongo.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_simple_observer.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_simulation_evals.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_ssrf.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_stability.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_structured.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_templates_coverage.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_time.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_tool_caching.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_tool_result_compression.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_unified_memory.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_unified_memory_config.py +0 -0
- {selectools-1.0.1 → selectools-1.2.0}/tests/test_yaml_config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: selectools
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.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>
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "selectools"
|
|
7
|
-
version = "1.0
|
|
7
|
+
version = "1.2.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.10"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Public exports for the selectools package."""
|
|
2
2
|
|
|
3
|
-
__version__ = "1.0
|
|
3
|
+
__version__ = "1.2.0"
|
|
4
4
|
|
|
5
5
|
# Import submodules (lazy loading for optional dependencies)
|
|
6
6
|
from . import embeddings, evals, guardrails, models, observe, patterns, rag, toolbox
|
|
@@ -13,6 +13,7 @@ from .agent.config_groups import (
|
|
|
13
13
|
MemoryConfig,
|
|
14
14
|
RetryConfig,
|
|
15
15
|
SessionConfig,
|
|
16
|
+
StructuredOutputConfig,
|
|
16
17
|
SummarizeConfig,
|
|
17
18
|
ToolConfig,
|
|
18
19
|
TraceConfig,
|
|
@@ -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, Callable, 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
|
|
@@ -50,8 +50,18 @@ class _ProviderCallerMixin:
|
|
|
50
50
|
stream_handler: Optional[Callable[[str], None]] = None,
|
|
51
51
|
trace: Optional[AgentTrace] = None,
|
|
52
52
|
run_id: Optional[str] = None,
|
|
53
|
+
response_format: Optional[Dict[str, Any]] = None,
|
|
54
|
+
tools_override: Optional[List[Any]] = None,
|
|
55
|
+
system_prompt_override: Optional[str] = None,
|
|
53
56
|
) -> Message:
|
|
54
57
|
call_start = time.time()
|
|
58
|
+
effective_tools = self.tools if tools_override is None else tools_override
|
|
59
|
+
effective_system_prompt = (
|
|
60
|
+
system_prompt_override if system_prompt_override is not None else self._system_prompt
|
|
61
|
+
)
|
|
62
|
+
provider_extra: Dict[str, Any] = {}
|
|
63
|
+
if response_format is not None:
|
|
64
|
+
provider_extra["response_format"] = response_format
|
|
55
65
|
|
|
56
66
|
cache_key: Optional[str] = None
|
|
57
67
|
if self.config.cache and not (
|
|
@@ -59,10 +69,11 @@ class _ProviderCallerMixin:
|
|
|
59
69
|
):
|
|
60
70
|
cache_key = CacheKeyBuilder.build(
|
|
61
71
|
model=self._effective_model,
|
|
62
|
-
system_prompt=
|
|
72
|
+
system_prompt=effective_system_prompt,
|
|
63
73
|
messages=self._history,
|
|
64
|
-
tools=
|
|
74
|
+
tools=effective_tools,
|
|
65
75
|
temperature=self.config.temperature,
|
|
76
|
+
response_format=response_format,
|
|
66
77
|
)
|
|
67
78
|
cached = self.config.cache.get(cache_key)
|
|
68
79
|
# Only treat a well-formed (Message, usage) pair as a cache hit;
|
|
@@ -82,7 +93,7 @@ class _ProviderCallerMixin:
|
|
|
82
93
|
run_id,
|
|
83
94
|
self._history,
|
|
84
95
|
self._effective_model,
|
|
85
|
-
|
|
96
|
+
effective_system_prompt,
|
|
86
97
|
)
|
|
87
98
|
self._notify_observers(
|
|
88
99
|
"on_llm_end",
|
|
@@ -122,12 +133,15 @@ class _ProviderCallerMixin:
|
|
|
122
133
|
run_id,
|
|
123
134
|
self._history,
|
|
124
135
|
self._effective_model,
|
|
125
|
-
|
|
136
|
+
effective_system_prompt,
|
|
126
137
|
)
|
|
127
138
|
|
|
128
139
|
if self.config.stream and getattr(self.provider, "supports_streaming", False):
|
|
129
140
|
response_text, streamed_tool_calls = self._streaming_call(
|
|
130
|
-
stream_handler=stream_handler
|
|
141
|
+
stream_handler=stream_handler,
|
|
142
|
+
response_format=response_format,
|
|
143
|
+
tools=effective_tools,
|
|
144
|
+
system_prompt=effective_system_prompt,
|
|
131
145
|
)
|
|
132
146
|
if run_id:
|
|
133
147
|
self._notify_observers("on_llm_end", run_id, response_text, None)
|
|
@@ -139,12 +153,13 @@ class _ProviderCallerMixin:
|
|
|
139
153
|
|
|
140
154
|
response_msg, usage_stats = self.provider.complete(
|
|
141
155
|
model=self._effective_model,
|
|
142
|
-
system_prompt=
|
|
156
|
+
system_prompt=effective_system_prompt,
|
|
143
157
|
messages=self._history,
|
|
144
|
-
tools=
|
|
158
|
+
tools=effective_tools,
|
|
145
159
|
temperature=self.config.temperature,
|
|
146
160
|
max_tokens=self.config.max_tokens,
|
|
147
161
|
timeout=self.config.request_timeout,
|
|
162
|
+
**provider_extra,
|
|
148
163
|
)
|
|
149
164
|
response_text = response_msg.content or ""
|
|
150
165
|
|
|
@@ -229,21 +244,29 @@ class _ProviderCallerMixin:
|
|
|
229
244
|
)
|
|
230
245
|
|
|
231
246
|
def _streaming_call(
|
|
232
|
-
self,
|
|
247
|
+
self,
|
|
248
|
+
stream_handler: Optional[Callable[[str], None]] = None,
|
|
249
|
+
response_format: Optional[Dict[str, Any]] = None,
|
|
250
|
+
tools: Optional[List[Any]] = None,
|
|
251
|
+
system_prompt: Optional[str] = None,
|
|
233
252
|
) -> Tuple[str, List[ToolCall]]:
|
|
234
253
|
if not getattr(self.provider, "supports_streaming", False):
|
|
235
254
|
raise ProviderError(f"Provider {self.provider.name} does not support streaming.")
|
|
236
255
|
|
|
256
|
+
provider_extra: Dict[str, Any] = {}
|
|
257
|
+
if response_format is not None:
|
|
258
|
+
provider_extra["response_format"] = response_format
|
|
237
259
|
aggregated: List[str] = []
|
|
238
260
|
tool_calls: List[ToolCall] = []
|
|
239
261
|
for chunk in self.provider.stream(
|
|
240
262
|
model=self._effective_model,
|
|
241
|
-
system_prompt=self._system_prompt,
|
|
263
|
+
system_prompt=self._system_prompt if system_prompt is None else system_prompt,
|
|
242
264
|
messages=self._history,
|
|
243
|
-
tools=self.tools,
|
|
265
|
+
tools=self.tools if tools is None else tools,
|
|
244
266
|
temperature=self.config.temperature,
|
|
245
267
|
max_tokens=self.config.max_tokens,
|
|
246
268
|
timeout=self.config.request_timeout,
|
|
269
|
+
**provider_extra,
|
|
247
270
|
):
|
|
248
271
|
if isinstance(chunk, str):
|
|
249
272
|
if chunk:
|
|
@@ -264,9 +287,19 @@ class _ProviderCallerMixin:
|
|
|
264
287
|
stream_handler: Optional[Callable[[str], None]] = None,
|
|
265
288
|
trace: Optional[AgentTrace] = None,
|
|
266
289
|
run_id: Optional[str] = None,
|
|
290
|
+
response_format: Optional[Dict[str, Any]] = None,
|
|
291
|
+
tools_override: Optional[List[Any]] = None,
|
|
292
|
+
system_prompt_override: Optional[str] = None,
|
|
267
293
|
) -> Message:
|
|
268
294
|
"""Async version of _call_provider with retry logic."""
|
|
269
295
|
call_start = time.time()
|
|
296
|
+
effective_tools = self.tools if tools_override is None else tools_override
|
|
297
|
+
effective_system_prompt = (
|
|
298
|
+
system_prompt_override if system_prompt_override is not None else self._system_prompt
|
|
299
|
+
)
|
|
300
|
+
provider_extra: Dict[str, Any] = {}
|
|
301
|
+
if response_format is not None:
|
|
302
|
+
provider_extra["response_format"] = response_format
|
|
270
303
|
|
|
271
304
|
cache_key: Optional[str] = None
|
|
272
305
|
if self.config.cache and not (
|
|
@@ -274,10 +307,11 @@ class _ProviderCallerMixin:
|
|
|
274
307
|
):
|
|
275
308
|
cache_key = CacheKeyBuilder.build(
|
|
276
309
|
model=self._effective_model,
|
|
277
|
-
system_prompt=
|
|
310
|
+
system_prompt=effective_system_prompt,
|
|
278
311
|
messages=self._history,
|
|
279
|
-
tools=
|
|
312
|
+
tools=effective_tools,
|
|
280
313
|
temperature=self.config.temperature,
|
|
314
|
+
response_format=response_format,
|
|
281
315
|
)
|
|
282
316
|
cached = self.config.cache.get(cache_key)
|
|
283
317
|
# Only treat a well-formed (Message, usage) pair as a cache hit;
|
|
@@ -297,14 +331,14 @@ class _ProviderCallerMixin:
|
|
|
297
331
|
run_id,
|
|
298
332
|
self._history,
|
|
299
333
|
self._effective_model,
|
|
300
|
-
|
|
334
|
+
effective_system_prompt,
|
|
301
335
|
)
|
|
302
336
|
await self._anotify_observers(
|
|
303
337
|
"on_llm_start",
|
|
304
338
|
run_id,
|
|
305
339
|
self._history,
|
|
306
340
|
self._effective_model,
|
|
307
|
-
|
|
341
|
+
effective_system_prompt,
|
|
308
342
|
)
|
|
309
343
|
self._notify_observers(
|
|
310
344
|
"on_llm_end",
|
|
@@ -357,19 +391,22 @@ class _ProviderCallerMixin:
|
|
|
357
391
|
run_id,
|
|
358
392
|
self._history,
|
|
359
393
|
self._effective_model,
|
|
360
|
-
|
|
394
|
+
effective_system_prompt,
|
|
361
395
|
)
|
|
362
396
|
await self._anotify_observers(
|
|
363
397
|
"on_llm_start",
|
|
364
398
|
run_id,
|
|
365
399
|
self._history,
|
|
366
400
|
self._effective_model,
|
|
367
|
-
|
|
401
|
+
effective_system_prompt,
|
|
368
402
|
)
|
|
369
403
|
|
|
370
404
|
if self.config.stream and getattr(self.provider, "supports_streaming", False):
|
|
371
405
|
response_text, streamed_tool_calls = await self._astreaming_call(
|
|
372
|
-
stream_handler=stream_handler
|
|
406
|
+
stream_handler=stream_handler,
|
|
407
|
+
response_format=response_format,
|
|
408
|
+
tools=effective_tools,
|
|
409
|
+
system_prompt=effective_system_prompt,
|
|
373
410
|
)
|
|
374
411
|
if run_id:
|
|
375
412
|
self._notify_observers("on_llm_end", run_id, response_text, None)
|
|
@@ -386,12 +423,13 @@ class _ProviderCallerMixin:
|
|
|
386
423
|
):
|
|
387
424
|
response_msg, usage_stats = await self.provider.acomplete(
|
|
388
425
|
model=self._effective_model,
|
|
389
|
-
system_prompt=
|
|
426
|
+
system_prompt=effective_system_prompt,
|
|
390
427
|
messages=self._history,
|
|
391
|
-
tools=
|
|
428
|
+
tools=effective_tools,
|
|
392
429
|
temperature=self.config.temperature,
|
|
393
430
|
max_tokens=self.config.max_tokens,
|
|
394
431
|
timeout=self.config.request_timeout,
|
|
432
|
+
**provider_extra,
|
|
395
433
|
)
|
|
396
434
|
response_text = response_msg.content or ""
|
|
397
435
|
else:
|
|
@@ -405,12 +443,13 @@ class _ProviderCallerMixin:
|
|
|
405
443
|
_get_async_provider_executor(),
|
|
406
444
|
lambda: self.provider.complete(
|
|
407
445
|
model=self._effective_model,
|
|
408
|
-
system_prompt=
|
|
446
|
+
system_prompt=effective_system_prompt,
|
|
409
447
|
messages=self._history,
|
|
410
|
-
tools=
|
|
448
|
+
tools=effective_tools,
|
|
411
449
|
temperature=self.config.temperature,
|
|
412
450
|
max_tokens=self.config.max_tokens,
|
|
413
451
|
timeout=self.config.request_timeout,
|
|
452
|
+
**provider_extra,
|
|
414
453
|
),
|
|
415
454
|
)
|
|
416
455
|
response_text = response_msg.content or ""
|
|
@@ -506,12 +545,21 @@ class _ProviderCallerMixin:
|
|
|
506
545
|
)
|
|
507
546
|
|
|
508
547
|
async def _astreaming_call(
|
|
509
|
-
self,
|
|
548
|
+
self,
|
|
549
|
+
stream_handler: Optional[Callable[[str], None]] = None,
|
|
550
|
+
response_format: Optional[Dict[str, Any]] = None,
|
|
551
|
+
tools: Optional[List[Any]] = None,
|
|
552
|
+
system_prompt: Optional[str] = None,
|
|
510
553
|
) -> Tuple[str, List[ToolCall]]:
|
|
511
554
|
"""Async version of _streaming_call."""
|
|
512
555
|
if not getattr(self.provider, "supports_streaming", False):
|
|
513
556
|
raise ProviderError(f"Provider {self.provider.name} does not support streaming.")
|
|
514
557
|
|
|
558
|
+
provider_extra: Dict[str, Any] = {}
|
|
559
|
+
if response_format is not None:
|
|
560
|
+
provider_extra["response_format"] = response_format
|
|
561
|
+
effective_tools = self.tools if tools is None else tools
|
|
562
|
+
effective_system_prompt = self._system_prompt if system_prompt is None else system_prompt
|
|
515
563
|
aggregated: List[str] = []
|
|
516
564
|
tool_calls: List[ToolCall] = []
|
|
517
565
|
|
|
@@ -521,12 +569,13 @@ class _ProviderCallerMixin:
|
|
|
521
569
|
async with aclosing(
|
|
522
570
|
self.provider.astream( # type: ignore[attr-defined]
|
|
523
571
|
model=self._effective_model,
|
|
524
|
-
system_prompt=
|
|
572
|
+
system_prompt=effective_system_prompt,
|
|
525
573
|
messages=self._history,
|
|
526
|
-
tools=
|
|
574
|
+
tools=effective_tools,
|
|
527
575
|
temperature=self.config.temperature,
|
|
528
576
|
max_tokens=self.config.max_tokens,
|
|
529
577
|
timeout=self.config.request_timeout,
|
|
578
|
+
**provider_extra,
|
|
530
579
|
)
|
|
531
580
|
) as stream:
|
|
532
581
|
async for chunk in stream:
|
|
@@ -546,12 +595,13 @@ class _ProviderCallerMixin:
|
|
|
546
595
|
with closing(
|
|
547
596
|
self.provider.stream(
|
|
548
597
|
model=self._effective_model,
|
|
549
|
-
system_prompt=
|
|
598
|
+
system_prompt=effective_system_prompt,
|
|
550
599
|
messages=self._history,
|
|
551
|
-
tools=
|
|
600
|
+
tools=effective_tools,
|
|
552
601
|
temperature=self.config.temperature,
|
|
553
602
|
max_tokens=self.config.max_tokens,
|
|
554
603
|
timeout=self.config.request_timeout,
|
|
604
|
+
**provider_extra,
|
|
555
605
|
)
|
|
556
606
|
) as stream:
|
|
557
607
|
for chunk in stream:
|
|
@@ -861,6 +861,11 @@ class _ToolExecutorMixin:
|
|
|
861
861
|
cached_entry = self._check_tool_cache(tool, parameters)
|
|
862
862
|
if cached_entry is not None:
|
|
863
863
|
raw, compressed = cached_entry
|
|
864
|
+
# Guardrails run on cache hits too — an entry written before
|
|
865
|
+
# a guardrail was configured must not bypass the stage.
|
|
866
|
+
guarded = self._run_tool_result_guardrails(tool_name, raw, trace, run_ctx=run_ctx)
|
|
867
|
+
if guarded != raw:
|
|
868
|
+
raw, compressed = guarded, None
|
|
864
869
|
return _Result(
|
|
865
870
|
tc, raw, False, 0.0, tool, 0, cached=True, cached_compressed=compressed
|
|
866
871
|
)
|
|
@@ -886,7 +891,10 @@ class _ToolExecutorMixin:
|
|
|
886
891
|
try:
|
|
887
892
|
result = self._execute_tool_with_timeout(tool, parameters, chunk_cb)
|
|
888
893
|
result = self._screen_tool_result(tool_name, result)
|
|
894
|
+
# Cache the pre-guardrail value: guardrails run at use time
|
|
895
|
+
# (fresh AND cached), so config changes apply retroactively.
|
|
889
896
|
self._store_tool_cache(tool, parameters, result)
|
|
897
|
+
result = self._run_tool_result_guardrails(tool_name, result, trace, run_ctx=run_ctx)
|
|
890
898
|
dur = time.time() - start
|
|
891
899
|
if run_id:
|
|
892
900
|
self._notify_observers(
|
|
@@ -1103,6 +1111,13 @@ class _ToolExecutorMixin:
|
|
|
1103
1111
|
cached_entry = self._check_tool_cache(tool, parameters)
|
|
1104
1112
|
if cached_entry is not None:
|
|
1105
1113
|
raw, compressed = cached_entry
|
|
1114
|
+
# Guardrails run on cache hits too — an entry written before
|
|
1115
|
+
# a guardrail was configured must not bypass the stage.
|
|
1116
|
+
guarded = await self._arun_tool_result_guardrails(
|
|
1117
|
+
tool_name, raw, trace, run_ctx=run_ctx
|
|
1118
|
+
)
|
|
1119
|
+
if guarded != raw:
|
|
1120
|
+
raw, compressed = guarded, None
|
|
1106
1121
|
return _Result(
|
|
1107
1122
|
tc, raw, False, 0.0, tool, 0, cached=True, cached_compressed=compressed
|
|
1108
1123
|
)
|
|
@@ -1130,7 +1145,12 @@ class _ToolExecutorMixin:
|
|
|
1130
1145
|
try:
|
|
1131
1146
|
result = await self._aexecute_tool_with_timeout(tool, parameters, chunk_cb)
|
|
1132
1147
|
result = self._screen_tool_result(tool_name, result)
|
|
1148
|
+
# Cache the pre-guardrail value: guardrails run at use time
|
|
1149
|
+
# (fresh AND cached), so config changes apply retroactively.
|
|
1133
1150
|
self._store_tool_cache(tool, parameters, result)
|
|
1151
|
+
result = await self._arun_tool_result_guardrails(
|
|
1152
|
+
tool_name, result, trace, run_ctx=run_ctx
|
|
1153
|
+
)
|
|
1134
1154
|
dur = time.time() - start
|
|
1135
1155
|
if run_id:
|
|
1136
1156
|
self._notify_observers(
|
|
@@ -1437,6 +1457,13 @@ class _ToolExecutorMixin:
|
|
|
1437
1457
|
cached_entry = self._check_tool_cache(tool, parameters)
|
|
1438
1458
|
if cached_entry is not None:
|
|
1439
1459
|
cached_raw, cached_compressed = cached_entry
|
|
1460
|
+
# Guardrails run on cache hits too — an entry written before a
|
|
1461
|
+
# guardrail was configured must not bypass the stage.
|
|
1462
|
+
guarded = self._run_tool_result_guardrails(
|
|
1463
|
+
tool_name, cached_raw, ctx.trace, run_ctx=ctx
|
|
1464
|
+
)
|
|
1465
|
+
if guarded != cached_raw:
|
|
1466
|
+
cached_raw, cached_compressed = guarded, None
|
|
1440
1467
|
ctx.trace.add(
|
|
1441
1468
|
TraceStep(
|
|
1442
1469
|
type=StepType.CACHE_HIT,
|
|
@@ -1475,15 +1502,16 @@ class _ToolExecutorMixin:
|
|
|
1475
1502
|
|
|
1476
1503
|
result = self._execute_tool_with_timeout(tool, parameters, chunk_callback)
|
|
1477
1504
|
result = self._screen_tool_result(tool_name, result)
|
|
1505
|
+
# Cache the pre-guardrail value: guardrails run at use time
|
|
1506
|
+
# (fresh AND cached), so config changes apply retroactively.
|
|
1507
|
+
self._store_tool_cache(tool, parameters, result)
|
|
1508
|
+
result = self._run_tool_result_guardrails(tool_name, result, ctx.trace, run_ctx=ctx)
|
|
1478
1509
|
duration = time.time() - start_time
|
|
1479
1510
|
|
|
1480
1511
|
self._notify_observers(
|
|
1481
1512
|
"on_tool_end", ctx.run_id, call_id, tool_name, result, duration * 1000
|
|
1482
1513
|
)
|
|
1483
1514
|
|
|
1484
|
-
# Store in tool result cache
|
|
1485
|
-
self._store_tool_cache(tool, parameters, result)
|
|
1486
|
-
|
|
1487
1515
|
ctx.trace.add(
|
|
1488
1516
|
TraceStep(
|
|
1489
1517
|
type=StepType.TOOL_EXECUTION,
|
|
@@ -1673,6 +1701,13 @@ class _ToolExecutorMixin:
|
|
|
1673
1701
|
cached_entry = self._check_tool_cache(tool, parameters)
|
|
1674
1702
|
if cached_entry is not None:
|
|
1675
1703
|
cached_raw, cached_compressed = cached_entry
|
|
1704
|
+
# Guardrails run on cache hits too — an entry written before a
|
|
1705
|
+
# guardrail was configured must not bypass the stage.
|
|
1706
|
+
guarded = await self._arun_tool_result_guardrails(
|
|
1707
|
+
tool_name, cached_raw, ctx.trace, run_ctx=ctx
|
|
1708
|
+
)
|
|
1709
|
+
if guarded != cached_raw:
|
|
1710
|
+
cached_raw, cached_compressed = guarded, None
|
|
1676
1711
|
ctx.trace.add(
|
|
1677
1712
|
TraceStep(
|
|
1678
1713
|
type=StepType.CACHE_HIT,
|
|
@@ -1714,6 +1749,12 @@ class _ToolExecutorMixin:
|
|
|
1714
1749
|
|
|
1715
1750
|
result = await self._aexecute_tool_with_timeout(tool, parameters, chunk_callback)
|
|
1716
1751
|
result = self._screen_tool_result(tool_name, result)
|
|
1752
|
+
# Cache the pre-guardrail value: guardrails run at use time
|
|
1753
|
+
# (fresh AND cached), so config changes apply retroactively.
|
|
1754
|
+
self._store_tool_cache(tool, parameters, result)
|
|
1755
|
+
result = await self._arun_tool_result_guardrails(
|
|
1756
|
+
tool_name, result, ctx.trace, run_ctx=ctx
|
|
1757
|
+
)
|
|
1717
1758
|
duration = time.time() - start_time
|
|
1718
1759
|
|
|
1719
1760
|
self._notify_observers(
|
|
@@ -1723,9 +1764,6 @@ class _ToolExecutorMixin:
|
|
|
1723
1764
|
"on_tool_end", ctx.run_id, call_id, tool_name, result, duration * 1000
|
|
1724
1765
|
)
|
|
1725
1766
|
|
|
1726
|
-
# Store in tool result cache
|
|
1727
|
-
self._store_tool_cache(tool, parameters, result)
|
|
1728
|
-
|
|
1729
1767
|
ctx.trace.add(
|
|
1730
1768
|
TraceStep(
|
|
1731
1769
|
type=StepType.TOOL_EXECUTION,
|
|
@@ -30,6 +30,7 @@ if TYPE_CHECKING:
|
|
|
30
30
|
MemoryConfig,
|
|
31
31
|
RetryConfig,
|
|
32
32
|
SessionConfig,
|
|
33
|
+
StructuredOutputConfig,
|
|
33
34
|
SummarizeConfig,
|
|
34
35
|
ToolConfig,
|
|
35
36
|
TraceConfig,
|
|
@@ -202,6 +203,8 @@ class AgentConfig:
|
|
|
202
203
|
budget: Optional["BudgetConfig"] = None
|
|
203
204
|
trace: Optional["TraceConfig"] = None
|
|
204
205
|
compress: Optional["CompressConfig"] = None
|
|
206
|
+
# Structured-output behavior (beta, v1.1). Group-only — no flat mirrors.
|
|
207
|
+
structured_output: Optional["StructuredOutputConfig"] = None
|
|
205
208
|
|
|
206
209
|
# -- Planning-as-config (ROADMAP P2). See PlanningConfig below and
|
|
207
210
|
# agent/_planning.py for the adapter. Self-contained: default None means
|
|
@@ -217,6 +220,7 @@ class AgentConfig:
|
|
|
217
220
|
MemoryConfig,
|
|
218
221
|
RetryConfig,
|
|
219
222
|
SessionConfig,
|
|
223
|
+
StructuredOutputConfig,
|
|
220
224
|
SummarizeConfig,
|
|
221
225
|
ToolConfig,
|
|
222
226
|
TraceConfig,
|
|
@@ -382,6 +386,10 @@ class AgentConfig:
|
|
|
382
386
|
keep_recent=self.compress_keep_recent,
|
|
383
387
|
)
|
|
384
388
|
|
|
389
|
+
self.structured_output = _unpack(self.structured_output, StructuredOutputConfig)
|
|
390
|
+
if self.structured_output is None:
|
|
391
|
+
self.structured_output = StructuredOutputConfig()
|
|
392
|
+
|
|
385
393
|
# -- Planning-as-config: auto-unpack dicts (for YAML / dict configs) --
|
|
386
394
|
if isinstance(self.planning, dict):
|
|
387
395
|
self.planning = PlanningConfig(**self.planning)
|
|
@@ -24,9 +24,9 @@ Usage::
|
|
|
24
24
|
from __future__ import annotations
|
|
25
25
|
|
|
26
26
|
from dataclasses import dataclass, field
|
|
27
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
27
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
|
|
28
28
|
|
|
29
|
-
from ..stability import stable
|
|
29
|
+
from ..stability import beta, stable
|
|
30
30
|
|
|
31
31
|
if TYPE_CHECKING:
|
|
32
32
|
from ..cancellation import CancellationToken
|
|
@@ -37,6 +37,7 @@ if TYPE_CHECKING:
|
|
|
37
37
|
from ..policy import ToolPolicy
|
|
38
38
|
from ..providers.base import Provider
|
|
39
39
|
from ..sessions import SessionStore
|
|
40
|
+
from ..types import Message
|
|
40
41
|
from ..unified_memory import UnifiedMemory
|
|
41
42
|
|
|
42
43
|
|
|
@@ -174,6 +175,63 @@ class GuardrailsConfig:
|
|
|
174
175
|
output_screening_patterns: Optional[List[str]] = None
|
|
175
176
|
|
|
176
177
|
|
|
178
|
+
@beta
|
|
179
|
+
@dataclass
|
|
180
|
+
class StructuredOutputConfig:
|
|
181
|
+
"""Structured-output (``response_format``) behavior settings (beta, v1.1).
|
|
182
|
+
|
|
183
|
+
Attributes:
|
|
184
|
+
native: Use the provider's native structured-output mode (OpenAI
|
|
185
|
+
``json_schema``, Gemini ``response_json_schema``) when the provider
|
|
186
|
+
advertises support, instead of injecting a schema instruction into
|
|
187
|
+
the system prompt. Providers without native support keep the
|
|
188
|
+
prompt-injection + parse behavior unchanged. Default: True.
|
|
189
|
+
final_turn_only: Keep the schema out of tool-loop turns entirely.
|
|
190
|
+
The loop runs to convergence with no schema pressure (so tool
|
|
191
|
+
calling and the text ``ToolCallParser`` work normally), then one
|
|
192
|
+
extra synthesis call — with the schema applied and no tools —
|
|
193
|
+
produces the validated structured final answer. In ``astream()``
|
|
194
|
+
the synthesis JSON is delivered only via the terminal
|
|
195
|
+
``AgentResult`` (``.content`` / ``.parsed``), never leaked as
|
|
196
|
+
content chunks. Costs at most one extra provider call per run
|
|
197
|
+
(see ``reuse_loop_answer`` / ``should_finalize`` /
|
|
198
|
+
``single_pass`` for the paths that avoid it). Default: False.
|
|
199
|
+
reuse_loop_answer: In ``final_turn_only`` mode, when the loop's
|
|
200
|
+
converged answer validates against the schema, use it directly
|
|
201
|
+
and skip the synthesis call entirely (v1.2, issue #164). The
|
|
202
|
+
gate is conservative: the WHOLE answer must be a single JSON
|
|
203
|
+
object (fragments embedded in prose never qualify), Pydantic
|
|
204
|
+
schemas validate fully, and dict schemas must carry every
|
|
205
|
+
top-level ``required`` key. Anything else falls back to the
|
|
206
|
+
synthesis call. Default: True.
|
|
207
|
+
should_finalize: Optional predicate ``(messages, last_response_text)
|
|
208
|
+
-> bool`` consulted in ``final_turn_only`` mode when the
|
|
209
|
+
converged answer did NOT validate. Return ``False`` to skip the
|
|
210
|
+
synthesis call for turns that need no structured output — the
|
|
211
|
+
run finishes with ``parsed=None`` and
|
|
212
|
+
``AgentResult.structured_status == "skipped"``. A validating
|
|
213
|
+
answer wins before the predicate runs (even with
|
|
214
|
+
``reuse_loop_answer=False``). Like ``stop_condition``, an
|
|
215
|
+
exception raised by the predicate propagates and aborts the run.
|
|
216
|
+
Default: None (always finalize, the v1.1 behavior).
|
|
217
|
+
single_pass: In ``final_turn_only`` mode with a provider that
|
|
218
|
+
advertises ``supports_native_structured_output_with_tools``
|
|
219
|
+
(OpenAI/Azure), carry the schema natively on the loop calls so
|
|
220
|
+
the converged answer IS the structured object — combined with
|
|
221
|
+
``reuse_loop_answer`` this eliminates the synthesis call (v1.2,
|
|
222
|
+
issue #166). Providers without combined support fall back to the
|
|
223
|
+
separate synthesis call. NOTE: in ``astream()`` the final answer
|
|
224
|
+
is then JSON and streams as content chunks (like plain native
|
|
225
|
+
mode). Default: False.
|
|
226
|
+
"""
|
|
227
|
+
|
|
228
|
+
native: bool = True
|
|
229
|
+
final_turn_only: bool = False
|
|
230
|
+
reuse_loop_answer: bool = True
|
|
231
|
+
should_finalize: Optional[Callable[[List["Message"], str], bool]] = None
|
|
232
|
+
single_pass: bool = False
|
|
233
|
+
|
|
234
|
+
|
|
177
235
|
@stable
|
|
178
236
|
@dataclass
|
|
179
237
|
class SessionConfig:
|
|
@@ -323,6 +381,7 @@ __stability__ = "stable"
|
|
|
323
381
|
|
|
324
382
|
__all__ = [
|
|
325
383
|
"RetryConfig",
|
|
384
|
+
"StructuredOutputConfig",
|
|
326
385
|
"ToolConfig",
|
|
327
386
|
"CoherenceConfig",
|
|
328
387
|
"GuardrailsConfig",
|