Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1157,8 +1157,8 @@ in v3.5.0. This major version formally acknowledges that breaking change.
```python
# Community mode - no secret needed
client = AxonFlow(
endpoint="http://localhost:8080",
client_id="my-app", # Used for request identification
endpoint="http://localhost:8080",
client_id="my-app", # Used for request identification
)
```

Expand Down Expand Up @@ -1213,20 +1213,20 @@ client = AxonFlow(
**Before (v0.x):**
```python
client = AxonFlow(
agent_url="http://localhost:8080",
orchestrator_url="http://localhost:8081",
portal_url="http://localhost:8082",
client_id="my-client",
client_secret="my-secret",
agent_url="http://localhost:8080",
orchestrator_url="http://localhost:8081",
portal_url="http://localhost:8082",
client_id="my-client",
client_secret="my-secret",
)
```

**After (v1.x):**
```python
client = AxonFlow(
endpoint="http://localhost:8080",
client_id="my-client",
client_secret="my-secret",
endpoint="http://localhost:8080",
client_id="my-client",
client_secret="my-secret",
)
```

Expand Down
70 changes: 29 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,20 @@ No Docker, no license, no installation. Rate-limited to 20 req/min. [Learn more]
import asyncio
from axonflow import AxonFlow


async def main():
async with AxonFlow(
endpoint="https://your-agent.axonflow.com",
client_id="your-client-id",
client_secret="your-client-secret"
client_secret="your-client-secret",
) as client:
# Execute a governed query
response = await client.proxy_llm_call(
user_token="user-jwt-token",
query="What is AI governance?",
request_type="chat"
user_token="user-jwt-token", query="What is AI governance?", request_type="chat"
)
print(response.data)


asyncio.run(main())
```

Expand All @@ -118,12 +118,10 @@ from axonflow import AxonFlow
with AxonFlow.sync(
endpoint="https://your-agent.axonflow.com",
client_id="your-client-id",
client_secret="your-client-secret"
client_secret="your-client-secret",
) as client:
response = client.proxy_llm_call(
user_token="user-jwt-token",
query="What is AI governance?",
request_type="chat"
user_token="user-jwt-token", query="What is AI governance?", request_type="chat"
)
print(response.data)
```
Expand All @@ -140,18 +138,15 @@ from axonflow import AxonFlow, TokenUsage
async with AxonFlow(...) as client:
# 1. Pre-check: Get policy approval
ctx = await client.get_policy_approved_context(
user_token="user-jwt",
query="Find patient records",
data_sources=["postgres"]
user_token="user-jwt", query="Find patient records", data_sources=["postgres"]
)

if not ctx.approved:
raise Exception(f"Blocked: {ctx.block_reason}")

# 2. Make LLM call directly (your code)
llm_response = await openai.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": str(ctx.approved_data)}]
model="gpt-4", messages=[{"role": "user", "content": str(ctx.approved_data)}]
)

# 3. Audit the call
Expand All @@ -163,9 +158,9 @@ async with AxonFlow(...) as client:
token_usage=TokenUsage(
prompt_tokens=llm_response.usage.prompt_tokens,
completion_tokens=llm_response.usage.completion_tokens,
total_tokens=llm_response.usage.total_tokens
total_tokens=llm_response.usage.total_tokens,
),
latency_ms=250
latency_ms=250,
)
```

Expand All @@ -186,8 +181,7 @@ wrapped = wrap_openai_client(openai, axonflow, user_token="user-123")

# Use as normal
response = wrapped.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "Hello!"}]
model="gpt-4", messages=[{"role": "user", "content": "Hello!"}]
)
```

Expand All @@ -204,7 +198,7 @@ result = await client.query_connector(
user_token="user-jwt",
connector_name="postgres",
operation="query",
params={"sql": "SELECT * FROM users LIMIT 10"}
params={"sql": "SELECT * FROM users LIMIT 10"},
)
```

Expand All @@ -218,7 +212,7 @@ result = await client.query_connector(
user_token="user-jwt",
connector_name="postgres",
operation="query",
params={"sql": "SELECT * FROM customers"}
params={"sql": "SELECT * FROM customers"},
)

# Check exfiltration info
Expand Down Expand Up @@ -247,8 +241,7 @@ Generate and execute multi-agent plans:
```python
# Generate a plan
plan = await client.generate_plan(
query="Book a flight and hotel for my trip to Paris",
domain="travel"
query="Book a flight and hotel for my trip to Paris", domain="travel"
)

print(f"Plan has {len(plan.steps)} steps")
Expand All @@ -265,19 +258,19 @@ from axonflow import AxonFlow, Mode, RetryConfig

