jaf-py 2.2.2__py3-none-any.whl → 2.2.4__py3-none-any.whl

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/core/__init__.py CHANGED
@@ -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",
jaf/core/engine.py CHANGED
@@ -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
 
jaf/core/proxy.py ADDED
@@ -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()
@@ -0,0 +1,126 @@
1
+ """
2
+ Helper functions for creating JAF core components with proxy support.
3
+
4
+ This module provides convenience functions to easily create model providers
5
+ with proxy configuration.
6
+ """
7
+
8
+ from typing import Optional, Dict, Any
9
+ from .proxy import ProxyConfig, get_default_proxy_config
10
+ from ..providers.model import make_litellm_provider
11
+
12
+
13
+ def make_proxy_enabled_litellm_provider(
14
+ base_url: str,
15
+ api_key: str = "anything",
16
+ default_timeout: Optional[float] = None,
17
+ proxy_url: Optional[str] = None,
18
+ proxy_username: Optional[str] = None,
19
+ proxy_password: Optional[str] = None,
20
+ use_env_proxy: bool = True
21
+ ):
22
+ """
23
+ Create a LiteLLM provider with proxy support.
24
+
25
+ Args:
26
+ base_url: Base URL for the LiteLLM server
27
+ api_key: API key (defaults to "anything" for local servers)
28
+ default_timeout: Default timeout for model API calls in seconds
29
+ proxy_url: Proxy URL (if not using environment variables)
30
+ proxy_username: Proxy username for authentication
31
+ proxy_password: Proxy password for authentication
32
+ use_env_proxy: Whether to use proxy settings from environment variables
33
+
34
+ Returns:
35
+ ModelProvider with proxy configuration
36
+ """
37
+ proxy_config = None
38
+
39
+ if proxy_url:
40
+ # Use explicitly provided proxy settings
41
+ from .proxy import ProxyAuth, create_proxy_config
42
+ proxy_config = create_proxy_config(
43
+ proxy_url=proxy_url,
44
+ username=proxy_username,
45
+ password=proxy_password
46
+ )
47
+ elif use_env_proxy:
48
+ # Use environment-based proxy settings
49
+ proxy_config = get_default_proxy_config()
50
+ # Only use proxy if actually configured in environment
51
+ if not proxy_config.http_proxy and not proxy_config.https_proxy:
52
+ proxy_config = None
53
+
54
+ return make_litellm_provider(
55
+ base_url=base_url,
56
+ api_key=api_key,
57
+ default_timeout=default_timeout,
58
+ proxy_config=proxy_config
59
+ )
60
+
61
+
62
+
63
+
64
+ def get_proxy_info() -> Dict[str, Any]:
65
+ """
66
+ Get information about current proxy configuration from environment.
67
+
68
+ Returns:
69
+ Dictionary with proxy configuration details
70
+ """
71
+ proxy_config = get_default_proxy_config()
72
+
73
+ return {
74
+ "http_proxy": proxy_config.http_proxy,
75
+ "https_proxy": proxy_config.https_proxy,
76
+ "no_proxy": proxy_config.no_proxy,
77
+ "has_auth": proxy_config.auth is not None,
78
+ "auth_username": proxy_config.auth.username if proxy_config.auth else None,
79
+ "is_configured": bool(proxy_config.http_proxy or proxy_config.https_proxy)
80
+ }
81
+
82
+
83
+ def validate_proxy_config(proxy_config: ProxyConfig) -> Dict[str, Any]:
84
+ """
85
+ Validate proxy configuration and return validation results.
86
+
87
+ Args:
88
+ proxy_config: Proxy configuration to validate
89
+
90
+ Returns:
91
+ Dictionary with validation results
92
+ """
93
+ results = {
94
+ "valid": True,
95
+ "warnings": [],
96
+ "errors": []
97
+ }
98
+
99
+ # Check if at least one proxy is configured
100
+ if not proxy_config.http_proxy and not proxy_config.https_proxy:
101
+ results["warnings"].append("No proxy URLs configured")
102
+
103
+ # Validate proxy URLs if configured
104
+ for proxy_type, proxy_url in [("HTTP", proxy_config.http_proxy), ("HTTPS", proxy_config.https_proxy)]:
105
+ if proxy_url:
106
+ try:
107
+ from urllib.parse import urlparse
108
+ parsed = urlparse(proxy_url)
109
+ if not parsed.scheme:
110
+ results["errors"].append(f"{proxy_type} proxy URL missing scheme: {proxy_url}")
111
+ results["valid"] = False
112
+ if not parsed.netloc:
113
+ results["errors"].append(f"{proxy_type} proxy URL missing host: {proxy_url}")
114
+ results["valid"] = False
115
+ except Exception as e:
116
+ results["errors"].append(f"Invalid {proxy_type} proxy URL: {e}")
117
+ results["valid"] = False
118
+
119
+ # Check authentication consistency
120
+ if proxy_config.auth:
121
+ if not proxy_config.auth.username:
122
+ results["warnings"].append("Proxy authentication configured but username is empty")
123
+ if not proxy_config.auth.password:
124
+ results["warnings"].append("Proxy authentication configured but password is empty")
125
+
126
+ return results
jaf/core/types.py CHANGED
@@ -378,6 +378,7 @@ class RunStartEventData:
378
378
  """Data for run start events."""
