jaf-py 2.3.1__py3-none-any.whl → 2.4.1__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/engine.py CHANGED
@@ -36,6 +36,8 @@ from .types import (
36
36
  LLMCallEndEventData,
37
37
  LLMCallStartEvent,
38
38
  LLMCallStartEventData,
39
+ AssistantMessageEvent,
40
+ AssistantMessageEventData,
39
41
  MaxTurnsExceeded,
40
42
  Message,
41
43
  ModelBehaviorError,
@@ -300,8 +302,115 @@ async def _run_internal(
300
302
  messages=state.messages
301
303
  ))))
302
304
 
303
- # Get completion from model provider
304
- llm_response = await config.model_provider.get_completion(state, current_agent, config)
305
+ # Get completion from model provider, prefer streaming if available
306
+ llm_response: Dict[str, Any]
307
+ assistant_event_streamed = False
308
+
309
+ get_stream = getattr(config.model_provider, "get_completion_stream", None)
310
+ if callable(get_stream):
311
+ try:
312
+ aggregated_text = ""
313
+ # Working array of partial tool calls
314
+ partial_tool_calls: List[Dict[str, Any]] = []
315
+
316
+ async for chunk in get_stream(state, current_agent, config): # type: ignore[arg-type]
317
+ # Text deltas
318
+ delta_text = getattr(chunk, "delta", None)
319
+ if delta_text:
320
+ aggregated_text += delta_text
321
+
322
+ # Tool call deltas
323
+ tcd = getattr(chunk, "tool_call_delta", None)
324
+ if tcd is not None:
325
+ idx = getattr(tcd, "index", 0) or 0
326
+ # Ensure slot exists
327
+ while len(partial_tool_calls) <= idx:
328
+ partial_tool_calls.append({
329
+ "id": None,
330
+ "type": "function",
331
+ "function": {"name": None, "arguments": ""}
332
+ })
333
+ target = partial_tool_calls[idx]
334
+ # id
335
+ tc_id = getattr(tcd, "id", None)
336
+ if tc_id:
337
+ target["id"] = tc_id
338
+ # function fields
339
+ fn = getattr(tcd, "function", None)
340
+ if fn is not None:
341
+ fn_name = getattr(fn, "name", None)
342
+ if fn_name:
343
+ target["function"]["name"] = fn_name
344
+ args_delta = getattr(fn, "arguments_delta", None)
345
+ if args_delta:
346
+ target["function"]["arguments"] += args_delta
347
+
348
+ # Emit partial assistant message when something changed
349
+ if delta_text or tcd is not None:
350
+ assistant_event_streamed = True
351
+ # Normalize tool_calls for message
352
+ message_tool_calls = None
353
+ if len(partial_tool_calls) > 0:
354
+ message_tool_calls = []
355
+ for i, tc in enumerate(partial_tool_calls):
356
+ message_tool_calls.append({
357
+ "id": tc["id"] or f"call_{i}",
358
+ "type": "function",
359
+ "function": {
360
+ "name": tc["function"]["name"] or "",
361
+ "arguments": tc["function"]["arguments"]
362
+ }
363
+ })
364
+
365
+ partial_msg = Message(
366
+ role=ContentRole.ASSISTANT,
367
+ content=aggregated_text or "",
368
+ tool_calls=None if not message_tool_calls else [
369
+ ToolCall(
370
+ id=mc["id"],
371
+ type="function",
372
+ function=ToolCallFunction(
373
+ name=mc["function"]["name"],
374
+ arguments=mc["function"]["arguments"],
375
+ ),
376
+ ) for mc in message_tool_calls
377
+ ],
378
+ )
379
+ try:
380
+ if config.on_event:
381
+ config.on_event(AssistantMessageEvent(data=to_event_data(
382
+ AssistantMessageEventData(message=partial_msg)
383
+ )))
384
+ except Exception as _e:
385
+ # Do not fail the run on callback errors
386
+ pass
387
+
388
+ # Build final response object compatible with downstream logic
389
+ final_tool_calls = None
390
+ if len(partial_tool_calls) > 0:
391
+ final_tool_calls = []
392
+ for i, tc in enumerate(partial_tool_calls):
393
+ final_tool_calls.append({
394
+ "id": tc["id"] or f"call_{i}",
395
+ "type": "function",
396
+ "function": {
397
+ "name": tc["function"]["name"] or "",
398
+ "arguments": tc["function"]["arguments"]
399
+ }
400
+ })
401
+
402
+ llm_response = {
403
+ "message": {
404
+ "content": aggregated_text or None,
405
+ "tool_calls": final_tool_calls
406
+ }
407
+ }
408
+ except Exception:
409
+ # Fallback to non-streaming on error
410
+ assistant_event_streamed = False
411
+ llm_response = await config.model_provider.get_completion(state, current_agent, config)
412
+ else:
413
+ llm_response = await config.model_provider.get_completion(state, current_agent, config)
305
414
 
306
415
  # Emit LLM call end event
307
416
  if config.on_event:
jaf/core/tools.py CHANGED
@@ -89,14 +89,12 @@ def create_function_tool(config: FunctionToolConfig) -> Tool:
89
89
  # Validate schema generation (cached for performance)
