selectools 1.0.0__tar.gz → 1.1.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.0/src/selectools.egg-info → selectools-1.1.0}/PKG-INFO +3 -1
- {selectools-1.0.0 → selectools-1.1.0}/README.md +2 -0
- {selectools-1.0.0 → selectools-1.1.0}/pyproject.toml +4 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/__init__.py +2 -1
- selectools-1.1.0/src/selectools/_time.py +42 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/agent/_lifecycle.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/agent/_memory_manager.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/agent/_provider_caller.py +76 -26
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/agent/_tool_executor.py +2 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/agent/config.py +9 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/agent/config_groups.py +29 -5
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/agent/core.py +316 -105
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/audit.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/cache.py +4 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/cache_semantic.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/checkpoint_postgres.py +3 -5
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/compose.py +1 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/embeddings/anthropic.py +0 -10
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/embeddings/cohere.py +0 -10
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/embeddings/gemini.py +0 -10
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/embeddings/openai.py +0 -10
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/embeddings/provider.py +6 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/history.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/pairwise.py +1 -5
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/serve.py +1 -5
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/templates.py +1 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/guardrails/pipeline.py +68 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/knowledge.py +7 -7
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/knowledge_store_redis.py +3 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/knowledge_store_supabase.py +3 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/mcp/bridge.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/mcp/client.py +1 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/mcp/config.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/mcp/multi.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/mcp/server.py +1 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/observe/langfuse.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/observe/otel.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/observe/trace_store.py +4 -6
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/observer.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/orchestration/checkpoint.py +4 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/orchestration/graph.py +2 -5
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/orchestration/node.py +1 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/orchestration/state.py +3 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/orchestration/supervisor.py +3 -7
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/patterns/debate.py +2 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/patterns/plan_and_execute.py +2 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/patterns/reflective.py +2 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/patterns/team_lead.py +2 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/_openai_compat.py +46 -9
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/anthropic_provider.py +131 -109
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/azure_openai_provider.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/fallback.py +1 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/gemini_provider.py +103 -127
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/ollama_provider.py +1 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/openai_provider.py +2 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/bm25.py +0 -6
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/hybrid.py +0 -16
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/loaders.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/stores/faiss.py +2 -2
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/stores/memory.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/stores/pinecone.py +4 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/stores/qdrant.py +1 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/scheduler.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/app.py +1 -3
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/cli.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/models.py +0 -25
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/sessions.py +39 -110
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/structured.py +19 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/datetime_tools.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/tools/base.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/trace.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/unified_memory.py +2 -3
- {selectools-1.0.0 → selectools-1.1.0/src/selectools.egg-info}/PKG-INFO +3 -1
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools.egg-info/SOURCES.txt +2 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_bug_hunt_batch1_core.py +0 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_bug_hunt_batch1_security.py +0 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_bug_hunt_batch1_tools.py +1 -5
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_bug_hunt_regression.py +1 -10
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_checkpoint_postgres.py +3 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_coherence.py +1 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_concurrency_smoke.py +1 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_consolidation_regression.py +2 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_coverage_orchestration.py +7 -14
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_document_loaders_extended.py +0 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_e2e_multimodal.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_entity_memory.py +1 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals.py +0 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_advanced.py +2 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_cli.py +5 -7
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_e2e.py +3 -11
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_final.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_hardening.py +0 -8
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_html_report.py +0 -6
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_new_evaluators.py +2 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_ralph_bugs.py +2 -7
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_release.py +1 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_serve.py +0 -5
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_v017_features.py +1 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_evals_v0191.py +1 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_features_in_graph.py +7 -12
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_final_coverage_a.py +1 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_final_coverage_b.py +10 -14
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_guardrails.py +100 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_hardening.py +1 -7
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_knowledge.py +1 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_knowledge_backend.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_knowledge_backend_redis.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_knowledge_backend_supabase.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_knowledge_graph.py +1 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_knowledge_stores.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_knowledge_stores_error_handling.py +0 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_mcp.py +2 -5
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_mcp_coverage.py +1 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_memory_boundary.py +0 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_memory_integration.py +0 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_multi_agent_edge_cases.py +2 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_multimodal.py +0 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_orchestration_checkpoint.py +4 -5
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_orchestration_evals.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_orchestration_graph.py +3 -9
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_orchestration_integration.py +0 -9
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_orchestration_supervisor.py +5 -6
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_patterns.py +5 -11
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_phase1_design_patterns.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_pipeline.py +0 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_pipeline_coverage.py +1 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_prompt_compression.py +0 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_property_based.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_remaining_coverage.py +4 -9
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_results.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_security.py +1 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_semantic_cache.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_serve_app_coverage.py +7 -11
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_serve_cli.py +0 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_sessions_dynamodb.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_sessions_edge_cases.py +1 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_sessions_redis.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_sessions_supabase.py +4 -5
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_starlette_app.py +0 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_structured_config.py +0 -10
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_structured_tool_results.py +1 -1
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_summarize_on_trim.py +2 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_terminal_actions.py +1 -2
- selectools-1.1.0/tests/test_time.py +66 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_token_estimation.py +0 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_trace.py +0 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_trace_html.py +0 -4
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_trace_store.py +1 -5
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_v016_regression.py +0 -2
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_v019_features.py +2 -3
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_visual_builder.py +3 -17
- {selectools-1.0.0 → selectools-1.1.0}/LICENSE +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/setup.cfg +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/_async_utils.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/_json_utils.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/_ssrf.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/a2a/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/a2a/client.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/a2a/server.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/a2a/types.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/agent/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/agent/_planning.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/analytics.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/cache_redis.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/cancellation.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/coherence.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/embeddings/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/entity_memory.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/env.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/__main__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/badge.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/dataset.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/evaluators.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/generator.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/html.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/junit.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/llm_evaluators.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/regression.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/report.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/snapshot.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/suite.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/evals/types.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/exceptions.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/guardrails/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/guardrails/base.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/guardrails/format.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/guardrails/injection.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/guardrails/length.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/guardrails/pii.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/guardrails/topic.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/guardrails/toxicity.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/knowledge_backends.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/knowledge_graph.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/knowledge_sanitizers.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/loop_detection.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/mcp/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/mcp/_loop.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/memory.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/models.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/observe/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/orchestration/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/parser.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/patterns/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/pending.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/pipeline.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/policy.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/pricing.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/prompt.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/base.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/litellm_provider.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/router.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/providers/stubs.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/chunking.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/reranker.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/stores/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/stores/chroma.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/stores/pgvector.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/stores/sqlite.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/rag/vector_store.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/results.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/security.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/_starlette_app.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/_static/builder.css +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/_static/builder.html +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/_static/builder.js +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/api.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/builder.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/serve/playground.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/stability.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/templates/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/templates/code_reviewer.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/templates/customer_support.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/templates/data_analyst.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/templates/rag_chatbot.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/templates/research_assistant.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/token_estimation.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/browser_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/calculator_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/code_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/data_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/db_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/discord_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/email_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/file_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/github_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/image_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/linear_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/memory_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/notion_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/pdf_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/reasoning_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/s3_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/search_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/slack_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/text_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/toolbox/web_tools.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/tools/__init__.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/tools/decorators.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/tools/loader.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/tools/registry.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/types.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools/usage.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools.egg-info/dependency_links.txt +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools.egg-info/entry_points.txt +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools.egg-info/requires.txt +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/src/selectools.egg-info/top_level.txt +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_a2a.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_agent_api.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_agent_hitl.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_approval_gate.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_architecture.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_async_observers.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_audit.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_bake_window_hunt.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_budget.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_cache.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_cache_redis.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_cancellation.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_conversation_branching.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_e2e_langfuse_observer.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_e2e_otel_observer.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_e2e_v0_21_0_apps.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_e2e_v0_21_0_simulations.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_env.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_guardrails_injection.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_knowledge_sanitizers.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_knowledge_store_redis.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_knowledge_store_supabase.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_langfuse_observer.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_loop_detection.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_memory.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_memory_async.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_model_switching.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_orchestration_e2e.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_orchestration_primitives.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_otel_observer.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_parser.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_pending.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_pending_redis_smoke.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_policy.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_prompt.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_reasoning_strategy.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_routing_mode.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_scheduler.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_sessions.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_sessions_mongo.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_simple_observer.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_simulation_evals.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_ssrf.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_stability.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_structured.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_templates_coverage.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_tool_caching.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_tool_result_compression.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_unified_memory.py +0 -0
- {selectools-1.0.0 → selectools-1.1.0}/tests/test_unified_memory_config.py +0 -0
- {selectools-1.0.0 → selectools-1.1.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.
|
|
3
|
+
Version: 1.1.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>
|
|
@@ -103,6 +103,8 @@ An open-source project from **[NichevLabs](https://nichevlabs.com)**.
|
|
|
103
103
|
|
|
104
104
|
**Multi-agent orchestration in plain Python.** Build agent graphs, compose pipelines with `|`, deploy with one command. No DSL, no compile step, no paid debugger. Works with OpenAI, Anthropic, Gemini, and Ollama.
|
|
105
105
|
|
|
106
|
+
> 🎉 **selectools is 1.0 — stable.** The public API is frozen; `@stable` symbols carry a 2-minor compatibility promise. Python 3.10+. See [What's New in v1.0](#whats-new-in-v10).
|
|
107
|
+
|
|
106
108
|
### 3 Ways to Build
|
|
107
109
|
|
|
108
110
|
```python
|
|
@@ -16,6 +16,8 @@ An open-source project from **[NichevLabs](https://nichevlabs.com)**.
|
|
|
16
16
|
|
|
17
17
|
**Multi-agent orchestration in plain Python.** Build agent graphs, compose pipelines with `|`, deploy with one command. No DSL, no compile step, no paid debugger. Works with OpenAI, Anthropic, Gemini, and Ollama.
|
|
18
18
|
|
|
19
|
+
> 🎉 **selectools is 1.0 — stable.** The public API is frozen; `@stable` symbols carry a 2-minor compatibility promise. Python 3.10+. See [What's New in v1.0](#whats-new-in-v10).
|
|
20
|
+
|
|
19
21
|
### 3 Ways to Build
|
|
20
22
|
|
|
21
23
|
```python
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "selectools"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.1.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"
|
|
@@ -154,15 +154,16 @@ ignore = [
|
|
|
154
154
|
"E501", # line too long (handled by formatter)
|
|
155
155
|
"E402", # module-level import not at top
|
|
156
156
|
"E721", # type comparison with == (allowed; legacy test pattern)
|
|
157
|
-
"F401", # unused imports (common in __init__.py re-exports)
|
|
158
157
|
"F541", # f-string without placeholder
|
|
159
|
-
"F841", # local variable assigned but never used
|
|
160
158
|
"B011", # do not call assert False
|
|
161
159
|
"B023", # function definition does not bind loop variable
|
|
162
160
|
"B904", # raise without `from` in except (TODO: fix pre-existing violations)
|
|
163
161
|
]
|
|
164
162
|
|
|
165
163
|
[tool.ruff.lint.per-file-ignores]
|
|
164
|
+
# F401 unused-import is expected in __init__.py re-export hubs (the public-API
|
|
165
|
+
# surface). Everywhere else, unused imports and unused locals are real findings.
|
|
166
|
+
"**/__init__.py" = ["F401"]
|
|
166
167
|
"tests/*" = ["B"]
|
|
167
168
|
"examples/*" = ["B", "F"]
|
|
168
169
|
"scripts/*" = ["B", "F"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Public exports for the selectools package."""
|
|
2
2
|
|
|
3
|
-
__version__ = "1.
|
|
3
|
+
__version__ = "1.1.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,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Internal time helpers.
|
|
2
|
+
|
|
3
|
+
Centralizes ISO-8601 timestamp parsing so every persistence backend handles a
|
|
4
|
+
trailing ``Z`` (UTC designator) the same way.
|
|
5
|
+
|
|
6
|
+
``datetime.fromisoformat`` did not accept the ``Z`` suffix until Python 3.11.
|
|
7
|
+
On Python 3.10 (the project's minimum) a ``Z``-suffixed timestamp such as
|
|
8
|
+
``"2026-06-15T12:00:00Z"`` raises ``ValueError``. Postgres/Supabase/Redis and
|
|
9
|
+
other stores routinely return that form, so parsing it inline crashed the
|
|
10
|
+
knowledge and checkpoint loaders on 3.10. ``parse_iso`` normalizes the suffix
|
|
11
|
+
before delegating, so the same code works on every supported interpreter.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from datetime import datetime, timezone
|
|
17
|
+
from typing import Union
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def parse_iso(value: Union[str, datetime]) -> datetime:
|
|
21
|
+
"""Parse an ISO-8601 timestamp, tolerating a trailing ``Z`` (UTC).
|
|
22
|
+
|
|
23
|
+
Accepts either a string or an already-parsed ``datetime`` (returned as-is,
|
|
24
|
+
so callers reading columns that may already be typed do not need their own
|
|
25
|
+
``isinstance`` guard). Timezone awareness is preserved exactly as encoded:
|
|
26
|
+
an offset-bearing string (including ``Z``) yields an aware datetime; a naive
|
|
27
|
+
string yields a naive datetime. Use :func:`ensure_aware` when a guaranteed
|
|
28
|
+
aware value is required.
|
|
29
|
+
"""
|
|
30
|
+
if isinstance(value, datetime):
|
|
31
|
+
return value
|
|
32
|
+
s = str(value)
|
|
33
|
+
if s.endswith("Z"):
|
|
34
|
+
s = s[:-1] + "+00:00"
|
|
35
|
+
return datetime.fromisoformat(s)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def ensure_aware(dt: datetime, *, tz: timezone = timezone.utc) -> datetime:
|
|
39
|
+
"""Attach ``tz`` (default UTC) to a naive datetime; pass aware ones through."""
|
|
40
|
+
if dt.tzinfo is None:
|
|
41
|
+
return dt.replace(tzinfo=tz)
|
|
42
|
+
return dt
|
|
@@ -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:
|
|
@@ -57,8 +57,8 @@ def _get_parallel_dispatch_executor() -> ThreadPoolExecutor:
|
|
|
57
57
|
|
|
58
58
|
|
|
59
59
|
from .._async_utils import run_in_executor_copyctx
|
|
60
|
-
from ..coherence import
|
|
61
|
-
from ..policy import ApprovalRequest, PolicyDecision, PolicyResult
|
|
60
|
+
from ..coherence import acheck_coherence, check_coherence
|
|
61
|
+
from ..policy import ApprovalRequest, PolicyDecision, PolicyResult
|
|
62
62
|
from ..security import screen_output as screen_tool_output
|
|
63
63
|
from ..trace import StepType, TraceStep
|
|
64
64
|
from ..types import Message, Role
|
|
@@ -7,7 +7,7 @@ from __future__ import annotations
|
|
|
7
7
|
from dataclasses import dataclass, field
|
|
8
8
|
from typing import TYPE_CHECKING, Any, Callable, Coroutine, Dict, List, Optional, Union
|
|
9
9
|
|
|
10
|
-
from ..stability import
|
|
10
|
+
from ..stability import stable
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from ..cache import Cache
|
|
@@ -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,23 +24,20 @@ Usage::
|
|
|
24
24
|
from __future__ import annotations
|
|
25
25
|
|
|
26
26
|
from dataclasses import dataclass, field
|
|
27
|
-
from typing import TYPE_CHECKING, Any,
|
|
27
|
+
from typing import TYPE_CHECKING, Any, 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
|
-
from ..cache import Cache
|
|
33
32
|
from ..cancellation import CancellationToken
|
|
34
33
|
from ..entity_memory import EntityMemory
|
|
35
34
|
from ..guardrails import GuardrailsPipeline
|
|
36
35
|
from ..knowledge import KnowledgeMemory
|
|
37
36
|
from ..knowledge_graph import KnowledgeGraphMemory
|
|
38
|
-
from ..observer import AgentObserver
|
|
39
37
|
from ..policy import ToolPolicy
|
|
40
38
|
from ..providers.base import Provider
|
|
41
39
|
from ..sessions import SessionStore
|
|
42
40
|
from ..unified_memory import UnifiedMemory
|
|
43
|
-
from ..usage import AgentUsage
|
|
44
41
|
|
|
45
42
|
|
|
46
43
|
@stable
|
|
@@ -177,6 +174,32 @@ class GuardrailsConfig:
|
|
|
177
174
|
output_screening_patterns: Optional[List[str]] = None
|
|
178
175
|
|
|
179
176
|
|
|
177
|
+
@beta
|
|
178
|
+
@dataclass
|
|
179
|
+
class StructuredOutputConfig:
|
|
180
|
+
"""Structured-output (``response_format``) behavior settings (beta, v1.1).
|
|
181
|
+
|
|
182
|
+
Attributes:
|
|
183
|
+
native: Use the provider's native structured-output mode (OpenAI
|
|
184
|
+
``json_schema``, Gemini ``response_json_schema``) when the provider
|
|
185
|
+
advertises support, instead of injecting a schema instruction into
|
|
186
|
+
the system prompt. Providers without native support keep the
|
|
187
|
+
prompt-injection + parse behavior unchanged. Default: True.
|
|
188
|
+
final_turn_only: Keep the schema out of tool-loop turns entirely.
|
|
189
|
+
The loop runs to convergence with no schema pressure (so tool
|
|
190
|
+
calling and the text ``ToolCallParser`` work normally), then one
|
|
191
|
+
extra synthesis call — with the schema applied and no tools —
|
|
192
|
+
produces the validated structured final answer. In ``astream()``
|
|
193
|
+
the synthesis JSON is delivered only via the terminal
|
|
194
|
+
``AgentResult`` (``.content`` / ``.parsed``), never leaked as
|
|
195
|
+
content chunks. Costs one extra provider call per run.
|
|
196
|
+
Default: False.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
native: bool = True
|
|
200
|
+
final_turn_only: bool = False
|
|
201
|
+
|
|
202
|
+
|
|
180
203
|
@stable
|
|
181
204
|
@dataclass
|
|
182
205
|
class SessionConfig:
|
|
@@ -326,6 +349,7 @@ __stability__ = "stable"
|
|
|
326
349
|
|
|
327
350
|
__all__ = [
|
|
328
351
|
"RetryConfig",
|
|
352
|
+
"StructuredOutputConfig",
|
|
329
353
|
"ToolConfig",
|
|
330
354
|
"CoherenceConfig",
|
|
331
355
|
"GuardrailsConfig",
|