# JAF (Juspay Agent Framework) - Python Implementation
<!--  -->
[](https://github.com/xynehq/jaf-py)
[](https://www.python.org/)
[](https://xynehq.github.io/jaf-py/)
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.
**Production Ready**: Complete feature parity with TypeScript version, comprehensive test suite, and production deployment support.
## **[Read the Full Documentation](https://xynehq.github.io/jaf-py/)**
**[ 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/)**
## Key Features
### **Complete TypeScript Conversion**
- **Full Feature Parity**: All TypeScript functionality converted to Python
- **Type Safety**: Pydantic models with runtime validation
- **Immutable State**: Functional programming principles preserved
- **Tool Integration**: Complete tool calling and execution system
### **Production Ready Server**
- **FastAPI Server**: High-performance async HTTP API
- **Auto Documentation**: Interactive API docs at `/docs`
- **Health Monitoring**: Built-in health checks and metrics
- **CORS Support**: Ready for browser integration
### **Model Context Protocol (MCP)**
- **MCP Client**: Full MCP specification support
- **Stdio & SSE**: Multiple transport protocols
- **Tool Integration**: Seamless MCP tool integration
- **Auto Discovery**: Dynamic tool loading from MCP servers
### **Enterprise Security**
- **Input Guardrails**: Content filtering and validation
- **Output Guardrails**: Response sanitization
- **Permission System**: Role-based access control
- **Audit Logging**: Complete interaction tracing
- **Proxy Support**: Corporate proxy integration with authentication
### **Observability & Monitoring**
- **Real-time Tracing**: Event-driven observability
- **OpenTelemetry Integration**: Distributed tracing with OTLP
- **Langfuse Tracing**: LLM observability and analytics
- **Structured Logging**: JSON-formatted logs
- **Error Handling**: Comprehensive error types and recovery
- **Performance Metrics**: Built-in timing and counters
### **Agent-as-Tool Architecture**
- **Hierarchical Orchestration**: Use agents as tools in other agents
- **Conditional Tool Enabling**: Enable/disable agent tools based on context
- **Session Management**: Configurable session inheritance for sub-agents
- **Flexible Output Extraction**: Custom extractors for agent tool outputs
### **Developer Experience**
- **CLI Tools**: Project initialization and management
- **Hot Reload**: Development server with auto-reload
- **Type Hints**: Full mypy compatibility
- **Rich Examples**: RAG, multi-agent, agent-as-tool, and server demos
- **Visual Architecture**: Graphviz-powered agent and tool diagrams
## Core Philosophy
- **Immutability**: All core data structures are deeply immutable
- **Pure Functions**: Core logic expressed as pure, predictable functions
- **Effects at the Edge**: Side effects isolated in Provider modules
- **Composition over Configuration**: Build complex behavior by composing simple functions
- **Type-Safe by Design**: Leverages Python's type system with Pydantic for runtime safety
## Quick Start
### Installation
```bash
# Install from GitHub (development version)
pip install git+https://github.com/xynehq/jaf-py.git
# Or install with all optional dependencies
pip install "jaf-py[all] @ git+https://github.com/xynehq/jaf-py.git"
# Install specific feature sets
pip install "jaf-py[server] @ git+https://github.com/xynehq/jaf-py.git" # FastAPI server support
pip install "jaf-py[memory] @ git+https://github.com/xynehq/jaf-py.git" # Redis/PostgreSQL memory providers
pip install "jaf-py[visualization] @ git+https://github.com/xynehq/jaf-py.git" # Graphviz visualization tools
pip install "jaf-py[tracing] @ git+https://github.com/xynehq/jaf-py.git" # OpenTelemetry and Langfuse tracing
pip install "jaf-py[dev] @ git+https://github.com/xynehq/jaf-py.git" # Development tools
```
### CLI Usage
JAF includes a powerful CLI for project management:
```bash
# Initialize a new JAF project
jaf init my-agent-project
# Run the development server
jaf server --host 0.0.0.0 --port 8000
# Show version and help
jaf version
jaf --help
```
### Development Setup
```bash
git clone https://github.com/xynehq/jaf-py
cd jaf-py
pip install -e ".[dev]"
# Run tests
pytest
# Type checking and linting
mypy jaf/
ruff check jaf/
black jaf/
# Documentation
pip install -r requirements-docs.txt
./docs.sh serve # Start documentation server
./docs.sh deploy # Deploy to GitHub Pages
```
## Documentation
### **[Official Documentation Website](https://xynehq.github.io/jaf-py/)**
The complete, searchable documentation is available at **[xynehq.github.io/jaf-py](https://xynehq.github.io/jaf-py/)** with:
- **Interactive navigation** with search and filtering
- **Dark/light mode** with automatic system preference detection
- **Mobile-responsive design** for documentation on any device
- **Live code examples** with syntax highlighting
- **API reference** with auto-generated documentation
- **Always up-to-date** with automatic deployments
### **Local Documentation**
For offline access, documentation is also available in the [`docs/`](docs/) directory:
- **[ Documentation Hub](docs/README.md)** - Your starting point for all documentation
- **[ Getting Started](docs/getting-started.md)** - Installation and first agent tutorial
- **[ Core Concepts](docs/core-concepts.md)** - JAF's functional architecture principles
- **[ API Reference](docs/api-reference.md)** - Complete Python API documentation
- **[ Tools Guide](docs/tools.md)** - Creating and using tools
- **[ Memory System](docs/memory-system.md)** - Persistence and memory providers
- **[ Model Providers](docs/model-providers.md)** - LiteLLM integration
- **[ Monitoring](docs/monitoring.md)** - Observability, metrics, and alerting
- **[ Server API](docs/server-api.md)** - FastAPI endpoints reference
- **[ Deployment](docs/deployment.md)** - Production deployment guide
- **[ Examples](docs/examples.md)** - Detailed example walkthroughs
- **[ Troubleshooting](docs/troubleshooting.md)** - Common issues and solutions
## Project Structure
```
jaf-py/
├── docs/ # Complete documentation
├── jaf/ # Core framework package
│ ├── core/ # Core types and engine
│ ├── memory/ # Conversation persistence
│ ├── providers/ # External integrations (LiteLLM, MCP)
│ ├── policies/ # Validation and security
│ ├── server/ # FastAPI server
│ └── cli.py # Command-line interface
├── examples/ # Example applications
└── tests/ # Test suite
```
## Architectural Visualization
JAF includes powerful visualization capabilities to help you understand and document your agent systems.
### Prerequisites
First, install the system Graphviz dependency:
```bash
# macOS
brew install graphviz
# Ubuntu/Debian
sudo apt-get install graphviz
# Windows (via Chocolatey)
choco install graphviz
```
Then install JAF with visualization support:
```bash
pip install "jaf-py[visualization]"
```
### Quick Start
```python
import asyncio
from jaf import Agent, Tool, generate_agent_graph, GraphOptions
# Create your agents
agent = Agent(
name='MyAgent',
instructions=lambda state: "I am a helpful assistant.",
tools=[my_tool],
handoffs=['OtherAgent']
)
# Generate visualization
async def main():
result = await generate_agent_graph(
[agent],
GraphOptions(
title="My Agent System",
output_path="./my-agents.png",
color_scheme="modern",
show_tool_details=True
)
)
if result.success:
print(f" Visualization saved to: {result.output_path}")
else:
print(f"❌ Error: {result.error}")
asyncio.run(main())
```
### Features
- **Multiple Color Schemes**: Choose from `default`, `modern`, or `minimal` themes
- **Agent Architecture**: Visualize agents, tools, and handoff relationships
- **Tool Ecosystems**: Generate dedicated tool interaction diagrams
- **Runner Architecture**: Show complete system architecture with session layers
- **Multiple Formats**: Export as PNG, SVG, or PDF
- **Customizable Layouts**: Support for various Graphviz layouts (`dot`, `circo`, `neato`, etc.)
### Example Output
The visualization system generates clear, professional diagrams showing:
- **Agent Nodes**: Rounded rectangles with agent names and model information
- **Tool Nodes**: Ellipses showing tool names and descriptions
- **Handoff Edges**: Dashed lines indicating agent handoff relationships
- **Tool Connections**: Colored edges connecting agents to their tools
- **Cluster Organization**: Grouped components in runner architecture views
### Advanced Usage
```python
from jaf.visualization import run_visualization_examples
# Run comprehensive examples
await run_visualization_examples()
# This generates multiple example files:
# - ./examples/agent-graph.png (agent system overview)
# - ./examples/tool-graph.png (tool ecosystem)
# - ./examples/runner-architecture.png (complete system)
# - ./examples/agent-modern.png (modern color scheme)
```
## Key Components
### Core Types
```python
from dataclasses import dataclass
from pydantic import BaseModel, Field
from jaf import Agent, Tool, RunState, run
# Define your context type
@dataclass
class MyContext:
user_id: str
permissions: list[str]
# Define tool schema
class CalculateArgs(BaseModel):
expression: str = Field(description="Math expression to evaluate")
# Create a tool
class CalculatorTool:
@property
def schema(self):
return type('ToolSchema', (), {
'name': 'calculate',
'description': 'Perform mathematical calculations',
'parameters': CalculateArgs
})()
async def execute(self, args: CalculateArgs, context: MyContext) -> str:
result = eval(args.expression) # Don't do this in production!
return f"{args.expression} = {result}"
# Define an agent
def create_math_agent():
def instructions(state):
return 'You are a helpful math tutor'
return Agent(
name='MathTutor',
instructions=instructions,
tools=[CalculatorTool()]
)
```
### Running the Framework
```python
import asyncio
from jaf import run, make_litellm_provider, generate_run_id, generate_trace_id
from jaf.core.types import RunState, RunConfig, Message
async def main():
model_provider = make_litellm_provider('http://localhost:4000')
math_agent = create_math_agent()
config = RunConfig(
agent_registry={'MathTutor': math_agent},
model_provider=model_provider,
max_turns=10,
on_event=lambda event: print(event), # Real-time tracing
)
initial_state = RunState(
run_id=generate_run_id(),
trace_id=generate_trace_id(),
messages=[Message(role='user', content='What is 2 + 2?')],
current_agent_name='MathTutor',
context=MyContext(user_id='user123', permissions=['user']),
turn_count=0,
)
result = await run(initial_state, config)
print(result.outcome.output if result.outcome.status == 'completed' else result.outcome.error)
asyncio.run(main())
```
## Security & Validation
### Composable Validation Policies
```python
from jaf.policies.validation import create_path_validator, create_permission_validator, compose_validations
# Create individual validators
path_validator = create_path_validator(['/shared', '/public'])
permission_validator = create_permission_validator('admin', lambda ctx: ctx)
# Compose them
combined_validator = compose_validations(path_validator, permission_validator)
# Apply to tools
secure_file_tool = with_validation(base_file_tool, combined_validator)
```
### Guardrails
```python
from jaf.policies.validation import create_content_filter, create_length_guardrail
config = RunConfig(
# ... other config
initial_input_guardrails=[
create_content_filter(['spam', 'inappropriate']), # Requires blocked patterns
create_length_guardrail(max_length=1000, min_length=1)
],
final_output_guardrails=[
create_content_filter(['harmful', 'unsafe'])
],
)
```
## Agent-as-Tool Functionality
JAF 2.2+ introduces powerful agent-as-tool capabilities, allowing you to use agents as tools within other agents for hierarchical orchestration:
```python
from jaf.core.agent_tool import create_agent_tool
from jaf.core.types import create_json_output_extractor
# Create specialized agents
spanish_agent = Agent(
name="spanish_translator",
instructions=lambda state: "Translate text to Spanish",
output_codec=TranslationOutput
)
french_agent = Agent(
name="french_translator",
instructions=lambda state: "Translate text to French",
output_codec=TranslationOutput
)
# Convert agents to tools with conditional enabling
spanish_tool = spanish_agent.as_tool(
tool_name="translate_to_spanish",
tool_description="Translate text to Spanish",
max_turns=3,
custom_output_extractor=create_json_output_extractor(),
is_enabled=True # Always enabled
)
french_tool = french_agent.as_tool(
tool_name="translate_to_french",
tool_description="Translate text to French",
max_turns=3,
custom_output_extractor=create_json_output_extractor(),
is_enabled=lambda context, agent: context.language_preference == "french_spanish"
)
# Create orchestrator agent using agent tools
orchestrator = Agent(
name="translation_orchestrator",
instructions=lambda state: "Use translation tools to respond in multiple languages",
tools=[spanish_tool, french_tool]
)
```
### Key Features:
- **Conditional Enabling**: Enable/disable agent tools based on runtime context
- **Session Management**: Configure whether sub-agents inherit parent session state
- **Custom Output Extraction**: Define how to extract and format agent tool outputs
- **Error Handling**: Robust error handling for failed agent tool executions
## 🔗 Agent Handoffs
```python
from jaf.policies.handoff import create_handoff_guardrail, HandoffPolicy
from pydantic import BaseModel
from enum import Enum
class AgentName(str, Enum):
MATH_TUTOR = 'MathTutor'
FILE_MANAGER = 'FileManager'
class HandoffOutput(BaseModel):
agent_name: AgentName
def create_triage_agent():
def instructions(state):
return 'Route requests to specialized agents'
return Agent(
name='TriageAgent',
instructions=instructions,
tools=[], # Regular tools would go here
handoffs=['MathTutor', 'FileManager'], # Allowed handoff targets
output_schema=HandoffOutput,
)
```
## Observability
### Real-time Tracing
```python
from jaf.core.tracing import ConsoleTraceCollector, FileTraceCollector, create_composite_trace_collector
# Console logging
console_tracer = ConsoleTraceCollector()
# File logging
file_tracer = FileTraceCollector('./traces.log')
# Composite tracing
tracer = create_composite_trace_collector(console_tracer, file_tracer)
config = RunConfig(
# ... other config
on_event=tracer.collect,
)
```
### OpenTelemetry Integration
JAF 2.2+ includes built-in OpenTelemetry support for distributed tracing:
```python
import os
from jaf.core.tracing import create_composite_trace_collector, ConsoleTraceCollector
# Configure OpenTelemetry endpoint
os.environ["TRACE_COLLECTOR_URL"] = "http://localhost:4318/v1/traces"
# Tracing will be automatically configured when creating a composite collector
trace_collector = create_composite_trace_collector(ConsoleTraceCollector())
config = RunConfig(
# ... other config
on_event=trace_collector.collect,
)
```
### Langfuse Integration
For LLM-specific observability and analytics:
```python
import os
from jaf.core.tracing import create_composite_trace_collector, ConsoleTraceCollector
# Configure Langfuse credentials
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-your-public-key"
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-your-secret-key"
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # or your self-hosted instance
# Langfuse tracing will be automatically configured
trace_collector = create_composite_trace_collector(ConsoleTraceCollector())
config = RunConfig(
# ... other config
on_event=trace_collector.collect,
)
```
### Error Handling
```python
from jaf.core.errors import JAFErrorHandler
if result.outcome.status == 'error':
formatted_error = JAFErrorHandler.format_error(result.outcome.error)
is_retryable = JAFErrorHandler.is_retryable(result.outcome.error)
severity = JAFErrorHandler.get_severity(result.outcome.error)
print(f"[{severity}] {formatted_error} (retryable: {is_retryable})")
```
## Provider Integrations
### A2A (Agent-to-Agent) Communication
JAF provides a robust A2A communication layer that allows agents to interact with each other. This is useful for building multi-agent systems where different agents have specialized skills.
```python
from jaf.a2a import create_a2a_agent, create_a2a_client, create_a2a_server
# Create agents
echo_agent = create_a2a_agent("EchoBot", "Echoes messages", "You are an echo bot.", [])
ping_agent = create_a2a_agent("PingBot", "Responds to pings", "You are a ping bot.", [])
# Create a server
server_config = {
"agents": {"EchoBot": echo_agent, "PingBot": ping_agent},
"agentCard": {"name": "Test Server"},
"port": 8080,
}
server = create_a2a_server(server_config)
# Create a client
client = create_a2a_client("http://localhost:8080")
```
### LiteLLM Provider
```python
from jaf.providers.model import make_litellm_provider
# Connect to LiteLLM proxy for 100+ model support
model_provider = make_litellm_provider(
'http://localhost:4000', # LiteLLM proxy URL
'your-api-key' # Optional API key
)
```
### MCP (Model Context Protocol) Integration
JAF includes full Model Context Protocol support for seamless tool integration:
```python
from jaf.providers.mcp import create_mcp_stdio_client, create_mcp_tools_from_client
# Connect to MCP server via stdio
mcp_client = create_mcp_stdio_client(['python', '-m', 'my_mcp_server'])
# Initialize and get all available tools
await mcp_client.initialize()
mcp_tools = await create_mcp_tools_from_client(mcp_client)
# Use MCP tools in your agent
def create_mcp_agent():
def instructions(state):
return "You have access to powerful MCP tools for various tasks."
return Agent(
name='MCPAgent',
instructions=instructions,
tools=mcp_tools # Automatically converted JAF tools
)
# SSE client is also supported
from jaf.providers.mcp import create_mcp_sse_client
sse_client = create_mcp_sse_client('http://localhost:8080/sse')
```
## Development Server
JAF includes a built-in development server for testing agents locally via HTTP endpoints:
```python
from jaf.server import run_server
from jaf.providers.model import make_litellm_provider
def create_my_agent():
def instructions(state):
return 'You are a helpful assistant'
return Agent(
name='MyAgent',
instructions=instructions,
tools=[calculator_tool, greeting_tool]
)
model_provider = make_litellm_provider('http://localhost:4000')
# Start server on port 3000
await run_server(
[create_my_agent()],
{'model_provider': model_provider},
{'port': 3000}
)
```
Server provides RESTful endpoints:
- `GET /health` - Health check
- `GET /agents` - List available agents
- `POST /chat` - General chat endpoint
- `POST /agents/{name}/chat` - Agent-specific endpoint
## Function Composition
JAF supports functional composition patterns for building complex behaviors from simple, reusable functions:
```python
from jaf import create_function_tool, ToolSource
# Enhanced tool with caching and retry
def with_cache(tool_func):
cache = {}
async def cached_execute(args, context):
cache_key = str(args)
if cache_key in cache:
return cache[cache_key]
result = await tool_func(args, context)
if result.status == "success":
cache[cache_key] = result
return result
return cached_execute
# Create enhanced tools by composition
enhanced_tool = create_function_tool({
'name': 'enhanced_search',
'description': 'Search with caching',
'execute': with_cache(base_search_function),
'parameters': SearchArgs,
'source': ToolSource.NATIVE
})
```
**Key Benefits:**
- **Reusability**: Write once, compose everywhere
- **Testability**: Each function can be tested in isolation
- **Type Safety**: Full type checking support
- **Performance**: Optimize individual pieces independently
## Example Applications
Explore the example applications to see the framework in action:
### 1. Multi-Agent Server Demo
```bash
cd examples
python server_example.py
```
**Features demonstrated:**
- Multiple specialized agents (math, weather, general)
- Tool integration (calculator, weather API)
- Agent handoffs and routing
- RESTful API with auto-documentation
- Real-time tracing and error handling
- Production-ready server configuration
**Available endpoints:**
- `GET /health` - Server health check
- `GET /agents` - List all available agents
- `POST /chat` - Chat with any agent
- `GET /docs` - Interactive API documentation
### 2. Agent-as-Tool Demo
```bash
cd examples
python agent_as_tool_example.py
# Or start as server
python agent_as_tool_example.py --server
```
**Features demonstrated:**
- Hierarchical agent orchestration
- Conditional tool enabling based on context
- Custom output extraction from agent tools
- Session management for sub-agents
- Translation agents working together
### 3. Tracing Integration Demos
```bash
# OpenTelemetry tracing example
cd examples
python otel_tracing_demo.py
# Langfuse tracing example
python langfuse_tracing_demo.py
```
**Features demonstrated:**
- OpenTelemetry distributed tracing setup
- Langfuse LLM observability integration
- Composite trace collectors
- Real-time monitoring and analytics
### 4. MCP Integration Demo
```bash
cd examples/mcp_demo
python main.py
```
**Features demonstrated:**
- Model Context Protocol integration
- Dynamic tool loading from MCP servers
- Secure filesystem operations
- MCP client configuration and management
## Testing
```bash
pytest # Run tests
ruff check . # Lint code
mypy . # Type checking
black . # Format code
```
## Architecture Principles
### Immutable State Machine
- All state transformations create new state objects
- No mutation of existing data structures
- Predictable, testable state transitions
### Type Safety
- Runtime validation with Pydantic schemas
- Compile-time safety with Python type hints
- NewType for type-safe identifiers
### Pure Functions
- Core logic is side-effect free
- Easy to test and reason about
- Deterministic behavior
### Effect Isolation
- Side effects only in Provider modules
- Clear boundaries between pure and impure code
- Easier mocking and testing
## 📜 License
MIT
## 🤝 Contributing
1. Fork the repository
2. Create a feature branch
3. Add tests for new functionality
4. Run the test suite
5. Submit a pull request
---
**JAF (Juspay Agentic Framework) v2.2** - Building the future of functional AI agent systems
Raw data
{
"_id": null,
"home_page": null,
"name": "jaf-py",
"maintainer": "JAF Contributors",
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": null,
"keywords": "ai, agents, functional, python, llm, mcp, model-context-protocol, agent-framework, functional-programming",
"author": "JAF Contributors",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/41/be/0220bbf31c765a88e9f61f2a105356637b3909299c01e831cbbddd28cdf5/jaf_py-2.5.7.tar.gz",
"platform": null,
"description": "# JAF (Juspay Agent Framework) - Python Implementation\n\n<!--  -->\n\n[](https://github.com/xynehq/jaf-py)\n[](https://www.python.org/)\n[](https://xynehq.github.io/jaf-py/)\n\nA 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.\n\n**Production Ready**: Complete feature parity with TypeScript version, comprehensive test suite, and production deployment support.\n\n## **[Read the Full Documentation](https://xynehq.github.io/jaf-py/)**\n\n**[ Get Started \u2192](https://xynehq.github.io/jaf-py/getting-started/)** | **[ API Reference \u2192](https://xynehq.github.io/jaf-py/api-reference/)** | **[ Examples \u2192](https://xynehq.github.io/jaf-py/examples/)**\n\n## Key Features\n\n### **Complete TypeScript Conversion**\n- **Full Feature Parity**: All TypeScript functionality converted to Python\n- **Type Safety**: Pydantic models with runtime validation\n- **Immutable State**: Functional programming principles preserved\n- **Tool Integration**: Complete tool calling and execution system\n\n### **Production Ready Server**\n- **FastAPI Server**: High-performance async HTTP API\n- **Auto Documentation**: Interactive API docs at `/docs`\n- **Health Monitoring**: Built-in health checks and metrics\n- **CORS Support**: Ready for browser integration\n\n### **Model Context Protocol (MCP)**\n- **MCP Client**: Full MCP specification support\n- **Stdio & SSE**: Multiple transport protocols\n- **Tool Integration**: Seamless MCP tool integration\n- **Auto Discovery**: Dynamic tool loading from MCP servers\n\n### **Enterprise Security**\n- **Input Guardrails**: Content filtering and validation\n- **Output Guardrails**: Response sanitization\n- **Permission System**: Role-based access control\n- **Audit Logging**: Complete interaction tracing\n- **Proxy Support**: Corporate proxy integration with authentication\n\n### **Observability & Monitoring**\n- **Real-time Tracing**: Event-driven observability\n- **OpenTelemetry Integration**: Distributed tracing with OTLP\n- **Langfuse Tracing**: LLM observability and analytics\n- **Structured Logging**: JSON-formatted logs\n- **Error Handling**: Comprehensive error types and recovery\n- **Performance Metrics**: Built-in timing and counters\n\n### **Agent-as-Tool Architecture**\n- **Hierarchical Orchestration**: Use agents as tools in other agents\n- **Conditional Tool Enabling**: Enable/disable agent tools based on context\n- **Session Management**: Configurable session inheritance for sub-agents\n- **Flexible Output Extraction**: Custom extractors for agent tool outputs\n\n### **Developer Experience**\n- **CLI Tools**: Project initialization and management\n- **Hot Reload**: Development server with auto-reload\n- **Type Hints**: Full mypy compatibility\n- **Rich Examples**: RAG, multi-agent, agent-as-tool, and server demos\n- **Visual Architecture**: Graphviz-powered agent and tool diagrams\n\n## Core Philosophy\n\n- **Immutability**: All core data structures are deeply immutable\n- **Pure Functions**: Core logic expressed as pure, predictable functions\n- **Effects at the Edge**: Side effects isolated in Provider modules\n- **Composition over Configuration**: Build complex behavior by composing simple functions\n- **Type-Safe by Design**: Leverages Python's type system with Pydantic for runtime safety\n\n## Quick Start\n\n### Installation\n\n```bash\n# Install from GitHub (development version)\npip install git+https://github.com/xynehq/jaf-py.git\n\n# Or install with all optional dependencies\npip install \"jaf-py[all] @ git+https://github.com/xynehq/jaf-py.git\"\n\n# Install specific feature sets\npip install \"jaf-py[server] @ git+https://github.com/xynehq/jaf-py.git\" # FastAPI server support\npip install \"jaf-py[memory] @ git+https://github.com/xynehq/jaf-py.git\" # Redis/PostgreSQL memory providers\npip install \"jaf-py[visualization] @ git+https://github.com/xynehq/jaf-py.git\" # Graphviz visualization tools\npip install \"jaf-py[tracing] @ git+https://github.com/xynehq/jaf-py.git\" # OpenTelemetry and Langfuse tracing\npip install \"jaf-py[dev] @ git+https://github.com/xynehq/jaf-py.git\" # Development tools\n```\n\n### CLI Usage\n\nJAF includes a powerful CLI for project management:\n\n```bash\n# Initialize a new JAF project\njaf init my-agent-project\n\n# Run the development server\njaf server --host 0.0.0.0 --port 8000\n\n# Show version and help\njaf version\njaf --help\n```\n\n### Development Setup\n\n```bash\ngit clone https://github.com/xynehq/jaf-py\ncd jaf-py\npip install -e \".[dev]\"\n\n# Run tests\npytest\n\n# Type checking and linting\nmypy jaf/\nruff check jaf/\nblack jaf/\n\n# Documentation\npip install -r requirements-docs.txt\n./docs.sh serve # Start documentation server\n./docs.sh deploy # Deploy to GitHub Pages\n```\n\n## Documentation\n\n### **[Official Documentation Website](https://xynehq.github.io/jaf-py/)**\n\nThe complete, searchable documentation is available at **[xynehq.github.io/jaf-py](https://xynehq.github.io/jaf-py/)** with:\n\n- **Interactive navigation** with search and filtering\n- **Dark/light mode** with automatic system preference detection \n- **Mobile-responsive design** for documentation on any device\n- **Live code examples** with syntax highlighting\n- **API reference** with auto-generated documentation\n- **Always up-to-date** with automatic deployments\n\n### **Local Documentation**\n\nFor offline access, documentation is also available in the [`docs/`](docs/) directory:\n\n- **[ Documentation Hub](docs/README.md)** - Your starting point for all documentation\n- **[ Getting Started](docs/getting-started.md)** - Installation and first agent tutorial\n- **[ Core Concepts](docs/core-concepts.md)** - JAF's functional architecture principles\n- **[ API Reference](docs/api-reference.md)** - Complete Python API documentation\n- **[ Tools Guide](docs/tools.md)** - Creating and using tools\n- **[ Memory System](docs/memory-system.md)** - Persistence and memory providers\n- **[ Model Providers](docs/model-providers.md)** - LiteLLM integration\n- **[ Monitoring](docs/monitoring.md)** - Observability, metrics, and alerting\n- **[ Server API](docs/server-api.md)** - FastAPI endpoints reference\n- **[ Deployment](docs/deployment.md)** - Production deployment guide\n- **[ Examples](docs/examples.md)** - Detailed example walkthroughs\n- **[ Troubleshooting](docs/troubleshooting.md)** - Common issues and solutions\n\n## Project Structure\n\n```\njaf-py/\n\u251c\u2500\u2500 docs/ # Complete documentation\n\u251c\u2500\u2500 jaf/ # Core framework package\n\u2502 \u251c\u2500\u2500 core/ # Core types and engine\n\u2502 \u251c\u2500\u2500 memory/ # Conversation persistence\n\u2502 \u251c\u2500\u2500 providers/ # External integrations (LiteLLM, MCP)\n\u2502 \u251c\u2500\u2500 policies/ # Validation and security\n\u2502 \u251c\u2500\u2500 server/ # FastAPI server\n\u2502 \u2514\u2500\u2500 cli.py # Command-line interface\n\u251c\u2500\u2500 examples/ # Example applications\n\u2514\u2500\u2500 tests/ # Test suite\n```\n\n## Architectural Visualization\n\nJAF includes powerful visualization capabilities to help you understand and document your agent systems.\n\n### Prerequisites\n\nFirst, install the system Graphviz dependency:\n\n```bash\n# macOS\nbrew install graphviz\n\n# Ubuntu/Debian \nsudo apt-get install graphviz\n\n# Windows (via Chocolatey)\nchoco install graphviz\n```\n\nThen install JAF with visualization support:\n\n```bash\npip install \"jaf-py[visualization]\"\n```\n\n### Quick Start\n\n```python\nimport asyncio\nfrom jaf import Agent, Tool, generate_agent_graph, GraphOptions\n\n# Create your agents\nagent = Agent(\n name='MyAgent',\n instructions=lambda state: \"I am a helpful assistant.\",\n tools=[my_tool],\n handoffs=['OtherAgent']\n)\n\n# Generate visualization\nasync def main():\n result = await generate_agent_graph(\n [agent],\n GraphOptions(\n title=\"My Agent System\",\n output_path=\"./my-agents.png\",\n color_scheme=\"modern\",\n show_tool_details=True\n )\n )\n \n if result.success:\n print(f\" Visualization saved to: {result.output_path}\")\n else:\n print(f\"\u274c Error: {result.error}\")\n\nasyncio.run(main())\n```\n\n### Features\n\n- **Multiple Color Schemes**: Choose from `default`, `modern`, or `minimal` themes\n- **Agent Architecture**: Visualize agents, tools, and handoff relationships \n- **Tool Ecosystems**: Generate dedicated tool interaction diagrams\n- **Runner Architecture**: Show complete system architecture with session layers\n- **Multiple Formats**: Export as PNG, SVG, or PDF\n- **Customizable Layouts**: Support for various Graphviz layouts (`dot`, `circo`, `neato`, etc.)\n\n### Example Output\n\nThe visualization system generates clear, professional diagrams showing:\n\n- **Agent Nodes**: Rounded rectangles with agent names and model information\n- **Tool Nodes**: Ellipses showing tool names and descriptions \n- **Handoff Edges**: Dashed lines indicating agent handoff relationships\n- **Tool Connections**: Colored edges connecting agents to their tools\n- **Cluster Organization**: Grouped components in runner architecture views\n\n### Advanced Usage\n\n```python\nfrom jaf.visualization import run_visualization_examples\n\n# Run comprehensive examples\nawait run_visualization_examples()\n\n# This generates multiple example files:\n# - ./examples/agent-graph.png (agent system overview)\n# - ./examples/tool-graph.png (tool ecosystem) \n# - ./examples/runner-architecture.png (complete system)\n# - ./examples/agent-modern.png (modern color scheme)\n```\n\n## Key Components\n\n### Core Types\n\n```python\nfrom dataclasses import dataclass\nfrom pydantic import BaseModel, Field\nfrom jaf import Agent, Tool, RunState, run\n\n# Define your context type\n@dataclass\nclass MyContext:\n user_id: str\n permissions: list[str]\n\n# Define tool schema\nclass CalculateArgs(BaseModel):\n expression: str = Field(description=\"Math expression to evaluate\")\n\n# Create a tool\nclass CalculatorTool:\n @property\n def schema(self):\n return type('ToolSchema', (), {\n 'name': 'calculate',\n 'description': 'Perform mathematical calculations',\n 'parameters': CalculateArgs\n })()\n \n async def execute(self, args: CalculateArgs, context: MyContext) -> str:\n result = eval(args.expression) # Don't do this in production!\n return f\"{args.expression} = {result}\"\n\n# Define an agent\ndef create_math_agent():\n def instructions(state):\n return 'You are a helpful math tutor'\n \n return Agent(\n name='MathTutor',\n instructions=instructions,\n tools=[CalculatorTool()]\n )\n```\n\n### Running the Framework\n\n```python\nimport asyncio\nfrom jaf import run, make_litellm_provider, generate_run_id, generate_trace_id\nfrom jaf.core.types import RunState, RunConfig, Message\n\nasync def main():\n model_provider = make_litellm_provider('http://localhost:4000')\n math_agent = create_math_agent()\n \n config = RunConfig(\n agent_registry={'MathTutor': math_agent},\n model_provider=model_provider,\n max_turns=10,\n on_event=lambda event: print(event), # Real-time tracing\n )\n \n initial_state = RunState(\n run_id=generate_run_id(),\n trace_id=generate_trace_id(),\n messages=[Message(role='user', content='What is 2 + 2?')],\n current_agent_name='MathTutor',\n context=MyContext(user_id='user123', permissions=['user']),\n turn_count=0,\n )\n \n result = await run(initial_state, config)\n print(result.outcome.output if result.outcome.status == 'completed' else result.outcome.error)\n\nasyncio.run(main())\n```\n\n## Security & Validation\n\n### Composable Validation Policies\n\n```python\nfrom jaf.policies.validation import create_path_validator, create_permission_validator, compose_validations\n\n# Create individual validators\npath_validator = create_path_validator(['/shared', '/public'])\npermission_validator = create_permission_validator('admin', lambda ctx: ctx)\n\n# Compose them\ncombined_validator = compose_validations(path_validator, permission_validator)\n\n# Apply to tools\nsecure_file_tool = with_validation(base_file_tool, combined_validator)\n```\n\n### Guardrails\n\n```python\nfrom jaf.policies.validation import create_content_filter, create_length_guardrail\n\nconfig = RunConfig(\n # ... other config\n initial_input_guardrails=[\n create_content_filter(['spam', 'inappropriate']), # Requires blocked patterns\n create_length_guardrail(max_length=1000, min_length=1)\n ],\n final_output_guardrails=[\n create_content_filter(['harmful', 'unsafe'])\n ],\n)\n```\n\n## Agent-as-Tool Functionality\n\nJAF 2.2+ introduces powerful agent-as-tool capabilities, allowing you to use agents as tools within other agents for hierarchical orchestration:\n\n```python\nfrom jaf.core.agent_tool import create_agent_tool\nfrom jaf.core.types import create_json_output_extractor\n\n# Create specialized agents\nspanish_agent = Agent(\n name=\"spanish_translator\",\n instructions=lambda state: \"Translate text to Spanish\",\n output_codec=TranslationOutput\n)\n\nfrench_agent = Agent(\n name=\"french_translator\", \n instructions=lambda state: \"Translate text to French\",\n output_codec=TranslationOutput\n)\n\n# Convert agents to tools with conditional enabling\nspanish_tool = spanish_agent.as_tool(\n tool_name=\"translate_to_spanish\",\n tool_description=\"Translate text to Spanish\",\n max_turns=3,\n custom_output_extractor=create_json_output_extractor(),\n is_enabled=True # Always enabled\n)\n\nfrench_tool = french_agent.as_tool(\n tool_name=\"translate_to_french\", \n tool_description=\"Translate text to French\",\n max_turns=3,\n custom_output_extractor=create_json_output_extractor(),\n is_enabled=lambda context, agent: context.language_preference == \"french_spanish\"\n)\n\n# Create orchestrator agent using agent tools\norchestrator = Agent(\n name=\"translation_orchestrator\",\n instructions=lambda state: \"Use translation tools to respond in multiple languages\",\n tools=[spanish_tool, french_tool]\n)\n```\n\n### Key Features:\n- **Conditional Enabling**: Enable/disable agent tools based on runtime context\n- **Session Management**: Configure whether sub-agents inherit parent session state\n- **Custom Output Extraction**: Define how to extract and format agent tool outputs\n- **Error Handling**: Robust error handling for failed agent tool executions\n\n## \ud83d\udd17 Agent Handoffs\n\n```python\nfrom jaf.policies.handoff import create_handoff_guardrail, HandoffPolicy\nfrom pydantic import BaseModel\nfrom enum import Enum\n\nclass AgentName(str, Enum):\n MATH_TUTOR = 'MathTutor'\n FILE_MANAGER = 'FileManager'\n\nclass HandoffOutput(BaseModel):\n agent_name: AgentName\n\ndef create_triage_agent():\n def instructions(state):\n return 'Route requests to specialized agents'\n \n return Agent(\n name='TriageAgent',\n instructions=instructions,\n tools=[], # Regular tools would go here\n handoffs=['MathTutor', 'FileManager'], # Allowed handoff targets\n output_schema=HandoffOutput,\n )\n```\n\n## Observability\n\n### Real-time Tracing\n\n```python\nfrom jaf.core.tracing import ConsoleTraceCollector, FileTraceCollector, create_composite_trace_collector\n\n# Console logging\nconsole_tracer = ConsoleTraceCollector()\n\n# File logging\nfile_tracer = FileTraceCollector('./traces.log')\n\n# Composite tracing\ntracer = create_composite_trace_collector(console_tracer, file_tracer)\n\nconfig = RunConfig(\n # ... other config\n on_event=tracer.collect,\n)\n```\n\n### OpenTelemetry Integration\n\nJAF 2.2+ includes built-in OpenTelemetry support for distributed tracing:\n\n```python\nimport os\nfrom jaf.core.tracing import create_composite_trace_collector, ConsoleTraceCollector\n\n# Configure OpenTelemetry endpoint\nos.environ[\"TRACE_COLLECTOR_URL\"] = \"http://localhost:4318/v1/traces\"\n\n# Tracing will be automatically configured when creating a composite collector\ntrace_collector = create_composite_trace_collector(ConsoleTraceCollector())\n\nconfig = RunConfig(\n # ... other config\n on_event=trace_collector.collect,\n)\n```\n\n### Langfuse Integration\n\nFor LLM-specific observability and analytics:\n\n```python\nimport os\nfrom jaf.core.tracing import create_composite_trace_collector, ConsoleTraceCollector\n\n# Configure Langfuse credentials\nos.environ[\"LANGFUSE_PUBLIC_KEY\"] = \"pk-lf-your-public-key\"\nos.environ[\"LANGFUSE_SECRET_KEY\"] = \"sk-lf-your-secret-key\" \nos.environ[\"LANGFUSE_HOST\"] = \"https://cloud.langfuse.com\" # or your self-hosted instance\n\n# Langfuse tracing will be automatically configured\ntrace_collector = create_composite_trace_collector(ConsoleTraceCollector())\n\nconfig = RunConfig(\n # ... other config\n on_event=trace_collector.collect,\n)\n```\n\n### Error Handling\n\n```python\nfrom jaf.core.errors import JAFErrorHandler\n\nif result.outcome.status == 'error':\n formatted_error = JAFErrorHandler.format_error(result.outcome.error)\n is_retryable = JAFErrorHandler.is_retryable(result.outcome.error)\n severity = JAFErrorHandler.get_severity(result.outcome.error)\n \n print(f\"[{severity}] {formatted_error} (retryable: {is_retryable})\")\n```\n\n## Provider Integrations\n\n### A2A (Agent-to-Agent) Communication\n\nJAF provides a robust A2A communication layer that allows agents to interact with each other. This is useful for building multi-agent systems where different agents have specialized skills.\n\n```python\nfrom jaf.a2a import create_a2a_agent, create_a2a_client, create_a2a_server\n\n# Create agents\necho_agent = create_a2a_agent(\"EchoBot\", \"Echoes messages\", \"You are an echo bot.\", [])\nping_agent = create_a2a_agent(\"PingBot\", \"Responds to pings\", \"You are a ping bot.\", [])\n\n# Create a server\nserver_config = {\n \"agents\": {\"EchoBot\": echo_agent, \"PingBot\": ping_agent},\n \"agentCard\": {\"name\": \"Test Server\"},\n \"port\": 8080,\n}\nserver = create_a2a_server(server_config)\n\n# Create a client\nclient = create_a2a_client(\"http://localhost:8080\")\n```\n\n### LiteLLM Provider\n\n```python\nfrom jaf.providers.model import make_litellm_provider\n\n# Connect to LiteLLM proxy for 100+ model support\nmodel_provider = make_litellm_provider(\n 'http://localhost:4000', # LiteLLM proxy URL\n 'your-api-key' # Optional API key\n)\n```\n\n### MCP (Model Context Protocol) Integration\n\nJAF includes full Model Context Protocol support for seamless tool integration:\n\n```python\nfrom jaf.providers.mcp import create_mcp_stdio_client, create_mcp_tools_from_client\n\n# Connect to MCP server via stdio\nmcp_client = create_mcp_stdio_client(['python', '-m', 'my_mcp_server'])\n\n# Initialize and get all available tools\nawait mcp_client.initialize()\nmcp_tools = await create_mcp_tools_from_client(mcp_client)\n\n# Use MCP tools in your agent\ndef create_mcp_agent():\n def instructions(state):\n return \"You have access to powerful MCP tools for various tasks.\"\n \n return Agent(\n name='MCPAgent',\n instructions=instructions,\n tools=mcp_tools # Automatically converted JAF tools\n )\n\n# SSE client is also supported\nfrom jaf.providers.mcp import create_mcp_sse_client\nsse_client = create_mcp_sse_client('http://localhost:8080/sse')\n```\n\n## Development Server\n\nJAF includes a built-in development server for testing agents locally via HTTP endpoints:\n\n```python\nfrom jaf.server import run_server\nfrom jaf.providers.model import make_litellm_provider\n\ndef create_my_agent():\n def instructions(state):\n return 'You are a helpful assistant'\n \n return Agent(\n name='MyAgent',\n instructions=instructions,\n tools=[calculator_tool, greeting_tool]\n )\n\nmodel_provider = make_litellm_provider('http://localhost:4000')\n\n# Start server on port 3000\nawait run_server(\n [create_my_agent()], \n {'model_provider': model_provider},\n {'port': 3000}\n)\n```\n\nServer provides RESTful endpoints:\n- `GET /health` - Health check\n- `GET /agents` - List available agents \n- `POST /chat` - General chat endpoint\n- `POST /agents/{name}/chat` - Agent-specific endpoint\n\n## Function Composition\n\nJAF supports functional composition patterns for building complex behaviors from simple, reusable functions:\n\n```python\nfrom jaf import create_function_tool, ToolSource\n\n# Enhanced tool with caching and retry\ndef with_cache(tool_func):\n cache = {}\n async def cached_execute(args, context):\n cache_key = str(args)\n if cache_key in cache:\n return cache[cache_key]\n result = await tool_func(args, context)\n if result.status == \"success\":\n cache[cache_key] = result\n return result\n return cached_execute\n\n# Create enhanced tools by composition\nenhanced_tool = create_function_tool({\n 'name': 'enhanced_search',\n 'description': 'Search with caching',\n 'execute': with_cache(base_search_function),\n 'parameters': SearchArgs,\n 'source': ToolSource.NATIVE\n})\n```\n\n**Key Benefits:**\n- **Reusability**: Write once, compose everywhere\n- **Testability**: Each function can be tested in isolation \n- **Type Safety**: Full type checking support\n- **Performance**: Optimize individual pieces independently\n\n## Example Applications\n\nExplore the example applications to see the framework in action:\n\n### 1. Multi-Agent Server Demo\n\n```bash\ncd examples\npython server_example.py\n```\n\n**Features demonstrated:**\n- Multiple specialized agents (math, weather, general)\n- Tool integration (calculator, weather API)\n- Agent handoffs and routing\n- RESTful API with auto-documentation\n- Real-time tracing and error handling\n- Production-ready server configuration\n\n**Available endpoints:**\n- `GET /health` - Server health check\n- `GET /agents` - List all available agents\n- `POST /chat` - Chat with any agent\n- `GET /docs` - Interactive API documentation\n\n### 2. Agent-as-Tool Demo\n\n```bash\ncd examples\npython agent_as_tool_example.py\n\n# Or start as server\npython agent_as_tool_example.py --server\n```\n\n**Features demonstrated:**\n- Hierarchical agent orchestration\n- Conditional tool enabling based on context\n- Custom output extraction from agent tools\n- Session management for sub-agents\n- Translation agents working together\n\n### 3. Tracing Integration Demos\n\n```bash\n# OpenTelemetry tracing example\ncd examples\npython otel_tracing_demo.py\n\n# Langfuse tracing example \npython langfuse_tracing_demo.py\n```\n\n**Features demonstrated:**\n- OpenTelemetry distributed tracing setup\n- Langfuse LLM observability integration\n- Composite trace collectors\n- Real-time monitoring and analytics\n\n### 4. MCP Integration Demo\n\n```bash\ncd examples/mcp_demo \npython main.py\n```\n\n**Features demonstrated:**\n- Model Context Protocol integration\n- Dynamic tool loading from MCP servers\n- Secure filesystem operations\n- MCP client configuration and management\n\n## Testing\n\n```bash\npytest # Run tests\nruff check . # Lint code\nmypy . # Type checking\nblack . # Format code\n```\n\n## Architecture Principles\n\n### Immutable State Machine\n- All state transformations create new state objects\n- No mutation of existing data structures\n- Predictable, testable state transitions\n\n### Type Safety\n- Runtime validation with Pydantic schemas\n- Compile-time safety with Python type hints\n- NewType for type-safe identifiers\n\n### Pure Functions\n- Core logic is side-effect free\n- Easy to test and reason about\n- Deterministic behavior\n\n### Effect Isolation\n- Side effects only in Provider modules\n- Clear boundaries between pure and impure code\n- Easier mocking and testing\n\n## \ud83d\udcdc License\n\nMIT\n\n## \ud83e\udd1d Contributing\n\n1. Fork the repository\n2. Create a feature branch\n3. Add tests for new functionality\n4. Run the test suite\n5. Submit a pull request\n\n---\n\n**JAF (Juspay Agentic Framework) v2.2** - Building the future of functional AI agent systems \n",
"bugtrack_url": null,
"license": null,
"summary": "A purely functional agent framework with immutable state and composable tools - Python implementation",
"version": "2.5.7",
"project_urls": {
"Changelog": "https://github.com/xynehq/jaf-py/releases",
"Documentation": "https://xynehq.github.io/jaf-py/",
"Homepage": "https://github.com/xynehq/jaf-py",
"Issues": "https://github.com/xynehq/jaf-py/issues",
"Repository": "https://github.com/xynehq/jaf-py"
},
"split_keywords": [
"ai",
" agents",
" functional",
" python",
" llm",
" mcp",
" model-context-protocol",
" agent-framework",
" functional-programming"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "f233c263f00fbe35e593c083866ac1b6de6adb433907bc9c10c56b0bf60d5e2c",
"md5": "bd61a976d23b8a6f48b1e84dc65e6e99",
"sha256": "022c40301bdfc52f21ea92c32952b921a9e3a55d8fd36509717489b4c0f41679"
},
"downloads": -1,
"filename": "jaf_py-2.5.7-py3-none-any.whl",
"has_sig": false,
"md5_digest": "bd61a976d23b8a6f48b1e84dc65e6e99",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 300383,
"upload_time": "2025-10-17T12:36:15",
"upload_time_iso_8601": "2025-10-17T12:36:15.131437Z",
"url": "https://files.pythonhosted.org/packages/f2/33/c263f00fbe35e593c083866ac1b6de6adb433907bc9c10c56b0bf60d5e2c/jaf_py-2.5.7-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "41be0220bbf31c765a88e9f61f2a105356637b3909299c01e831cbbddd28cdf5",
"md5": "6b13f9bdb7051ccdeac5da2405f6f628",
"sha256": "e30f8ada53520db805761d5f7ca62c7a0bd5331ca7b0d7ec3bdb515f5caaf4b3"
},
"downloads": -1,
"filename": "jaf_py-2.5.7.tar.gz",
"has_sig": false,
"md5_digest": "6b13f9bdb7051ccdeac5da2405f6f628",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 347897,
"upload_time": "2025-10-17T12:36:16",
"upload_time_iso_8601": "2025-10-17T12:36:16.512273Z",
"url": "https://files.pythonhosted.org/packages/41/be/0220bbf31c765a88e9f61f2a105356637b3909299c01e831cbbddd28cdf5/jaf_py-2.5.7.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-17 12:36:16",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "xynehq",
"github_project": "jaf-py",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "jaf-py"
}