90
90
  if not hasattr(parameters, '_schema_validated'):
91
91
  try:
92
+ # Generate schema once to validate the model is well-formed.
93
+ # Allow empty object schemas (no parameters) for tools that take no args.
92
94
  if hasattr(parameters, 'model_json_schema'):
93
- test_schema = parameters.model_json_schema()
94
- if not test_schema.get('properties'):
95
- raise ValueError(f"Tool '{tool_name}' has no properties in schema. Check your Pydantic model fields.")
95
+ _ = parameters.model_json_schema()
96
96
  elif hasattr(parameters, 'schema'):
97
- test_schema = parameters.schema()
98
- if not test_schema.get('properties'):
99
- raise ValueError(f"Tool '{tool_name}' has no properties in schema. Check your Pydantic model fields.")
97
+ _ = parameters.schema()
100
98
  parameters._schema_validated = True
101
99
  except Exception as e:
102
100
  logger.error(f"Tool {tool_name} schema generation failed: {e}")
jaf/core/types.py CHANGED
@@ -5,7 +5,7 @@ This module defines all the fundamental data structures and types used throughou
5
5
  the framework, maintaining immutability and type safety.
6
6
  """
7
7
 
8
- from collections.abc import Awaitable
8
+ from collections.abc import Awaitable, AsyncIterator
9
9
 
10
10
  # ReadOnly is only available in Python 3.13+, so we'll use a simpler approach
11
11
  from dataclasses import dataclass, field
@@ -416,6 +416,15 @@ class LLMCallEndEvent:
416
416
  data: LLMCallEndEventData = field(default_factory=lambda: LLMCallEndEventData(None, TraceId(""), RunId("")))
417
417
 
418
418
  @dataclass(frozen=True)
419
+ class AssistantMessageEventData:
420
+ """Data for assistant message events (partial or complete)."""
421
+ message: Message
422
+
423
+ @dataclass(frozen=True)
424
+ class AssistantMessageEvent:
425
+ type: Literal['assistant_message'] = 'assistant_message'
426
+ data: AssistantMessageEventData = field(default_factory=lambda: AssistantMessageEventData(Message(role=ContentRole.ASSISTANT, content="")))
427
+ @dataclass(frozen=True)
419
428
  class ToolCallStartEventData:
420
429
  """Data for tool call start events."""
421
430
  tool_name: str
@@ -515,6 +524,7 @@ TraceEvent = Union[
515
524
  OutputParseEvent,
516
525
  LLMCallStartEvent,
517
526
  LLMCallEndEvent,
527
+ AssistantMessageEvent,
518
528
  ToolCallStartEvent,
519
529
  ToolCallEndEvent,
520
530
  HandoffEvent,
@@ -532,6 +542,30 @@ class ModelCompletionResponse:
532
542
  """Response structure from model completion."""
533
543
  message: Optional[ModelCompletionMessage] = None
534
544
 
545
+ # Streaming chunk structures for provider-level streaming support
546
+ @dataclass(frozen=True)
547
+ class ToolCallFunctionDelta:
548
+ """Function fields that may stream as deltas."""
549
+ name: Optional[str] = None
550
+ arguments_delta: Optional[str] = None
551
+
552
+ @dataclass(frozen=True)
553
+ class ToolCallDelta:
554
+ """Represents a partial tool call delta in a streamed response."""
555
+ index: int
556
+ id: Optional[str] = None
557
+ type: Literal['function'] = 'function'
558
+ function: Optional[ToolCallFunctionDelta] = None
559
+
560
+ @dataclass(frozen=True)
561
+ class CompletionStreamChunk:
562
+ """A streamed chunk from the model provider."""
563
+ delta: Optional[str] = None
564
+ tool_call_delta: Optional[ToolCallDelta] = None
565
+ is_done: Optional[bool] = False
566
+ finish_reason: Optional[str] = None
567
+ raw: Optional[Any] = None
568
+
535
569
  @runtime_checkable
536
570
  class ModelProvider(Protocol[Ctx]):
537
571
  """Protocol for model providers."""
@@ -545,6 +579,15 @@ class ModelProvider(Protocol[Ctx]):
545
579
  """Get completion from the model."""
546
580
  ...
547
581
 
582
+ async def get_completion_stream(
583
+ self,
584
+ state: RunState[Ctx],
585
+ agent: Agent[Ctx, Any],
586
+ config: 'RunConfig[Ctx]'
587
+ ) -> AsyncIterator[CompletionStreamChunk]:
588
+ """Optional streaming API: yields incremental deltas while generating."""
589
+ ...
590
+
548
591
  @dataclass(frozen=True)
549
592
  class RunConfig(Generic[Ctx]):
550
593
  """Configuration for running agents."""
jaf/providers/model.py CHANGED
@@ -5,13 +5,14 @@ This module provides model providers that integrate with various LLM services,
5
5
  starting with LiteLLM for multi-provider support.
6
6
  """
7
7
 
8
- from typing import Any, Dict, Optional, TypeVar
8
+ from typing import Any, Dict, Optional, TypeVar, AsyncIterator, List
9
+ import asyncio
9
10
  import httpx
10
11
 
11
12
  from openai import OpenAI
12
13
  from pydantic import BaseModel
