jaf-py 2.2.2__tar.gz → 2.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. {jaf_py-2.2.2 → jaf_py-2.2.4}/PKG-INFO +144 -5
  2. {jaf_py-2.2.2 → jaf_py-2.2.4}/README.md +143 -4
  3. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/__init__.py +5 -0
  4. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/engine.py +6 -2
  5. jaf_py-2.2.4/jaf/core/proxy.py +141 -0
  6. jaf_py-2.2.4/jaf/core/proxy_helpers.py +126 -0
  7. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/types.py +1 -0
  8. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/providers/model.py +27 -6
  9. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/PKG-INFO +144 -5
  10. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/SOURCES.txt +3 -0
  11. {jaf_py-2.2.2 → jaf_py-2.2.4}/pyproject.toml +1 -1
  12. jaf_py-2.2.4/tests/test_proxy_simple.py +117 -0
  13. {jaf_py-2.2.2 → jaf_py-2.2.4}/LICENSE +0 -0
  14. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/__init__.py +0 -0
  15. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/__init__.py +0 -0
  16. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/agent.py +0 -0
  17. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/agent_card.py +0 -0
  18. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/client.py +0 -0
  19. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/__init__.py +0 -0
  20. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/client_example.py +0 -0
  21. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/integration_example.py +0 -0
  22. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/rag_demo/__init__.py +0 -0
  23. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/server_demo/__init__.py +0 -0
  24. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/server_example.py +0 -0
  25. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/__init__.py +0 -0
  26. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/cleanup.py +0 -0
  27. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/factory.py +0 -0
  28. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/providers/__init__.py +0 -0
  29. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/providers/composite.py +0 -0
  30. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/providers/in_memory.py +0 -0
  31. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/providers/postgres.py +0 -0
  32. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/providers/redis.py +0 -0
  33. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/serialization.py +0 -0
  34. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/__init__.py +0 -0
  35. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/run_comprehensive_tests.py +0 -0
  36. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/test_cleanup.py +0 -0
  37. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/test_serialization.py +0 -0
  38. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/test_stress_concurrency.py +0 -0
  39. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/test_task_lifecycle.py +0 -0
  40. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/types.py +0 -0
  41. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/protocol.py +0 -0
  42. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/server.py +0 -0
  43. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/standalone_client.py +0 -0
  44. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/__init__.py +0 -0
  45. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/run_tests.py +0 -0
  46. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/test_agent.py +0 -0
  47. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/test_client.py +0 -0
  48. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/test_integration.py +0 -0
  49. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/test_protocol.py +0 -0
  50. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/test_types.py +0 -0
  51. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/types.py +0 -0
  52. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/cli.py +0 -0
  53. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/agent_tool.py +0 -0
  54. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/analytics.py +0 -0
  55. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/composition.py +0 -0
  56. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/errors.py +0 -0
  57. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/performance.py +0 -0
  58. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/streaming.py +0 -0
  59. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/tool_results.py +0 -0
  60. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/tools.py +0 -0
  61. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/tracing.py +0 -0
  62. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/workflows.py +0 -0
  63. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/exceptions.py +0 -0
  64. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/__init__.py +0 -0
  65. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/factory.py +0 -0
  66. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/providers/__init__.py +0 -0
  67. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/providers/in_memory.py +0 -0
  68. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/providers/postgres.py +0 -0
  69. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/providers/redis.py +0 -0
  70. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/types.py +0 -0
  71. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/utils.py +0 -0
  72. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/plugins/__init__.py +0 -0
  73. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/plugins/base.py +0 -0
  74. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/policies/__init__.py +0 -0
  75. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/policies/handoff.py +0 -0
  76. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/policies/validation.py +0 -0
  77. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/providers/__init__.py +0 -0
  78. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/providers/mcp.py +0 -0
  79. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/server/__init__.py +0 -0
  80. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/server/main.py +0 -0
  81. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/server/server.py +0 -0
  82. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/server/types.py +0 -0
  83. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/__init__.py +0 -0
  84. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/example.py +0 -0
  85. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/functional_core.py +0 -0
  86. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/graphviz.py +0 -0
  87. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/imperative_shell.py +0 -0
  88. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/types.py +0 -0
  89. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/dependency_links.txt +0 -0
  90. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/entry_points.txt +0 -0
  91. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/requires.txt +0 -0
  92. {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/top_level.txt +0 -0
  93. {jaf_py-2.2.2 → jaf_py-2.2.4}/setup.cfg +0 -0
  94. {jaf_py-2.2.2 → jaf_py-2.2.4}/setup.py +0 -0
  95. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_a2a_deep.py +0 -0
  96. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_a2a_examples.py +0 -0
  97. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_api_reference_examples.py +0 -0
  98. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_callback_system_examples.py +0 -0
  99. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_coffee_tool.py +0 -0
  100. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_conversation_id_fix.py +0 -0
  101. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_deployment_examples.py +0 -0
  102. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_docs_code_examples.py +0 -0
  103. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_engine.py +0 -0
  104. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_engine_manual.py +0 -0
  105. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_error_handling_examples.py +0 -0
  106. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_getting_started_examples.py +0 -0
  107. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_math_tool.py +0 -0
  108. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_mcp_comprehensive.py +0 -0
  109. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_mcp_docs.py +0 -0
  110. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_mcp_real_functionality.py +0 -0
  111. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_mcp_transports.py +0 -0
  112. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_memory_system_examples.py +0 -0
  113. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_model_providers_examples.py +0 -0
  114. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_property_based.py +0 -0
  115. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_redis_fixes.py +0 -0
  116. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_redis_memory.py +0 -0
  117. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_server_api_examples.py +0 -0
  118. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_session_continuity.py +0 -0
  119. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_streamable_http_mcp_example.py +0 -0
  120. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_timeout_functionality.py +0 -0
  121. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_tool_integration.py +0 -0
  122. {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaf-py
3
- Version: 2.2.2
3
+ Version: 2.2.4
4
4
  Summary: A purely functional agent framework with immutable state and composable tools - Python implementation
5
5
  Author: JAF Contributors
6
6
  Maintainer: JAF Contributors
@@ -73,7 +73,7 @@ Dynamic: license-file
73
73
 
74
74
  <!-- ![JAF Banner](docs/cover.png) -->
75
75
 
76
- [![Version](https://img.shields.io/badge/version-2.0.0-blue.svg)](https://github.com/xynehq/jaf-py)
76
+ [![Version](https://img.shields.io/badge/version-2.2.3-blue.svg)](https://github.com/xynehq/jaf-py)
77
77
  [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
78
78
  [![Docs](https://img.shields.io/badge/Docs-Live-brightgreen)](https://xynehq.github.io/jaf-py/)
79
79
 
@@ -110,18 +110,27 @@ A purely functional agent framework with immutable state and composable tools, p
110
110
  - ✅ **Output Guardrails**: Response sanitization
111
111
  - ✅ **Permission System**: Role-based access control
112
112
  - ✅ **Audit Logging**: Complete interaction tracing
113
+ - ✅ **Proxy Support**: Corporate proxy integration with authentication
113
114
 
114
115
  ### 📊 **Observability & Monitoring**
115
116
  - ✅ **Real-time Tracing**: Event-driven observability
117
+ - ✅ **OpenTelemetry Integration**: Distributed tracing with OTLP
118
+ - ✅ **Langfuse Tracing**: LLM observability and analytics
116
119
  - ✅ **Structured Logging**: JSON-formatted logs
117
120
  - ✅ **Error Handling**: Comprehensive error types and recovery
118
121
  - ✅ **Performance Metrics**: Built-in timing and counters
119
122
 
123
+ ### 🤖 **Agent-as-Tool Architecture**
124
+ - ✅ **Hierarchical Orchestration**: Use agents as tools in other agents
125
+ - ✅ **Conditional Tool Enabling**: Enable/disable agent tools based on context
126
+ - ✅ **Session Management**: Configurable session inheritance for sub-agents
127
+ - ✅ **Flexible Output Extraction**: Custom extractors for agent tool outputs
128
+
120
129
  ### 🔧 **Developer Experience**
121
130
  - ✅ **CLI Tools**: Project initialization and management
122
131
  - ✅ **Hot Reload**: Development server with auto-reload
123
132
  - ✅ **Type Hints**: Full mypy compatibility
124
- - ✅ **Rich Examples**: RAG, multi-agent, and server demos
133
+ - ✅ **Rich Examples**: RAG, multi-agent, agent-as-tool, and server demos
125
134
  - ✅ **Visual Architecture**: Graphviz-powered agent and tool diagrams
126
135
 
127
136
  ## 🎯 Core Philosophy
@@ -147,6 +156,7 @@ pip install "jaf-py[all] @ git+https://github.com/xynehq/jaf-py.git"
147
156
  pip install "jaf-py[server] @ git+https://github.com/xynehq/jaf-py.git" # FastAPI server support
148
157
  pip install "jaf-py[memory] @ git+https://github.com/xynehq/jaf-py.git" # Redis/PostgreSQL memory providers
149
158
  pip install "jaf-py[visualization] @ git+https://github.com/xynehq/jaf-py.git" # Graphviz visualization tools
159
+ pip install "jaf-py[tracing] @ git+https://github.com/xynehq/jaf-py.git" # OpenTelemetry and Langfuse tracing
150
160
  pip install "jaf-py[dev] @ git+https://github.com/xynehq/jaf-py.git" # Development tools
151
161
  ```
152
162
 
@@ -211,6 +221,7 @@ For offline access, documentation is also available in the [`docs/`](docs/) dire
211
221
  - **[🔧 Tools Guide](docs/tools.md)** - Creating and using tools
212
222
  - **[💾 Memory System](docs/memory-system.md)** - Persistence and memory providers
213
223
  - **[🤖 Model Providers](docs/model-providers.md)** - LiteLLM integration
224
+ - **[📊 Monitoring](docs/monitoring.md)** - Observability, metrics, and alerting
214
225
  - **[🌐 Server API](docs/server-api.md)** - FastAPI endpoints reference
215
226
  - **[📦 Deployment](docs/deployment.md)** - Production deployment guide
216
227
  - **[🎮 Examples](docs/examples.md)** - Detailed example walkthroughs
@@ -438,6 +449,58 @@ config = RunConfig(
438
449
  )
439
450
  ```
440
451
 
452
+ ## 🤖 Agent-as-Tool Functionality
453
+
454
+ JAF 2.2+ introduces powerful agent-as-tool capabilities, allowing you to use agents as tools within other agents for hierarchical orchestration:
455
+
456
+ ```python
457
+ from jaf.core.agent_tool import create_agent_tool
458
+ from jaf.core.types import create_json_output_extractor
459
+
460
+ # Create specialized agents
461
+ spanish_agent = Agent(
462
+ name="spanish_translator",
463
+ instructions=lambda state: "Translate text to Spanish",
464
+ output_codec=TranslationOutput
465
+ )
466
+
467
+ french_agent = Agent(
468
+ name="french_translator",
469
+ instructions=lambda state: "Translate text to French",
470
+ output_codec=TranslationOutput
471
+ )
472
+
473
+ # Convert agents to tools with conditional enabling
474
+ spanish_tool = spanish_agent.as_tool(
475
+ tool_name="translate_to_spanish",
476
+ tool_description="Translate text to Spanish",
477
+ max_turns=3,
478
+ custom_output_extractor=create_json_output_extractor(),
479
+ is_enabled=True # Always enabled
480
+ )
481
+
482
+ french_tool = french_agent.as_tool(
483
+ tool_name="translate_to_french",
484
+ tool_description="Translate text to French",
485
+ max_turns=3,
486
+ custom_output_extractor=create_json_output_extractor(),
487
+ is_enabled=lambda context, agent: context.language_preference == "french_spanish"
488
+ )
489
+
490
+ # Create orchestrator agent using agent tools
491
+ orchestrator = Agent(
492
+ name="translation_orchestrator",
493
+ instructions=lambda state: "Use translation tools to respond in multiple languages",
494
+ tools=[spanish_tool, french_tool]
495
+ )
496
+ ```
497
+
498
+ ### Key Features:
499
+ - **Conditional Enabling**: Enable/disable agent tools based on runtime context
500
+ - **Session Management**: Configure whether sub-agents inherit parent session state
501
+ - **Custom Output Extraction**: Define how to extract and format agent tool outputs
502
+ - **Error Handling**: Robust error handling for failed agent tool executions
503
+
441
504
  ## 🔗 Agent Handoffs
442
505
 
443
506
  ```python
@@ -487,6 +550,48 @@ config = RunConfig(
487
550
  )
488
551
  ```
489
552
 
553
+ ### OpenTelemetry Integration
554
+
555
+ JAF 2.2+ includes built-in OpenTelemetry support for distributed tracing:
556
+
557
+ ```python
558
+ import os
559
+ from jaf.core.tracing import create_composite_trace_collector, ConsoleTraceCollector
560
+
561
+ # Configure OpenTelemetry endpoint
562
+ os.environ["TRACE_COLLECTOR_URL"] = "http://localhost:4318/v1/traces"
563
+
564
+ # Tracing will be automatically configured when creating a composite collector
565
+ trace_collector = create_composite_trace_collector(ConsoleTraceCollector())
566
+
567
+ config = RunConfig(
568
+ # ... other config
569
+ on_event=trace_collector.collect,
570
+ )
571
+ ```
572
+
573
+ ### Langfuse Integration
574
+
575
+ For LLM-specific observability and analytics:
576
+
577
+ ```python
578
+ import os
579
+ from jaf.core.tracing import create_composite_trace_collector, ConsoleTraceCollector
580
+
581
+ # Configure Langfuse credentials
582
+ os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-your-public-key"
583
+ os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-your-secret-key"
584
+ os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # or your self-hosted instance
585
+
586
+ # Langfuse tracing will be automatically configured
587
+ trace_collector = create_composite_trace_collector(ConsoleTraceCollector())
588
+
589
+ config = RunConfig(
590
+ # ... other config
591
+ on_event=trace_collector.collect,
592
+ )
593
+ ```
594
+
490
595
  ### Error Handling
491
596
 
492
597
  ```python
@@ -662,7 +767,41 @@ python server_example.py
662
767
  - `POST /chat` - Chat with any agent
663
768
  - `GET /docs` - Interactive API documentation
664
769
 
665
- ### 2. MCP Integration Demo
770
+ ### 2. Agent-as-Tool Demo
771
+
772
+ ```bash
773
+ cd examples
774
+ python agent_as_tool_example.py
775
+
776
+ # Or start as server
777
+ python agent_as_tool_example.py --server
778
+ ```
779
+
780
+ **Features demonstrated:**
781
+ - ✅ Hierarchical agent orchestration
782
+ - ✅ Conditional tool enabling based on context
783
+ - ✅ Custom output extraction from agent tools
784
+ - ✅ Session management for sub-agents
785
+ - ✅ Translation agents working together
786
+
787
+ ### 3. Tracing Integration Demos
788
+
789
+ ```bash
790
+ # OpenTelemetry tracing example
791
+ cd examples
792
+ python otel_tracing_demo.py
793
+
794
+ # Langfuse tracing example
795
+ python langfuse_tracing_demo.py
796
+ ```
797
+
798
+ **Features demonstrated:**
799
+ - ✅ OpenTelemetry distributed tracing setup
800
+ - ✅ Langfuse LLM observability integration
801
+ - ✅ Composite trace collectors
802
+ - ✅ Real-time monitoring and analytics
803
+
804
+ ### 4. MCP Integration Demo
666
805
 
667
806
  ```bash
668
807
  cd examples/mcp_demo
@@ -720,4 +859,4 @@ MIT
720
859
 
721
860
  ---
722
861
 
723
- **JAF (Juspay Agentic Framework) v2.0** - Building the future of functional AI agent systems 🚀
862
+ **JAF (Juspay Agentic Framework) v2.2** - Building the future of functional AI agent systems 🚀
@@ -2,7 +2,7 @@
2
2
 
3
3
  <!-- ![JAF Banner](docs/cover.png) -->
4
4
 
5
- [![Version](https://img.shields.io/badge/version-2.0.0-blue.svg)](https://github.com/xynehq/jaf-py)
5
+ [![Version](https://img.shields.io/badge/version-2.2.3-blue.svg)](https://github.com/xynehq/jaf-py)
6
6
  [![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
7
7
  [![Docs](https://img.shields.io/badge/Docs-Live-brightgreen)](https://xynehq.github.io/jaf-py/)
8
8
 
@@ -39,18 +39,27 @@ A purely functional agent framework with immutable state and composable tools, p
39
39
  - ✅ **Output Guardrails**: Response sanitization
40
40
  - ✅ **Permission System**: Role-based access control
41
41
  - ✅ **Audit Logging**: Complete interaction tracing
42
+ - ✅ **Proxy Support**: Corporate proxy integration with authentication
42
43
 
43
44
  ### 📊 **Observability & Monitoring**
44
45
  - ✅ **Real-time Tracing**: Event-driven observability
46
+ - ✅ **OpenTelemetry Integration**: Distributed tracing with OTLP
47
+ - ✅ **Langfuse Tracing**: LLM observability and analytics
45
48
  - ✅ **Structured Logging**: JSON-formatted logs
46
49
  - ✅ **Error Handling**: Comprehensive error types and recovery
47
50
  - ✅ **Performance Metrics**: Built-in timing and counters
48
51
 
52
+ ### 🤖 **Agent-as-Tool Architecture**
53
+ - ✅ **Hierarchical Orchestration**: Use agents as tools in other agents
54
+ - ✅ **Conditional Tool Enabling**: Enable/disable agent tools based on context
55
+ - ✅ **Session Management**: Configurable session inheritance for sub-agents
56
+ - ✅ **Flexible Output Extraction**: Custom extractors for agent tool outputs
57
+
49
58
  ### 🔧 **Developer Experience**
50
59
  - ✅ **CLI Tools**: Project initialization and management
51
60
  - ✅ **Hot Reload**: Development server with auto-reload
52
61
  - ✅ **Type Hints**: Full mypy compatibility
53
- - ✅ **Rich Examples**: RAG, multi-agent, and server demos
62
+ - ✅ **Rich Examples**: RAG, multi-agent, agent-as-tool, and server demos
54
63
  - ✅ **Visual Architecture**: Graphviz-powered agent and tool diagrams
55
64
 
56
65
  ## 🎯 Core Philosophy
@@ -76,6 +85,7 @@ pip install "jaf-py[all] @ git+https://github.com/xynehq/jaf-py.git"
76
85
  pip install "jaf-py[server] @ git+https://github.com/xynehq/jaf-py.git" # FastAPI server support
77
86
  pip install "jaf-py[memory] @ git+https://github.com/xynehq/jaf-py.git" # Redis/PostgreSQL memory providers
78
87
  pip install "jaf-py[visualization] @ git+https://github.com/xynehq/jaf-py.git" # Graphviz visualization tools
88
+ pip install "jaf-py[tracing] @ git+https://github.com/xynehq/jaf-py.git" # OpenTelemetry and Langfuse tracing
79
89
  pip install "jaf-py[dev] @ git+https://github.com/xynehq/jaf-py.git" # Development tools
80
90
  ```
81
91
 
@@ -140,6 +150,7 @@ For offline access, documentation is also available in the [`docs/`](docs/) dire
140
150
  - **[🔧 Tools Guide](docs/tools.md)** - Creating and using tools
141
151
  - **[💾 Memory System](docs/memory-system.md)** - Persistence and memory providers
142
152
  - **[🤖 Model Providers](docs/model-providers.md)** - LiteLLM integration
153
+ - **[📊 Monitoring](docs/monitoring.md)** - Observability, metrics, and alerting
143
154
  - **[🌐 Server API](docs/server-api.md)** - FastAPI endpoints reference
144
155
  - **[📦 Deployment](docs/deployment.md)** - Production deployment guide
145
156
  - **[🎮 Examples](docs/examples.md)** - Detailed example walkthroughs
@@ -367,6 +378,58 @@ config = RunConfig(
367
378
  )
368
379
  ```
369
380
 
381
+ ## 🤖 Agent-as-Tool Functionality
382
+
383
+ JAF 2.2+ introduces powerful agent-as-tool capabilities, allowing you to use agents as tools within other agents for hierarchical orchestration:
384
+
385
+ ```python
386
+ from jaf.core.agent_tool import create_agent_tool
387
+ from jaf.core.types import create_json_output_extractor
388
+
389
+ # Create specialized agents
390
+ spanish_agent = Agent(
391
+ name="spanish_translator",
392
+ instructions=lambda state: "Translate text to Spanish",
393
+ output_codec=TranslationOutput
394
+ )
395
+
396
+ french_agent = Agent(
397
+ name="french_translator",
398
+ instructions=lambda state: "Translate text to French",
399
+ output_codec=TranslationOutput
400
+ )
401
+
402
+ # Convert agents to tools with conditional enabling
403
+ spanish_tool = spanish_agent.as_tool(
404
+ tool_name="translate_to_spanish",
405
+ tool_description="Translate text to Spanish",
406
+ max_turns=3,
407
+ custom_output_extractor=create_json_output_extractor(),
408
+ is_enabled=True # Always enabled
409
+ )
410
+
411
+ french_tool = french_agent.as_tool(
412
+ tool_name="translate_to_french",
413
+ tool_description="Translate text to French",
414
+ max_turns=3,
415
+ custom_output_extractor=create_json_output_extractor(),
416
+ is_enabled=lambda context, agent: context.language_preference == "french_spanish"
417
+ )
418
+
419
+ # Create orchestrator agent using agent tools
420
+ orchestrator = Agent(
421
+ name="translation_orchestrator",
422
+ instructions=lambda state: "Use translation tools to respond in multiple languages",
423
+ tools=[spanish_tool, french_tool]
424
+ )
425
+ ```
426
+
427
+ ### Key Features:
428
+ - **Conditional Enabling**: Enable/disable agent tools based on runtime context
429
+ - **Session Management**: Configure whether sub-agents inherit parent session state
430
+ - **Custom Output Extraction**: Define how to extract and format agent tool outputs
431
+ - **Error Handling**: Robust error handling for failed agent tool executions
432
+
370
433
  ## 🔗 Agent Handoffs
371
434
 
372
435
  ```python
@@ -416,6 +479,48 @@ config = RunConfig(
416
479
  )
417
480
  ```
418
481
 
482
+ ### OpenTelemetry Integration
483
+
484
+ JAF 2.2+ includes built-in OpenTelemetry support for distributed tracing:
485
+
486
+ ```python
487
+ import os
488
+ from jaf.core.tracing import create_composite_trace_collector, ConsoleTraceCollector
489
+
490
+ # Configure OpenTelemetry endpoint
491
+ os.environ["TRACE_COLLECTOR_URL"] = "http://localhost:4318/v1/traces"
492
+
493
+ # Tracing will be automatically configured when creating a composite collector
494
+ trace_collector = create_composite_trace_collector(ConsoleTraceCollector())
495
+
496
+ config = RunConfig(
497
+ # ... other config
498
+ on_event=trace_collector.collect,
499
+ )
500
+ ```
501
+
502
+ ### Langfuse Integration
503
+
504
+ For LLM-specific observability and analytics:
505
+
506
+ ```python
507
+ import os
508
+ from jaf.core.tracing import create_composite_trace_collector, ConsoleTraceCollector
509
+
510
+ # Configure Langfuse credentials
511
+ os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-your-public-key"
512
+ os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-your-secret-key"
513
+ os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # or your self-hosted instance
514
+
515
+ # Langfuse tracing will be automatically configured
516
+ trace_collector = create_composite_trace_collector(ConsoleTraceCollector())
517
+
518
+ config = RunConfig(
519
+ # ... other config
520
+ on_event=trace_collector.collect,
521
+ )
522
+ ```
523
+
419
524
  ### Error Handling
420
525
 
421
526
  ```python
@@ -591,7 +696,41 @@ python server_example.py
591
696
  - `POST /chat` - Chat with any agent
592
697
  - `GET /docs` - Interactive API documentation
593
698
 
594
- ### 2. MCP Integration Demo
699
+ ### 2. Agent-as-Tool Demo
700
+
701
+ ```bash
702
+ cd examples
703
+ python agent_as_tool_example.py
704
+
705
+ # Or start as server
706
+ python agent_as_tool_example.py --server
707
+ ```
708
+
709
+ **Features demonstrated:**
710
+ - ✅ Hierarchical agent orchestration
711
+ - ✅ Conditional tool enabling based on context
712
+ - ✅ Custom output extraction from agent tools
713
+ - ✅ Session management for sub-agents
714
+ - ✅ Translation agents working together
715
+
716
+ ### 3. Tracing Integration Demos
717
+
718
+ ```bash
719
+ # OpenTelemetry tracing example
720
+ cd examples
721
+ python otel_tracing_demo.py
722
+
723
+ # Langfuse tracing example
724
+ python langfuse_tracing_demo.py
725
+ ```
726
+
727
+ **Features demonstrated:**
728
+ - ✅ OpenTelemetry distributed tracing setup
729
+ - ✅ Langfuse LLM observability integration
730
+ - ✅ Composite trace collectors
731
+ - ✅ Real-time monitoring and analytics
732
+
733
+ ### 4. MCP Integration Demo
595
734
 
596
735
  ```bash
597
736
  cd examples/mcp_demo
@@ -649,4 +788,4 @@ MIT
649
788
 
650
789
  ---
651
790
 
652
- **JAF (Juspay Agentic Framework) v2.0** - Building the future of functional AI agent systems 🚀
791
+ **JAF (Juspay Agentic Framework) v2.2** - Building the future of functional AI agent systems 🚀
@@ -13,6 +13,7 @@ from .agent_tool import (
13
13
  get_current_run_config,
14
14
  set_current_run_config,
15
15
  )
16
+ from .proxy import ProxyConfig, ProxyAuth, create_proxy_config, get_default_proxy_config
16
17
 
17
18
  __all__ = [
18
19
  "Agent",
@@ -22,6 +23,8 @@ __all__ = [
22
23
  "Message",
23
24
  "ModelConfig",
24
25
  "ModelProvider",
26
+ "ProxyAuth",
27
+ "ProxyConfig",
25
28
  "RunConfig",
26
29
  "RunId",
27
30
  "RunResult",
@@ -39,9 +42,11 @@ __all__ = [
39
42
  "create_conditional_enabler",
40
43
  "create_default_output_extractor",
41
44
  "create_json_output_extractor",
45
+ "create_proxy_config",
42
46
  "create_run_id",
43
47
  "create_trace_id",
44
48
  "get_current_run_config",
49
+ "get_default_proxy_config",
45
50
  "require_permissions",
46
51
  "run",
47
52
  "set_current_run_config",
@@ -91,7 +91,11 @@ async def run(
91
91
  set_current_run_config(config)
92
92
 
93
93
  if config.on_event:
94
- config.on_event(RunStartEvent(data=to_event_data(RunStartEventData(run_id=initial_state.run_id, trace_id=initial_state.trace_id))))
94
+ config.on_event(RunStartEvent(data=to_event_data(RunStartEventData(
95
+ run_id=initial_state.run_id,
96
+ trace_id=initial_state.trace_id,
97
+ session_id=config.conversation_id
98
+ ))))
95
99
 
96
100
  state_with_memory = await _load_conversation_history(initial_state, config)
97
101
  result = await _run_internal(state_with_memory, config)
@@ -631,7 +635,7 @@ async def _execute_tool_calls(
631
635
  result=result_string,
632
636
  trace_id=state.trace_id,
633
637
  run_id=state.run_id,
634
- tool_result=tool_result_obj,
638
+ tool_result=tool_result,
635
639
  status='success'
636
640
  ))))
637
641
 
@@ -0,0 +1,141 @@
1
+ """
2
+ Proxy configuration for JAF agents and HTTP clients.
3
+
4
+ This module provides unified proxy configuration that can be used across
5
+ different HTTP clients (httpx, OpenAI, etc.) in the JAF framework.
6
+ """
7
+
8
+ import os
9
+ from dataclasses import dataclass
10
+ from typing import Optional, Dict, Any, Union
11
+ from urllib.parse import urlparse
12
+
13
+
14
+ @dataclass
15
+ class ProxyAuth:
16
+ """Proxy authentication configuration."""
17
+ username: str
18
+ password: str
19
+
20
+
21
+ @dataclass
22
+ class ProxyConfig:
23
+ """Proxy configuration for HTTP clients."""
24
+ http_proxy: Optional[str] = None
25
+ https_proxy: Optional[str] = None
26
+ no_proxy: Optional[str] = None
27
+ auth: Optional[ProxyAuth] = None
28
+
29
+ @classmethod
30
+ def from_environment(cls) -> 'ProxyConfig':
31
+ """Create proxy configuration from environment variables."""
32
+ return cls(
33
+ http_proxy=os.getenv('HTTP_PROXY') or os.getenv('http_proxy'),
34
+ https_proxy=os.getenv('HTTPS_PROXY') or os.getenv('https_proxy'),
35
+ no_proxy=os.getenv('NO_PROXY') or os.getenv('no_proxy'),
36
+ auth=ProxyAuth(
37
+ username=os.getenv('PROXY_USERNAME', ''),
38
+ password=os.getenv('PROXY_PASSWORD', '')
39
+ ) if os.getenv('PROXY_USERNAME') else None
40
+ )
41
+
42
+ @classmethod
43
+ def from_url(cls, proxy_url: str, auth: Optional[ProxyAuth] = None) -> 'ProxyConfig':
44
+ """Create proxy configuration from a single URL."""
45
+ return cls(
46
+ http_proxy=proxy_url,
47
+ https_proxy=proxy_url,
48
+ auth=auth
49
+ )
50
+
51
+ def to_httpx_proxies(self) -> Optional[Dict[str, str]]:
52
+ """Convert to httpx proxies format."""
53
+ if not self.http_proxy and not self.https_proxy:
54
+ return None
55
+
56
+ proxies = {}
57
+
58
+ if self.http_proxy:
59
+ proxies['http://'] = self._add_auth_to_url(self.http_proxy)
60
+
61
+ if self.https_proxy:
62
+ proxies['https://'] = self._add_auth_to_url(self.https_proxy)
63
+
64
+ return proxies if proxies else None
65
+
66
+ def to_openai_proxies(self) -> Optional[Dict[str, str]]:
67
+ """Convert to OpenAI client proxies format."""
68
+ # OpenAI client supports httpx-style proxy configuration
69
+ return self.to_httpx_proxies()
70
+
71
+ def _add_auth_to_url(self, url: str) -> str:
72
+ """Add authentication to proxy URL if configured."""
73
+ if not self.auth or not self.auth.username:
74
+ return url
75
+
76
+ parsed = urlparse(url)
77
+
78
+ # If URL already has auth, don't override
79
+ if '@' in parsed.netloc:
80
+ return url
81
+
82
+ auth_string = f"{self.auth.username}:{self.auth.password}"
83
+
84
+ # Reconstruct URL with auth
85
+ if parsed.port:
86
+ netloc = f"{auth_string}@{parsed.hostname}:{parsed.port}"
87
+ else:
88
+ netloc = f"{auth_string}@{parsed.hostname}"
89
+
90
+ return f"{parsed.scheme}://{netloc}{parsed.path}"
91
+
92
+ def should_bypass_proxy(self, host: str) -> bool:
93
+ """Check if a host should bypass the proxy based on no_proxy settings."""
94
+ if not self.no_proxy:
95
+ return False
96
+
97
+ no_proxy_hosts = [h.strip() for h in self.no_proxy.split(',')]
98
+
99
+ for no_proxy_host in no_proxy_hosts:
100
+ if not no_proxy_host:
101
+ continue
102
+
103
+ # Exact match
104
+ if host == no_proxy_host:
105
+ return True
106
+
107
+ # Wildcard match (e.g., *.example.com)
108
+ if no_proxy_host.startswith('*'):
109
+ suffix = no_proxy_host[1:]
110
+ if host.endswith(suffix):
111
+ return True
112
+
113
+ # Domain suffix match
114
+ if no_proxy_host.startswith('.'):
115
+ if host.endswith(no_proxy_host) or host == no_proxy_host[1:]:
116
+ return True
117
+
118
+ return False
119
+
120
+
121
+ def create_proxy_config(
122
+ proxy_url: Optional[str] = None,
123
+ username: Optional[str] = None,
124
+ password: Optional[str] = None,
125
+ no_proxy: Optional[str] = None
126
+ ) -> ProxyConfig:
127
+ """Create a proxy configuration with optional parameters."""
128
+ auth = ProxyAuth(username, password) if username else None
129
+
130
+ if proxy_url:
131
+ config = ProxyConfig.from_url(proxy_url, auth)
132
+ if no_proxy:
133
+ config.no_proxy = no_proxy
134
+ return config
135
+
136
+ return ProxyConfig.from_environment()
137
+
138
+
139
+ def get_default_proxy_config() -> ProxyConfig:
140
+ """Get the default proxy configuration from environment variables."""
141
+ return ProxyConfig.from_environment()