379
379
  run_id: RunId
380
380
  trace_id: TraceId
381
+ session_id: Optional[str] = None
381
382
 
382
383
  @dataclass(frozen=True)
383
384
  class RunStartEvent:
jaf/providers/model.py CHANGED
@@ -6,18 +6,21 @@ starting with LiteLLM for multi-provider support.
6
6
  """
7
7
 
8
8
  from typing import Any, Dict, Optional, TypeVar
9
+ import httpx
9
10
 
10
11
  from openai import OpenAI
11
12
  from pydantic import BaseModel
12
13
 
13
14
  from ..core.types import Agent, ContentRole, Message, ModelProvider, RunConfig, RunState
15
+ from ..core.proxy import ProxyConfig
14
16
 
15
17
  Ctx = TypeVar('Ctx')
16
18
 
17
19
  def make_litellm_provider(
18
20
  base_url: str,
19
21
  api_key: str = "anything",
20
- default_timeout: Optional[float] = None
22
+ default_timeout: Optional[float] = None,
23
+ proxy_config: Optional[ProxyConfig] = None
21
24
  ) -> ModelProvider[Ctx]:
22
25
  """
23
26
  Create a LiteLLM-compatible model provider.
@@ -26,6 +29,7 @@ def make_litellm_provider(
26
29
  base_url: Base URL for the LiteLLM server
27
30
  api_key: API key (defaults to "anything" for local servers)
28
31
  default_timeout: Default timeout for model API calls in seconds
32
+ proxy_config: Optional proxy configuration
29
33
 
30
34
  Returns:
31
35
  ModelProvider instance
@@ -35,11 +39,28 @@ def make_litellm_provider(
35
39
  def __init__(self):
36
40
  # Default to "anything" if api_key is not provided, for local servers
37
41
  effective_api_key = api_key if api_key is not None else "anything"
38
- self.client = OpenAI(
39
- base_url=base_url,
40
- api_key=effective_api_key,
41
- # Note: dangerouslyAllowBrowser is JavaScript-specific
42
- )
42
+
43
+ # Configure HTTP client with proxy support
44
+ client_kwargs = {
45
+ "base_url": base_url,
46
+ "api_key": effective_api_key,
47
+ }
48
+
49
+ if proxy_config:
50
+ proxies = proxy_config.to_httpx_proxies()
51
+ if proxies:
52
+ # Create httpx client with proxy configuration
53
+ try:
54
+ # Use the https proxy if available, otherwise http proxy
55
+ proxy_url = proxies.get('https://') or proxies.get('http://')
56
+ if proxy_url:
57
+ http_client = httpx.Client(proxy=proxy_url)
58
+ client_kwargs["http_client"] = http_client
59
+ except Exception as e:
60
+ print(f"Warning: Could not configure proxy: {e}")
61
+ # Fall back to environment variables for proxy
62
+
63
+ self.client = OpenAI(**client_kwargs)
43
64
  self.default_timeout = default_timeout
44
65
 
45
66
  async def get_completion(
@@ -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 🚀
@@ -38,18 +38,20 @@ jaf/a2a/tests/test_client.py,sha256=L5h7DtQRVlULiRhRLtrmaCoYdvmbXsgLTy3QQ6KgmNM,
38
38
  jaf/a2a/tests/test_integration.py,sha256=I7LdgwN99mAOljM9kYtK7dGMMntTSWKMw_oLOcJjinU,18454
39
39
  jaf/a2a/tests/test_protocol.py,sha256=He3vGlBfIazpppAnuSybutrvjIN3VGxEleAohrVd9hc,23287
40
40
  jaf/a2a/tests/test_types.py,sha256=PgRjDVJrHSXuu05z0B5lsSUUY5qEdQLFJbLBIExyVgI,18384
41
- jaf/core/__init__.py,sha256=jpeow6G3SpDtYaY4hdEoTQ2ZgPUSUZTDR_AFwrs2cRE,1155
41
+ jaf/core/__init__.py,sha256=rBvP_7TGbJICDJnA7a3qyX8yQErCDWaGAn5WzpyH4gU,1339
42
42
  jaf/core/agent_tool.py,sha256=8TcBuSxGmDTW5F_GhBU_m5S43nYqkjO4qTrNERraAig,11656
43
43
  jaf/core/analytics.py,sha256=NrUfOLLTDIhOzdfc65ZqS9AJ4ZAP9BtNtga69q0YdYw,23265
44
44
  jaf/core/composition.py,sha256=IVxRO1Q9nK7JRH32qQ4p8WMIUu66BhqPNrlTNMGFVwE,26317
45
- jaf/core/engine.py,sha256=KqY87WFpmfU-mPzTzLXnlncyq2xKDSYONJiz0tYJZfI,26861
45
+ jaf/core/engine.py,sha256=ydlF2m9GWyHXWAP6dng8sOwxEWcaaH8etkImorfY0aY,26954
46
46
  jaf/core/errors.py,sha256=5fwTNhkojKRQ4wZj3lZlgDnAsrYyjYOwXJkIr5EGNUc,5539
47
47
  jaf/core/performance.py,sha256=jedQmTEkrKMD6_Aw1h8PdG-5TsdYSFFT7Or6k5dmN2g,9974
48
+ jaf/core/proxy.py,sha256=_WM3cpRlSQLYpgSBrnY30UPMe2iZtlqDQ65kppE-WY0,4609
49
+ jaf/core/proxy_helpers.py,sha256=i7a5fAX9rLmO4FMBX51-yRkTFwfWedzQNgnLmeLUd_A,4370
48
50
  jaf/core/streaming.py,sha256=c5o9iqpjoYV2LrUpG6qLWCYrWcP-DCcZsvMbyqKunp8,16089
49
51
  jaf/core/tool_results.py,sha256=-bTOqOX02lMyslp5Z4Dmuhx0cLd5o7kgR88qK2HO_sw,11323
50
52
  jaf/core/tools.py,sha256=SbJRRr4y_xxNYNTulZg6OiyNaHBlo_qXWYY510jxQEs,16489
51
53
  jaf/core/tracing.py,sha256=r96LCi4dV7BSoqVuFMXI7j72G9jOwpbEjnhYqubuQzc,23455
52
- jaf/core/types.py,sha256=RWHkWm18bAFx7SMSuCGi1cQnCmQR9yxkyz-FppDpglM,16868
54
+ jaf/core/types.py,sha256=fF7_UMFTPUzGQQxZ9tGoWdjF5LSI02RHoJ9vvSe3W8I,16905
53
55
  jaf/core/workflows.py,sha256=Ul-82gzjIXtkhnSMSPv-8igikjkMtW1EBo9yrfodtvI,26294
54
56
  jaf/memory/__init__.py,sha256=-L98xlvihurGAzF0DnXtkueDVvO_wV2XxxEwAWdAj50,1400
55
57
  jaf/memory/factory.py,sha256=Fh6JyvQtCKe38DZV5-NnC9vPRCvzBgSSPFIGaX7Nt5E,2958
@@ -66,7 +68,7 @@ jaf/policies/handoff.py,sha256=KJYYuL9T6v6DECRhnsS2Je6q4Aj9_zC5d_KBnvEnZNE,8318
66
68
  jaf/policies/validation.py,sha256=wn-7ynH10E5nk-_r1_kHIYHrBGmLX0EFr-FUTHrsxvc,10903
67
69
  jaf/providers/__init__.py,sha256=j_o-Rubr8d9tNYlFWb6fvzkxIBl3JKK_iabj9wTFia0,2114
68
70
  jaf/providers/mcp.py,sha256=WxcC8gUFpDBBYyhorMcc1jHq3xMDMBtnwyRPthfL0S0,13074
69
- jaf/providers/model.py,sha256=hKDLLE4sMqCiQT03VcNSgsvMp3phMvFQewRDDsP-qWw,6781
71
+ jaf/providers/model.py,sha256=9w2mph6g1TaLOFp3t0VtN2kYEfTa-lyYAVjAqQg3wrU,7748
70
72
  jaf/server/__init__.py,sha256=K0vSgTfzn3oM54UX9BeAROpaksYY43mFtfjSXoQrhXA,339
71
73
  jaf/server/main.py,sha256=CTb0ywbPIq9ELfay5MKChVR7BpIQOoEbPjPfpzo2aBQ,2152
72
74
  jaf/server/server.py,sha256=o-n6dPWmlu4_v5ozVW3BTOm1b5kHBQhqRYlHh5sXL-k,8048
@@ -77,9 +79,9 @@ jaf/visualization/functional_core.py,sha256=zedMDZbvjuOugWwnh6SJ2stvRNQX1Hlkb9Ab
77
79
  jaf/visualization/graphviz.py,sha256=WTOM6UP72-lVKwI4_SAr5-GCC3ouckxHv88ypCDQWJ0,12056
78
80
  jaf/visualization/imperative_shell.py,sha256=GpMrAlMnLo2IQgyB2nardCz09vMvAzaYI46MyrvJ0i4,2593
79
81
  jaf/visualization/types.py,sha256=QQcbVeQJLuAOXk8ynd08DXIS-PVCnv3R-XVE9iAcglw,1389
80
- jaf_py-2.2.2.dist-info/licenses/LICENSE,sha256=LXUQBJxdyr-7C4bk9cQBwvsF_xwA-UVstDTKabpcjlI,1063
81
- jaf_py-2.2.2.dist-info/METADATA,sha256=08sGQ4y4S6KFtf5_ay-VnraZ14z5lR4qIqqBkT_xMNw,23077
82
- jaf_py-2.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
83
- jaf_py-2.2.2.dist-info/entry_points.txt,sha256=OtIJeNJpb24kgGrqRx9szGgDx1vL9ayq8uHErmu7U5w,41
84
- jaf_py-2.2.2.dist-info/top_level.txt,sha256=Xu1RZbGaM4_yQX7bpalo881hg7N_dybaOW282F15ruE,4
85
- jaf_py-2.2.2.dist-info/RECORD,,
82
+ jaf_py-2.2.4.dist-info/licenses/LICENSE,sha256=LXUQBJxdyr-7C4bk9cQBwvsF_xwA-UVstDTKabpcjlI,1063
83
+ jaf_py-2.2.4.dist-info/METADATA,sha256=LRWLY4HHpn57BSrHesTA1i5gXSP95zA7Wt1vjIx5P7w,27613
84
+ jaf_py-2.2.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
85
+ jaf_py-2.2.4.dist-info/entry_points.txt,sha256=OtIJeNJpb24kgGrqRx9szGgDx1vL9ayq8uHErmu7U5w,41
86
+ jaf_py-2.2.4.dist-info/top_level.txt,sha256=Xu1RZbGaM4_yQX7bpalo881hg7N_dybaOW282F15ruE,4
87
+ jaf_py-2.2.4.dist-info/RECORD,,
File without changes