13
14
 
14
- from ..core.types import Agent, ContentRole, Message, ModelProvider, RunConfig, RunState
15
+ from ..core.types import Agent, ContentRole, Message, ModelProvider, RunConfig, RunState, CompletionStreamChunk, ToolCallDelta, ToolCallFunctionDelta
15
16
  from ..core.proxy import ProxyConfig
16
17
 
17
18
  Ctx = TypeVar('Ctx')
@@ -169,6 +170,168 @@ def make_litellm_provider(
169
170
  'prompt': messages
170
171
  }
171
172
 
173
+ async def get_completion_stream(
174
+ self,
175
+ state: RunState[Ctx],
176
+ agent: Agent[Ctx, Any],
177
+ config: RunConfig[Ctx]
178
+ ) -> AsyncIterator[CompletionStreamChunk]:
179
+ """
180
+ Stream completion chunks from the model provider, yielding text deltas and tool-call deltas.
181
+ Uses OpenAI-compatible streaming via LiteLLM endpoint.
182
+ """
183
+ # Determine model to use
184
+ model = (config.model_override or
185
+ (agent.model_config.name if agent.model_config else "gpt-4o"))
186
+
187
+ # Create system message
188
+ system_message = {
189
+ "role": "system",
190
+ "content": agent.instructions(state)
191
+ }
192
+
193
+ # Convert messages to OpenAI format
194
+ messages = [system_message] + [
195
+ _convert_message(msg) for msg in state.messages
196
+ ]
197
+
198
+ # Convert tools to OpenAI format
199
+ tools = None
200
+ if agent.tools:
201
+ tools = [
202
+ {
203
+ "type": "function",
204
+ "function": {
205
+ "name": tool.schema.name,
206
+ "description": tool.schema.description,
207
+ "parameters": _pydantic_to_json_schema(tool.schema.parameters),
208
+ }
209
+ }
210
+ for tool in agent.tools
211
+ ]
212
+
213
+ # Determine tool choice behavior
214
+ last_message = state.messages[-1] if state.messages else None
215
+ is_after_tool_call = last_message and (last_message.role == ContentRole.TOOL or last_message.role == 'tool')
216
+
217
+ # Prepare request parameters
218
+ request_params: Dict[str, Any] = {
219
+ "model": model,
220
+ "messages": messages,
221
+ }
222
+
223
+ # Add optional parameters
224
+ if agent.model_config:
225
+ if agent.model_config.temperature is not None:
226
+ request_params["temperature"] = agent.model_config.temperature
227
+ if agent.model_config.max_tokens is not None:
228
+ request_params["max_tokens"] = agent.model_config.max_tokens
229
+
230
+ if tools:
231
+ request_params["tools"] = tools
232
+ # Set tool_choice to auto when tools are available
233
+ request_params["tool_choice"] = "auto"
234
+
235
+ if agent.output_codec:
236
+ request_params["response_format"] = {"type": "json_object"}
237
+
238
+ # Enable streaming
239
+ request_params["stream"] = True
240
+
241
+ loop = asyncio.get_running_loop()
242
+ queue: asyncio.Queue = asyncio.Queue(maxsize=256)
243
+ SENTINEL = object()
244
+
245
+ def _put(item: CompletionStreamChunk):
246
+ try:
247
+ asyncio.run_coroutine_threadsafe(queue.put(item), loop)
248
+ except RuntimeError:
249
+ # Event loop closed; drop silently
250
+ pass
251
+
252
+ def _producer():
253
+ try:
254
+ stream = self.client.chat.completions.create(**request_params)
255
+ for chunk in stream:
256
+ try:
257
+ # Best-effort extraction of raw for debugging
258
+ try:
259
+ raw_obj = chunk.model_dump() # pydantic BaseModel
260
+ except Exception:
261
+ raw_obj = None
262
+
263
+ choice = None
264
+ if getattr(chunk, "choices", None):
265
+ choice = chunk.choices[0]
266
+
267
+ if choice is None:
268
+ continue
269
+
270
+ delta = getattr(choice, "delta", None)
271
+ finish_reason = getattr(choice, "finish_reason", None)
272
+
273
+ # Text content delta
274
+ if delta is not None:
275
+ content_delta = getattr(delta, "content", None)
276
+ if content_delta:
277
+ _put(CompletionStreamChunk(delta=content_delta, raw=raw_obj))
278
+
279
+ # Tool call deltas
280
+ tool_calls = getattr(delta, "tool_calls", None)
281
+ if isinstance(tool_calls, list):
282
+ for tc in tool_calls:
283
+ # Each tc is likely a pydantic model with .index/.id/.function
284
+ try:
285
+ idx = getattr(tc, "index", 0) or 0
286
+ tc_id = getattr(tc, "id", None)
287
+ fn = getattr(tc, "function", None)
288
+ fn_name = getattr(fn, "name", None) if fn is not None else None
289
+ # OpenAI streams "arguments" as incremental deltas
290
+ args_delta = getattr(fn, "arguments", None) if fn is not None else None
291
+
292
+ _put(CompletionStreamChunk(
293
+ tool_call_delta=ToolCallDelta(
294
+ index=idx,
295
+ id=tc_id,
296
+ type='function',
297
+ function=ToolCallFunctionDelta(
298
+ name=fn_name,
299
+ arguments_delta=args_delta
300
+ )
301
+ ),
302
+ raw=raw_obj
303
+ ))
304
+ except Exception:
305
+ # Skip malformed tool-call deltas
306
+ continue
307
+
308
+ # Completion ended
309
+ if finish_reason:
310
+ _put(CompletionStreamChunk(is_done=True, finish_reason=finish_reason, raw=raw_obj))
311
+ except Exception:
312
+ # Skip individual chunk errors, keep streaming
313
+ continue
314
+ except Exception:
315
+ # On top-level stream error, signal done
316
+ pass
317
+ finally:
318
+ try:
319
+ asyncio.run_coroutine_threadsafe(queue.put(SENTINEL), loop)
320
+ except RuntimeError:
321
+ pass
322
+
323
+ # Start producer in background
324
+ loop.run_in_executor(None, _producer)
325
+
326
+ # Consume queue and yield
327
+ while True:
328
+ item = await queue.get()
329
+ if item is SENTINEL:
330
+ break
331
+ # Guarantee type for consumers
332
+ if isinstance(item, CompletionStreamChunk):
333
+ yield item
334
+
172
335
  return LiteLLMProvider()