client = AxonFlow(
endpoint="https://your-agent.axonflow.com",
client_id="your-client-id", # Required for enterprise features
client_secret="your-client-secret", # Required for enterprise features
mode=Mode.PRODUCTION, # or Mode.SANDBOX
debug=True, # Enable debug logging
timeout=60.0, # Request timeout in seconds
retry_config=RetryConfig( # Retry configuration
client_id="your-client-id", # Required for enterprise features
client_secret="your-client-secret", # Required for enterprise features
mode=Mode.PRODUCTION, # or Mode.SANDBOX
debug=True, # Enable debug logging
timeout=60.0, # Request timeout in seconds
retry_config=RetryConfig( # Retry configuration
enabled=True,
max_attempts=3,
initial_delay=1.0,
max_delay=30.0,
),
cache_enabled=True, # Enable response caching
cache_ttl=60.0, # Cache TTL in seconds
cache_enabled=True, # Enable response caching
cache_ttl=60.0, # Cache TTL in seconds
)
```

Expand Down Expand Up @@ -351,15 +344,13 @@ Complete working examples for all features are available in the [examples folder
```python
# PII Detection - Automatically detect sensitive data
result = await client.get_policy_approved_context(
user_token="user-123",
query="My SSN is 123-45-6789"
user_token="user-123", query="My SSN is 123-45-6789"
)
# result.approved = True, result.requires_redaction = True (SSN detected)

# SQL Injection Detection - Block malicious queries
result = await client.get_policy_approved_context(
user_token="user-123",
query="SELECT * FROM users; DROP TABLE users;"
user_token="user-123", query="SELECT * FROM users; DROP TABLE users;"
)
# result.approved = False, result.block_reason = "SQL injection detected"

Expand All @@ -371,22 +362,19 @@ policies = await client.list_policies()
await client.create_dynamic_policy(
name="block-competitor-queries",
conditions={"contains": ["competitor", "pricing"]},
action="block"
action="block",
)

# MCP Connectors - Query external data sources
resp = await client.query_connector(
user_token="user-123",
connector_name="postgres-db",
operation="query",
params={"sql": "SELECT name FROM customers"}
params={"sql": "SELECT name FROM customers"},
)

# Multi-Agent Planning - Orchestrate complex workflows
plan = await client.generate_plan(
query="Research AI governance regulations",
domain="legal"
)
plan = await client.generate_plan(query="Research AI governance regulations", domain="legal")
result = await client.execute_plan(plan.plan_id)

# Audit Logging - Track all LLM interactions
Expand All @@ -396,7 +384,7 @@ await client.audit_llm_call(
provider="openai",
model="gpt-4",
token_usage=TokenUsage(prompt_tokens=100, completion_tokens=200, total_tokens=300),
latency_ms=450
latency_ms=450,
)
```

Expand All @@ -410,7 +398,7 @@ pr_result = await client.review_pull_request(
repo_owner="your-org",
repo_name="your-repo",
pr_number=123,
check_types=["security", "style", "performance"]
check_types=["security", "style", "performance"],
)

# Cost Controls - Budget management for LLM usage
Expand Down
2 changes: 1 addition & 1 deletion axonflow/heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class HeartbeatState:
couldn't find a cache dir.
"""

def __init__(self, stamp_path: Path | None | object = _USE_DEFAULT_CACHE_DIR) -> None:
def __init__(self, stamp_path: Path | object | None = _USE_DEFAULT_CACHE_DIR) -> None:
self._lock = threading.Lock()
self._last_checked_monotonic: float | None = None
self._in_flight = False
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ dev = [
"pytest-httpx>=0.22.0",
"mypy>=1.5.0",
"types-cachetools>=5.0.0",
"ruff>=0.15.8,<0.16",
"ruff>=0.16,<0.17",
"black>=23.0.0",
"isort>=5.12.0",
"pre-commit>=3.0.0",
Expand Down Expand Up @@ -136,6 +136,7 @@ ignore = [
"COM812", # Trailing comma (conflicts with formatter)
"ISC001", # Single line implicit string concatenation
"PLR0913", # Too many arguments
"PLR0917", # Too many positional arguments - public SDK signatures are API decisions (mirror the Go SDK)
"TRY003", # Avoid long exception messages
"A001", # Variable shadowing builtin
"A004", # Import shadowing builtin
Expand Down
10 changes: 6 additions & 4 deletions scripts/lint_no_falsey_clobber.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,12 @@ def visit_BoolOp(self, node: ast.BoolOp) -> None:
(
lineno,
col,
f"falsey-clobber: `or` falls through on every falsy "
f"value (0, False, '', [], {{}}), not just None. "
f"Use `... if X is not None else fallback`. "
f"Line: {snippet.strip()}",
(
f"falsey-clobber: `or` falls through on every falsy "
f"value (0, False, '', [], {{}}), not just None. "
f"Use `... if X is not None else fallback`. "
f"Line: {snippet.strip()}"
),
)
)
self.generic_visit(node)
Expand Down
Loading