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.
- {jaf_py-2.2.2 → jaf_py-2.2.4}/PKG-INFO +144 -5
- {jaf_py-2.2.2 → jaf_py-2.2.4}/README.md +143 -4
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/__init__.py +5 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/engine.py +6 -2
- jaf_py-2.2.4/jaf/core/proxy.py +141 -0
- jaf_py-2.2.4/jaf/core/proxy_helpers.py +126 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/types.py +1 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/providers/model.py +27 -6
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/PKG-INFO +144 -5
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/SOURCES.txt +3 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/pyproject.toml +1 -1
- jaf_py-2.2.4/tests/test_proxy_simple.py +117 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/LICENSE +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/agent.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/agent_card.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/client.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/client_example.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/integration_example.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/rag_demo/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/server_demo/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/examples/server_example.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/cleanup.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/factory.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/providers/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/providers/composite.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/providers/in_memory.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/providers/postgres.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/providers/redis.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/serialization.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/run_comprehensive_tests.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/test_cleanup.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/test_serialization.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/test_stress_concurrency.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/tests/test_task_lifecycle.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/memory/types.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/protocol.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/server.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/standalone_client.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/run_tests.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/test_agent.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/test_client.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/test_integration.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/test_protocol.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/tests/test_types.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/a2a/types.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/cli.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/agent_tool.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/analytics.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/composition.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/errors.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/performance.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/streaming.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/tool_results.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/tools.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/tracing.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/core/workflows.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/exceptions.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/factory.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/providers/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/providers/in_memory.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/providers/postgres.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/providers/redis.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/types.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/memory/utils.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/plugins/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/plugins/base.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/policies/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/policies/handoff.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/policies/validation.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/providers/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/providers/mcp.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/server/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/server/main.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/server/server.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/server/types.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/__init__.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/example.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/functional_core.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/graphviz.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/imperative_shell.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf/visualization/types.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/dependency_links.txt +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/entry_points.txt +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/requires.txt +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/jaf_py.egg-info/top_level.txt +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/setup.cfg +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/setup.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_a2a_deep.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_a2a_examples.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_api_reference_examples.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_callback_system_examples.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_coffee_tool.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_conversation_id_fix.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_deployment_examples.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_docs_code_examples.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_engine.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_engine_manual.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_error_handling_examples.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_getting_started_examples.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_math_tool.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_mcp_comprehensive.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_mcp_docs.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_mcp_real_functionality.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_mcp_transports.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_memory_system_examples.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_model_providers_examples.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_property_based.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_redis_fixes.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_redis_memory.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_server_api_examples.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_session_continuity.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_streamable_http_mcp_example.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_timeout_functionality.py +0 -0
- {jaf_py-2.2.2 → jaf_py-2.2.4}/tests/test_tool_integration.py +0 -0
- {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.
|
|
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
|
<!--  -->
|
|
75
75
|
|
|
76
|
-
[](https://github.com/xynehq/jaf-py)
|
|
77
77
|
[](https://www.python.org/)
|
|
78
78
|
[](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.
|
|
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.
|
|
862
|
+
**JAF (Juspay Agentic Framework) v2.2** - Building the future of functional AI agent systems 🚀
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
<!--  -->
|
|
4
4
|
|
|
5
|
-
[](https://github.com/xynehq/jaf-py)
|
|
6
6
|
[](https://www.python.org/)
|
|
7
7
|
[](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.
|
|
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.
|
|
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(
|
|
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=
|
|
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()
|