173
336
 
174
337
  def _convert_message(msg: Message) -> Dict[str, Any]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jaf-py
3
- Version: 2.3.1
3
+ Version: 2.4.1
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,67 +73,67 @@ Dynamic: license-file
73
73
 
74
74
  <!-- ![JAF Banner](docs/cover.png) -->
75
75
 
76
- [![Version](https://img.shields.io/badge/version-2.2.3-blue.svg)](https://github.com/xynehq/jaf-py)
76
+ [![Version](https://img.shields.io/badge/version-2.3.1-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
 
80
80
  A purely functional agent framework with immutable state and composable tools, professionally converted from TypeScript to Python. JAF enables building production-ready AI agent systems with built-in security, observability, and error handling.
81
81
 
82
- **🎯 Production Ready**: Complete feature parity with TypeScript version, comprehensive test suite, and production deployment support.
83
-
84
- ## 📚 **[Read the Full Documentation](https://xynehq.github.io/jaf-py/)**
85
-
86
- **[🚀 Get Started →](https://xynehq.github.io/jaf-py/getting-started/)** | **[📖 API Reference →](https://xynehq.github.io/jaf-py/api-reference/)** | **[🎮 Examples →](https://xynehq.github.io/jaf-py/examples/)**
87
-
88
- ## Key Features
89
-
90
- ### 🏗️ **Complete TypeScript Conversion**
91
- -**Full Feature Parity**: All TypeScript functionality converted to Python
92
- -**Type Safety**: Pydantic models with runtime validation
93
- -**Immutable State**: Functional programming principles preserved
94
- -**Tool Integration**: Complete tool calling and execution system
95
-
96
- ### 🚀 **Production Ready Server**
97
- -**FastAPI Server**: High-performance async HTTP API
98
- -**Auto Documentation**: Interactive API docs at `/docs`
99
- -**Health Monitoring**: Built-in health checks and metrics
100
- -**CORS Support**: Ready for browser integration
101
-
102
- ### 🔌 **Model Context Protocol (MCP)**
103
- -**MCP Client**: Full MCP specification support
104
- -**Stdio & SSE**: Multiple transport protocols
105
- -**Tool Integration**: Seamless MCP tool integration
106
- -**Auto Discovery**: Dynamic tool loading from MCP servers
107
-
108
- ### 🛡️ **Enterprise Security**
109
- -**Input Guardrails**: Content filtering and validation
110
- -**Output Guardrails**: Response sanitization
111
- -**Permission System**: Role-based access control
112
- -**Audit Logging**: Complete interaction tracing
113
- -**Proxy Support**: Corporate proxy integration with authentication
114
-
115
- ### 📊 **Observability & Monitoring**
116
- -**Real-time Tracing**: Event-driven observability
117
- -**OpenTelemetry Integration**: Distributed tracing with OTLP
118
- -**Langfuse Tracing**: LLM observability and analytics
119
- -**Structured Logging**: JSON-formatted logs
120
- -**Error Handling**: Comprehensive error types and recovery
121
- -**Performance Metrics**: Built-in timing and counters
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
-
129
- ### 🔧 **Developer Experience**
130
- -**CLI Tools**: Project initialization and management
131
- -**Hot Reload**: Development server with auto-reload
132
- -**Type Hints**: Full mypy compatibility
133
- -**Rich Examples**: RAG, multi-agent, agent-as-tool, and server demos
134
- -**Visual Architecture**: Graphviz-powered agent and tool diagrams
135
-
136
- ## 🎯 Core Philosophy
82
+ **Production Ready**: Complete feature parity with TypeScript version, comprehensive test suite, and production deployment support.
83
+
84
+ ## **[Read the Full Documentation](https://xynehq.github.io/jaf-py/)**
85
+
86
+ **[ Get Started →](https://xynehq.github.io/jaf-py/getting-started/)** | **[ API Reference →](https://xynehq.github.io/jaf-py/api-reference/)** | **[ Examples →](https://xynehq.github.io/jaf-py/examples/)**
87
+
88
+ ## Key Features
89
+
90
+ ### **Complete TypeScript Conversion**
91
+ - **Full Feature Parity**: All TypeScript functionality converted to Python
92
+ - **Type Safety**: Pydantic models with runtime validation
93
+ - **Immutable State**: Functional programming principles preserved
94
+ - **Tool Integration**: Complete tool calling and execution system
95
+
96
+ ### **Production Ready Server**
97
+ - **FastAPI Server**: High-performance async HTTP API
98
+ - **Auto Documentation**: Interactive API docs at `/docs`
99
+ - **Health Monitoring**: Built-in health checks and metrics
100
+ - **CORS Support**: Ready for browser integration
101
+
102
+ ### **Model Context Protocol (MCP)**
103
+ - **MCP Client**: Full MCP specification support
104
+ - **Stdio & SSE**: Multiple transport protocols
105
+ - **Tool Integration**: Seamless MCP tool integration
106
+ - **Auto Discovery**: Dynamic tool loading from MCP servers
107
+
108
+ ### **Enterprise Security**
109
+ - **Input Guardrails**: Content filtering and validation
110
+ - **Output Guardrails**: Response sanitization
111
+ - **Permission System**: Role-based access control
112
+ - **Audit Logging**: Complete interaction tracing
113
+ - **Proxy Support**: Corporate proxy integration with authentication
114
+
115
+ ### **Observability & Monitoring**
116
+ - **Real-time Tracing**: Event-driven observability
117
+ - **OpenTelemetry Integration**: Distributed tracing with OTLP
118
+ - **Langfuse Tracing**: LLM observability and analytics
119
+ - **Structured Logging**: JSON-formatted logs
120
+ - **Error Handling**: Comprehensive error types and recovery
121
+ - **Performance Metrics**: Built-in timing and counters
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
+
129
+ ### **Developer Experience**
130
+ - **CLI Tools**: Project initialization and management
131
+ - **Hot Reload**: Development server with auto-reload
132
+ - **Type Hints**: Full mypy compatibility
133
+ - **Rich Examples**: RAG, multi-agent, agent-as-tool, and server demos
134
+ - **Visual Architecture**: Graphviz-powered agent and tool diagrams
135
+
136
+ ## Core Philosophy
137
137
 
138
138
  - **Immutability**: All core data structures are deeply immutable
139
139
  - **Pure Functions**: Core logic expressed as pure, predictable functions
@@ -141,7 +141,7 @@ A purely functional agent framework with immutable state and composable tools, p
141
141
  - **Composition over Configuration**: Build complex behavior by composing simple functions
142
142
  - **Type-Safe by Design**: Leverages Python's type system with Pydantic for runtime safety
143
143
 
144
- ## 🚀 Quick Start
144
+ ## Quick Start
145
145
 
146
146
  ### Installation
147
147
 
@@ -197,37 +197,37 @@ pip install -r requirements-docs.txt
197
197
  ./docs.sh deploy # Deploy to GitHub Pages
198
198
  ```
199
199
 
200
- ## 📖 Documentation
200
+ ## Documentation
201
201
 
202
- ### 🌐 **[Official Documentation Website](https://xynehq.github.io/jaf-py/)**
202
+ ### **[Official Documentation Website](https://xynehq.github.io/jaf-py/)**
203
203
 
204
204
  The complete, searchable documentation is available at **[xynehq.github.io/jaf-py](https://xynehq.github.io/jaf-py/)** with:
205
205
 
206
- -**Interactive navigation** with search and filtering
207
- -**Dark/light mode** with automatic system preference detection
208
- -**Mobile-responsive design** for documentation on any device
209
- -**Live code examples** with syntax highlighting
210
- -**API reference** with auto-generated documentation
211
- -**Always up-to-date** with automatic deployments
206
+ - **Interactive navigation** with search and filtering
207
+ - **Dark/light mode** with automatic system preference detection
208
+ - **Mobile-responsive design** for documentation on any device
209
+ - **Live code examples** with syntax highlighting
210
+ - **API reference** with auto-generated documentation
211
+ - **Always up-to-date** with automatic deployments
212
212
 
213
- ### 📁 **Local Documentation**
213
+ ### **Local Documentation**
214
214
 
215
215
  For offline access, documentation is also available in the [`docs/`](docs/) directory:
216
216
 
217
- - **[📚 Documentation Hub](docs/README.md)** - Your starting point for all documentation
218
- - **[🚀 Getting Started](docs/getting-started.md)** - Installation and first agent tutorial
219
- - **[🏗️ Core Concepts](docs/core-concepts.md)** - JAF's functional architecture principles
220
- - **[📋 API Reference](docs/api-reference.md)** - Complete Python API documentation
221
- - **[🔧 Tools Guide](docs/tools.md)** - Creating and using tools
222
- - **[💾 Memory System](docs/memory-system.md)** - Persistence and memory providers
223
- - **[🤖 Model Providers](docs/model-providers.md)** - LiteLLM integration
224
- - **[📊 Monitoring](docs/monitoring.md)** - Observability, metrics, and alerting
225
- - **[🌐 Server API](docs/server-api.md)** - FastAPI endpoints reference
226
- - **[📦 Deployment](docs/deployment.md)** - Production deployment guide
227
- - **[🎮 Examples](docs/examples.md)** - Detailed example walkthroughs
228
- - **[🔧 Troubleshooting](docs/troubleshooting.md)** - Common issues and solutions
229
-
230
- ## 📁 Project Structure
217
+ - **[ Documentation Hub](docs/README.md)** - Your starting point for all documentation
218
+ - **[ Getting Started](docs/getting-started.md)** - Installation and first agent tutorial
219
+ - **[ Core Concepts](docs/core-concepts.md)** - JAF's functional architecture principles
220
+ - **[ API Reference](docs/api-reference.md)** - Complete Python API documentation
221
+ - **[ Tools Guide](docs/tools.md)** - Creating and using tools
222
+ - **[ Memory System](docs/memory-system.md)** - Persistence and memory providers
223
+ - **[ Model Providers](docs/model-providers.md)** - LiteLLM integration
224
+ - **[ Monitoring](docs/monitoring.md)** - Observability, metrics, and alerting
225
+ - **[ Server API](docs/server-api.md)** - FastAPI endpoints reference
226
+ - **[ Deployment](docs/deployment.md)** - Production deployment guide
227
+ - **[ Examples](docs/examples.md)** - Detailed example walkthroughs
228
+ - **[ Troubleshooting](docs/troubleshooting.md)** - Common issues and solutions
229
+
230
+ ## Project Structure
231
231
 
232
232
  ```
233
233
  jaf-py/
@@ -243,7 +243,7 @@ jaf-py/
243
243
  └── tests/ # Test suite
244
244
  ```
245
245
 
246
- ## 🎨 Architectural Visualization
246
+ ## Architectural Visualization
247
247
 
248
248
  JAF includes powerful visualization capabilities to help you understand and document your agent systems.
249
249
 
@@ -295,7 +295,7 @@ async def main():
295
295
  )
296
296
 
297
297
  if result.success:
298
- print(f" Visualization saved to: {result.output_path}")
298
+ print(f" Visualization saved to: {result.output_path}")
299
299
  else:
300
300
  print(f"❌ Error: {result.error}")
301
301
 
@@ -304,12 +304,12 @@ asyncio.run(main())
304
304
 
305
305
  ### Features
306
306
 
307
- - **🎨 Multiple Color Schemes**: Choose from `default`, `modern`, or `minimal` themes
308
- - **📊 Agent Architecture**: Visualize agents, tools, and handoff relationships
309
- - **🔧 Tool Ecosystems**: Generate dedicated tool interaction diagrams
310
- - **🏃 Runner Architecture**: Show complete system architecture with session layers
311
- - **📄 Multiple Formats**: Export as PNG, SVG, or PDF
312
- - **⚙️ Customizable Layouts**: Support for various Graphviz layouts (`dot`, `circo`, `neato`, etc.)
307
+ - **Multiple Color Schemes**: Choose from `default`, `modern`, or `minimal` themes
308
+ - **Agent Architecture**: Visualize agents, tools, and handoff relationships
309
+ - **Tool Ecosystems**: Generate dedicated tool interaction diagrams
310
+ - **Runner Architecture**: Show complete system architecture with session layers
311
+ - **Multiple Formats**: Export as PNG, SVG, or PDF
312
+ - **Customizable Layouts**: Support for various Graphviz layouts (`dot`, `circo`, `neato`, etc.)
313
313
 
314
314
  ### Example Output
315
315
 
@@ -336,7 +336,7 @@ await run_visualization_examples()
336
336
  # - ./examples/agent-modern.png (modern color scheme)
337
337
  ```
338
338
 
339
- ## 🏗️ Key Components
339
+ ## Key Components
340
340
 
341
341
  ### Core Types
342
342
 
@@ -414,7 +414,7 @@ async def main():
414
414
  asyncio.run(main())
415
415
  ```
416
416
 
417
- ## 🛡️ Security & Validation
417
+ ## Security & Validation
418
418
 
419
419
  ### Composable Validation Policies
420
420
 
@@ -449,7 +449,7 @@ config = RunConfig(
449
449
  )
450
450
  ```
451
451
 
452
- ## 🤖 Agent-as-Tool Functionality
452
+ ## Agent-as-Tool Functionality
453
453
 
454
454
  JAF 2.2+ introduces powerful agent-as-tool capabilities, allowing you to use agents as tools within other agents for hierarchical orchestration:
455
455
 
@@ -528,7 +528,7 @@ def create_triage_agent():
528
528
  )
529
529
  ```
530
530
 
531
- ## 📊 Observability
531
+ ## Observability
532
532
 
533
533
  ### Real-time Tracing
534
534
 
@@ -605,7 +605,7 @@ if result.outcome.status == 'error':
605
605
  print(f"[{severity}] {formatted_error} (retryable: {is_retryable})")
606
606
  ```
607
607
 
608
- ## 🔌 Provider Integrations
608
+ ## Provider Integrations
609
609
 
610
610
  ### A2A (Agent-to-Agent) Communication
611
611
 
@@ -672,7 +672,7 @@ from jaf.providers.mcp import create_mcp_sse_client
672
672
  sse_client = create_mcp_sse_client('http://localhost:8080/sse')
673
673
  ```
674
674
 
675
- ## 🚀 Development Server
675
+ ## Development Server
676
676
 
677
677
  JAF includes a built-in development server for testing agents locally via HTTP endpoints:
678
678
 
@@ -706,7 +706,7 @@ Server provides RESTful endpoints:
706
706
  - `POST /chat` - General chat endpoint
707
707
  - `POST /agents/{name}/chat` - Agent-specific endpoint
708
708
 
709
- ## 🔧 Function Composition
709
+ ## Function Composition
710
710
 
711
711
  JAF supports functional composition patterns for building complex behaviors from simple, reusable functions:
712
712
 
@@ -742,7 +742,7 @@ enhanced_tool = create_function_tool({
742
742
  - **Type Safety**: Full type checking support
743
743
  - **Performance**: Optimize individual pieces independently
744
744
 
745
- ## 🎮 Example Applications
745
+ ## Example Applications
746
746
 
747
747
  Explore the example applications to see the framework in action:
748
748
 
@@ -754,12 +754,12 @@ python server_example.py
754
754
  ```
755
755
 
756
756
  **Features demonstrated:**
757
- -Multiple specialized agents (math, weather, general)
758
- -Tool integration (calculator, weather API)
759
- -Agent handoffs and routing
760
- -RESTful API with auto-documentation
761
- -Real-time tracing and error handling
762
- -Production-ready server configuration
757
+ - Multiple specialized agents (math, weather, general)
758
+ - Tool integration (calculator, weather API)
759
+ - Agent handoffs and routing
760
+ - RESTful API with auto-documentation
761
+ - Real-time tracing and error handling
762
+ - Production-ready server configuration
763
763
 
764
764
  **Available endpoints:**
765
765
  - `GET /health` - Server health check
@@ -778,11 +778,11 @@ python agent_as_tool_example.py --server
778
778
  ```
779
779
 
780
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
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
786
 
787
787
  ### 3. Tracing Integration Demos
788
788
 
@@ -796,10 +796,10 @@ python langfuse_tracing_demo.py
796
796
  ```
797
797
 
798
798
  **Features demonstrated:**
799
- -OpenTelemetry distributed tracing setup
800
- -Langfuse LLM observability integration
801
- -Composite trace collectors
802
- -Real-time monitoring and analytics
799
+ - OpenTelemetry distributed tracing setup
800
+ - Langfuse LLM observability integration
801
+ - Composite trace collectors
802
+ - Real-time monitoring and analytics
803
803
 
804
804
  ### 4. MCP Integration Demo
805
805
 
@@ -809,12 +809,12 @@ python main.py
809
809
  ```
810
810
 
811
811
  **Features demonstrated:**
812
- -Model Context Protocol integration
813
- -Dynamic tool loading from MCP servers
814
- -Secure filesystem operations
815
- -MCP client configuration and management
812
+ - Model Context Protocol integration
813
+ - Dynamic tool loading from MCP servers
814
+ - Secure filesystem operations
815
+ - MCP client configuration and management
816
816
 
817
- ## 🧪 Testing
817
+ ## Testing
818
818
 
819
819
  ```bash
820
820
  pytest # Run tests
@@ -823,7 +823,7 @@ mypy . # Type checking
823
823
  black . # Format code
824
824
  ```
825
825
 
826
- ## 🏛️ Architecture Principles
826
+ ## Architecture Principles
827
827
 
828
828
  ### Immutable State Machine
829
829
  - All state transformations create new state objects
@@ -859,4 +859,4 @@ MIT
859
859
 
860
860
  ---
861
861
 
862
- **JAF (Juspay Agentic Framework) v2.2** - Building the future of functional AI agent systems 🚀
862
+ **JAF (Juspay Agentic Framework) v2.2** - Building the future of functional AI agent systems
@@ -42,16 +42,16 @@ 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=v6yXp-zJF9Cj_9qlaW1rmCz7_N3YuDU02xxlzl-howM,27122
45
+ jaf/core/engine.py,sha256=lsmvVDpmuyo7akALplvJvZvOonSzClCG-0EGbc7o5yQ,32061
46
46
  jaf/core/errors.py,sha256=5fwTNhkojKRQ4wZj3lZlgDnAsrYyjYOwXJkIr5EGNUc,5539
47
47
  jaf/core/performance.py,sha256=jedQmTEkrKMD6_Aw1h8PdG-5TsdYSFFT7Or6k5dmN2g,9974
48
48
  jaf/core/proxy.py,sha256=_WM3cpRlSQLYpgSBrnY30UPMe2iZtlqDQ65kppE-WY0,4609
49
49
  jaf/core/proxy_helpers.py,sha256=i7a5fAX9rLmO4FMBX51-yRkTFwfWedzQNgnLmeLUd_A,4370
50
50
  jaf/core/streaming.py,sha256=c5o9iqpjoYV2LrUpG6qLWCYrWcP-DCcZsvMbyqKunp8,16089
51
51
  jaf/core/tool_results.py,sha256=-bTOqOX02lMyslp5Z4Dmuhx0cLd5o7kgR88qK2HO_sw,11323
52
- jaf/core/tools.py,sha256=SbJRRr4y_xxNYNTulZg6OiyNaHBlo_qXWYY510jxQEs,16489
52
+ jaf/core/tools.py,sha256=84N9A7QQ3xxcOs2eUUot3nmCnt5i7iZT9VwkuzuFBxQ,16274
53
53
  jaf/core/tracing.py,sha256=3ByTbpYMWHJku-NEasK5ncyMOdv7vHwmG6ybJP19pxE,31659
54
- jaf/core/types.py,sha256=9UXrPkepw7opgv1VGbPAC1Zx80RP4-ouRxtb6kVTA-A,17063
54
+ jaf/core/types.py,sha256=SdtvjBNdjyvKGvGpGeUMUnYVGMpriy3QnCHUwPkz3Sg,18596
55
55
  jaf/core/workflows.py,sha256=Ul-82gzjIXtkhnSMSPv-8igikjkMtW1EBo9yrfodtvI,26294
56
56
  jaf/memory/__init__.py,sha256=-L98xlvihurGAzF0DnXtkueDVvO_wV2XxxEwAWdAj50,1400
57
57
  jaf/memory/factory.py,sha256=Fh6JyvQtCKe38DZV5-NnC9vPRCvzBgSSPFIGaX7Nt5E,2958
@@ -68,7 +68,7 @@ jaf/policies/handoff.py,sha256=KJYYuL9T6v6DECRhnsS2Je6q4Aj9_zC5d_KBnvEnZNE,8318
68
68
  jaf/policies/validation.py,sha256=wn-7ynH10E5nk-_r1_kHIYHrBGmLX0EFr-FUTHrsxvc,10903
69
69
  jaf/providers/__init__.py,sha256=j_o-Rubr8d9tNYlFWb6fvzkxIBl3JKK_iabj9wTFia0,2114
70
70
  jaf/providers/mcp.py,sha256=WxcC8gUFpDBBYyhorMcc1jHq3xMDMBtnwyRPthfL0S0,13074
71
- jaf/providers/model.py,sha256=9w2mph6g1TaLOFp3t0VtN2kYEfTa-lyYAVjAqQg3wrU,7748
71
+ jaf/providers/model.py,sha256=nulXAItAptJX6andlZWWGw19iVaqgloVkRzGNRvIKPk,15137
72
72
  jaf/server/__init__.py,sha256=K0vSgTfzn3oM54UX9BeAROpaksYY43mFtfjSXoQrhXA,339
73
73
  jaf/server/main.py,sha256=CTb0ywbPIq9ELfay5MKChVR7BpIQOoEbPjPfpzo2aBQ,2152
74
74
  jaf/server/server.py,sha256=o-n6dPWmlu4_v5ozVW3BTOm1b5kHBQhqRYlHh5sXL-k,8048
@@ -79,9 +79,9 @@ jaf/visualization/functional_core.py,sha256=zedMDZbvjuOugWwnh6SJ2stvRNQX1Hlkb9Ab
79
79
  jaf/visualization/graphviz.py,sha256=WTOM6UP72-lVKwI4_SAr5-GCC3ouckxHv88ypCDQWJ0,12056
80
80
  jaf/visualization/imperative_shell.py,sha256=GpMrAlMnLo2IQgyB2nardCz09vMvAzaYI46MyrvJ0i4,2593
81
81
  jaf/visualization/types.py,sha256=QQcbVeQJLuAOXk8ynd08DXIS-PVCnv3R-XVE9iAcglw,1389
82
- jaf_py-2.3.1.dist-info/licenses/LICENSE,sha256=LXUQBJxdyr-7C4bk9cQBwvsF_xwA-UVstDTKabpcjlI,1063
83
- jaf_py-2.3.1.dist-info/METADATA,sha256=0o3VxXPHt1FO6lHDttZyFGNDk-WPH5nxaX9zXoAvcLg,27613
84
- jaf_py-2.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
85
- jaf_py-2.3.1.dist-info/entry_points.txt,sha256=OtIJeNJpb24kgGrqRx9szGgDx1vL9ayq8uHErmu7U5w,41
86
- jaf_py-2.3.1.dist-info/top_level.txt,sha256=Xu1RZbGaM4_yQX7bpalo881hg7N_dybaOW282F15ruE,4
87
- jaf_py-2.3.1.dist-info/RECORD,,
82
+ jaf_py-2.4.1.dist-info/licenses/LICENSE,sha256=LXUQBJxdyr-7C4bk9cQBwvsF_xwA-UVstDTKabpcjlI,1063
83
+ jaf_py-2.4.1.dist-info/METADATA,sha256=F38_7dDyrX_DFA71FLRmSkqzfoubdyGqUT_iKAjEF5w,27216
84
+ jaf_py-2.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
85
+ jaf_py-2.4.1.dist-info/entry_points.txt,sha256=OtIJeNJpb24kgGrqRx9szGgDx1vL9ayq8uHErmu7U5w,41
86
+ jaf_py-2.4.1.dist-info/top_level.txt,sha256=Xu1RZbGaM4_yQX7bpalo881hg7N_dybaOW282F15ruE,4
87
+ jaf_py-2.4.1.dist-info/RECORD,,
File without changes