selectools 0.23.0__tar.gz → 0.24.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {selectools-0.23.0/src/selectools.egg-info → selectools-0.24.0}/PKG-INFO +59 -8
- {selectools-0.23.0 → selectools-0.24.0}/README.md +51 -7
- {selectools-0.23.0 → selectools-0.24.0}/pyproject.toml +10 -1
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/__init__.py +67 -1
- selectools-0.24.0/src/selectools/a2a/__init__.py +54 -0
- selectools-0.24.0/src/selectools/a2a/client.py +174 -0
- selectools-0.24.0/src/selectools/a2a/server.py +406 -0
- selectools-0.24.0/src/selectools/a2a/types.py +169 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/agent/_tool_executor.py +13 -2
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/agent/core.py +9 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/entity_memory.py +4 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/knowledge.py +174 -2
- selectools-0.24.0/src/selectools/knowledge_backends.py +220 -0
- selectools-0.24.0/src/selectools/pending.py +1123 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/providers/__init__.py +5 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/providers/anthropic_provider.py +70 -8
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/providers/gemini_provider.py +105 -14
- selectools-0.24.0/src/selectools/providers/litellm_provider.py +192 -0
- selectools-0.24.0/src/selectools/providers/router.py +610 -0
- selectools-0.24.0/src/selectools/results.py +224 -0
- selectools-0.24.0/src/selectools/serve/__init__.py +54 -0
- selectools-0.24.0/src/selectools/serve/api.py +552 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/serve/cli.py +34 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/sessions.py +512 -3
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/__init__.py +75 -1
- selectools-0.24.0/src/selectools/toolbox/calculator_tools.py +332 -0
- selectools-0.24.0/src/selectools/toolbox/email_tools.py +245 -0
- selectools-0.24.0/src/selectools/toolbox/linear_tools.py +286 -0
- selectools-0.24.0/src/selectools/toolbox/notion_tools.py +259 -0
- selectools-0.24.0/src/selectools/toolbox/pdf_tools.py +155 -0
- selectools-0.24.0/src/selectools/toolbox/slack_tools.py +201 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/tools/base.py +8 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/types.py +3 -0
- selectools-0.24.0/src/selectools/unified_memory.py +926 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/usage.py +8 -0
- {selectools-0.23.0 → selectools-0.24.0/src/selectools.egg-info}/PKG-INFO +59 -8
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools.egg-info/SOURCES.txt +25 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools.egg-info/requires.txt +9 -0
- selectools-0.24.0/tests/test_a2a.py +455 -0
- selectools-0.24.0/tests/test_agent_api.py +430 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_entity_memory.py +9 -0
- selectools-0.24.0/tests/test_knowledge_backend.py +255 -0
- selectools-0.24.0/tests/test_knowledge_backend_redis.py +171 -0
- selectools-0.24.0/tests/test_knowledge_backend_supabase.py +231 -0
- selectools-0.24.0/tests/test_pending.py +1024 -0
- selectools-0.24.0/tests/test_results.py +366 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_serve_cli.py +39 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_sessions.py +334 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_sessions_redis.py +68 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_sessions_supabase.py +114 -4
- selectools-0.24.0/tests/test_unified_memory.py +427 -0
- selectools-0.23.0/src/selectools/serve/__init__.py +0 -24
- {selectools-0.23.0 → selectools-0.24.0}/LICENSE +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/setup.cfg +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/_async_utils.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/agent/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/agent/_lifecycle.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/agent/_memory_manager.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/agent/_provider_caller.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/agent/config.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/agent/config_groups.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/analytics.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/audit.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/cache.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/cache_redis.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/cache_semantic.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/cancellation.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/checkpoint_postgres.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/coherence.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/compose.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/embeddings/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/embeddings/anthropic.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/embeddings/cohere.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/embeddings/gemini.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/embeddings/openai.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/embeddings/provider.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/env.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/__main__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/badge.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/dataset.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/evaluators.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/generator.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/history.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/html.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/junit.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/llm_evaluators.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/pairwise.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/regression.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/report.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/serve.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/snapshot.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/suite.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/templates.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/evals/types.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/exceptions.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/guardrails/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/guardrails/base.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/guardrails/format.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/guardrails/length.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/guardrails/pii.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/guardrails/pipeline.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/guardrails/topic.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/guardrails/toxicity.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/knowledge_graph.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/knowledge_store_redis.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/knowledge_store_supabase.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/loop_detection.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/mcp/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/mcp/_loop.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/mcp/bridge.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/mcp/client.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/mcp/config.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/mcp/multi.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/mcp/server.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/memory.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/models.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/observe/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/observe/langfuse.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/observe/otel.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/observe/trace_store.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/observer.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/orchestration/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/orchestration/checkpoint.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/orchestration/graph.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/orchestration/node.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/orchestration/state.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/orchestration/supervisor.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/parser.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/patterns/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/patterns/debate.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/patterns/plan_and_execute.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/patterns/reflective.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/patterns/team_lead.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/pipeline.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/policy.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/pricing.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/prompt.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/providers/_openai_compat.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/providers/azure_openai_provider.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/providers/base.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/providers/fallback.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/providers/ollama_provider.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/providers/openai_provider.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/providers/stubs.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/bm25.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/chunking.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/hybrid.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/loaders.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/reranker.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/stores/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/stores/chroma.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/stores/faiss.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/stores/memory.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/stores/pgvector.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/stores/pinecone.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/stores/qdrant.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/stores/sqlite.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/rag/vector_store.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/security.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/serve/_starlette_app.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/serve/_static/builder.css +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/serve/_static/builder.html +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/serve/_static/builder.js +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/serve/app.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/serve/builder.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/serve/models.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/serve/playground.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/stability.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/structured.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/templates/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/templates/code_reviewer.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/templates/customer_support.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/templates/data_analyst.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/templates/rag_chatbot.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/templates/research_assistant.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/token_estimation.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/code_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/data_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/datetime_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/db_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/file_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/github_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/memory_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/search_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/text_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/toolbox/web_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/tools/__init__.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/tools/decorators.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/tools/loader.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/tools/registry.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools/trace.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools.egg-info/dependency_links.txt +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools.egg-info/entry_points.txt +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/src/selectools.egg-info/top_level.txt +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_approval_gate.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_architecture.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_async_observers.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_audit.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_budget.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_bug_hunt_batch1_core.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_bug_hunt_batch1_security.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_bug_hunt_batch1_tools.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_bug_hunt_regression.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_cache.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_cache_redis.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_cancellation.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_checkpoint_postgres.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_coherence.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_concurrency_smoke.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_consolidation_regression.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_conversation_branching.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_coverage_orchestration.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_document_loaders_extended.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_e2e_langfuse_observer.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_e2e_multimodal.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_e2e_otel_observer.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_e2e_v0_21_0_apps.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_e2e_v0_21_0_simulations.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_env.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_advanced.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_cli.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_e2e.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_final.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_hardening.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_html_report.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_new_evaluators.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_ralph_bugs.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_release.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_serve.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_v017_features.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_evals_v0191.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_features_in_graph.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_final_coverage_a.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_final_coverage_b.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_guardrails.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_hardening.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_knowledge.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_knowledge_graph.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_knowledge_store_redis.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_knowledge_store_supabase.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_knowledge_stores.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_knowledge_stores_error_handling.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_langfuse_observer.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_loop_detection.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_mcp.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_mcp_coverage.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_memory.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_memory_async.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_memory_boundary.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_memory_integration.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_model_switching.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_multi_agent_edge_cases.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_multimodal.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_orchestration_checkpoint.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_orchestration_e2e.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_orchestration_evals.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_orchestration_graph.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_orchestration_integration.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_orchestration_primitives.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_orchestration_supervisor.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_otel_observer.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_parser.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_patterns.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_phase1_design_patterns.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_pipeline.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_pipeline_coverage.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_policy.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_prompt.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_prompt_compression.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_property_based.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_reasoning_strategy.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_remaining_coverage.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_routing_mode.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_security.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_semantic_cache.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_serve_app_coverage.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_sessions_edge_cases.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_simple_observer.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_simulation_evals.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_stability.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_starlette_app.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_structured.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_structured_config.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_structured_tool_results.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_summarize_on_trim.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_templates_coverage.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_terminal_actions.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_token_estimation.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_tool_caching.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_trace.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_trace_html.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_trace_store.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_v016_regression.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_v019_features.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_visual_builder.py +0 -0
- {selectools-0.23.0 → selectools-0.24.0}/tests/test_yaml_config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: selectools
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24.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>
|
|
@@ -55,10 +55,16 @@ Requires-Dist: beautifulsoup4>=4.12.0; extra == "rag"
|
|
|
55
55
|
Provides-Extra: observe
|
|
56
56
|
Requires-Dist: opentelemetry-api>=1.20.0; extra == "observe"
|
|
57
57
|
Requires-Dist: langfuse>=2.0.0; extra == "observe"
|
|
58
|
+
Provides-Extra: toolbox
|
|
59
|
+
Requires-Dist: requests>=2.28.0; extra == "toolbox"
|
|
60
|
+
Requires-Dist: slack-sdk>=3.27.0; extra == "toolbox"
|
|
61
|
+
Requires-Dist: pdfplumber>=0.11.0; extra == "toolbox"
|
|
58
62
|
Provides-Extra: evals
|
|
59
63
|
Requires-Dist: pyyaml>=6.0.0; extra == "evals"
|
|
60
64
|
Provides-Extra: mcp
|
|
61
65
|
Requires-Dist: mcp<2.0.0,>=1.0.0; extra == "mcp"
|
|
66
|
+
Provides-Extra: litellm
|
|
67
|
+
Requires-Dist: litellm>=1.0.0; extra == "litellm"
|
|
62
68
|
Provides-Extra: postgres
|
|
63
69
|
Requires-Dist: psycopg2-binary>=2.9.0; extra == "postgres"
|
|
64
70
|
Provides-Extra: supabase
|
|
@@ -67,6 +73,7 @@ Provides-Extra: serve
|
|
|
67
73
|
Requires-Dist: pyyaml>=6.0.0; extra == "serve"
|
|
68
74
|
Requires-Dist: starlette>=0.27.0; extra == "serve"
|
|
69
75
|
Requires-Dist: uvicorn[standard]>=0.24.0; extra == "serve"
|
|
76
|
+
Requires-Dist: httpx>=0.24.0; extra == "serve"
|
|
70
77
|
Dynamic: license-file
|
|
71
78
|
|
|
72
79
|
```
|
|
@@ -101,6 +108,33 @@ result = AgentGraph.chain(planner, writer, reviewer).run("Write a blog post")
|
|
|
101
108
|
# selectools serve agent.yaml
|
|
102
109
|
```
|
|
103
110
|
|
|
111
|
+
## What's New in v0.24
|
|
112
|
+
|
|
113
|
+
### v0.24.0 — Production Interop
|
|
114
|
+
|
|
115
|
+
Twelve features focused on running agents in production and connecting them to everything else: serve agents as REST APIs, talk to other agents over A2A, reach 100+ models through LiteLLM, route by cost, and persist memory anywhere.
|
|
116
|
+
|
|
117
|
+
- **Agent-as-API** — `selectools.serve.AgentAPI` turns any Agent (or list of agents) into a production Starlette ASGI app: `POST /v1/chat`, SSE streaming, session CRUD, bearer auth, per-user session isolation. CLI: `selectools serve agent.yaml --api --port 8000`.
|
|
118
|
+
- **A2A protocol** — `A2AServer` + `A2AClient` for agent-to-agent communication: Agent Card discovery (`/.well-known/agent.json`) and JSON-RPC 2.0 `message/send`/`tasks/get`/`tasks/cancel`.
|
|
119
|
+
- **LiteLLMProvider** — instant access to 100+ models (DeepSeek, Groq, Bedrock, ...) via `pip install selectools[litellm]`.
|
|
120
|
+
- **RouterProvider** — cost-optimized routing across model tiers with a deterministic complexity classifier and failure escalation.
|
|
121
|
+
- **Anthropic prompt caching** — opt-in `cache_system=True` / `cache_tools=True` with cache hit-rate fields on `UsageStats`.
|
|
122
|
+
- **UnifiedMemory** — tiered memory orchestrating conversation, knowledge, entity, and the new episodic tier, with token-aware compaction and federated `recall()`.
|
|
123
|
+
- **Cross-session search** — `store.search(query)` on all four SessionStore backends (FTS5-accelerated on SQLite).
|
|
124
|
+
- **KnowledgeBackend** — Supabase/Redis blob persistence for `KnowledgeMemory` on ephemeral infrastructure (Railway, Lambda, Cloud Run).
|
|
125
|
+
- **ToolResult + Artifact** — typed tool results with a `kind` discriminator, plus an `emit_artifact()` side-channel surfaced on `AgentResult.artifacts`.
|
|
126
|
+
- **Deferred confirmation flow** — `selectools.pending` for chat-channel destructive tools where the user's "yes" arrives as a separate webhook turn.
|
|
127
|
+
- **Toolbox expansion** — 15 new tools (33 → 48): safe calculator, email, PDF extraction, Slack, Notion, Linear.
|
|
128
|
+
- **Gemini schema sanitization** — bare `list` and `Dict[K, V]` tool parameters no longer 400 on the Gemini API; loud warnings for flash-lite's silent-empty-response failure mode.
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from selectools.serve import AgentAPI
|
|
132
|
+
|
|
133
|
+
api = AgentAPI(agent, auth_key="secret") # ASGI app: uvicorn main:api
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
See `CHANGELOG.md` for the full entry (5,968 tests, 106 examples).
|
|
137
|
+
|
|
104
138
|
## What's New in v0.23
|
|
105
139
|
|
|
106
140
|
### v0.23.0 — Supabase Sessions + Builder RAG
|
|
@@ -570,7 +604,15 @@ report.to_html("report.html")
|
|
|
570
604
|
| **Audit Logging** | JSONL audit trail with privacy controls (redact, hash, omit) and daily rotation. |
|
|
571
605
|
| **Tool Output Screening** | Prompt injection detection with 15 built-in patterns. Per-tool or global. |
|
|
572
606
|
| **Coherence Checking** | LLM-based verification that tool calls match user intent — catches injection-driven tool misuse. |
|
|
573
|
-
| **Persistent Sessions** | `SessionStore` with JSON file, SQLite, and
|
|
607
|
+
| **Persistent Sessions** | `SessionStore` with JSON file, SQLite, Redis, and Supabase backends. Auto-save/load with TTL expiry, cross-session `search()`. |
|
|
608
|
+
| **Agent-as-API** | `AgentAPI` serves any agent as a Starlette REST API — chat, SSE streaming, session CRUD, bearer auth, per-user session isolation. |
|
|
609
|
+
| **A2A Protocol** | `A2AServer` + `A2AClient`: Agent Card discovery and JSON-RPC 2.0 messaging between agents. |
|
|
610
|
+
| **LiteLLM Bridge** | `LiteLLMProvider` unlocks 100+ models (DeepSeek, Groq, Bedrock, ...) through one provider class. |
|
|
611
|
+
| **Cost-Based Routing** | `RouterProvider` classifies request complexity and routes to model tiers with automatic failure escalation. |
|
|
612
|
+
| **Unified Memory** | `UnifiedMemory` orchestrates conversation, knowledge, entity, and episodic tiers with token-aware compaction and federated `recall()`. |
|
|
613
|
+
| **Typed Tool Results** | `ToolResult` base with `kind` discriminator; `emit_artifact()` side-channel surfaces files/URLs on `AgentResult.artifacts`. |
|
|
614
|
+
| **Deferred Confirmation** | `selectools.pending` — confirm destructive chat-channel tools across webhook turns with TTL, scope, and args-digest matching. |
|
|
615
|
+
| **Prompt Caching** | Anthropic `cache_system`/`cache_tools` markers with cache hit-rate fields on `UsageStats`. |
|
|
574
616
|
| **Entity Memory** | LLM-based entity extraction with deduplication, LRU pruning, and system prompt injection. |
|
|
575
617
|
| **Knowledge Graph** | Relationship triple extraction with in-memory and SQLite storage and keyword-based querying. |
|
|
576
618
|
| **Cross-Session Knowledge** | Daily logs + persistent facts with auto-registered `remember` tool. |
|
|
@@ -585,7 +627,9 @@ report.to_html("report.html")
|
|
|
585
627
|
|
|
586
628
|
## What's Included
|
|
587
629
|
|
|
588
|
-
- **
|
|
630
|
+
- **6 LLM Providers**: OpenAI, Azure OpenAI, Anthropic, Gemini, Ollama, LiteLLM (100+ models) + FallbackProvider (auto-failover) + RouterProvider (cost-based routing)
|
|
631
|
+
- **Agent-as-API**: `AgentAPI` — production REST endpoints (chat, SSE streaming, sessions) from any agent
|
|
632
|
+
- **A2A Protocol**: Agent Card discovery + JSON-RPC 2.0 agent-to-agent messaging
|
|
589
633
|
- **Structured Output**: Pydantic / JSON Schema `response_format` with auto-retry
|
|
590
634
|
- **Execution Traces**: `result.trace` with typed timeline of every agent step
|
|
591
635
|
- **Reasoning Visibility**: `result.reasoning` explains *why* the agent chose a tool
|
|
@@ -598,9 +642,14 @@ report.to_html("report.html")
|
|
|
598
642
|
- **Dynamic Tool Loading**: Plugin system with hot-reload support
|
|
599
643
|
- **Response Caching**: InMemoryCache and RedisCache with stats tracking
|
|
600
644
|
- **152 Model Registry**: Type-safe constants with pricing and metadata
|
|
601
|
-
- **Pre-built Toolbox**:
|
|
602
|
-
- **Persistent Sessions**:
|
|
645
|
+
- **Pre-built Toolbox**: 48 tools for files, data, text, datetime, web, code, search, GitHub, DB, calculator, email, PDF, Slack, Notion, Linear
|
|
646
|
+
- **Persistent Sessions**: 4 backends (JSON file, SQLite, Redis, Supabase) with TTL and cross-session search
|
|
603
647
|
- **Entity Memory**: LLM-based named entity extraction and tracking
|
|
648
|
+
- **Unified Memory**: tiered conversation/knowledge/entity/episodic memory with token-aware compaction
|
|
649
|
+
- **Knowledge Backends**: Supabase/Redis blob persistence for KnowledgeMemory on ephemeral infra
|
|
650
|
+
- **Typed Tool Results**: `ToolResult` base class + `Artifact` side-channel via `emit_artifact()`
|
|
651
|
+
- **Deferred Confirmation**: `selectools.pending` for chat-channel destructive-tool confirmation
|
|
652
|
+
- **Anthropic Prompt Caching**: `cache_system`/`cache_tools` with hit-rate visibility on `UsageStats`
|
|
604
653
|
- **Knowledge Graph**: Triple extraction with in-memory and SQLite storage
|
|
605
654
|
- **Cross-Session Knowledge**: Daily logs + persistent memory with `remember` tool, pluggable stores (File, SQLite), importance scoring, TTL
|
|
606
655
|
- **Token Budget & Cancellation**: `max_total_tokens`, `max_cost_usd` hard limits; `CancellationToken` for cooperative stopping
|
|
@@ -611,10 +660,10 @@ report.to_html("report.html")
|
|
|
611
660
|
- **Conversation Branching**: `ConversationMemory.branch()` and `SessionStore.branch()` for A/B exploration and checkpointing
|
|
612
661
|
- **Multi-Agent Orchestration**: `AgentGraph` with routing, parallel execution, HITL, checkpointing; `SupervisorAgent` with 4 strategies (plan_and_execute, round_robin, dynamic, magentic)
|
|
613
662
|
- **Composable Pipelines**: `Pipeline` + `@step` + `|` operator + `parallel()` + `branch()` — chain agents, tools, and transforms
|
|
614
|
-
- **
|
|
663
|
+
- **106 Examples**: Multi-agent graphs, RAG, hybrid search, streaming, structured output, traces, batch, policy, observer, guardrails, audit, sessions (incl. Supabase), entity memory, knowledge graph, eval framework, advanced agent patterns, stability markers, HTML trace viewer, agent-as-API, A2A, routing, unified memory, and more
|
|
615
664
|
- **Built-in Eval Framework**: 50 evaluators (30 deterministic + 21 LLM-as-judge), A/B testing, regression detection, HTML reports, JUnit XML, snapshot testing
|
|
616
665
|
- **AgentObserver Protocol**: 45 lifecycle events with `run_id` correlation, `LoggingObserver`, `SimpleStepObserver`, OTel export
|
|
617
|
-
- **
|
|
666
|
+
- **5968 Tests**: Unit, integration, regression, and E2E with real API calls
|
|
618
667
|
|
|
619
668
|
## Install
|
|
620
669
|
|
|
@@ -960,6 +1009,8 @@ See [docs/modules/STREAMING.md](https://github.com/johnnichev/selectools/blob/ma
|
|
|
960
1009
|
| **Anthropic** | Yes | Yes | Yes | Paid |
|
|
961
1010
|
| **Gemini** | Yes | Yes | Yes | Free tier |
|
|
962
1011
|
| **Ollama** | Yes | No | No | Free (local) |
|
|
1012
|
+
| **LiteLLM** | Yes | Yes | Yes | Varies (100+ models) |
|
|
1013
|
+
| **Router** | Yes | Yes | Yes | Varies (cost-routes tiers) |
|
|
963
1014
|
| **Fallback** | Yes | Yes | Yes | Varies (wraps others) |
|
|
964
1015
|
| **Local** | No | No | No | Free (testing) |
|
|
965
1016
|
|
|
@@ -1238,7 +1289,7 @@ pytest tests/ -x -q # All tests
|
|
|
1238
1289
|
pytest tests/ -k "not e2e" # Skip E2E (no API keys needed)
|
|
1239
1290
|
```
|
|
1240
1291
|
|
|
1241
|
-
|
|
1292
|
+
5968 tests covering parsing, agent loop, providers, RAG pipeline, hybrid search, advanced chunking, dynamic tools, caching, streaming, guardrails, sessions, memory, eval framework, budget/cancellation, knowledge stores, orchestration, pipelines, agent patterns, stability markers, trace viewer, serve API, A2A, routing, and E2E integration with real API calls.
|
|
1242
1293
|
|
|
1243
1294
|
## License
|
|
1244
1295
|
|
|
@@ -30,6 +30,33 @@ result = AgentGraph.chain(planner, writer, reviewer).run("Write a blog post")
|
|
|
30
30
|
# selectools serve agent.yaml
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
+
## What's New in v0.24
|
|
34
|
+
|
|
35
|
+
### v0.24.0 — Production Interop
|
|
36
|
+
|
|
37
|
+
Twelve features focused on running agents in production and connecting them to everything else: serve agents as REST APIs, talk to other agents over A2A, reach 100+ models through LiteLLM, route by cost, and persist memory anywhere.
|
|
38
|
+
|
|
39
|
+
- **Agent-as-API** — `selectools.serve.AgentAPI` turns any Agent (or list of agents) into a production Starlette ASGI app: `POST /v1/chat`, SSE streaming, session CRUD, bearer auth, per-user session isolation. CLI: `selectools serve agent.yaml --api --port 8000`.
|
|
40
|
+
- **A2A protocol** — `A2AServer` + `A2AClient` for agent-to-agent communication: Agent Card discovery (`/.well-known/agent.json`) and JSON-RPC 2.0 `message/send`/`tasks/get`/`tasks/cancel`.
|
|
41
|
+
- **LiteLLMProvider** — instant access to 100+ models (DeepSeek, Groq, Bedrock, ...) via `pip install selectools[litellm]`.
|
|
42
|
+
- **RouterProvider** — cost-optimized routing across model tiers with a deterministic complexity classifier and failure escalation.
|
|
43
|
+
- **Anthropic prompt caching** — opt-in `cache_system=True` / `cache_tools=True` with cache hit-rate fields on `UsageStats`.
|
|
44
|
+
- **UnifiedMemory** — tiered memory orchestrating conversation, knowledge, entity, and the new episodic tier, with token-aware compaction and federated `recall()`.
|
|
45
|
+
- **Cross-session search** — `store.search(query)` on all four SessionStore backends (FTS5-accelerated on SQLite).
|
|
46
|
+
- **KnowledgeBackend** — Supabase/Redis blob persistence for `KnowledgeMemory` on ephemeral infrastructure (Railway, Lambda, Cloud Run).
|
|
47
|
+
- **ToolResult + Artifact** — typed tool results with a `kind` discriminator, plus an `emit_artifact()` side-channel surfaced on `AgentResult.artifacts`.
|
|
48
|
+
- **Deferred confirmation flow** — `selectools.pending` for chat-channel destructive tools where the user's "yes" arrives as a separate webhook turn.
|
|
49
|
+
- **Toolbox expansion** — 15 new tools (33 → 48): safe calculator, email, PDF extraction, Slack, Notion, Linear.
|
|
50
|
+
- **Gemini schema sanitization** — bare `list` and `Dict[K, V]` tool parameters no longer 400 on the Gemini API; loud warnings for flash-lite's silent-empty-response failure mode.
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from selectools.serve import AgentAPI
|
|
54
|
+
|
|
55
|
+
api = AgentAPI(agent, auth_key="secret") # ASGI app: uvicorn main:api
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
See `CHANGELOG.md` for the full entry (5,968 tests, 106 examples).
|
|
59
|
+
|
|
33
60
|
## What's New in v0.23
|
|
34
61
|
|
|
35
62
|
### v0.23.0 — Supabase Sessions + Builder RAG
|
|
@@ -499,7 +526,15 @@ report.to_html("report.html")
|
|
|
499
526
|
| **Audit Logging** | JSONL audit trail with privacy controls (redact, hash, omit) and daily rotation. |
|
|
500
527
|
| **Tool Output Screening** | Prompt injection detection with 15 built-in patterns. Per-tool or global. |
|
|
501
528
|
| **Coherence Checking** | LLM-based verification that tool calls match user intent — catches injection-driven tool misuse. |
|
|
502
|
-
| **Persistent Sessions** | `SessionStore` with JSON file, SQLite, and
|
|
529
|
+
| **Persistent Sessions** | `SessionStore` with JSON file, SQLite, Redis, and Supabase backends. Auto-save/load with TTL expiry, cross-session `search()`. |
|
|
530
|
+
| **Agent-as-API** | `AgentAPI` serves any agent as a Starlette REST API — chat, SSE streaming, session CRUD, bearer auth, per-user session isolation. |
|
|
531
|
+
| **A2A Protocol** | `A2AServer` + `A2AClient`: Agent Card discovery and JSON-RPC 2.0 messaging between agents. |
|
|
532
|
+
| **LiteLLM Bridge** | `LiteLLMProvider` unlocks 100+ models (DeepSeek, Groq, Bedrock, ...) through one provider class. |
|
|
533
|
+
| **Cost-Based Routing** | `RouterProvider` classifies request complexity and routes to model tiers with automatic failure escalation. |
|
|
534
|
+
| **Unified Memory** | `UnifiedMemory` orchestrates conversation, knowledge, entity, and episodic tiers with token-aware compaction and federated `recall()`. |
|
|
535
|
+
| **Typed Tool Results** | `ToolResult` base with `kind` discriminator; `emit_artifact()` side-channel surfaces files/URLs on `AgentResult.artifacts`. |
|
|
536
|
+
| **Deferred Confirmation** | `selectools.pending` — confirm destructive chat-channel tools across webhook turns with TTL, scope, and args-digest matching. |
|
|
537
|
+
| **Prompt Caching** | Anthropic `cache_system`/`cache_tools` markers with cache hit-rate fields on `UsageStats`. |
|
|
503
538
|
| **Entity Memory** | LLM-based entity extraction with deduplication, LRU pruning, and system prompt injection. |
|
|
504
539
|
| **Knowledge Graph** | Relationship triple extraction with in-memory and SQLite storage and keyword-based querying. |
|
|
505
540
|
| **Cross-Session Knowledge** | Daily logs + persistent facts with auto-registered `remember` tool. |
|
|
@@ -514,7 +549,9 @@ report.to_html("report.html")
|
|
|
514
549
|
|
|
515
550
|
## What's Included
|
|
516
551
|
|
|
517
|
-
- **
|
|
552
|
+
- **6 LLM Providers**: OpenAI, Azure OpenAI, Anthropic, Gemini, Ollama, LiteLLM (100+ models) + FallbackProvider (auto-failover) + RouterProvider (cost-based routing)
|
|
553
|
+
- **Agent-as-API**: `AgentAPI` — production REST endpoints (chat, SSE streaming, sessions) from any agent
|
|
554
|
+
- **A2A Protocol**: Agent Card discovery + JSON-RPC 2.0 agent-to-agent messaging
|
|
518
555
|
- **Structured Output**: Pydantic / JSON Schema `response_format` with auto-retry
|
|
519
556
|
- **Execution Traces**: `result.trace` with typed timeline of every agent step
|
|
520
557
|
- **Reasoning Visibility**: `result.reasoning` explains *why* the agent chose a tool
|
|
@@ -527,9 +564,14 @@ report.to_html("report.html")
|
|
|
527
564
|
- **Dynamic Tool Loading**: Plugin system with hot-reload support
|
|
528
565
|
- **Response Caching**: InMemoryCache and RedisCache with stats tracking
|
|
529
566
|
- **152 Model Registry**: Type-safe constants with pricing and metadata
|
|
530
|
-
- **Pre-built Toolbox**:
|
|
531
|
-
- **Persistent Sessions**:
|
|
567
|
+
- **Pre-built Toolbox**: 48 tools for files, data, text, datetime, web, code, search, GitHub, DB, calculator, email, PDF, Slack, Notion, Linear
|
|
568
|
+
- **Persistent Sessions**: 4 backends (JSON file, SQLite, Redis, Supabase) with TTL and cross-session search
|
|
532
569
|
- **Entity Memory**: LLM-based named entity extraction and tracking
|
|
570
|
+
- **Unified Memory**: tiered conversation/knowledge/entity/episodic memory with token-aware compaction
|
|
571
|
+
- **Knowledge Backends**: Supabase/Redis blob persistence for KnowledgeMemory on ephemeral infra
|
|
572
|
+
- **Typed Tool Results**: `ToolResult` base class + `Artifact` side-channel via `emit_artifact()`
|
|
573
|
+
- **Deferred Confirmation**: `selectools.pending` for chat-channel destructive-tool confirmation
|
|
574
|
+
- **Anthropic Prompt Caching**: `cache_system`/`cache_tools` with hit-rate visibility on `UsageStats`
|
|
533
575
|
- **Knowledge Graph**: Triple extraction with in-memory and SQLite storage
|
|
534
576
|
- **Cross-Session Knowledge**: Daily logs + persistent memory with `remember` tool, pluggable stores (File, SQLite), importance scoring, TTL
|
|
535
577
|
- **Token Budget & Cancellation**: `max_total_tokens`, `max_cost_usd` hard limits; `CancellationToken` for cooperative stopping
|
|
@@ -540,10 +582,10 @@ report.to_html("report.html")
|
|
|
540
582
|
- **Conversation Branching**: `ConversationMemory.branch()` and `SessionStore.branch()` for A/B exploration and checkpointing
|
|
541
583
|
- **Multi-Agent Orchestration**: `AgentGraph` with routing, parallel execution, HITL, checkpointing; `SupervisorAgent` with 4 strategies (plan_and_execute, round_robin, dynamic, magentic)
|
|
542
584
|
- **Composable Pipelines**: `Pipeline` + `@step` + `|` operator + `parallel()` + `branch()` — chain agents, tools, and transforms
|
|
543
|
-
- **
|
|
585
|
+
- **106 Examples**: Multi-agent graphs, RAG, hybrid search, streaming, structured output, traces, batch, policy, observer, guardrails, audit, sessions (incl. Supabase), entity memory, knowledge graph, eval framework, advanced agent patterns, stability markers, HTML trace viewer, agent-as-API, A2A, routing, unified memory, and more
|
|
544
586
|
- **Built-in Eval Framework**: 50 evaluators (30 deterministic + 21 LLM-as-judge), A/B testing, regression detection, HTML reports, JUnit XML, snapshot testing
|
|
545
587
|
- **AgentObserver Protocol**: 45 lifecycle events with `run_id` correlation, `LoggingObserver`, `SimpleStepObserver`, OTel export
|
|
546
|
-
- **
|
|
588
|
+
- **5968 Tests**: Unit, integration, regression, and E2E with real API calls
|
|
547
589
|
|
|
548
590
|
## Install
|
|
549
591
|
|
|
@@ -889,6 +931,8 @@ See [docs/modules/STREAMING.md](https://github.com/johnnichev/selectools/blob/ma
|
|
|
889
931
|
| **Anthropic** | Yes | Yes | Yes | Paid |
|
|
890
932
|
| **Gemini** | Yes | Yes | Yes | Free tier |
|
|
891
933
|
| **Ollama** | Yes | No | No | Free (local) |
|
|
934
|
+
| **LiteLLM** | Yes | Yes | Yes | Varies (100+ models) |
|
|
935
|
+
| **Router** | Yes | Yes | Yes | Varies (cost-routes tiers) |
|
|
892
936
|
| **Fallback** | Yes | Yes | Yes | Varies (wraps others) |
|
|
893
937
|
| **Local** | No | No | No | Free (testing) |
|
|
894
938
|
|
|
@@ -1167,7 +1211,7 @@ pytest tests/ -x -q # All tests
|
|
|
1167
1211
|
pytest tests/ -k "not e2e" # Skip E2E (no API keys needed)
|
|
1168
1212
|
```
|
|
1169
1213
|
|
|
1170
|
-
|
|
1214
|
+
5968 tests covering parsing, agent loop, providers, RAG pipeline, hybrid search, advanced chunking, dynamic tools, caching, streaming, guardrails, sessions, memory, eval framework, budget/cancellation, knowledge stores, orchestration, pipelines, agent patterns, stability markers, trace viewer, serve API, A2A, routing, and E2E integration with real API calls.
|
|
1171
1215
|
|
|
1172
1216
|
## License
|
|
1173
1217
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "selectools"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.24.0"
|
|
8
8
|
description = "Production-ready Python framework for AI agents with multi-agent graphs, hybrid RAG, guardrails, audit logging, 50 evaluators, and a visual builder. Supports OpenAI, Anthropic, Gemini, Ollama. By NichevLabs."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -75,12 +75,20 @@ observe = [
|
|
|
75
75
|
"opentelemetry-api>=1.20.0",
|
|
76
76
|
"langfuse>=2.0.0",
|
|
77
77
|
]
|
|
78
|
+
toolbox = [
|
|
79
|
+
"requests>=2.28.0",
|
|
80
|
+
"slack-sdk>=3.27.0",
|
|
81
|
+
"pdfplumber>=0.11.0",
|
|
82
|
+
]
|
|
78
83
|
evals = [
|
|
79
84
|
"pyyaml>=6.0.0",
|
|
80
85
|
]
|
|
81
86
|
mcp = [
|
|
82
87
|
"mcp>=1.0.0,<2.0.0",
|
|
83
88
|
]
|
|
89
|
+
litellm = [
|
|
90
|
+
"litellm>=1.0.0",
|
|
91
|
+
]
|
|
84
92
|
postgres = [
|
|
85
93
|
"psycopg2-binary>=2.9.0",
|
|
86
94
|
]
|
|
@@ -91,6 +99,7 @@ serve = [
|
|
|
91
99
|
"pyyaml>=6.0.0",
|
|
92
100
|
"starlette>=0.27.0",
|
|
93
101
|
"uvicorn[standard]>=0.24.0",
|
|
102
|
+
"httpx>=0.24.0",
|
|
94
103
|
]
|
|
95
104
|
|
|
96
105
|
[project.scripts]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Public exports for the selectools package."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.
|
|
3
|
+
__version__ = "0.24.0"
|
|
4
4
|
|
|
5
5
|
# Import submodules (lazy loading for optional dependencies)
|
|
6
6
|
from . import embeddings, evals, guardrails, models, observe, patterns, rag, toolbox
|
|
@@ -50,11 +50,16 @@ from .guardrails import (
|
|
|
50
50
|
)
|
|
51
51
|
from .knowledge import (
|
|
52
52
|
FileKnowledgeStore,
|
|
53
|
+
KnowledgeBackend,
|
|
53
54
|
KnowledgeEntry,
|
|
54
55
|
KnowledgeMemory,
|
|
55
56
|
KnowledgeStore,
|
|
56
57
|
SQLiteKnowledgeStore,
|
|
57
58
|
)
|
|
59
|
+
from .knowledge_backends import (
|
|
60
|
+
RedisKnowledgeBackend,
|
|
61
|
+
SupabaseKnowledgeBackend,
|
|
62
|
+
)
|
|
58
63
|
from .knowledge_graph import (
|
|
59
64
|
InMemoryTripleStore,
|
|
60
65
|
KnowledgeGraphMemory,
|
|
@@ -122,6 +127,20 @@ from .patterns import (
|
|
|
122
127
|
TeamLeadAgent,
|
|
123
128
|
TeamLeadResult,
|
|
124
129
|
)
|
|
130
|
+
from .pending import (
|
|
131
|
+
ChannelAgent,
|
|
132
|
+
ConfirmOutcome,
|
|
133
|
+
ConfirmParser,
|
|
134
|
+
InMemoryPendingStore,
|
|
135
|
+
PendingAction,
|
|
136
|
+
PendingActionExistsError,
|
|
137
|
+
PendingActionStore,
|
|
138
|
+
PendingConfirmation,
|
|
139
|
+
RedisPendingStore,
|
|
140
|
+
RegexConfirmParser,
|
|
141
|
+
compute_args_digest,
|
|
142
|
+
stash_pending,
|
|
143
|
+
)
|
|
125
144
|
from .pipeline import Pipeline, Step, StepResult, branch, cache_step, parallel, retry, step
|
|
126
145
|
from .policy import PolicyDecision, PolicyResult, ToolPolicy
|
|
127
146
|
from .pricing import PRICING, calculate_cost, calculate_embedding_cost, get_model_pricing
|
|
@@ -130,13 +149,16 @@ from .providers.anthropic_provider import AnthropicProvider
|
|
|
130
149
|
from .providers.azure_openai_provider import AzureOpenAIProvider
|
|
131
150
|
from .providers.fallback import FallbackProvider
|
|
132
151
|
from .providers.gemini_provider import GeminiProvider
|
|
152
|
+
from .providers.litellm_provider import LiteLLMProvider
|
|
133
153
|
from .providers.ollama_provider import OllamaProvider
|
|
134
154
|
from .providers.openai_provider import OpenAIProvider
|
|
135
155
|
from .providers.stubs import LocalProvider
|
|
156
|
+
from .results import Ambiguous, Artifact, NotFound, ToolResult, emit_artifact
|
|
136
157
|
from .sessions import (
|
|
137
158
|
JsonFileSessionStore,
|
|
138
159
|
RedisSessionStore,
|
|
139
160
|
SessionMetadata,
|
|
161
|
+
SessionSearchResult,
|
|
140
162
|
SessionStore,
|
|
141
163
|
SQLiteSessionStore,
|
|
142
164
|
SupabaseSessionStore,
|
|
@@ -147,6 +169,16 @@ from .token_estimation import TokenEstimate, estimate_run_tokens, estimate_token
|
|
|
147
169
|
from .tools import Tool, ToolParameter, ToolRegistry, tool
|
|
148
170
|
from .trace import AgentTrace, StepType, TraceStep, trace_to_html, trace_to_json
|
|
149
171
|
from .types import AgentResult, ContentPart, Message, Role, ToolCall, image_message, text_content
|
|
172
|
+
from .unified_memory import (
|
|
173
|
+
DEFAULT_IMPORTANCE_RULES,
|
|
174
|
+
Episode,
|
|
175
|
+
EpisodicMemory,
|
|
176
|
+
ImportanceRule,
|
|
177
|
+
InMemoryKnowledgeStore,
|
|
178
|
+
RecallResult,
|
|
179
|
+
UnifiedMemory,
|
|
180
|
+
score_importance,
|
|
181
|
+
)
|
|
150
182
|
from .usage import AgentUsage, UsageStats
|
|
151
183
|
|
|
152
184
|
__all__ = [
|
|
@@ -172,6 +204,7 @@ __all__ = [
|
|
|
172
204
|
"AnthropicProvider",
|
|
173
205
|
"GeminiProvider",
|
|
174
206
|
"OllamaProvider",
|
|
207
|
+
"LiteLLMProvider",
|
|
175
208
|
"LocalProvider",
|
|
176
209
|
"FallbackProvider",
|
|
177
210
|
"ToolRegistry",
|
|
@@ -218,6 +251,25 @@ __all__ = [
|
|
|
218
251
|
"PolicyResult",
|
|
219
252
|
# Structured output
|
|
220
253
|
"ResponseFormat",
|
|
254
|
+
# Typed tool results + artifact side-channel (issue #59)
|
|
255
|
+
"ToolResult",
|
|
256
|
+
"Ambiguous",
|
|
257
|
+
"NotFound",
|
|
258
|
+
"Artifact",
|
|
259
|
+
"emit_artifact",
|
|
260
|
+
# Deferred confirmation flow (issue #58)
|
|
261
|
+
"PendingAction",
|
|
262
|
+
"PendingActionStore",
|
|
263
|
+
"PendingActionExistsError",
|
|
264
|
+
"InMemoryPendingStore",
|
|
265
|
+
"RedisPendingStore",
|
|
266
|
+
"ConfirmOutcome",
|
|
267
|
+
"ConfirmParser",
|
|
268
|
+
"RegexConfirmParser",
|
|
269
|
+
"PendingConfirmation",
|
|
270
|
+
"ChannelAgent",
|
|
271
|
+
"stash_pending",
|
|
272
|
+
"compute_args_digest",
|
|
221
273
|
# Stability markers
|
|
222
274
|
"stable",
|
|
223
275
|
"beta",
|
|
@@ -261,6 +313,7 @@ __all__ = [
|
|
|
261
313
|
# Sessions
|
|
262
314
|
"SessionStore",
|
|
263
315
|
"SessionMetadata",
|
|
316
|
+
"SessionSearchResult",
|
|
264
317
|
"JsonFileSessionStore",
|
|
265
318
|
"SQLiteSessionStore",
|
|
266
319
|
"RedisSessionStore",
|
|
@@ -277,6 +330,10 @@ __all__ = [
|
|
|
277
330
|
# Knowledge stores (optional deps: redis, supabase)
|
|
278
331
|
# from selectools.knowledge_store_redis import RedisKnowledgeStore
|
|
279
332
|
# from selectools.knowledge_store_supabase import SupabaseKnowledgeStore
|
|
333
|
+
# Knowledge backends (blob persistence between deploys; lazy optional deps)
|
|
334
|
+
"KnowledgeBackend",
|
|
335
|
+
"SupabaseKnowledgeBackend",
|
|
336
|
+
"RedisKnowledgeBackend",
|
|
280
337
|
# Token estimation
|
|
281
338
|
"TokenEstimate",
|
|
282
339
|
"estimate_tokens",
|
|
@@ -287,6 +344,15 @@ __all__ = [
|
|
|
287
344
|
"InMemoryTripleStore",
|
|
288
345
|
"SQLiteTripleStore",
|
|
289
346
|
"KnowledgeGraphMemory",
|
|
347
|
+
# Unified Memory (tiered memory with auto-promotion)
|
|
348
|
+
"UnifiedMemory",
|
|
349
|
+
"EpisodicMemory",
|
|
350
|
+
"Episode",
|
|
351
|
+
"ImportanceRule",
|
|
352
|
+
"InMemoryKnowledgeStore",
|
|
353
|
+
"RecallResult",
|
|
354
|
+
"DEFAULT_IMPORTANCE_RULES",
|
|
355
|
+
"score_importance",
|
|
290
356
|
# Submodules (for lazy loading)
|
|
291
357
|
"embeddings",
|
|
292
358
|
"observe",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2A protocol — agent-to-agent communication (Google-backed standard).
|
|
3
|
+
|
|
4
|
+
Serving::
|
|
5
|
+
|
|
6
|
+
from selectools.serve import A2AServer # or selectools.a2a
|
|
7
|
+
|
|
8
|
+
server = A2AServer(agent=my_agent, auth_token="sk-...")
|
|
9
|
+
server.serve(port=8000)
|
|
10
|
+
|
|
11
|
+
Consuming::
|
|
12
|
+
|
|
13
|
+
from selectools.a2a import A2AClient
|
|
14
|
+
|
|
15
|
+
client = A2AClient("https://other-agent.example.com")
|
|
16
|
+
card = await client.discover() # reads /.well-known/agent.json
|
|
17
|
+
result = await client.send_task("Research quantum computing trends")
|
|
18
|
+
|
|
19
|
+
The server requires ``starlette`` and the client requires ``httpx``
|
|
20
|
+
(both ship with ``pip install selectools[serve]``); imports are lazy so
|
|
21
|
+
this package loads without them.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from typing import Any
|
|
25
|
+
|
|
26
|
+
from .types import A2AError, A2ATask, AgentCard, AgentSkill, TaskState
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"A2AClient",
|
|
30
|
+
"A2AError",
|
|
31
|
+
"A2AServer",
|
|
32
|
+
"A2ATask",
|
|
33
|
+
"AgentCard",
|
|
34
|
+
"AgentSkill",
|
|
35
|
+
"TaskState",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def __getattr__(name: str) -> Any:
|
|
40
|
+
"""Lazily import the server (starlette) and client (httpx)."""
|
|
41
|
+
if name == "A2AServer":
|
|
42
|
+
try:
|
|
43
|
+
from .server import A2AServer
|
|
44
|
+
except ImportError as exc:
|
|
45
|
+
raise ImportError(
|
|
46
|
+
"A2AServer requires the 'starlette' package. "
|
|
47
|
+
"Install it with: pip install selectools[serve]"
|
|
48
|
+
) from exc
|
|
49
|
+
return A2AServer
|
|
50
|
+
if name == "A2AClient":
|
|
51
|
+
from .client import A2AClient
|
|
52
|
+
|
|
53
|
+
return A2AClient
|
|
54
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2A client — discover and talk to remote A2A agents.
|
|
3
|
+
|
|
4
|
+
Usage::
|
|
5
|
+
|
|
6
|
+
from selectools.a2a import A2AClient
|
|
7
|
+
|
|
8
|
+
client = A2AClient("https://other-agent.example.com")
|
|
9
|
+
card = await client.discover() # reads /.well-known/agent.json
|
|
10
|
+
result = await client.send_task("Research quantum computing trends")
|
|
11
|
+
print(result.text)
|
|
12
|
+
|
|
13
|
+
# Sync code can use the *_sync wrappers:
|
|
14
|
+
card = client.discover_sync()
|
|
15
|
+
|
|
16
|
+
Requires ``httpx`` (ships with ``pip install selectools[serve]``); the
|
|
17
|
+
import is lazy so this module loads without it.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import uuid
|
|
23
|
+
from typing import Any, Dict, List, Optional
|
|
24
|
+
|
|
25
|
+
from .._async_utils import run_sync
|
|
26
|
+
from ..stability import beta
|
|
27
|
+
from .types import A2AError, A2ATask, AgentCard
|
|
28
|
+
|
|
29
|
+
__all__ = ["A2AClient"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _httpx() -> Any:
|
|
33
|
+
try:
|
|
34
|
+
import httpx
|
|
35
|
+
except ImportError as exc:
|
|
36
|
+
raise ImportError(
|
|
37
|
+
"A2AClient requires the 'httpx' package. Install it with: pip install selectools[serve]"
|
|
38
|
+
) from exc
|
|
39
|
+
return httpx
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@beta
|
|
43
|
+
class A2AClient:
|
|
44
|
+
"""Client for a remote A2A agent (Agent Card discovery + task sending).
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
base_url: Root URL of the remote agent, e.g.
|
|
48
|
+
``"https://other-agent.example.com"``. The Agent Card is read
|
|
49
|
+
from ``{base_url}/.well-known/agent.json`` and tasks are posted
|
|
50
|
+
to ``{base_url}/a2a``.
|
|
51
|
+
auth_token: Optional bearer token sent as
|
|
52
|
+
``Authorization: Bearer <auth_token>`` on every request.
|
|
53
|
+
timeout: Request timeout in seconds (default 30).
|
|
54
|
+
transport: Optional ``httpx`` transport — pass
|
|
55
|
+
``httpx.ASGITransport(app=server)`` to talk to an in-process
|
|
56
|
+
:class:`~selectools.a2a.server.A2AServer` without a socket.
|
|
57
|
+
|
|
58
|
+
Raises:
|
|
59
|
+
A2AError: On transport failures (non-2xx responses) and JSON-RPC
|
|
60
|
+
protocol errors. Agent failures are NOT raised — they come back
|
|
61
|
+
as an :class:`A2ATask` with ``state == TaskState.FAILED``.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
base_url: str,
|
|
67
|
+
auth_token: Optional[str] = None,
|
|
68
|
+
timeout: float = 30.0,
|
|
69
|
+
transport: Optional[Any] = None,
|
|
70
|
+
) -> None:
|
|
71
|
+
self._base_url = base_url.rstrip("/")
|
|
72
|
+
self._auth_token = auth_token
|
|
73
|
+
self._timeout = timeout
|
|
74
|
+
self._transport = transport
|
|
75
|
+
|
|
76
|
+
def _headers(self) -> Dict[str, str]:
|
|
77
|
+
headers = {"Content-Type": "application/json"}
|
|
78
|
+
if self._auth_token:
|
|
79
|
+
headers["Authorization"] = f"Bearer {self._auth_token}"
|
|
80
|
+
return headers
|
|
81
|
+
|
|
82
|
+
def _async_client(self) -> Any:
|
|
83
|
+
httpx = _httpx()
|
|
84
|
+
kwargs: Dict[str, Any] = {"timeout": self._timeout}
|
|
85
|
+
if self._transport is not None:
|
|
86
|
+
kwargs["transport"] = self._transport
|
|
87
|
+
return httpx.AsyncClient(**kwargs)
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def _check_http(response: Any) -> None:
|
|
91
|
+
if response.status_code >= 400:
|
|
92
|
+
raise A2AError(f"HTTP {response.status_code} from A2A server: {response.text[:500]}")
|
|
93
|
+
|
|
94
|
+
# ── discovery ────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
async def discover(self) -> AgentCard:
|
|
97
|
+
"""Fetch and parse the remote Agent Card from /.well-known/agent.json."""
|
|
98
|
+
async with self._async_client() as client:
|
|
99
|
+
response = await client.get(
|
|
100
|
+
f"{self._base_url}/.well-known/agent.json", headers=self._headers()
|
|
101
|
+
)
|
|
102
|
+
self._check_http(response)
|
|
103
|
+
return AgentCard.from_dict(response.json())
|
|
104
|
+
|
|
105
|
+
def discover_sync(self) -> AgentCard:
|
|
106
|
+
"""Synchronous wrapper around :meth:`discover`."""
|
|
107
|
+
return run_sync(self.discover())
|
|
108
|
+
|
|
109
|
+
# ── tasks ────────────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
async def _call(self, method: str, params: Dict[str, Any]) -> Any:
|
|
112
|
+
"""Issue one JSON-RPC call against POST /a2a and return its result."""
|
|
113
|
+
payload = {
|
|
114
|
+
"jsonrpc": "2.0",
|
|
115
|
+
"id": uuid.uuid4().hex,
|
|
116
|
+
"method": method,
|
|
117
|
+
"params": params,
|
|
118
|
+
}
|
|
119
|
+
async with self._async_client() as client:
|
|
120
|
+
response = await client.post(
|
|
121
|
+
f"{self._base_url}/a2a", json=payload, headers=self._headers()
|
|
122
|
+
)
|
|
123
|
+
self._check_http(response)
|
|
124
|
+
body = response.json()
|
|
125
|
+
if "error" in body:
|
|
126
|
+
error = body["error"] or {}
|
|
127
|
+
raise A2AError(
|
|
128
|
+
str(error.get("message", "Unknown A2A error")),
|
|
129
|
+
code=error.get("code"),
|
|
130
|
+
data=error.get("data"),
|
|
131
|
+
)
|
|
132
|
+
return body.get("result")
|
|
133
|
+
|
|
134
|
+
async def send_task(
|
|
135
|
+
self,
|
|
136
|
+
text: str,
|
|
137
|
+
context_id: Optional[str] = None,
|
|
138
|
+
extra_parts: Optional[List[Dict[str, Any]]] = None,
|
|
139
|
+
) -> A2ATask:
|
|
140
|
+
"""Send a text task via ``message/send`` and return the final task.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
text: The task prompt (becomes a part of kind ``"text"``).
|
|
144
|
+
context_id: Optional conversation context id to group tasks.
|
|
145
|
+
extra_parts: Optional additional A2A parts (``file``/``data``)
|
|
146
|
+
appended after the text part. The v1 server is text-first.
|
|
147
|
+
"""
|
|
148
|
+
parts: List[Dict[str, Any]] = [{"kind": "text", "text": text}]
|
|
149
|
+
if extra_parts:
|
|
150
|
+
parts.extend(extra_parts)
|
|
151
|
+
message: Dict[str, Any] = {
|
|
152
|
+
"role": "user",
|
|
153
|
+
"parts": parts,
|
|
154
|
+
"messageId": uuid.uuid4().hex,
|
|
155
|
+
"kind": "message",
|
|
156
|
+
}
|
|
157
|
+
if context_id:
|
|
158
|
+
message["contextId"] = context_id
|
|
159
|
+
result = await self._call("message/send", {"message": message})
|
|
160
|
+
return A2ATask.from_dict(result or {})
|
|
161
|
+
|
|
162
|
+
def send_task_sync(
|
|
163
|
+
self,
|
|
164
|
+
text: str,
|
|
165
|
+
context_id: Optional[str] = None,
|
|
166
|
+
extra_parts: Optional[List[Dict[str, Any]]] = None,
|
|
167
|
+
) -> A2ATask:
|
|
168
|
+
"""Synchronous wrapper around :meth:`send_task`."""
|
|
169
|
+
return run_sync(self.send_task(text, context_id=context_id, extra_parts=extra_parts))
|
|
170
|
+
|
|
171
|
+
async def get_task(self, task_id: str) -> A2ATask:
|
|
172
|
+
"""Fetch a previously created task via ``tasks/get``."""
|
|
173
|
+
result = await self._call("tasks/get", {"id": task_id})
|
|
174
|
+
return A2ATask.from_dict(result or {})
|