docs: add Next.js documentation site with @hanzo/mdx
- Fumadocs-based docs with @hanzo/mdx and @hanzo/ui integration - Comprehensive SDK documentation for all packages: - hanzo-agent SDK (agents, tools, guardrails, tracing) - hanzo-mcp tools (filesystem, shell, browser, memory, etc.) - Client, models, embeddings, chat APIs - Tailwind 4 + Next.js 16 + React 19 stack - Static export for GitHub Pages deployment
@@ -0,0 +1,27 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
out/
|
||||
|
||||
# Fumadocs generated
|
||||
.source/
|
||||
|
||||
# Build
|
||||
dist/
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
*.log
|
||||
|
||||
# Local env
|
||||
.env*.local
|
||||
|
||||
# Vercel
|
||||
.vercel
|
||||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
@@ -0,0 +1,227 @@
|
||||
# Zen Coder Training Documentation
|
||||
|
||||
This document describes the training infrastructure and methodology for the Zen Coder model family.
|
||||
|
||||
## Overview
|
||||
|
||||
Zen Coder is a code-specialized LLM trained on the **Zen Agentic Dataset** - a curated collection of:
|
||||
- Git commit history and diffs from 1,452+ repositories
|
||||
- Claude Code debug sessions (agentic interactions)
|
||||
- Code review and documentation examples
|
||||
- Multi-language programming samples
|
||||
|
||||
## Dataset Statistics
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Total Tokens | ~8.47B |
|
||||
| Training Samples | 1.44M |
|
||||
| Validation Samples | 75K |
|
||||
| Prepared Data Size | 4 GB |
|
||||
| Source Repositories | 1,452+ |
|
||||
|
||||
### Data Sources
|
||||
|
||||
1. **Git History** (~12GB)
|
||||
- Commit messages and diffs
|
||||
- Full source files at each commit
|
||||
- Author metadata
|
||||
|
||||
2. **Claude Debug Sessions** (~12GB)
|
||||
- Real agentic coding interactions
|
||||
- Tool usage patterns
|
||||
- Multi-turn problem solving
|
||||
|
||||
3. **Claude Full Conversations** (~2GB)
|
||||
- Extended coding sessions
|
||||
- Architecture discussions
|
||||
- Code review examples
|
||||
|
||||
4. **Additional Sources** (~4GB)
|
||||
- Internal development logs
|
||||
- Documentation examples
|
||||
- Test cases
|
||||
|
||||
## Training Configuration
|
||||
|
||||
### Base Model
|
||||
- **Model**: `Qwen/Qwen3-4B-Instruct-2507`
|
||||
- **Parameters**: 4B
|
||||
- **Architecture**: Qwen3 transformer
|
||||
|
||||
### LoRA Configuration
|
||||
|
||||
```yaml
|
||||
fine_tune_type: lora
|
||||
num_layers: -1 # ALL layers
|
||||
batch_size: 1
|
||||
grad_accumulation: 8 # Effective batch = 8
|
||||
learning_rate: 1e-5
|
||||
optimizer: adamw
|
||||
max_seq_length: 4096
|
||||
mask_prompt: true # Train on completions only
|
||||
grad_checkpoint: true # Memory optimization
|
||||
```
|
||||
|
||||
### Training Parameters
|
||||
|
||||
| Parameter | Value |
|
||||
|-----------|-------|
|
||||
| Total Iterations | 50,000 |
|
||||
| Checkpoint Every | 1,000 iters |
|
||||
| Eval Every | 500 iters |
|
||||
| Estimated Time | ~3 days |
|
||||
|
||||
## Data Preparation
|
||||
|
||||
### Chunking Strategy
|
||||
|
||||
Long sequences are chunked to fit within the 4096 token context:
|
||||
|
||||
```python
|
||||
MAX_CHARS_PER_CHUNK = 12000 # ~3K tokens per chunk
|
||||
|
||||
def chunk_messages(messages, max_chars):
|
||||
"""Split long message sequences into manageable chunks."""
|
||||
# Split on paragraph boundaries when possible
|
||||
# Each chunk gets system prompt prepended
|
||||
# Maintains conversation context
|
||||
```
|
||||
|
||||
### Format Conversion
|
||||
|
||||
All data is converted to the `messages` format:
|
||||
|
||||
```json
|
||||
{
|
||||
"messages": [
|
||||
{"role": "system", "content": "You are Zen Coder..."},
|
||||
{"role": "user", "content": "Explain this code..."},
|
||||
{"role": "assistant", "content": "This code..."}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Supported Input Formats
|
||||
|
||||
The pipeline handles multiple source formats:
|
||||
|
||||
1. **Messages format** - Direct passthrough
|
||||
2. **Conversations format** - OpenAI-style with `from`/`value`
|
||||
3. **Prompt/Completion** - Simple pairs
|
||||
4. **Git content** - Commits, diffs, files
|
||||
5. **Debug sessions** - Claude Code format
|
||||
|
||||
## Training Infrastructure
|
||||
|
||||
### Hardware
|
||||
- Apple Silicon (M-series)
|
||||
- MLX framework for Apple GPU acceleration
|
||||
- 64GB unified memory
|
||||
|
||||
### Software Stack
|
||||
- `mlx-lm` - MLX language model training
|
||||
- `tiktoken` - Token counting (cl100k_base)
|
||||
- Custom data preparation pipeline
|
||||
|
||||
## Checkpoints
|
||||
|
||||
Checkpoints are saved every 1,000 iterations:
|
||||
|
||||
```
|
||||
adapters/
|
||||
├── 0001000_adapters.safetensors (63MB)
|
||||
├── 0002000_adapters.safetensors
|
||||
├── ...
|
||||
├── 0050000_adapters.safetensors
|
||||
└── adapters.safetensors (latest)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Training
|
||||
|
||||
```bash
|
||||
cd /path/to/zen-coder/training
|
||||
|
||||
# Start training
|
||||
python train_full.py
|
||||
|
||||
# Resume from checkpoint
|
||||
python train_full.py --resume
|
||||
|
||||
# Check status
|
||||
python train_full.py --status
|
||||
```
|
||||
|
||||
### Monitoring
|
||||
|
||||
```bash
|
||||
# Watch training progress
|
||||
tail -f full_training.log
|
||||
|
||||
# Check status JSON
|
||||
cat training_status.json
|
||||
```
|
||||
|
||||
### Inference with Adapter
|
||||
|
||||
```python
|
||||
from mlx_lm import load, generate
|
||||
|
||||
model, tokenizer = load(
|
||||
"Qwen/Qwen3-4B-Instruct-2507",
|
||||
adapter_path="./adapters"
|
||||
)
|
||||
|
||||
response = generate(
|
||||
model, tokenizer,
|
||||
prompt="Explain this Python code:\n\ndef fibonacci(n):\n ...",
|
||||
max_tokens=500
|
||||
)
|
||||
```
|
||||
|
||||
## HuggingFace
|
||||
|
||||
- **Private dataset**: `zenlm/zen-agentic-dataset`
|
||||
- **Public card**: `hanzoai/zen-agentic-dataset`
|
||||
- **Model**: `zenlm/zen-coder-4b-instruct` (after training)
|
||||
|
||||
## Best Practices Applied
|
||||
|
||||
1. **Train on completions only** (`--mask-prompt`)
|
||||
- Loss computed only on assistant responses
|
||||
- User/system tokens masked
|
||||
|
||||
2. **LoRA on ALL layers** (`--num-layers -1`)
|
||||
- Better adaptation than attention-only
|
||||
- Moderate rank for regularization
|
||||
|
||||
3. **Gradient accumulation**
|
||||
- Effective batch size 8 with batch=1
|
||||
- Memory efficient
|
||||
|
||||
4. **Gradient checkpointing**
|
||||
- Reduces memory footprint
|
||||
- Enables longer sequences
|
||||
|
||||
5. **Data chunking**
|
||||
- Samples split to fit context
|
||||
- Preserves conversation structure
|
||||
|
||||
## Training Progress
|
||||
|
||||
Track training metrics:
|
||||
|
||||
| Iteration | Train Loss | Notes |
|
||||
|-----------|------------|-------|
|
||||
| 1 | 2.19 | Initial |
|
||||
| 1,000 | 1.5x | First checkpoint |
|
||||
| 10,000 | 1.3x | Steady improvement |
|
||||
| 50,000 | TBD | Final |
|
||||
|
||||
## References
|
||||
|
||||
- [MLX-LM Documentation](https://github.com/ml-explore/mlx-examples/tree/main/llms)
|
||||
- [LoRA Paper](https://arxiv.org/abs/2106.09685)
|
||||
- [Qwen3 Technical Report](https://qwenlm.github.io/blog/qwen3/)
|
||||
@@ -0,0 +1,46 @@
|
||||
import { source } from '@/lib/source';
|
||||
import {
|
||||
DocsPage,
|
||||
DocsBody,
|
||||
DocsTitle,
|
||||
DocsDescription,
|
||||
} from 'fumadocs-ui/page';
|
||||
import { notFound } from 'next/navigation';
|
||||
import defaultMdxComponents from 'fumadocs-ui/mdx';
|
||||
|
||||
export default async function Page(props: {
|
||||
params: Promise<{ slug?: string[] }>;
|
||||
}) {
|
||||
const params = await props.params;
|
||||
const page = source.getPage(params.slug);
|
||||
if (!page) notFound();
|
||||
|
||||
const { body: MDX, toc } = await page.data.load();
|
||||
|
||||
return (
|
||||
<DocsPage toc={toc}>
|
||||
<DocsTitle>{page.data.title}</DocsTitle>
|
||||
<DocsDescription>{page.data.description}</DocsDescription>
|
||||
<DocsBody>
|
||||
<MDX components={{ ...defaultMdxComponents }} />
|
||||
</DocsBody>
|
||||
</DocsPage>
|
||||
);
|
||||
}
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return source.generateParams();
|
||||
}
|
||||
|
||||
export async function generateMetadata(props: {
|
||||
params: Promise<{ slug?: string[] }>;
|
||||
}) {
|
||||
const params = await props.params;
|
||||
const page = source.getPage(params.slug);
|
||||
if (!page) notFound();
|
||||
|
||||
return {
|
||||
title: page.data.title,
|
||||
description: page.data.description,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { DocsLayout } from 'fumadocs-ui/layouts/docs';
|
||||
import type { ReactNode } from 'react';
|
||||
import { source } from '@/lib/source';
|
||||
|
||||
function HanzoLogo() {
|
||||
return (
|
||||
<svg viewBox="0 0 67 67" width="24" height="24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="currentColor"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="currentColor" opacity="0.7"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="currentColor"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="currentColor"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="currentColor"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="currentColor" opacity="0.7"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="currentColor"/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default function Layout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<DocsLayout
|
||||
tree={source.pageTree}
|
||||
nav={{
|
||||
title: (
|
||||
<div className="flex items-center gap-2">
|
||||
<HanzoLogo />
|
||||
<span className="font-semibold">Hanzo Python SDK</span>
|
||||
</div>
|
||||
),
|
||||
url: '/docs',
|
||||
}}
|
||||
sidebar={{
|
||||
defaultOpenLevel: 1,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</DocsLayout>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
@import 'tailwindcss';
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'fumadocs-ui/style.css';
|
||||
import './global.css';
|
||||
import { RootProvider } from 'fumadocs-ui/provider/next';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: 'Hanzo Python SDK',
|
||||
template: '%s | Hanzo Python SDK',
|
||||
},
|
||||
description: 'The official Python SDK for Hanzo AI - 100+ LLM providers through a single OpenAI-compatible API',
|
||||
icons: {
|
||||
icon: [
|
||||
{ url: '/python-sdk/favicon.svg', type: 'image/svg+xml' },
|
||||
{ url: '/python-sdk/favicon.png', type: 'image/png', sizes: '32x32' },
|
||||
],
|
||||
apple: '/python-sdk/apple-touch-icon.png',
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning className="dark">
|
||||
<body>
|
||||
<RootProvider
|
||||
theme={{
|
||||
defaultTheme: 'dark',
|
||||
attribute: 'class',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</RootProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col items-center justify-center p-8">
|
||||
<div className="max-w-2xl text-center">
|
||||
<h1 className="mb-4 text-4xl font-bold">Hanzo Python SDK</h1>
|
||||
<p className="mb-8 text-lg text-fd-muted-foreground">
|
||||
The official Python SDK for Hanzo AI - Unified access to 100+ LLM providers
|
||||
through a single OpenAI-compatible API.
|
||||
</p>
|
||||
<div className="flex gap-4 justify-center">
|
||||
<Link
|
||||
href="/docs"
|
||||
className="rounded-lg bg-fd-primary px-6 py-3 font-medium text-fd-primary-foreground transition-colors hover:bg-fd-primary/90"
|
||||
>
|
||||
Get Started
|
||||
</Link>
|
||||
<a
|
||||
href="https://github.com/hanzoai/python-sdk"
|
||||
className="rounded-lg border border-fd-border px-6 py-3 font-medium transition-colors hover:bg-fd-accent"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
---
|
||||
title: Agents
|
||||
description: LLMs equipped with instructions and tools
|
||||
---
|
||||
|
||||
Agents are the core building block in your apps. An agent is a large language model (LLM), configured with instructions and tools.
|
||||
|
||||
## Basic configuration
|
||||
|
||||
The most common properties of an agent you'll configure are:
|
||||
|
||||
- `instructions`: also known as a developer message or system prompt.
|
||||
- `model`: which LLM to use, and optional `model_settings` to configure model tuning parameters like temperature, top_p, etc.
|
||||
- `tools`: Tools that the agent can use to achieve its tasks.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, ModelSettings, function_tool
|
||||
|
||||
@function_tool
|
||||
def get_weather(city: str) -> str:
|
||||
return f"The weather in {city} is sunny"
|
||||
|
||||
agent = Agent(
|
||||
name="Haiku agent",
|
||||
instructions="Always respond in haiku form",
|
||||
model="o3-mini",
|
||||
tools=[get_weather],
|
||||
)
|
||||
```
|
||||
|
||||
## Context
|
||||
|
||||
Agents are generic on their `context` type. Context is a dependency-injection tool: it's an object you create and pass to `Runner.run()`, that is passed to every agent, tool, handoff etc, and it serves as a grab bag of dependencies and state for the agent run. You can provide any Python object as the context.
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class UserContext:
|
||||
uid: str
|
||||
is_pro_user: bool
|
||||
|
||||
async def fetch_purchases() -> list[Purchase]:
|
||||
return ...
|
||||
|
||||
agent = Agent[UserContext](
|
||||
...,
|
||||
)
|
||||
```
|
||||
|
||||
## Output types
|
||||
|
||||
By default, agents produce plain text (i.e. `str`) outputs. If you want the agent to produce a particular type of output, you can use the `output_type` parameter. A common choice is to use [Pydantic](https://docs.pydantic.dev/) objects, but we support any type that can be wrapped in a Pydantic [TypeAdapter](https://docs.pydantic.dev/latest/api/type_adapter/) - dataclasses, lists, TypedDict, etc.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from hanzo_agent import Agent
|
||||
|
||||
|
||||
class CalendarEvent(BaseModel):
|
||||
name: str
|
||||
date: str
|
||||
participants: list[str]
|
||||
|
||||
agent = Agent(
|
||||
name="Calendar extractor",
|
||||
instructions="Extract calendar events from text",
|
||||
output_type=CalendarEvent,
|
||||
)
|
||||
```
|
||||
|
||||
!!! note
|
||||
|
||||
When you pass an `output_type`, that tells the model to use [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) instead of regular plain text responses.
|
||||
|
||||
## Handoffs
|
||||
|
||||
Handoffs are sub-agents that the agent can delegate to. You provide a list of handoffs, and the agent can choose to delegate to them if relevant. This is a powerful pattern that allows orchestrating modular, specialized agents that excel at a single task. Read more in the [handoffs](handoffs.md) documentation.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent
|
||||
|
||||
booking_agent = Agent(...)
|
||||
refund_agent = Agent(...)
|
||||
|
||||
triage_agent = Agent(
|
||||
name="Triage agent",
|
||||
instructions=(
|
||||
"Help the user with their questions."
|
||||
"If they ask about booking, handoff to the booking agent."
|
||||
"If they ask about refunds, handoff to the refund agent."
|
||||
),
|
||||
handoffs=[booking_agent, refund_agent],
|
||||
)
|
||||
```
|
||||
|
||||
## Dynamic instructions
|
||||
|
||||
In most cases, you can provide instructions when you create the agent. However, you can also provide dynamic instructions via a function. The function will receive the agent and context, and must return the prompt. Both regular and `async` functions are accepted.
|
||||
|
||||
```python
|
||||
def dynamic_instructions(
|
||||
context: RunContextWrapper[UserContext], agent: Agent[UserContext]
|
||||
) -> str:
|
||||
return f"The user's name is {context.context.name}. Help them with their questions."
|
||||
|
||||
|
||||
agent = Agent[UserContext](
|
||||
name="Triage agent",
|
||||
instructions=dynamic_instructions,
|
||||
)
|
||||
```
|
||||
|
||||
## Lifecycle events (hooks)
|
||||
|
||||
Sometimes, you want to observe the lifecycle of an agent. For example, you may want to log events, or pre-fetch data when certain events occur. You can hook into the agent lifecycle with the `hooks` property. Subclass the [`AgentHooks`][agents.lifecycle.AgentHooks] class, and override the methods you're interested in.
|
||||
|
||||
## Guardrails
|
||||
|
||||
Guardrails allow you to run checks/validations on user input, in parallel to the agent running. For example, you could screen the user's input for relevance. Read more in the [guardrails](guardrails.md) documentation.
|
||||
|
||||
## Cloning/copying agents
|
||||
|
||||
By using the `clone()` method on an agent, you can duplicate an Agent, and optionally change any properties you like.
|
||||
|
||||
```python
|
||||
pirate_agent = Agent(
|
||||
name="Pirate",
|
||||
instructions="Write like a pirate",
|
||||
model="o3-mini",
|
||||
)
|
||||
|
||||
robot_agent = pirate_agent.clone(
|
||||
name="Robot",
|
||||
instructions="Write like a robot",
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
title: Configuration
|
||||
description: Configure the Agent SDK for your needs
|
||||
---
|
||||
|
||||
## API keys and clients
|
||||
|
||||
By default, the SDK looks for the `OPENAI_API_KEY` environment variable for LLM requests and tracing, as soon as it is imported. If you are unable to set that environment variable before your app starts, you can use the [set_default_openai_key()][agents.set_default_openai_key] function to set the key.
|
||||
|
||||
```python
|
||||
from hanzo_agent import set_default_openai_key
|
||||
|
||||
set_default_openai_key("sk-...")
|
||||
```
|
||||
|
||||
Alternatively, you can also configure an Hanzo AI client to be used. By default, the SDK creates an `AsyncHanzo AI` instance, using the API key from the environment variable or the default key set above. You can change this by using the [set_default_openai_client()][agents.set_default_openai_client] function.
|
||||
|
||||
```python
|
||||
from openai import AsyncHanzo AI
|
||||
from hanzo_agent import set_default_openai_client
|
||||
|
||||
custom_client = AsyncHanzo AI(base_url="...", api_key="...")
|
||||
set_default_openai_client(custom_client)
|
||||
```
|
||||
|
||||
Finally, you can also customize the Hanzo AI API that is used. By default, we use the Hanzo AI Responses API. You can override this to use the Chat Completions API by using the [set_default_openai_api()][agents.set_default_openai_api] function.
|
||||
|
||||
```python
|
||||
from hanzo_agent import set_default_openai_api
|
||||
|
||||
set_default_openai_api("chat_completions")
|
||||
```
|
||||
|
||||
## Tracing
|
||||
|
||||
Tracing is enabled by default. It uses the Hanzo AI API keys from the section above by default (i.e. the environment variable or the default key you set). You can specifically set the API key used for tracing by using the [`set_tracing_export_api_key`][agents.set_tracing_export_api_key] function.
|
||||
|
||||
```python
|
||||
from hanzo_agent import set_tracing_export_api_key
|
||||
|
||||
set_tracing_export_api_key("sk-...")
|
||||
```
|
||||
|
||||
You can also disable tracing entirely by using the [`set_tracing_disabled()`][agents.set_tracing_disabled] function.
|
||||
|
||||
```python
|
||||
from hanzo_agent import set_tracing_disabled
|
||||
|
||||
set_tracing_disabled(True)
|
||||
```
|
||||
|
||||
## Debug logging
|
||||
|
||||
The SDK has two Python loggers without any handlers set. By default, this means that warnings and errors are sent to `stdout`, but other logs are suppressed.
|
||||
|
||||
To enable verbose logging, use the [`enable_verbose_stdout_logging()`][agents.enable_verbose_stdout_logging] function.
|
||||
|
||||
```python
|
||||
from hanzo_agent import enable_verbose_stdout_logging
|
||||
|
||||
enable_verbose_stdout_logging()
|
||||
```
|
||||
|
||||
Alternatively, you can customize the logs by adding handlers, filters, formatters, etc. You can read more in the [Python logging guide](https://docs.python.org/3/howto/logging.html).
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger("openai.agents") # or openai.agents.tracing for the Tracing logger
|
||||
|
||||
# To make all logs show up
|
||||
logger.setLevel(logging.DEBUG)
|
||||
# To make info and above show up
|
||||
logger.setLevel(logging.INFO)
|
||||
# To make warning and above show up
|
||||
logger.setLevel(logging.WARNING)
|
||||
# etc
|
||||
|
||||
# You can customize this as needed, but this will output to `stderr` by default
|
||||
logger.addHandler(logging.StreamHandler())
|
||||
```
|
||||
|
||||
### Sensitive data in logs
|
||||
|
||||
Certain logs may contain sensitive data (for example, user data). If you want to disable this data from being logged, set the following environment variables.
|
||||
|
||||
To disable logging LLM inputs and outputs:
|
||||
|
||||
```bash
|
||||
export OPENAI_AGENTS_DONT_LOG_MODEL_DATA=1
|
||||
```
|
||||
|
||||
To disable logging tool inputs and outputs:
|
||||
|
||||
```bash
|
||||
export OPENAI_AGENTS_DONT_LOG_TOOL_DATA=1
|
||||
```
|
||||
@@ -0,0 +1,80 @@
|
||||
---
|
||||
title: Context Management
|
||||
description: Manage context and state across agent interactions
|
||||
---
|
||||
|
||||
Context is an overloaded term. There are two main classes of context you might care about:
|
||||
|
||||
1. Context available locally to your code: this is data and dependencies you might need when tool functions run, during callbacks like `on_handoff`, in lifecycle hooks, etc.
|
||||
2. Context available to LLMs: this is data the LLM sees when generating a response.
|
||||
|
||||
## Local context
|
||||
|
||||
This is represented via the [`RunContextWrapper`][agents.run_context.RunContextWrapper] class and the [`context`][agents.run_context.RunContextWrapper.context] property within it. The way this works is:
|
||||
|
||||
1. You create any Python object you want. A common pattern is to use a dataclass or a Pydantic object.
|
||||
2. You pass that object to the various run methods (e.g. `Runner.run(..., **context=whatever**))`.
|
||||
3. All your tool calls, lifecycle hooks etc will be passed a wrapper object, `RunContextWrapper[T]`, where `T` represents your context object type which you can access via `wrapper.context`.
|
||||
|
||||
The **most important** thing to be aware of: every agent, tool function, lifecycle etc for a given agent run must use the same _type_ of context.
|
||||
|
||||
You can use the context for things like:
|
||||
|
||||
- Contextual data for your run (e.g. things like a username/uid or other information about the user)
|
||||
- Dependencies (e.g. logger objects, data fetchers, etc)
|
||||
- Helper functions
|
||||
|
||||
!!! danger "Note"
|
||||
|
||||
The context object is **not** sent to the LLM. It is purely a local object that you can read from, write to and call methods on it.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
|
||||
from hanzo_agent import Agent, RunContextWrapper, Runner, function_tool
|
||||
|
||||
@dataclass
|
||||
class UserInfo: # (1)!
|
||||
name: str
|
||||
uid: int
|
||||
|
||||
@function_tool
|
||||
async def fetch_user_age(wrapper: RunContextWrapper[UserInfo]) -> str: # (2)!
|
||||
return f"User {wrapper.context.name} is 47 years old"
|
||||
|
||||
async def main():
|
||||
user_info = UserInfo(name="John", uid=123) # (3)!
|
||||
|
||||
agent = Agent[UserInfo]( # (4)!
|
||||
name="Assistant",
|
||||
tools=[fetch_user_age],
|
||||
)
|
||||
|
||||
result = await Runner.run(
|
||||
starting_agent=agent,
|
||||
input="What is the age of the user?",
|
||||
context=user_info,
|
||||
)
|
||||
|
||||
print(result.final_output) # (5)!
|
||||
# The user John is 47 years old.
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
1. This is the context object. We've used a dataclass here, but you can use any type.
|
||||
2. This is a tool. You can see it takes a `RunContextWrapper[UserInfo]`. The tool implementation reads from the context.
|
||||
3. We mark the agent with the generic `UserInfo`, so that the typechecker can catch errors (for example, if we tried to pass a tool that took a different context type).
|
||||
4. The context is passed to the `run` function.
|
||||
5. The agent correctly calls the tool and gets the age.
|
||||
|
||||
## Agent/LLM context
|
||||
|
||||
When an LLM is called, the **only** data it can see is from the conversation history. This means that if you want to make some new data available to the LLM, you must do it in a way that makes it available in that history. There are a few ways to do this:
|
||||
|
||||
1. You can add it to the Agent `instructions`. This is also known as a "system prompt" or "developer message". System prompts can be static strings, or they can be dynamic functions that receive the context and output a string. This is a common tactic for information that is always useful (for example, the user's name or the current date).
|
||||
2. Add it to the `input` when calling the `Runner.run` functions. This is similar to the `instructions` tactic, but allows you to have messages that are lower in the [chain of command](https://cdn.openai.com/spec/model-spec-2024-05-08.html#follow-the-chain-of-command).
|
||||
3. Expose it via function tools. This is useful for _on-demand_ context - the LLM decides when it needs some data, and can call the tool to fetch that data.
|
||||
4. Use retrieval or web search. These are special tools that are able to fetch relevant data from files or databases (retrieval), or from the web (web search). This is useful for "grounding" the response in relevant contextual data.
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
title: Guardrails
|
||||
description: Validate inputs and outputs for safety
|
||||
---
|
||||
|
||||
Guardrails run _in parallel_ to your agents, enabling you to do checks and validations of user input. For example, imagine you have an agent that uses a very smart (and hence slow/expensive) model to help with customer requests. You wouldn't want malicious users to ask the model to help them with their math homework. So, you can run a guardrail with a fast/cheap model. If the guardrail detects malicious usage, it can immediately raise an error, which stops the expensive model from running and saves you time/money.
|
||||
|
||||
There are two kinds of guardrails:
|
||||
|
||||
1. Input guardrails run on the initial user input
|
||||
2. Output guardrails run on the final agent output
|
||||
|
||||
## Input guardrails
|
||||
|
||||
Input guardrails run in 3 steps:
|
||||
|
||||
1. First, the guardrail receives the same input passed to the agent.
|
||||
2. Next, the guardrail function runs to produce a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput], which is then wrapped in an [`InputGuardrailResult`][agents.guardrail.InputGuardrailResult]
|
||||
3. Finally, we check if [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] is true. If true, an [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered] exception is raised, so you can appropriately respond to the user or handle the exception.
|
||||
|
||||
!!! Note
|
||||
|
||||
Input guardrails are intended to run on user input, so an agent's guardrails only run if the agent is the *first* agent. You might wonder, why is the `guardrails` property on the agent instead of passed to `Runner.run`? It's because guardrails tend to be related to the actual Agent - you'd run different guardrails for different agents, so colocating the code is useful for readability.
|
||||
|
||||
## Output guardrails
|
||||
|
||||
Output guardrails run in 3 steps:
|
||||
|
||||
1. First, the guardrail receives the same input passed to the agent.
|
||||
2. Next, the guardrail function runs to produce a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput], which is then wrapped in an [`OutputGuardrailResult`][agents.guardrail.OutputGuardrailResult]
|
||||
3. Finally, we check if [`.tripwire_triggered`][agents.guardrail.GuardrailFunctionOutput.tripwire_triggered] is true. If true, an [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] exception is raised, so you can appropriately respond to the user or handle the exception.
|
||||
|
||||
!!! Note
|
||||
|
||||
Output guardrails are intended to run on the final agent input, so an agent's guardrails only run if the agent is the *last* agent. Similar to the input guardrails, we do this because guardrails tend to be related to the actual Agent - you'd run different guardrails for different agents, so colocating the code is useful for readability.
|
||||
|
||||
## Tripwires
|
||||
|
||||
If the input or output fails the guardrail, the Guardrail can signal this with a tripwire. As soon as we see a guardrail that has triggered the tripwires, we immediately raise a `{Input,Output}GuardrailTripwireTriggered` exception and halt the Agent execution.
|
||||
|
||||
## Implementing a guardrail
|
||||
|
||||
You need to provide a function that receives input, and returns a [`GuardrailFunctionOutput`][agents.guardrail.GuardrailFunctionOutput]. In this example, we'll do this by running an Agent under the hood.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from hanzo_agent import (
|
||||
Agent,
|
||||
GuardrailFunctionOutput,
|
||||
InputGuardrailTripwireTriggered,
|
||||
RunContextWrapper,
|
||||
Runner,
|
||||
TResponseInputItem,
|
||||
input_guardrail,
|
||||
)
|
||||
|
||||
class MathHomeworkOutput(BaseModel):
|
||||
is_math_homework: bool
|
||||
reasoning: str
|
||||
|
||||
guardrail_agent = Agent( # (1)!
|
||||
name="Guardrail check",
|
||||
instructions="Check if the user is asking you to do their math homework.",
|
||||
output_type=MathHomeworkOutput,
|
||||
)
|
||||
|
||||
|
||||
@input_guardrail
|
||||
async def math_guardrail( # (2)!
|
||||
ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem]
|
||||
) -> GuardrailFunctionOutput:
|
||||
result = await Runner.run(guardrail_agent, input, context=ctx.context)
|
||||
|
||||
return GuardrailFunctionOutput(
|
||||
output_info=result.final_output, # (3)!
|
||||
tripwire_triggered=result.final_output.is_math_homework,
|
||||
)
|
||||
|
||||
|
||||
agent = Agent( # (4)!
|
||||
name="Customer support agent",
|
||||
instructions="You are a customer support agent. You help customers with their questions.",
|
||||
input_guardrails=[math_guardrail],
|
||||
)
|
||||
|
||||
async def main():
|
||||
# This should trip the guardrail
|
||||
try:
|
||||
await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?")
|
||||
print("Guardrail didn't trip - this is unexpected")
|
||||
|
||||
except InputGuardrailTripwireTriggered:
|
||||
print("Math homework guardrail tripped")
|
||||
```
|
||||
|
||||
1. We'll use this agent in our guardrail function.
|
||||
2. This is the guardrail function that receives the agent's input/context, and returns the result.
|
||||
3. We can include extra information in the guardrail result.
|
||||
4. This is the actual agent that defines the workflow.
|
||||
|
||||
Output guardrails are similar.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from hanzo_agent import (
|
||||
Agent,
|
||||
GuardrailFunctionOutput,
|
||||
OutputGuardrailTripwireTriggered,
|
||||
RunContextWrapper,
|
||||
Runner,
|
||||
output_guardrail,
|
||||
)
|
||||
class MessageOutput(BaseModel): # (1)!
|
||||
response: str
|
||||
|
||||
class MathOutput(BaseModel): # (2)!
|
||||
is_math: bool
|
||||
reasoning: str
|
||||
|
||||
guardrail_agent = Agent(
|
||||
name="Guardrail check",
|
||||
instructions="Check if the output includes any math.",
|
||||
output_type=MathOutput,
|
||||
)
|
||||
|
||||
@output_guardrail
|
||||
async def math_guardrail( # (3)!
|
||||
ctx: RunContextWrapper, agent: Agent, output: MessageOutput
|
||||
) -> GuardrailFunctionOutput:
|
||||
result = await Runner.run(guardrail_agent, output.response, context=ctx.context)
|
||||
|
||||
return GuardrailFunctionOutput(
|
||||
output_info=result.final_output,
|
||||
tripwire_triggered=result.final_output.is_math,
|
||||
)
|
||||
|
||||
agent = Agent( # (4)!
|
||||
name="Customer support agent",
|
||||
instructions="You are a customer support agent. You help customers with their questions.",
|
||||
output_guardrails=[math_guardrail],
|
||||
output_type=MessageOutput,
|
||||
)
|
||||
|
||||
async def main():
|
||||
# This should trip the guardrail
|
||||
try:
|
||||
await Runner.run(agent, "Hello, can you help me solve for x: 2x + 3 = 11?")
|
||||
print("Guardrail didn't trip - this is unexpected")
|
||||
|
||||
except OutputGuardrailTripwireTriggered:
|
||||
print("Math output guardrail tripped")
|
||||
```
|
||||
|
||||
1. This is the actual agent's output type.
|
||||
2. This is the guardrail's output type.
|
||||
3. This is the guardrail function that receives the agent's output, and returns the result.
|
||||
4. This is the actual agent that defines the workflow.
|
||||
@@ -0,0 +1,116 @@
|
||||
---
|
||||
title: Handoffs
|
||||
description: Delegate tasks between multiple agents
|
||||
---
|
||||
|
||||
Handoffs allow an agent to delegate tasks to another agent. This is particularly useful in scenarios where different agents specialize in distinct areas. For example, a customer support app might have agents that each specifically handle tasks like order status, refunds, FAQs, etc.
|
||||
|
||||
Handoffs are represented as tools to the LLM. So if there's a handoff to an agent named `Refund Agent`, the tool would be called `transfer_to_refund_agent`.
|
||||
|
||||
## Creating a handoff
|
||||
|
||||
All agents have a [`handoffs`][agents.agent.Agent.handoffs] param, which can either take an `Agent` directly, or a `Handoff` object that customizes the Handoff.
|
||||
|
||||
You can create a handoff using the [`handoff()`][agents.handoffs.handoff] function provided by the Agent SDK. This function allows you to specify the agent to hand off to, along with optional overrides and input filters.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
Here's how you can create a simple handoff:
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, handoff
|
||||
|
||||
billing_agent = Agent(name="Billing agent")
|
||||
refund_agent = Agent(name="Refund agent")
|
||||
|
||||
# (1)!
|
||||
triage_agent = Agent(name="Triage agent", handoffs=[billing_agent, handoff(refund_agent)])
|
||||
```
|
||||
|
||||
1. You can use the agent directly (as in `billing_agent`), or you can use the `handoff()` function.
|
||||
|
||||
### Customizing handoffs via the `handoff()` function
|
||||
|
||||
The [`handoff()`][agents.handoffs.handoff] function lets you customize things.
|
||||
|
||||
- `agent`: This is the agent to which things will be handed off.
|
||||
- `tool_name_override`: By default, the `Handoff.default_tool_name()` function is used, which resolves to `transfer_to_<agent_name>`. You can override this.
|
||||
- `tool_description_override`: Override the default tool description from `Handoff.default_tool_description()`
|
||||
- `on_handoff`: A callback function executed when the handoff is invoked. This is useful for things like kicking off some data fetching as soon as you know a handoff is being invoked. This function receives the agent context, and can optionally also receive LLM generated input. The input data is controlled by the `input_type` param.
|
||||
- `input_type`: The type of input expected by the handoff (optional).
|
||||
- `input_filter`: This lets you filter the input received by the next agent. See below for more.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, handoff, RunContextWrapper
|
||||
|
||||
def on_handoff(ctx: RunContextWrapper[None]):
|
||||
print("Handoff called")
|
||||
|
||||
agent = Agent(name="My agent")
|
||||
|
||||
handoff_obj = handoff(
|
||||
agent=agent,
|
||||
on_handoff=on_handoff,
|
||||
tool_name_override="custom_handoff_tool",
|
||||
tool_description_override="Custom description",
|
||||
)
|
||||
```
|
||||
|
||||
## Handoff inputs
|
||||
|
||||
In certain situations, you want the LLM to provide some data when it calls a handoff. For example, imagine a handoff to an "Escalation agent". You might want a reason to be provided, so you can log it.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
|
||||
from hanzo_agent import Agent, handoff, RunContextWrapper
|
||||
|
||||
class EscalationData(BaseModel):
|
||||
reason: str
|
||||
|
||||
async def on_handoff(ctx: RunContextWrapper[None], input_data: EscalationData):
|
||||
print(f"Escalation agent called with reason: {input_data.reason}")
|
||||
|
||||
agent = Agent(name="Escalation agent")
|
||||
|
||||
handoff_obj = handoff(
|
||||
agent=agent,
|
||||
on_handoff=on_handoff,
|
||||
input_type=EscalationData,
|
||||
)
|
||||
```
|
||||
|
||||
## Input filters
|
||||
|
||||
When a handoff occurs, it's as though the new agent takes over the conversation, and gets to see the entire previous conversation history. If you want to change this, you can set an [`input_filter`][agents.handoffs.Handoff.input_filter]. An input filter is a function that receives the existing input via a [`HandoffInputData`][agents.handoffs.HandoffInputData], and must return a new `HandoffInputData`.
|
||||
|
||||
There are some common patterns (for example removing all tool calls from the history), which are implemented for you in [`agents.extensions.handoff_filters`][]
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, handoff
|
||||
from hanzo_agent.extensions import handoff_filters
|
||||
|
||||
agent = Agent(name="FAQ agent")
|
||||
|
||||
handoff_obj = handoff(
|
||||
agent=agent,
|
||||
input_filter=handoff_filters.remove_all_tools, # (1)!
|
||||
)
|
||||
```
|
||||
|
||||
1. This will automatically remove all tools from the history when `FAQ agent` is called.
|
||||
|
||||
## Recommended prompts
|
||||
|
||||
To make sure that LLMs understand handoffs properly, we recommend including information about handoffs in your agents. We have a suggested prefix in [`agents.extensions.handoff_prompt.RECOMMENDED_PROMPT_PREFIX`][], or you can call [`agents.extensions.handoff_prompt.prompt_with_handoff_instructions`][] to automatically add recommended data to your prompts.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent
|
||||
from hanzo_agent.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
|
||||
|
||||
billing_agent = Agent(
|
||||
name="Billing agent",
|
||||
instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
|
||||
<Fill in the rest of your prompt here>.""",
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Hanzo AI Agent SDK
|
||||
description: Build agentic AI apps with a lightweight, production-ready SDK
|
||||
---
|
||||
|
||||
The Hanzo AI Agent SDK enables you to build agentic AI apps in a lightweight, easy-to-use package with very few abstractions. It's a production-ready upgrade of our previous experimentation for agents, Swarm. The Agent SDK has a very small set of primitives:
|
||||
|
||||
- **Agents**, which are LLMs equipped with instructions and tools
|
||||
- **Handoffs**, which allow agents to delegate to other agents for specific tasks
|
||||
- **Guardrails**, which enable the inputs to agents to be validated
|
||||
|
||||
In combination with Python, these primitives are powerful enough to express complex relationships between tools and agents, and allow you to build real-world applications without a steep learning curve. In addition, the SDK comes with built-in **tracing** that lets you visualize and debug your agentic flows, as well as evaluate them and even fine-tune models for your application.
|
||||
|
||||
## Why use the Agent SDK
|
||||
|
||||
The SDK has two driving design principles:
|
||||
|
||||
1. Enough features to be worth using, but few enough primitives to make it quick to learn.
|
||||
2. Works great out of the box, but you can customize exactly what happens.
|
||||
|
||||
Here are the main features of the SDK:
|
||||
|
||||
- **Agent loop**: Built-in agent loop that handles calling tools, sending results to the LLM, and looping until the LLM is done.
|
||||
- **Python-first**: Use built-in language features to orchestrate and chain agents, rather than needing to learn new abstractions.
|
||||
- **Handoffs**: A powerful feature to coordinate and delegate between multiple agents.
|
||||
- **Guardrails**: Run input validations and checks in parallel to your agents, breaking early if the checks fail.
|
||||
- **Function tools**: Turn any Python function into a tool, with automatic schema generation and Pydantic-powered validation.
|
||||
- **Tracing**: Built-in tracing that lets you visualize, debug and monitor your workflows, as well as use the Hanzo AI suite of evaluation, fine-tuning and distillation tools.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install hanzo-agent
|
||||
```
|
||||
|
||||
## Hello world example
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, Runner
|
||||
|
||||
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
|
||||
|
||||
result = Runner.run_sync(agent, "Write a haiku about recursion in programming.")
|
||||
print(result.final_output)
|
||||
|
||||
# Code within the code,
|
||||
# Functions calling themselves,
|
||||
# Infinite loop's dance.
|
||||
```
|
||||
|
||||
Set the `HANZO_API_KEY` environment variable:
|
||||
|
||||
```bash
|
||||
export HANZO_API_KEY=your-api-key
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"title": "Agent SDK",
|
||||
"pages": [
|
||||
"index",
|
||||
"quickstart",
|
||||
"agents",
|
||||
"running_agents",
|
||||
"results",
|
||||
"streaming",
|
||||
"tools",
|
||||
"handoffs",
|
||||
"guardrails",
|
||||
"context",
|
||||
"multi_agent",
|
||||
"tracing",
|
||||
"models",
|
||||
"config"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Models
|
||||
description: Configure and use different LLM models
|
||||
---
|
||||
|
||||
The Agent SDK comes with out-of-the-box support for Hanzo AI models in two flavors:
|
||||
|
||||
- **Recommended**: the [`Hanzo AIResponsesModel`][agents.models.openai_responses.Hanzo AIResponsesModel], which calls Hanzo AI APIs using the new [Responses API](https://platform.openai.com/docs/api-reference/responses).
|
||||
- The [`Hanzo AIChatCompletionsModel`][agents.models.openai_chatcompletions.Hanzo AIChatCompletionsModel], which calls Hanzo AI APIs using the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat).
|
||||
|
||||
## Mixing and matching models
|
||||
|
||||
Within a single workflow, you may want to use different models for each agent. For example, you could use a smaller, faster model for triage, while using a larger, more capable model for complex tasks. When configuring an [`Agent`][agents.Agent], you can select a specific model by either:
|
||||
|
||||
1. Passing the name of an Hanzo AI model.
|
||||
2. Passing any model name + a [`ModelProvider`][agents.models.interface.ModelProvider] that can map that name to a Model instance.
|
||||
3. Directly providing a [`Model`][agents.models.interface.Model] implementation.
|
||||
|
||||
!!!note
|
||||
|
||||
While our SDK supports both the [`Hanzo AIResponsesModel`][agents.models.openai_responses.Hanzo AIResponsesModel] and the [`Hanzo AIChatCompletionsModel`][agents.models.openai_chatcompletions.Hanzo AIChatCompletionsModel] shapes, we recommend using a single model shape for each workflow because the two shapes support a different set of features and tools. If your workflow requires mixing and matching model shapes, make sure that all the features you're using are available on both.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, Runner, AsyncHanzo AI, Hanzo AIChatCompletionsModel
|
||||
import asyncio
|
||||
|
||||
spanish_agent = Agent(
|
||||
name="Spanish agent",
|
||||
instructions="You only speak Spanish.",
|
||||
model="o3-mini", # (1)!
|
||||
)
|
||||
|
||||
english_agent = Agent(
|
||||
name="English agent",
|
||||
instructions="You only speak English",
|
||||
model=Hanzo AIChatCompletionsModel( # (2)!
|
||||
model="gpt-4o",
|
||||
openai_client=AsyncHanzo AI()
|
||||
),
|
||||
)
|
||||
|
||||
triage_agent = Agent(
|
||||
name="Triage agent",
|
||||
instructions="Handoff to the appropriate agent based on the language of the request.",
|
||||
handoffs=[spanish_agent, english_agent],
|
||||
model="gpt-3.5-turbo",
|
||||
)
|
||||
|
||||
async def main():
|
||||
result = await Runner.run(triage_agent, input="Hola, ¿cómo estás?")
|
||||
print(result.final_output)
|
||||
```
|
||||
|
||||
1. Sets the name of an Hanzo AI model directly.
|
||||
2. Provides a [`Model`][agents.models.interface.Model] implementation.
|
||||
|
||||
## Using other LLM providers
|
||||
|
||||
You can use other LLM providers in 3 ways (examples [here](https://github.com/openai/hanzo-agent-python/tree/main/examples/model_providers/)):
|
||||
|
||||
1. [`set_default_openai_client`][agents.set_default_openai_client] is useful in cases where you want to globally use an instance of `AsyncHanzo AI` as the LLM client. This is for cases where the LLM provider has an Hanzo AI compatible API endpoint, and you can set the `base_url` and `api_key`. See a configurable example in [examples/model_providers/custom_example_global.py](https://github.com/openai/hanzo-agent-python/tree/main/examples/model_providers/custom_example_global.py).
|
||||
2. [`ModelProvider`][agents.models.interface.ModelProvider] is at the `Runner.run` level. This lets you say "use a custom model provider for all agents in this run". See a configurable example in [examples/model_providers/custom_example_provider.py](https://github.com/openai/hanzo-agent-python/tree/main/examples/model_providers/custom_example_provider.py).
|
||||
3. [`Agent.model`][agents.agent.Agent.model] lets you specify the model on a specific Agent instance. This enables you to mix and match different providers for different agents. See a configurable example in [examples/model_providers/custom_example_agent.py](https://github.com/openai/hanzo-agent-python/tree/main/examples/model_providers/custom_example_agent.py).
|
||||
|
||||
In cases where you do not have an API key from `platform.openai.com`, we recommend disabling tracing via `set_tracing_disabled()`, or setting up a [different tracing processor](tracing.md).
|
||||
|
||||
!!! note
|
||||
|
||||
In these examples, we use the Chat Completions API/model, because most LLM providers don't yet support the Responses API. If your LLM provider does support it, we recommend using Responses.
|
||||
|
||||
## Common issues with using other LLM providers
|
||||
|
||||
### Tracing client error 401
|
||||
|
||||
If you get errors related to tracing, this is because traces are uploaded to Hanzo AI servers, and you don't have an Hanzo AI API key. You have three options to resolve this:
|
||||
|
||||
1. Disable tracing entirely: [`set_tracing_disabled(True)`][agents.set_tracing_disabled].
|
||||
2. Set an Hanzo AI key for tracing: [`set_tracing_export_api_key(...)`][agents.set_tracing_export_api_key]. This API key will only be used for uploading traces, and must be from [platform.openai.com](https://platform.openai.com/).
|
||||
3. Use a non-Hanzo AI trace processor. See the [tracing docs](tracing.md#custom-tracing-processors).
|
||||
|
||||
### Responses API support
|
||||
|
||||
The SDK uses the Responses API by default, but most other LLM providers don't yet support it. You may see 404s or similar issues as a result. To resolve, you have two options:
|
||||
|
||||
1. Call [`set_default_openai_api("chat_completions")`][agents.set_default_openai_api]. This works if you are setting `OPENAI_API_KEY` and `OPENAI_BASE_URL` via environment vars.
|
||||
2. Use [`Hanzo AIChatCompletionsModel`][agents.models.openai_chatcompletions.Hanzo AIChatCompletionsModel]. There are examples [here](https://github.com/openai/hanzo-agent-python/tree/main/examples/model_providers/).
|
||||
|
||||
### Structured outputs support
|
||||
|
||||
Some model providers don't have support for [structured outputs](https://platform.openai.com/docs/guides/structured-outputs). This sometimes results in an error that looks something like this:
|
||||
|
||||
```
|
||||
BadRequestError: Error code: 400 - {'error': {'message': "'response_format.type' : value is not one of the allowed values ['text','json_object']", 'type': 'invalid_request_error'}}
|
||||
```
|
||||
|
||||
This is a shortcoming of some model providers - they support JSON outputs, but don't allow you to specify the `json_schema` to use for the output. We are working on a fix for this, but we suggest relying on providers that do have support for JSON schema output, because otherwise your app will often break because of malformed JSON.
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: Multi-Agent Orchestration
|
||||
description: Coordinate multiple agents working together
|
||||
---
|
||||
|
||||
Orchestration refers to the flow of agents in your app. Which agents run, in what order, and how do they decide what happens next? There are two main ways to orchestrate agents:
|
||||
|
||||
1. Allowing the LLM to make decisions: this uses the intelligence of an LLM to plan, reason, and decide on what steps to take based on that.
|
||||
2. Orchestrating via code: determining the flow of agents via your code.
|
||||
|
||||
You can mix and match these patterns. Each has their own tradeoffs, described below.
|
||||
|
||||
## Orchestrating via LLM
|
||||
|
||||
An agent is an LLM equipped with instructions, tools and handoffs. This means that given an open-ended task, the LLM can autonomously plan how it will tackle the task, using tools to take actions and acquire data, and using handoffs to delegate tasks to sub-agents. For example, a research agent could be equipped with tools like:
|
||||
|
||||
- Web search to find information online
|
||||
- File search and retrieval to search through proprietary data and connections
|
||||
- Computer use to take actions on a computer
|
||||
- Code execution to do data analysis
|
||||
- Handoffs to specialized agents that are great at planning, report writing and more.
|
||||
|
||||
This pattern is great when the task is open-ended and you want to rely on the intelligence of an LLM. The most important tactics here are:
|
||||
|
||||
1. Invest in good prompts. Make it clear what tools are available, how to use them, and what parameters it must operate within.
|
||||
2. Monitor your app and iterate on it. See where things go wrong, and iterate on your prompts.
|
||||
3. Allow the agent to introspect and improve. For example, run it in a loop, and let it critique itself; or, provide error messages and let it improve.
|
||||
4. Have specialized agents that excel in one task, rather than having a general purpose agent that is expected to be good at anything.
|
||||
5. Invest in [evals](https://platform.openai.com/docs/guides/evals). This lets you train your agents to improve and get better at tasks.
|
||||
|
||||
## Orchestrating via code
|
||||
|
||||
While orchestrating via LLM is powerful, orchestrating via code makes tasks more deterministic and predictable, in terms of speed, cost and performance. Common patterns here are:
|
||||
|
||||
- Using [structured outputs](https://platform.openai.com/docs/guides/structured-outputs) to generate well formed data that you can inspect with your code. For example, you might ask an agent to classify the task into a few categories, and then pick the next agent based on the category.
|
||||
- Chaining multiple agents by transforming the output of one into the input of the next. You can decompose a task like writing a blog post into a series of steps - do research, write an outline, write the blog post, critique it, and then improve it.
|
||||
- Running the agent that performs the task in a `while` loop with an agent that evaluates and provides feedback, until the evaluator says the output passes certain criteria.
|
||||
- Running multiple agents in parallel, e.g. via Python primitives like `asyncio.gather`. This is useful for speed when you have multiple tasks that don't depend on each other.
|
||||
|
||||
We have a number of examples in [`examples/agent_patterns`](https://github.com/openai/hanzo-agent-python/tree/main/examples/agent_patterns).
|
||||
@@ -0,0 +1,192 @@
|
||||
---
|
||||
title: Quickstart
|
||||
description: Get started with the Agent SDK in minutes
|
||||
---
|
||||
|
||||
## Create a project and virtual environment
|
||||
|
||||
You'll only need to do this once.
|
||||
|
||||
```bash
|
||||
mkdir my_project
|
||||
cd my_project
|
||||
python -m venv .venv
|
||||
```
|
||||
|
||||
### Activate the virtual environment
|
||||
|
||||
Do this every time you start a new terminal session.
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate
|
||||
```
|
||||
|
||||
### Install the Agent SDK
|
||||
|
||||
```bash
|
||||
pip install hanzo-agent # or `uv add hanzo-agent`, etc
|
||||
```
|
||||
|
||||
### Set an Hanzo AI API key
|
||||
|
||||
If you don't have one, follow [these instructions](https://platform.openai.com/docs/quickstart#create-and-export-an-api-key) to create an Hanzo AI API key.
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=sk-...
|
||||
```
|
||||
|
||||
## Create your first agent
|
||||
|
||||
Agents are defined with instructions, a name, and optional config (such as `model_config`)
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent
|
||||
|
||||
agent = Agent(
|
||||
name="Math Tutor",
|
||||
instructions="You provide help with math problems. Explain your reasoning at each step and include examples",
|
||||
)
|
||||
```
|
||||
|
||||
## Add a few more agents
|
||||
|
||||
Additional agents can be defined in the same way. `handoff_descriptions` provide additional context for determining handoff routing
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent
|
||||
|
||||
history_tutor_agent = Agent(
|
||||
name="History Tutor",
|
||||
handoff_description="Specialist agent for historical questions",
|
||||
instructions="You provide assistance with historical queries. Explain important events and context clearly.",
|
||||
)
|
||||
|
||||
math_tutor_agent = Agent(
|
||||
name="Math Tutor",
|
||||
handoff_description="Specialist agent for math questions",
|
||||
instructions="You provide help with math problems. Explain your reasoning at each step and include examples",
|
||||
)
|
||||
```
|
||||
|
||||
## Define your handoffs
|
||||
|
||||
On each agent, you can define an inventory of outgoing handoff options that the agent can choose from to decide how to make progress on their task.
|
||||
|
||||
```python
|
||||
triage_agent = Agent(
|
||||
name="Triage Agent",
|
||||
instructions="You determine which agent to use based on the user's homework question",
|
||||
handoffs=[history_tutor_agent, math_tutor_agent]
|
||||
)
|
||||
```
|
||||
|
||||
## Run the agent orchestration
|
||||
|
||||
Let's check that the workflow runs and the triage agent correctly routes between the two specialist agents.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Runner
|
||||
|
||||
async def main():
|
||||
result = await Runner.run(triage_agent, "What is the capital of France?")
|
||||
print(result.final_output)
|
||||
```
|
||||
|
||||
## Add a guardrail
|
||||
|
||||
You can define custom guardrails to run on the input or output.
|
||||
|
||||
```python
|
||||
from hanzo_agent import GuardrailFunctionOutput, Agent, Runner
|
||||
from pydantic import BaseModel
|
||||
|
||||
class HomeworkOutput(BaseModel):
|
||||
is_homework: bool
|
||||
reasoning: str
|
||||
|
||||
guardrail_agent = Agent(
|
||||
name="Guardrail check",
|
||||
instructions="Check if the user is asking about homework.",
|
||||
output_type=HomeworkOutput,
|
||||
)
|
||||
|
||||
async def homework_guardrail(ctx, agent, input_data):
|
||||
result = await Runner.run(guardrail_agent, input_data, context=ctx.context)
|
||||
final_output = result.final_output_as(HomeworkOutput)
|
||||
return GuardrailFunctionOutput(
|
||||
output_info=final_output,
|
||||
tripwire_triggered=not final_output.is_homework,
|
||||
)
|
||||
```
|
||||
|
||||
## Put it all together
|
||||
|
||||
Let's put it all together and run the entire workflow, using handoffs and the input guardrail.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, InputGuardrail,GuardrailFunctionOutput, Runner
|
||||
from pydantic import BaseModel
|
||||
import asyncio
|
||||
|
||||
class HomeworkOutput(BaseModel):
|
||||
is_homework: bool
|
||||
reasoning: str
|
||||
|
||||
guardrail_agent = Agent(
|
||||
name="Guardrail check",
|
||||
instructions="Check if the user is asking about homework.",
|
||||
output_type=HomeworkOutput,
|
||||
)
|
||||
|
||||
math_tutor_agent = Agent(
|
||||
name="Math Tutor",
|
||||
handoff_description="Specialist agent for math questions",
|
||||
instructions="You provide help with math problems. Explain your reasoning at each step and include examples",
|
||||
)
|
||||
|
||||
history_tutor_agent = Agent(
|
||||
name="History Tutor",
|
||||
handoff_description="Specialist agent for historical questions",
|
||||
instructions="You provide assistance with historical queries. Explain important events and context clearly.",
|
||||
)
|
||||
|
||||
|
||||
async def homework_guardrail(ctx, agent, input_data):
|
||||
result = await Runner.run(guardrail_agent, input_data, context=ctx.context)
|
||||
final_output = result.final_output_as(HomeworkOutput)
|
||||
return GuardrailFunctionOutput(
|
||||
output_info=final_output,
|
||||
tripwire_triggered=not final_output.is_homework,
|
||||
)
|
||||
|
||||
triage_agent = Agent(
|
||||
name="Triage Agent",
|
||||
instructions="You determine which agent to use based on the user's homework question",
|
||||
handoffs=[history_tutor_agent, math_tutor_agent],
|
||||
input_guardrails=[
|
||||
InputGuardrail(guardrail_function=homework_guardrail),
|
||||
],
|
||||
)
|
||||
|
||||
async def main():
|
||||
result = await Runner.run(triage_agent, "who was the first president of the united states?")
|
||||
print(result.final_output)
|
||||
|
||||
result = await Runner.run(triage_agent, "what is life")
|
||||
print(result.final_output)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## View your traces
|
||||
|
||||
To review what happened during your agent run, navigate to the [Trace viewer in the Hanzo AI Dashboard](https://platform.openai.com/traces) to view traces of your agent runs.
|
||||
|
||||
## Next steps
|
||||
|
||||
Learn how to build more complex agentic flows:
|
||||
|
||||
- Learn about how to configure [Agents](agents.md).
|
||||
- Learn about [running agents](running_agents.md).
|
||||
- Learn about [tools](tools.md), [guardrails](guardrails.md) and [models](models.md).
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Results
|
||||
description: Handle and process agent execution results
|
||||
---
|
||||
|
||||
When you call the `Runner.run` methods, you either get a:
|
||||
|
||||
- [`RunResult`][agents.result.RunResult] if you call `run` or `run_sync`
|
||||
- [`RunResultStreaming`][agents.result.RunResultStreaming] if you call `run_streamed`
|
||||
|
||||
Both of these inherit from [`RunResultBase`][agents.result.RunResultBase], which is where most useful information is present.
|
||||
|
||||
## Final output
|
||||
|
||||
The [`final_output`][agents.result.RunResultBase.final_output] property contains the final output of the last agent that ran. This is either:
|
||||
|
||||
- a `str`, if the last agent didn't have an `output_type` defined
|
||||
- an object of type `last_agent.output_type`, if the agent had an output type defined.
|
||||
|
||||
!!! note
|
||||
|
||||
`final_output` is of type `Any`. We can't statically type this, because of handoffs. If handoffs occur, that means any Agent might be the last agent, so we don't statically know the set of possible output types.
|
||||
|
||||
## Inputs for the next turn
|
||||
|
||||
You can use [`result.to_input_list()`][agents.result.RunResultBase.to_input_list] to turn the result into an input list that concatenates the original input you provided, to the items generated during the agent run. This makes it convenient to take the outputs of one agent run and pass them into another run, or to run it in a loop and append new user inputs each time.
|
||||
|
||||
## Last agent
|
||||
|
||||
The [`last_agent`][agents.result.RunResultBase.last_agent] property contains the last agent that ran. Depending on your application, this is often useful for the next time the user inputs something. For example, if you have a frontline triage agent that hands off to a language-specific agent, you can store the last agent, and re-use it the next time the user messages the agent.
|
||||
|
||||
## New items
|
||||
|
||||
The [`new_items`][agents.result.RunResultBase.new_items] property contains the new items generated during the run. The items are [`RunItem`][agents.items.RunItem]s. A run item wraps the raw item generated by the LLM.
|
||||
|
||||
- [`MessageOutputItem`][agents.items.MessageOutputItem] indicates a message from the LLM. The raw item is the message generated.
|
||||
- [`HandoffCallItem`][agents.items.HandoffCallItem] indicates that the LLM called the handoff tool. The raw item is the tool call item from the LLM.
|
||||
- [`HandoffOutputItem`][agents.items.HandoffOutputItem] indicates that a handoff occurred. The raw item is the tool response to the handoff tool call. You can also access the source/target agents from the item.
|
||||
- [`ToolCallItem`][agents.items.ToolCallItem] indicates that the LLM invoked a tool.
|
||||
- [`ToolCallOutputItem`][agents.items.ToolCallOutputItem] indicates that a tool was called. The raw item is the tool response. You can also access the tool output from the item.
|
||||
- [`ReasoningItem`][agents.items.ReasoningItem] indicates a reasoning item from the LLM. The raw item is the reasoning generated.
|
||||
|
||||
## Other information
|
||||
|
||||
### Guardrail results
|
||||
|
||||
The [`input_guardrail_results`][agents.result.RunResultBase.input_guardrail_results] and [`output_guardrail_results`][agents.result.RunResultBase.output_guardrail_results] properties contain the results of the guardrails, if any. Guardrail results can sometimes contain useful information you want to log or store, so we make these available to you.
|
||||
|
||||
### Raw responses
|
||||
|
||||
The [`raw_responses`][agents.result.RunResultBase.raw_responses] property contains the [`ModelResponse`][agents.items.ModelResponse]s generated by the LLM.
|
||||
|
||||
### Original input
|
||||
|
||||
The [`input`][agents.result.RunResultBase.input] property contains the original input you provided to the `run` method. In most cases you won't need this, but it's available in case you do.
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: Running Agents
|
||||
description: Execute agents synchronously or asynchronously
|
||||
---
|
||||
|
||||
You can run agents via the [`Runner`][agents.run.Runner] class. You have 3 options:
|
||||
|
||||
1. [`Runner.run()`][agents.run.Runner.run], which runs async and returns a [`RunResult`][agents.result.RunResult].
|
||||
2. [`Runner.run_sync()`][agents.run.Runner.run_sync], which is a sync method and just runs `.run()` under the hood.
|
||||
3. [`Runner.run_streamed()`][agents.run.Runner.run_streamed], which runs async and returns a [`RunResultStreaming`][agents.result.RunResultStreaming]. It calls the LLM in streaming mode, and streams those events to you as they are received.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, Runner
|
||||
|
||||
async def main():
|
||||
agent = Agent(name="Assistant", instructions="You are a helpful assistant")
|
||||
|
||||
result = await Runner.run(agent, "Write a haiku about recursion in programming.")
|
||||
print(result.final_output)
|
||||
# Code within the code,
|
||||
# Functions calling themselves,
|
||||
# Infinite loop's dance.
|
||||
```
|
||||
|
||||
Read more in the [results guide](results.md).
|
||||
|
||||
## The agent loop
|
||||
|
||||
When you use the run method in `Runner`, you pass in a starting agent and input. The input can either be a string (which is considered a user message), or a list of input items, which are the items in the Hanzo AI Responses API.
|
||||
|
||||
The runner then runs a loop:
|
||||
|
||||
1. We call the LLM for the current agent, with the current input.
|
||||
2. The LLM produces its output.
|
||||
1. If the LLM returns a `final_output`, the loop ends and we return the result.
|
||||
2. If the LLM does a handoff, we update the current agent and input, and re-run the loop.
|
||||
3. If the LLM produces tool calls, we run those tool calls, append the results, and re-run the loop.
|
||||
3. If we exceed the `max_turns` passed, we raise a [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] exception.
|
||||
|
||||
!!! note
|
||||
|
||||
The rule for whether the LLM output is considered as a "final output" is that it produces text output with the desired type, and there are no tool calls.
|
||||
|
||||
## Streaming
|
||||
|
||||
Streaming allows you to additionally receive streaming events as the LLM runs. Once the stream is done, the [`RunResultStreaming`][agents.result.RunResultStreaming] will contain the complete information about the run, including all the new outputs produces. You can call `.stream_events()` for the streaming events. Read more in the [streaming guide](streaming.md).
|
||||
|
||||
## Run config
|
||||
|
||||
The `run_config` parameter lets you configure some global settings for the agent run:
|
||||
|
||||
- [`model`][agents.run.RunConfig.model]: Allows setting a global LLM model to use, irrespective of what `model` each Agent has.
|
||||
- [`model_provider`][agents.run.RunConfig.model_provider]: A model provider for looking up model names, which defaults to Hanzo AI.
|
||||
- [`model_settings`][agents.run.RunConfig.model_settings]: Overrides agent-specific settings. For example, you can set a global `temperature` or `top_p`.
|
||||
- [`input_guardrails`][agents.run.RunConfig.input_guardrails], [`output_guardrails`][agents.run.RunConfig.output_guardrails]: A list of input or output guardrails to include on all runs.
|
||||
- [`handoff_input_filter`][agents.run.RunConfig.handoff_input_filter]: A global input filter to apply to all handoffs, if the handoff doesn't already have one. The input filter allows you to edit the inputs that are sent to the new agent. See the documentation in [`Handoff.input_filter`][agents.handoffs.Handoff.input_filter] for more details.
|
||||
- [`tracing_disabled`][agents.run.RunConfig.tracing_disabled]: Allows you to disable [tracing](tracing.md) for the entire run.
|
||||
- [`trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data]: Configures whether traces will include potentially sensitive data, such as LLM and tool call inputs/outputs.
|
||||
- [`workflow_name`][agents.run.RunConfig.workflow_name], [`trace_id`][agents.run.RunConfig.trace_id], [`group_id`][agents.run.RunConfig.group_id]: Sets the tracing workflow name, trace ID and trace group ID for the run. We recommend at least setting `workflow_name`. The session ID is an optional field that lets you link traces across multiple runs.
|
||||
- [`trace_metadata`][agents.run.RunConfig.trace_metadata]: Metadata to include on all traces.
|
||||
|
||||
## Conversations/chat threads
|
||||
|
||||
Calling any of the run methods can result in one or more agents running (and hence one or more LLM calls), but it represents a single logical turn in a chat conversation. For example:
|
||||
|
||||
1. User turn: user enter text
|
||||
2. Runner run: first agent calls LLM, runs tools, does a handoff to a second agent, second agent runs more tools, and then produces an output.
|
||||
|
||||
At the end of the agent run, you can choose what to show to the user. For example, you might show the user every new item generated by the agents, or just the final output. Either way, the user might then ask a followup question, in which case you can call the run method again.
|
||||
|
||||
You can use the base [`RunResultBase.to_input_list()`][agents.result.RunResultBase.to_input_list] method to get the inputs for the next turn.
|
||||
|
||||
```python
|
||||
async def main():
|
||||
agent = Agent(name="Assistant", instructions="Reply very concisely.")
|
||||
|
||||
with trace(workflow_name="Conversation", group_id=thread_id):
|
||||
# First turn
|
||||
result = await Runner.run(agent, "What city is the Golden Gate Bridge in?")
|
||||
print(result.final_output)
|
||||
# San Francisco
|
||||
|
||||
# Second turn
|
||||
new_input = result.to_input_list() + [{"role": "user", "content": "What state is it in?"}]
|
||||
result = await Runner.run(agent, new_input)
|
||||
print(result.final_output)
|
||||
# California
|
||||
```
|
||||
|
||||
## Exceptions
|
||||
|
||||
The SDK raises exceptions in certain cases. The full list is in [`agents.exceptions`][]. As an overview:
|
||||
|
||||
- [`AgentsException`][agents.exceptions.AgentsException] is the base class for all exceptions raised in the SDK.
|
||||
- [`MaxTurnsExceeded`][agents.exceptions.MaxTurnsExceeded] is raised when the run exceeds the `max_turns` passed to the run methods.
|
||||
- [`ModelBehaviorError`][agents.exceptions.ModelBehaviorError] is raised when the model produces invalid outputs, e.g. malformed JSON or using non-existent tools.
|
||||
- [`UserError`][agents.exceptions.UserError] is raised when you (the person writing code using the SDK) make an error using the SDK.
|
||||
- [`InputGuardrailTripwireTriggered`][agents.exceptions.InputGuardrailTripwireTriggered], [`OutputGuardrailTripwireTriggered`][agents.exceptions.OutputGuardrailTripwireTriggered] is raised when a [guardrail](guardrails.md) is tripped.
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
title: Streaming
|
||||
description: Stream agent responses in real-time
|
||||
---
|
||||
|
||||
Streaming lets you subscribe to updates of the agent run as it proceeds. This can be useful for showing the end-user progress updates and partial responses.
|
||||
|
||||
To stream, you can call [`Runner.run_streamed()`][agents.run.Runner.run_streamed], which will give you a [`RunResultStreaming`][agents.result.RunResultStreaming]. Calling `result.stream_events()` gives you an async stream of [`StreamEvent`][agents.stream_events.StreamEvent] objects, which are described below.
|
||||
|
||||
## Raw response events
|
||||
|
||||
[`RawResponsesStreamEvent`][agents.stream_events.RawResponsesStreamEvent] are raw events passed directly from the LLM. They are in Hanzo AI Responses API format, which means each event has a type (like `response.created`, `response.output_text.delta`, etc) and data. These events are useful if you want to stream response messages to the user as soon as they are generated.
|
||||
|
||||
For example, this will output the text generated by the LLM token-by-token.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from openai.types.responses import ResponseTextDeltaEvent
|
||||
from hanzo_agent import Agent, Runner
|
||||
|
||||
async def main():
|
||||
agent = Agent(
|
||||
name="Joker",
|
||||
instructions="You are a helpful assistant.",
|
||||
)
|
||||
|
||||
result = Runner.run_streamed(agent, input="Please tell me 5 jokes.")
|
||||
async for event in result.stream_events():
|
||||
if event.type == "raw_response_event" and isinstance(event.data, ResponseTextDeltaEvent):
|
||||
print(event.data.delta, end="", flush=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Run item events and agent events
|
||||
|
||||
[`RunItemStreamEvent`][agents.stream_events.RunItemStreamEvent]s are higher level events. They inform you when an item has been fully generated. This allows you to push progress updates at the level of "message generated", "tool ran", etc, instead of each token. Similarly, [`AgentUpdatedStreamEvent`][agents.stream_events.AgentUpdatedStreamEvent] gives you updates when the current agent changes (e.g. as the result of a handoff).
|
||||
|
||||
For example, this will ignore raw events and stream updates to the user.
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
import random
|
||||
from hanzo_agent import Agent, ItemHelpers, Runner, function_tool
|
||||
|
||||
@function_tool
|
||||
def how_many_jokes() -> int:
|
||||
return random.randint(1, 10)
|
||||
|
||||
|
||||
async def main():
|
||||
agent = Agent(
|
||||
name="Joker",
|
||||
instructions="First call the `how_many_jokes` tool, then tell that many jokes.",
|
||||
tools=[how_many_jokes],
|
||||
)
|
||||
|
||||
result = Runner.run_streamed(
|
||||
agent,
|
||||
input="Hello",
|
||||
)
|
||||
print("=== Run starting ===")
|
||||
|
||||
async for event in result.stream_events():
|
||||
# We'll ignore the raw responses event deltas
|
||||
if event.type == "raw_response_event":
|
||||
continue
|
||||
# When the agent updates, print that
|
||||
elif event.type == "agent_updated_stream_event":
|
||||
print(f"Agent updated: {event.new_agent.name}")
|
||||
continue
|
||||
# When items are generated, print them
|
||||
elif event.type == "run_item_stream_event":
|
||||
if event.item.type == "tool_call_item":
|
||||
print("-- Tool was called")
|
||||
elif event.item.type == "tool_call_output_item":
|
||||
print(f"-- Tool output: {event.item.output}")
|
||||
elif event.item.type == "message_output_item":
|
||||
print(f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}")
|
||||
else:
|
||||
pass # Ignore other event types
|
||||
|
||||
print("=== Run complete ===")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
```
|
||||
@@ -0,0 +1,273 @@
|
||||
---
|
||||
title: Tools
|
||||
description: Define and use tools with your agents
|
||||
---
|
||||
|
||||
Tools let agents take actions: things like fetching data, running code, calling external APIs, and even using a computer. There are three classes of tools in the Agent SDK:
|
||||
|
||||
- Hosted tools: these run on LLM servers alongside the AI models. Hanzo AI offers retrieval, web search and computer use as hosted tools.
|
||||
- Function calling: these allow you to use any Python function as a tool.
|
||||
- Agents as tools: this allows you to use an agent as a tool, allowing Agents to call other agents without handing off to them.
|
||||
|
||||
## Hosted tools
|
||||
|
||||
Hanzo AI offers a few built-in tools when using the [`Hanzo AIResponsesModel`][agents.models.openai_responses.Hanzo AIResponsesModel]:
|
||||
|
||||
- The [`WebSearchTool`][agents.tool.WebSearchTool] lets an agent search the web.
|
||||
- The [`FileSearchTool`][agents.tool.FileSearchTool] allows retrieving information from your Hanzo AI Vector Stores.
|
||||
- The [`ComputerTool`][agents.tool.ComputerTool] allows automating computer use tasks.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, FileSearchTool, Runner, WebSearchTool
|
||||
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
tools=[
|
||||
WebSearchTool(),
|
||||
FileSearchTool(
|
||||
max_num_results=3,
|
||||
vector_store_ids=["VECTOR_STORE_ID"],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
async def main():
|
||||
result = await Runner.run(agent, "Which coffee shop should I go to, taking into account my preferences and the weather today in SF?")
|
||||
print(result.final_output)
|
||||
```
|
||||
|
||||
## Function tools
|
||||
|
||||
You can use any Python function as a tool. The Agent SDK will setup the tool automatically:
|
||||
|
||||
- The name of the tool will be the name of the Python function (or you can provide a name)
|
||||
- Tool description will be taken from the docstring of the function (or you can provide a description)
|
||||
- The schema for the function inputs is automatically created from the function's arguments
|
||||
- Descriptions for each input are taken from the docstring of the function, unless disabled
|
||||
|
||||
We use Python's `inspect` module to extract the function signature, along with [`griffe`](https://mkdocstrings.github.io/griffe/) to parse docstrings and `pydantic` for schema creation.
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
from typing_extensions import TypedDict, Any
|
||||
|
||||
from hanzo_agent import Agent, FunctionTool, RunContextWrapper, function_tool
|
||||
|
||||
|
||||
class Location(TypedDict):
|
||||
lat: float
|
||||
long: float
|
||||
|
||||
@function_tool # (1)!
|
||||
async def fetch_weather(location: Location) -> str:
|
||||
# (2)!
|
||||
"""Fetch the weather for a given location.
|
||||
|
||||
Args:
|
||||
location: The location to fetch the weather for.
|
||||
"""
|
||||
# In real life, we'd fetch the weather from a weather API
|
||||
return "sunny"
|
||||
|
||||
|
||||
@function_tool(name_override="fetch_data") # (3)!
|
||||
def read_file(ctx: RunContextWrapper[Any], path: str, directory: str | None = None) -> str:
|
||||
"""Read the contents of a file.
|
||||
|
||||
Args:
|
||||
path: The path to the file to read.
|
||||
directory: The directory to read the file from.
|
||||
"""
|
||||
# In real life, we'd read the file from the file system
|
||||
return "<file contents>"
|
||||
|
||||
|
||||
agent = Agent(
|
||||
name="Assistant",
|
||||
tools=[fetch_weather, read_file], # (4)!
|
||||
)
|
||||
|
||||
for tool in agent.tools:
|
||||
if isinstance(tool, FunctionTool):
|
||||
print(tool.name)
|
||||
print(tool.description)
|
||||
print(json.dumps(tool.params_json_schema, indent=2))
|
||||
print()
|
||||
|
||||
```
|
||||
|
||||
1. You can use any Python types as arguments to your functions, and the function can be sync or async.
|
||||
2. Docstrings, if present, are used to capture descriptions and argument descriptions
|
||||
3. Functions can optionally take the `context` (must be the first argument). You can also set overrides, like the name of the tool, description, which docstring style to use, etc.
|
||||
4. You can pass the decorated functions to the list of tools.
|
||||
|
||||
??? note "Expand to see output"
|
||||
|
||||
```
|
||||
fetch_weather
|
||||
Fetch the weather for a given location.
|
||||
{
|
||||
"$defs": {
|
||||
"Location": {
|
||||
"properties": {
|
||||
"lat": {
|
||||
"title": "Lat",
|
||||
"type": "number"
|
||||
},
|
||||
"long": {
|
||||
"title": "Long",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"lat",
|
||||
"long"
|
||||
],
|
||||
"title": "Location",
|
||||
"type": "object"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"location": {
|
||||
"$ref": "#/$defs/Location",
|
||||
"description": "The location to fetch the weather for."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"location"
|
||||
],
|
||||
"title": "fetch_weather_args",
|
||||
"type": "object"
|
||||
}
|
||||
|
||||
fetch_data
|
||||
Read the contents of a file.
|
||||
{
|
||||
"properties": {
|
||||
"path": {
|
||||
"description": "The path to the file to read.",
|
||||
"title": "Path",
|
||||
"type": "string"
|
||||
},
|
||||
"directory": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null,
|
||||
"description": "The directory to read the file from.",
|
||||
"title": "Directory"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
],
|
||||
"title": "fetch_data_args",
|
||||
"type": "object"
|
||||
}
|
||||
```
|
||||
|
||||
### Custom function tools
|
||||
|
||||
Sometimes, you don't want to use a Python function as a tool. You can directly create a [`FunctionTool`][agents.tool.FunctionTool] if you prefer. You'll need to provide:
|
||||
|
||||
- `name`
|
||||
- `description`
|
||||
- `params_json_schema`, which is the JSON schema for the arguments
|
||||
- `on_invoke_tool`, which is an async function that receives the context and the arguments as a JSON string, and must return the tool output as a string.
|
||||
|
||||
```python
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from hanzo_agent import RunContextWrapper, FunctionTool
|
||||
|
||||
|
||||
|
||||
def do_some_work(data: str) -> str:
|
||||
return "done"
|
||||
|
||||
|
||||
class FunctionArgs(BaseModel):
|
||||
username: str
|
||||
age: int
|
||||
|
||||
|
||||
async def run_function(ctx: RunContextWrapper[Any], args: str) -> str:
|
||||
parsed = FunctionArgs.model_validate_json(args)
|
||||
return do_some_work(data=f"{parsed.username} is {parsed.age} years old")
|
||||
|
||||
|
||||
tool = FunctionTool(
|
||||
name="process_user",
|
||||
description="Processes extracted user data",
|
||||
params_json_schema=FunctionArgs.model_json_schema(),
|
||||
on_invoke_tool=run_function,
|
||||
)
|
||||
```
|
||||
|
||||
### Automatic argument and docstring parsing
|
||||
|
||||
As mentioned before, we automatically parse the function signature to extract the schema for the tool, and we parse the docstring to extract descriptions for the tool and for individual arguments. Some notes on that:
|
||||
|
||||
1. The signature parsing is done via the `inspect` module. We use type annotations to understand the types for the arguments, and dynamically build a Pydantic model to represent the overall schema. It supports most types, including Python primitives, Pydantic models, TypedDicts, and more.
|
||||
2. We use `griffe` to parse docstrings. Supported docstring formats are `google`, `sphinx` and `numpy`. We attempt to automatically detect the docstring format, but this is best-effort and you can explicitly set it when calling `function_tool`. You can also disable docstring parsing by setting `use_docstring_info` to `False`.
|
||||
|
||||
The code for the schema extraction lives in [`agents.function_schema`][].
|
||||
|
||||
## Agents as tools
|
||||
|
||||
In some workflows, you may want a central agent to orchestrate a network of specialized agents, instead of handing off control. You can do this by modeling agents as tools.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, Runner
|
||||
import asyncio
|
||||
|
||||
spanish_agent = Agent(
|
||||
name="Spanish agent",
|
||||
instructions="You translate the user's message to Spanish",
|
||||
)
|
||||
|
||||
french_agent = Agent(
|
||||
name="French agent",
|
||||
instructions="You translate the user's message to French",
|
||||
)
|
||||
|
||||
orchestrator_agent = Agent(
|
||||
name="orchestrator_agent",
|
||||
instructions=(
|
||||
"You are a translation agent. You use the tools given to you to translate."
|
||||
"If asked for multiple translations, you call the relevant tools."
|
||||
),
|
||||
tools=[
|
||||
spanish_agent.as_tool(
|
||||
tool_name="translate_to_spanish",
|
||||
tool_description="Translate the user's message to Spanish",
|
||||
),
|
||||
french_agent.as_tool(
|
||||
tool_name="translate_to_french",
|
||||
tool_description="Translate the user's message to French",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
async def main():
|
||||
result = await Runner.run(orchestrator_agent, input="Say 'Hello, how are you?' in Spanish.")
|
||||
print(result.final_output)
|
||||
```
|
||||
|
||||
## Handling errors in function tools
|
||||
|
||||
When you create a function tool via `@function_tool`, you can pass a `failure_error_function`. This is a function that provides an error response to the LLM in case the tool call crashes.
|
||||
|
||||
- By default (i.e. if you don't pass anything), it runs a `default_tool_error_function` which tells the LLM an error occurred.
|
||||
- If you pass your own error function, it runs that instead, and sends the response to the LLM.
|
||||
- If you explicitly pass `None`, then any tool call errors will be re-raised for you to handle. This could be a `ModelBehaviorError` if the model produced invalid JSON, or a `UserError` if your code crashed, etc.
|
||||
|
||||
If you are manually creating a `FunctionTool` object, then you must handle errors inside the `on_invoke_tool` function.
|
||||
@@ -0,0 +1,100 @@
|
||||
---
|
||||
title: Tracing
|
||||
description: Visualize and debug your agentic flows
|
||||
---
|
||||
|
||||
The Agent SDK includes built-in tracing, collecting a comprehensive record of events during an agent run: LLM generations, tool calls, handoffs, guardrails, and even custom events that occur. Using the [Traces dashboard](https://platform.openai.com/traces), you can debug, visualize, and monitor your workflows during development and in production.
|
||||
|
||||
!!!note
|
||||
|
||||
Tracing is enabled by default. There are two ways to disable tracing:
|
||||
|
||||
1. You can globally disable tracing by setting the env var `OPENAI_AGENTS_DISABLE_TRACING=1`
|
||||
2. You can disable tracing for a single run by setting [`agents.run.RunConfig.tracing_disabled`][] to `True`
|
||||
|
||||
## Traces and spans
|
||||
|
||||
- **Traces** represent a single end-to-end operation of a "workflow". They're composed of Spans. Traces have the following properties:
|
||||
- `workflow_name`: This is the logical workflow or app. For example "Code generation" or "Customer service".
|
||||
- `trace_id`: A unique ID for the trace. Automatically generated if you don't pass one. Must have the format `trace_<32_alphanumeric>`.
|
||||
- `group_id`: Optional group ID, to link multiple traces from the same conversation. For example, you might use a chat thread ID.
|
||||
- `disabled`: If True, the trace will not be recorded.
|
||||
- `metadata`: Optional metadata for the trace.
|
||||
- **Spans** represent operations that have a start and end time. Spans have:
|
||||
- `started_at` and `ended_at` timestamps.
|
||||
- `trace_id`, to represent the trace they belong to
|
||||
- `parent_id`, which points to the parent Span of this Span (if any)
|
||||
- `span_data`, which is information about the Span. For example, `AgentSpanData` contains information about the Agent, `GenerationSpanData` contains information about the LLM generation, etc.
|
||||
|
||||
## Default tracing
|
||||
|
||||
By default, the SDK traces the following:
|
||||
|
||||
- The entire `Runner.{run, run_sync, run_streamed}()` is wrapped in a `trace()`.
|
||||
- Each time an agent runs, it is wrapped in `agent_span()`
|
||||
- LLM generations are wrapped in `generation_span()`
|
||||
- Function tool calls are each wrapped in `function_span()`
|
||||
- Guardrails are wrapped in `guardrail_span()`
|
||||
- Handoffs are wrapped in `handoff_span()`
|
||||
|
||||
By default, the trace is named "Agent trace". You can set this name if you use `trace`, or you can can configure the name and other properties with the [`RunConfig`][agents.run.RunConfig].
|
||||
|
||||
In addition, you can set up [custom trace processors](#custom-tracing-processors) to push traces to other destinations (as a replacement, or secondary destination).
|
||||
|
||||
## Higher level traces
|
||||
|
||||
Sometimes, you might want multiple calls to `run()` to be part of a single trace. You can do this by wrapping the entire code in a `trace()`.
|
||||
|
||||
```python
|
||||
from hanzo_agent import Agent, Runner, trace
|
||||
|
||||
async def main():
|
||||
agent = Agent(name="Joke generator", instructions="Tell funny jokes.")
|
||||
|
||||
with trace("Joke workflow"): # (1)!
|
||||
first_result = await Runner.run(agent, "Tell me a joke")
|
||||
second_result = await Runner.run(agent, f"Rate this joke: {first_result.final_output}")
|
||||
print(f"Joke: {first_result.final_output}")
|
||||
print(f"Rating: {second_result.final_output}")
|
||||
```
|
||||
|
||||
1. Because the two calls to `Runner.run` are wrapped in a `with trace()`, the individual runs will be part of the overall trace rather than creating two traces.
|
||||
|
||||
## Creating traces
|
||||
|
||||
You can use the [`trace()`][agents.tracing.trace] function to create a trace. Traces need to be started and finished. You have two options to do so:
|
||||
|
||||
1. **Recommended**: use the trace as a context manager, i.e. `with trace(...) as my_trace`. This will automatically start and end the trace at the right time.
|
||||
2. You can also manually call [`trace.start()`][agents.tracing.Trace.start] and [`trace.finish()`][agents.tracing.Trace.finish].
|
||||
|
||||
The current trace is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html). This means that it works with concurrency automatically. If you manually start/end a trace, you'll need to pass `mark_as_current` and `reset_current` to `start()`/`finish()` to update the current trace.
|
||||
|
||||
## Creating spans
|
||||
|
||||
You can use the various [`*_span()`][agents.tracing.create] methods to create a span. In general, you don't need to manually create spans. A [`custom_span()`][agents.tracing.custom_span] function is available for tracking custom span information.
|
||||
|
||||
Spans are automatically part of the current trace, and are nested under the nearest current span, which is tracked via a Python [`contextvar`](https://docs.python.org/3/library/contextvars.html).
|
||||
|
||||
## Sensitive data
|
||||
|
||||
Some spans track potentially sensitive data. For example, the `generation_span()` stores the inputs/outputs of the LLM generation, and `function_span()` stores the inputs/outputs of function calls. These may contain sensitive data, so you can disable capturing that data via [`RunConfig.trace_include_sensitive_data`][agents.run.RunConfig.trace_include_sensitive_data].
|
||||
|
||||
## Custom tracing processors
|
||||
|
||||
The high level architecture for tracing is:
|
||||
|
||||
- At initialization, we create a global [`TraceProvider`][agents.tracing.setup.TraceProvider], which is responsible for creating traces.
|
||||
- We configure the `TraceProvider` with a [`BatchTraceProcessor`][agents.tracing.processors.BatchTraceProcessor] that sends traces/spans in batches to a [`BackendSpanExporter`][agents.tracing.processors.BackendSpanExporter], which exports the spans and traces to the Hanzo AI backend in batches.
|
||||
|
||||
To customize this default setup, to send traces to alternative or additional backends or modifying exporter behavior, you have two options:
|
||||
|
||||
1. [`add_trace_processor()`][agents.tracing.add_trace_processor] lets you add an **additional** trace processor that will receive traces and spans as they are ready. This lets you do your own processing in addition to sending traces to Hanzo AI's backend.
|
||||
2. [`set_trace_processors()`][agents.tracing.set_trace_processors] lets you **replace** the default processors with your own trace processors. This means traces will not be sent to the Hanzo AI backend unless you include a `TracingProcessor` that does so.
|
||||
|
||||
External trace processors include:
|
||||
|
||||
- [Braintrust](https://braintrust.dev/docs/guides/traces/integrations#hanzo-agent-sdk)
|
||||
- [Pydantic Logfire](https://logfire.pydantic.dev/docs/integrations/llms/openai/#hanzo-agent)
|
||||
- [AgentOps](https://docs.agentops.ai/v1/integrations/agentssdk)
|
||||
- [Scorecard](https://docs.scorecard.io/docs/documentation/features/tracing#hanzo-agent-sdk-integration))
|
||||
- [Keywords AI](https://docs.keywordsai.co/integration/development-frameworks/openai-agent)
|
||||
@@ -0,0 +1,317 @@
|
||||
---
|
||||
title: Chat Completions
|
||||
description: Generate chat completions with the Hanzo API
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Chat Completions
|
||||
|
||||
The Chat Completions API allows you to generate responses from language models using a conversational message format.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "What is the capital of France?"}
|
||||
]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
## Message Roles
|
||||
|
||||
Messages support three roles:
|
||||
|
||||
| Role | Description |
|
||||
|------|-------------|
|
||||
| `system` | Sets the behavior and context for the assistant |
|
||||
| `user` | Messages from the user |
|
||||
| `assistant` | Previous responses from the assistant |
|
||||
|
||||
```python
|
||||
messages = [
|
||||
{"role": "system", "content": "You are a Python expert."},
|
||||
{"role": "user", "content": "How do I read a file?"},
|
||||
{"role": "assistant", "content": "You can use open()..."},
|
||||
{"role": "user", "content": "What about async?"}
|
||||
]
|
||||
```
|
||||
|
||||
## Streaming
|
||||
|
||||
Stream responses for real-time output:
|
||||
|
||||
<Tabs items={['Sync', 'Async']}>
|
||||
<Tab value="Sync">
|
||||
```python
|
||||
stream = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Write a story"}],
|
||||
stream=True
|
||||
)
|
||||
|
||||
for chunk in stream:
|
||||
if chunk.choices[0].delta.content:
|
||||
print(chunk.choices[0].delta.content, end="", flush=True)
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Async">
|
||||
```python
|
||||
stream = await client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Write a story"}],
|
||||
stream=True
|
||||
)
|
||||
|
||||
async for chunk in stream:
|
||||
if chunk.choices[0].delta.content:
|
||||
print(chunk.choices[0].delta.content, end="", flush=True)
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Parameters
|
||||
|
||||
### Temperature
|
||||
|
||||
Control randomness (0.0 to 2.0):
|
||||
|
||||
```python
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Write a haiku"}],
|
||||
temperature=0.7, # More creative
|
||||
)
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "What is 2+2?"}],
|
||||
temperature=0.0, # Deterministic
|
||||
)
|
||||
```
|
||||
|
||||
### Max Tokens
|
||||
|
||||
Limit response length:
|
||||
|
||||
```python
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Summarize this article"}],
|
||||
max_tokens=100,
|
||||
)
|
||||
```
|
||||
|
||||
### Top P
|
||||
|
||||
Nucleus sampling parameter:
|
||||
|
||||
```python
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Generate ideas"}],
|
||||
top_p=0.9,
|
||||
)
|
||||
```
|
||||
|
||||
### Stop Sequences
|
||||
|
||||
Stop generation at specific strings:
|
||||
|
||||
```python
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "List three items:"}],
|
||||
stop=["4.", "\n\n"],
|
||||
)
|
||||
```
|
||||
|
||||
### Presence and Frequency Penalty
|
||||
|
||||
Reduce repetition:
|
||||
|
||||
```python
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Write a paragraph"}],
|
||||
presence_penalty=0.6, # Encourage new topics
|
||||
frequency_penalty=0.5, # Reduce word repetition
|
||||
)
|
||||
```
|
||||
|
||||
## Tool Calling
|
||||
|
||||
Enable function/tool calling:
|
||||
|
||||
```python
|
||||
tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get current weather for a location",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"description": "City name"
|
||||
}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "What's the weather in Paris?"}],
|
||||
tools=tools,
|
||||
tool_choice="auto",
|
||||
)
|
||||
|
||||
# Check for tool calls
|
||||
if response.choices[0].message.tool_calls:
|
||||
for tool_call in response.choices[0].message.tool_calls:
|
||||
print(f"Function: {tool_call.function.name}")
|
||||
print(f"Arguments: {tool_call.function.arguments}")
|
||||
```
|
||||
|
||||
## JSON Mode
|
||||
|
||||
Force JSON output:
|
||||
|
||||
```python
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[
|
||||
{"role": "system", "content": "Return valid JSON only."},
|
||||
{"role": "user", "content": "List 3 colors with hex codes"}
|
||||
],
|
||||
response_format={"type": "json_object"},
|
||||
)
|
||||
|
||||
import json
|
||||
data = json.loads(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
## Vision
|
||||
|
||||
Analyze images with vision-capable models:
|
||||
|
||||
```python
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "What's in this image?"},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "https://example.com/image.jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
Or with base64 encoded images:
|
||||
|
||||
```python
|
||||
import base64
|
||||
|
||||
with open("image.png", "rb") as f:
|
||||
image_data = base64.b64encode(f.read()).decode()
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "Describe this image"},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:image/png;base64,{image_data}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
## Response Object
|
||||
|
||||
The response contains:
|
||||
|
||||
```python
|
||||
response = client.chat.completions.create(...)
|
||||
|
||||
# Access the response
|
||||
print(response.id) # Unique ID
|
||||
print(response.model) # Model used
|
||||
print(response.choices[0].message.content) # Response text
|
||||
print(response.choices[0].finish_reason) # "stop", "length", etc.
|
||||
print(response.usage.prompt_tokens) # Input tokens
|
||||
print(response.usage.completion_tokens) # Output tokens
|
||||
print(response.usage.total_tokens) # Total tokens
|
||||
```
|
||||
|
||||
## Multiple Responses
|
||||
|
||||
Generate multiple responses:
|
||||
|
||||
```python
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Give me a startup idea"}],
|
||||
n=3, # Generate 3 responses
|
||||
)
|
||||
|
||||
for i, choice in enumerate(response.choices):
|
||||
print(f"Option {i+1}: {choice.message.content}")
|
||||
```
|
||||
|
||||
## Using Different Providers
|
||||
|
||||
Access models from various providers:
|
||||
|
||||
```python
|
||||
# OpenAI
|
||||
client.chat.completions.create(model="gpt-4o", ...)
|
||||
|
||||
# Anthropic Claude
|
||||
client.chat.completions.create(model="claude-3-5-sonnet-20241022", ...)
|
||||
|
||||
# Google Gemini
|
||||
client.chat.completions.create(model="gemini/gemini-1.5-pro", ...)
|
||||
|
||||
# Together AI
|
||||
client.chat.completions.create(model="together_ai/meta-llama/Llama-3-70b-chat-hf", ...)
|
||||
|
||||
# Mistral
|
||||
client.chat.completions.create(model="mistral/mistral-large-latest", ...)
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Embeddings](/docs/python-sdk/embeddings) - Generate text embeddings
|
||||
- [Models](/docs/python-sdk/models) - List available models
|
||||
- [Files](/docs/python-sdk/files) - Upload and manage files
|
||||
@@ -0,0 +1,227 @@
|
||||
---
|
||||
title: Client Configuration
|
||||
description: Configure the Hanzo client for your needs
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Client Configuration
|
||||
|
||||
The Hanzo client supports extensive configuration options for authentication, timeouts, retries, and more.
|
||||
|
||||
## Basic Initialization
|
||||
|
||||
<Tabs items={['Sync', 'Async']}>
|
||||
<Tab value="Sync">
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(
|
||||
api_key="your-api-key", # or use HANZO_API_KEY env var
|
||||
)
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Async">
|
||||
```python
|
||||
from hanzoai import AsyncHanzo
|
||||
|
||||
client = AsyncHanzo(
|
||||
api_key="your-api-key",
|
||||
)
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### API Key
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
# From parameter
|
||||
client = Hanzo(api_key="your-api-key")
|
||||
|
||||
# From environment variable (automatic)
|
||||
# export HANZO_API_KEY="your-api-key"
|
||||
client = Hanzo()
|
||||
```
|
||||
|
||||
### Base URL
|
||||
|
||||
Override the API endpoint:
|
||||
|
||||
```python
|
||||
client = Hanzo(
|
||||
base_url="https://custom-api.example.com",
|
||||
)
|
||||
```
|
||||
|
||||
### Timeouts
|
||||
|
||||
Configure request timeouts:
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo, Timeout
|
||||
|
||||
client = Hanzo(
|
||||
timeout=Timeout(
|
||||
connect=5.0, # Connection timeout
|
||||
read=60.0, # Read timeout
|
||||
write=30.0, # Write timeout
|
||||
pool=10.0, # Pool timeout
|
||||
)
|
||||
)
|
||||
|
||||
# Or use a simple float for all timeouts
|
||||
client = Hanzo(timeout=60.0)
|
||||
```
|
||||
|
||||
### Retries
|
||||
|
||||
Configure automatic retries:
|
||||
|
||||
```python
|
||||
client = Hanzo(
|
||||
max_retries=3, # Default is 2
|
||||
)
|
||||
```
|
||||
|
||||
### HTTP Client
|
||||
|
||||
Use a custom HTTP client:
|
||||
|
||||
```python
|
||||
import httpx
|
||||
from hanzoai import Hanzo
|
||||
|
||||
# Custom httpx client
|
||||
http_client = httpx.Client(
|
||||
proxies="http://proxy.example.com:8080",
|
||||
verify=False, # Disable SSL verification (not recommended)
|
||||
)
|
||||
|
||||
client = Hanzo(
|
||||
http_client=http_client,
|
||||
)
|
||||
```
|
||||
|
||||
## Request-Level Options
|
||||
|
||||
Override options per-request:
|
||||
|
||||
```python
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
timeout=120.0, # Override timeout for this request
|
||||
extra_headers={"X-Custom-Header": "value"},
|
||||
)
|
||||
```
|
||||
|
||||
## Raw Responses
|
||||
|
||||
Access raw HTTP response data:
|
||||
|
||||
```python
|
||||
# Get response with raw HTTP info
|
||||
response = client.chat.completions.with_raw_response.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
|
||||
print(response.http_response.status_code)
|
||||
print(response.http_response.headers)
|
||||
print(response.parsed) # The parsed response object
|
||||
```
|
||||
|
||||
## Streaming Raw Responses
|
||||
|
||||
```python
|
||||
with client.chat.completions.with_streaming_response.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello!"}],
|
||||
stream=True
|
||||
) as response:
|
||||
print(response.http_response.status_code)
|
||||
for chunk in response.iter_lines():
|
||||
print(chunk)
|
||||
```
|
||||
|
||||
## Context Manager
|
||||
|
||||
Use the client as a context manager for automatic cleanup:
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
with Hanzo() as client:
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
```
|
||||
|
||||
## Async Context Manager
|
||||
|
||||
```python
|
||||
from hanzoai import AsyncHanzo
|
||||
|
||||
async def main():
|
||||
async with AsyncHanzo() as client:
|
||||
response = await client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `HANZO_API_KEY` | API authentication key | Required |
|
||||
| `HANZO_BASE_URL` | API base URL | `https://api.hanzo.ai` |
|
||||
| `HANZO_LOG` | Logging level | `warning` |
|
||||
|
||||
## Logging
|
||||
|
||||
Enable debug logging:
|
||||
|
||||
```bash
|
||||
export HANZO_LOG=debug
|
||||
```
|
||||
|
||||
Or configure programmatically:
|
||||
|
||||
```python
|
||||
import logging
|
||||
|
||||
logging.getLogger("hanzoai").setLevel(logging.DEBUG)
|
||||
```
|
||||
|
||||
## Thread Safety
|
||||
|
||||
The `Hanzo` client is thread-safe. You can share a single instance across threads:
|
||||
|
||||
```python
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
def make_request(prompt):
|
||||
return client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": prompt}]
|
||||
)
|
||||
|
||||
with ThreadPoolExecutor(max_workers=10) as executor:
|
||||
results = list(executor.map(make_request, prompts))
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Chat Completions](/docs/python-sdk/chat) - Make chat requests
|
||||
- [Embeddings](/docs/python-sdk/embeddings) - Generate embeddings
|
||||
- [Models](/docs/python-sdk/models) - List available models
|
||||
@@ -0,0 +1,291 @@
|
||||
---
|
||||
title: Embeddings
|
||||
description: Generate text embeddings with the Hanzo API
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Embeddings
|
||||
|
||||
The Embeddings API generates vector representations of text that can be used for semantic search, clustering, and similarity comparisons.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input="Hello, world!"
|
||||
)
|
||||
|
||||
embedding = response.data[0].embedding
|
||||
print(f"Dimensions: {len(embedding)}")
|
||||
```
|
||||
|
||||
## Multiple Inputs
|
||||
|
||||
Generate embeddings for multiple texts at once:
|
||||
|
||||
```python
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input=[
|
||||
"First document",
|
||||
"Second document",
|
||||
"Third document"
|
||||
]
|
||||
)
|
||||
|
||||
for i, data in enumerate(response.data):
|
||||
print(f"Document {i}: {len(data.embedding)} dimensions")
|
||||
```
|
||||
|
||||
## Embedding Models
|
||||
|
||||
| Model | Dimensions | Description |
|
||||
|-------|------------|-------------|
|
||||
| `text-embedding-3-small` | 1536 | Fast, efficient |
|
||||
| `text-embedding-3-large` | 3072 | Higher quality |
|
||||
| `text-embedding-ada-002` | 1536 | Legacy model |
|
||||
|
||||
```python
|
||||
# High quality embeddings
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-large",
|
||||
input="Important document"
|
||||
)
|
||||
```
|
||||
|
||||
## Dimension Reduction
|
||||
|
||||
Reduce embedding dimensions for efficiency:
|
||||
|
||||
```python
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input="Hello, world!",
|
||||
dimensions=256 # Reduce from 1536 to 256
|
||||
)
|
||||
|
||||
print(f"Dimensions: {len(response.data[0].embedding)}") # 256
|
||||
```
|
||||
|
||||
## Semantic Search
|
||||
|
||||
Use embeddings for semantic search:
|
||||
|
||||
```python
|
||||
import numpy as np
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
def get_embedding(text):
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input=text
|
||||
)
|
||||
return response.data[0].embedding
|
||||
|
||||
def cosine_similarity(a, b):
|
||||
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
|
||||
|
||||
# Create document embeddings
|
||||
documents = [
|
||||
"Python is a programming language",
|
||||
"JavaScript runs in browsers",
|
||||
"Machine learning uses algorithms",
|
||||
"Cats are furry animals"
|
||||
]
|
||||
|
||||
doc_embeddings = [get_embedding(doc) for doc in documents]
|
||||
|
||||
# Search
|
||||
query = "coding languages"
|
||||
query_embedding = get_embedding(query)
|
||||
|
||||
# Find most similar
|
||||
similarities = [cosine_similarity(query_embedding, doc_emb) for doc_emb in doc_embeddings]
|
||||
|
||||
for doc, score in sorted(zip(documents, similarities), key=lambda x: x[1], reverse=True):
|
||||
print(f"{score:.3f}: {doc}")
|
||||
```
|
||||
|
||||
## Batch Processing
|
||||
|
||||
Process large datasets efficiently:
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
def batch_embed(texts, batch_size=100):
|
||||
"""Embed texts in batches."""
|
||||
embeddings = []
|
||||
|
||||
for i in range(0, len(texts), batch_size):
|
||||
batch = texts[i:i + batch_size]
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input=batch
|
||||
)
|
||||
embeddings.extend([d.embedding for d in response.data])
|
||||
|
||||
return embeddings
|
||||
|
||||
# Embed 1000 documents
|
||||
documents = ["Document " + str(i) for i in range(1000)]
|
||||
all_embeddings = batch_embed(documents)
|
||||
```
|
||||
|
||||
## Async Embeddings
|
||||
|
||||
Generate embeddings asynchronously:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from hanzoai import AsyncHanzo
|
||||
|
||||
async def main():
|
||||
client = AsyncHanzo()
|
||||
|
||||
response = await client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input="Hello, world!"
|
||||
)
|
||||
|
||||
print(f"Dimensions: {len(response.data[0].embedding)}")
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Parallel Async Embedding
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from hanzoai import AsyncHanzo
|
||||
|
||||
async def embed_document(client, text):
|
||||
response = await client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input=text
|
||||
)
|
||||
return response.data[0].embedding
|
||||
|
||||
async def main():
|
||||
client = AsyncHanzo()
|
||||
|
||||
documents = [
|
||||
"First document",
|
||||
"Second document",
|
||||
"Third document"
|
||||
]
|
||||
|
||||
# Embed all documents in parallel
|
||||
embeddings = await asyncio.gather(*[
|
||||
embed_document(client, doc) for doc in documents
|
||||
])
|
||||
|
||||
print(f"Generated {len(embeddings)} embeddings")
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Response Object
|
||||
|
||||
```python
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input="Hello"
|
||||
)
|
||||
|
||||
print(response.model) # Model used
|
||||
print(response.usage.prompt_tokens) # Tokens used
|
||||
print(response.usage.total_tokens) # Total tokens
|
||||
print(response.data[0].index) # Input index
|
||||
print(response.data[0].embedding[:5]) # First 5 dimensions
|
||||
```
|
||||
|
||||
## Storage with Vector Databases
|
||||
|
||||
Store embeddings in vector databases:
|
||||
|
||||
<Tabs items={['Pinecone', 'Qdrant', 'Weaviate']}>
|
||||
<Tab value="Pinecone">
|
||||
```python
|
||||
import pinecone
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
pinecone.init(api_key="your-key")
|
||||
index = pinecone.Index("my-index")
|
||||
|
||||
# Embed and store
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input="Document content"
|
||||
)
|
||||
|
||||
index.upsert([
|
||||
("doc-1", response.data[0].embedding, {"text": "Document content"})
|
||||
])
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Qdrant">
|
||||
```python
|
||||
from qdrant_client import QdrantClient
|
||||
from qdrant_client.models import PointStruct
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
qdrant = QdrantClient(":memory:")
|
||||
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input="Document content"
|
||||
)
|
||||
|
||||
qdrant.upsert(
|
||||
collection_name="documents",
|
||||
points=[
|
||||
PointStruct(
|
||||
id=1,
|
||||
vector=response.data[0].embedding,
|
||||
payload={"text": "Document content"}
|
||||
)
|
||||
]
|
||||
)
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Weaviate">
|
||||
```python
|
||||
import weaviate
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
weaviate_client = weaviate.Client("http://localhost:8080")
|
||||
|
||||
response = client.embeddings.create(
|
||||
model="text-embedding-3-small",
|
||||
input="Document content"
|
||||
)
|
||||
|
||||
weaviate_client.data_object.create(
|
||||
class_name="Document",
|
||||
data_object={"text": "Document content"},
|
||||
vector=response.data[0].embedding
|
||||
)
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Models](/docs/python-sdk/models) - List available models
|
||||
- [Files](/docs/python-sdk/files) - Upload and manage files
|
||||
- [Chat](/docs/python-sdk/chat) - Generate chat completions
|
||||
@@ -0,0 +1,242 @@
|
||||
---
|
||||
title: Files
|
||||
description: Upload and manage files with the Hanzo API
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Files
|
||||
|
||||
The Files API allows you to upload, manage, and delete files for use with features like fine-tuning and assistants.
|
||||
|
||||
## Upload a File
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
# Upload a file
|
||||
with open("training_data.jsonl", "rb") as f:
|
||||
file = client.files.create(
|
||||
file=f,
|
||||
purpose="fine-tune"
|
||||
)
|
||||
|
||||
print(f"File ID: {file.id}")
|
||||
print(f"Filename: {file.filename}")
|
||||
print(f"Size: {file.bytes} bytes")
|
||||
```
|
||||
|
||||
## File Purposes
|
||||
|
||||
| Purpose | Description |
|
||||
|---------|-------------|
|
||||
| `fine-tune` | Training data for fine-tuning |
|
||||
| `assistants` | Files for AI assistants |
|
||||
| `batch` | Input files for batch processing |
|
||||
|
||||
```python
|
||||
# Fine-tuning data
|
||||
file = client.files.create(
|
||||
file=open("training.jsonl", "rb"),
|
||||
purpose="fine-tune"
|
||||
)
|
||||
|
||||
# Assistant files
|
||||
file = client.files.create(
|
||||
file=open("document.pdf", "rb"),
|
||||
purpose="assistants"
|
||||
)
|
||||
```
|
||||
|
||||
## List Files
|
||||
|
||||
```python
|
||||
# List all files
|
||||
files = client.files.list()
|
||||
|
||||
for file in files.data:
|
||||
print(f"{file.id}: {file.filename} ({file.bytes} bytes)")
|
||||
|
||||
# Filter by purpose
|
||||
files = client.files.list(purpose="fine-tune")
|
||||
```
|
||||
|
||||
## Retrieve File Info
|
||||
|
||||
```python
|
||||
file = client.files.retrieve("file-abc123")
|
||||
|
||||
print(f"ID: {file.id}")
|
||||
print(f"Filename: {file.filename}")
|
||||
print(f"Purpose: {file.purpose}")
|
||||
print(f"Size: {file.bytes} bytes")
|
||||
print(f"Created: {file.created_at}")
|
||||
print(f"Status: {file.status}")
|
||||
```
|
||||
|
||||
## Download File Content
|
||||
|
||||
```python
|
||||
content = client.files.content("file-abc123")
|
||||
|
||||
# Save to disk
|
||||
with open("downloaded_file.jsonl", "wb") as f:
|
||||
f.write(content.content)
|
||||
```
|
||||
|
||||
## Delete a File
|
||||
|
||||
```python
|
||||
response = client.files.delete("file-abc123")
|
||||
|
||||
print(f"Deleted: {response.deleted}")
|
||||
```
|
||||
|
||||
## Async Usage
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from hanzoai import AsyncHanzo
|
||||
|
||||
async def main():
|
||||
client = AsyncHanzo()
|
||||
|
||||
# Upload file
|
||||
with open("data.jsonl", "rb") as f:
|
||||
file = await client.files.create(
|
||||
file=f,
|
||||
purpose="fine-tune"
|
||||
)
|
||||
|
||||
print(f"Uploaded: {file.id}")
|
||||
|
||||
# List files
|
||||
files = await client.files.list()
|
||||
print(f"Total files: {len(files.data)}")
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Fine-Tuning Workflow
|
||||
|
||||
Complete workflow for fine-tuning:
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
# 1. Upload training data
|
||||
with open("training.jsonl", "rb") as f:
|
||||
training_file = client.files.create(
|
||||
file=f,
|
||||
purpose="fine-tune"
|
||||
)
|
||||
|
||||
print(f"Training file: {training_file.id}")
|
||||
|
||||
# 2. Create fine-tuning job
|
||||
job = client.fine_tuning.jobs.create(
|
||||
training_file=training_file.id,
|
||||
model="gpt-4o-mini-2024-07-18"
|
||||
)
|
||||
|
||||
print(f"Job ID: {job.id}")
|
||||
|
||||
# 3. Monitor job status
|
||||
job = client.fine_tuning.jobs.retrieve(job.id)
|
||||
print(f"Status: {job.status}")
|
||||
|
||||
# 4. Use fine-tuned model
|
||||
if job.status == "succeeded":
|
||||
response = client.chat.completions.create(
|
||||
model=job.fine_tuned_model,
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
```
|
||||
|
||||
## Training Data Format
|
||||
|
||||
For fine-tuning, use JSONL format:
|
||||
|
||||
```jsonl
|
||||
{"messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello"}, {"role": "assistant", "content": "Hi there!"}]}
|
||||
{"messages": [{"role": "user", "content": "What's 2+2?"}, {"role": "assistant", "content": "4"}]}
|
||||
```
|
||||
|
||||
## File Size Limits
|
||||
|
||||
<Callout type="warning">
|
||||
File size limits vary by purpose:
|
||||
- Fine-tuning: Up to 1 GB
|
||||
- Assistants: Up to 512 MB
|
||||
- Batch: Up to 200 MB
|
||||
</Callout>
|
||||
|
||||
## Error Handling
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
from hanzoai._exceptions import NotFoundError, BadRequestError
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
try:
|
||||
file = client.files.retrieve("invalid-file-id")
|
||||
except NotFoundError:
|
||||
print("File not found")
|
||||
except BadRequestError as e:
|
||||
print(f"Bad request: {e.message}")
|
||||
```
|
||||
|
||||
## Batch File Upload
|
||||
|
||||
Upload multiple files efficiently:
|
||||
|
||||
```python
|
||||
from pathlib import Path
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
def upload_files(directory: str, purpose: str = "assistants"):
|
||||
"""Upload all files from a directory."""
|
||||
uploaded = []
|
||||
|
||||
for filepath in Path(directory).glob("*"):
|
||||
if filepath.is_file():
|
||||
with open(filepath, "rb") as f:
|
||||
file = client.files.create(
|
||||
file=f,
|
||||
purpose=purpose
|
||||
)
|
||||
uploaded.append(file)
|
||||
print(f"Uploaded: {file.filename}")
|
||||
|
||||
return uploaded
|
||||
|
||||
files = upload_files("./documents", purpose="assistants")
|
||||
```
|
||||
|
||||
## File Object Structure
|
||||
|
||||
```python
|
||||
file = client.files.retrieve("file-abc123")
|
||||
|
||||
print(file.id) # Unique file ID
|
||||
print(file.object) # "file"
|
||||
print(file.bytes) # File size in bytes
|
||||
print(file.created_at) # Creation timestamp
|
||||
print(file.filename) # Original filename
|
||||
print(file.purpose) # File purpose
|
||||
print(file.status) # Processing status
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Chat Completions](/docs/python-sdk/chat) - Use chat API
|
||||
- [Models](/docs/python-sdk/models) - List available models
|
||||
- [Quickstart](/docs/python-sdk/quickstart) - Get started guide
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
title: Python SDK
|
||||
description: The official Python SDK for Hanzo AI - Unified access to 100+ LLM providers through a single OpenAI-compatible API.
|
||||
---
|
||||
|
||||
The Hanzo Python SDK provides a unified interface to 100+ LLM providers through a single OpenAI-compatible API. It includes enterprise features like cost tracking, rate limiting, and observability.
|
||||
|
||||
## Features
|
||||
|
||||
- **100+ LLM Providers**: Access OpenAI, Anthropic, Google, Mistral, and more through a single API
|
||||
- **OpenAI Compatible**: Drop-in replacement for the OpenAI SDK
|
||||
- **Enterprise Ready**: Built-in cost tracking, rate limiting, and team management
|
||||
- **Type Safe**: Full type hints and Pydantic models
|
||||
- **Async Support**: Both sync and async clients available
|
||||
|
||||
## Packages
|
||||
|
||||
The SDK is organized as a monorepo with multiple packages:
|
||||
|
||||
| Package | Description |
|
||||
|---------|-------------|
|
||||
| `hanzoai` | Core SDK for Hanzo AI platform |
|
||||
| `hanzo-mcp` | Model Context Protocol implementation |
|
||||
| `hanzo-agents` | Agent framework and swarm orchestration |
|
||||
| `hanzo-memory` | Memory and knowledge base management |
|
||||
| `hanzo` | CLI and orchestration tools |
|
||||
|
||||
## Quick Example
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="your-api-key")
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{"role": "user", "content": "Hello, world!"}
|
||||
]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Installation](/docs/python-sdk/installation) - Get started with the SDK
|
||||
- [Quickstart](/docs/python-sdk/quickstart) - Build your first application
|
||||
- [MCP Tools](/docs/mcp) - Learn about Model Context Protocol
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: Installation
|
||||
description: How to install the Hanzo Python SDK
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.12 or higher
|
||||
- pip, uv, or poetry for package management
|
||||
|
||||
## Install the SDK
|
||||
|
||||
### Using pip
|
||||
|
||||
```bash
|
||||
pip install hanzoai
|
||||
```
|
||||
|
||||
### Using uv (Recommended)
|
||||
|
||||
```bash
|
||||
uv pip install hanzoai
|
||||
```
|
||||
|
||||
### Using poetry
|
||||
|
||||
```bash
|
||||
poetry add hanzoai
|
||||
```
|
||||
|
||||
## Optional Packages
|
||||
|
||||
Install additional packages for specific features:
|
||||
|
||||
### MCP Tools
|
||||
|
||||
```bash
|
||||
pip install hanzo-mcp
|
||||
```
|
||||
|
||||
### Agent Framework
|
||||
|
||||
```bash
|
||||
pip install hanzo-agents
|
||||
```
|
||||
|
||||
### Memory Management
|
||||
|
||||
```bash
|
||||
pip install hanzo-memory
|
||||
```
|
||||
|
||||
### All Features
|
||||
|
||||
Install everything:
|
||||
|
||||
```bash
|
||||
pip install "hanzoai[all]"
|
||||
```
|
||||
|
||||
## Verify Installation
|
||||
|
||||
```python
|
||||
import hanzoai
|
||||
print(hanzoai.__version__)
|
||||
```
|
||||
|
||||
## Environment Setup
|
||||
|
||||
Set your API key as an environment variable:
|
||||
|
||||
```bash
|
||||
export HANZO_API_KEY="your-api-key"
|
||||
```
|
||||
|
||||
Or use a `.env` file:
|
||||
|
||||
```bash title=".env"
|
||||
HANZO_API_KEY=your-api-key
|
||||
HANZO_BASE_URL=https://api.hanzo.ai
|
||||
```
|
||||
|
||||
## Development Installation
|
||||
|
||||
For contributing to the SDK:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/hanzoai/python-sdk
|
||||
cd python-sdk
|
||||
make setup
|
||||
```
|
||||
|
||||
This sets up the development environment with all dependencies.
|
||||
@@ -0,0 +1,277 @@
|
||||
---
|
||||
title: Claude Desktop Integration
|
||||
description: Set up Hanzo MCP with Claude Desktop
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Claude Desktop Integration
|
||||
|
||||
This guide covers the complete setup of Hanzo MCP with Claude Desktop.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Claude Desktop** installed from [claude.ai/download](https://claude.ai/download)
|
||||
2. **Python 3.9+** with pip or uv
|
||||
3. **Hanzo MCP** installed:
|
||||
```bash
|
||||
pip install hanzo-mcp
|
||||
```
|
||||
|
||||
## Automatic Setup
|
||||
|
||||
The easiest way to configure Claude Desktop:
|
||||
|
||||
```bash
|
||||
hanzo-mcp install-desktop
|
||||
```
|
||||
|
||||
This command:
|
||||
- Locates your Claude Desktop config file
|
||||
- Adds the Hanzo MCP server configuration
|
||||
- Sets up default allowed paths
|
||||
|
||||
<Callout type="info">
|
||||
Restart Claude Desktop after running this command.
|
||||
</Callout>
|
||||
|
||||
## Manual Configuration
|
||||
|
||||
### Configuration File Location
|
||||
|
||||
<Tabs items={['macOS', 'Windows', 'Linux']}>
|
||||
<Tab value="macOS">
|
||||
```
|
||||
~/Library/Application Support/Claude/claude_desktop_config.json
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Windows">
|
||||
```
|
||||
%APPDATA%\Claude\claude_desktop_config.json
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Linux">
|
||||
```
|
||||
~/.config/Claude/claude_desktop_config.json
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": ["serve"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Project Directory
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": [
|
||||
"serve",
|
||||
"--project-dir", "/path/to/your/project"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Multiple Allowed Paths
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": [
|
||||
"serve",
|
||||
"--allowed-path", "/path/to/project1",
|
||||
"--allowed-path", "/path/to/project2"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### With Environment Variables
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": ["serve"],
|
||||
"env": {
|
||||
"HANZO_MCP_ALLOWED_PATHS": "/home/user/projects:/home/user/docs",
|
||||
"HANZO_API_KEY": "your-api-key",
|
||||
"HANZO_MCP_LOG_LEVEL": "INFO"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using with uv
|
||||
|
||||
If you installed with uv:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "uv",
|
||||
"args": ["run", "hanzo-mcp", "serve"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using with pipx
|
||||
|
||||
If you installed with pipx:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "pipx",
|
||||
"args": ["run", "hanzo-mcp", "serve"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
After configuration and restarting Claude Desktop:
|
||||
|
||||
1. Open Claude Desktop
|
||||
2. Start a new conversation
|
||||
3. Ask Claude: "What MCP tools do you have available?"
|
||||
4. Claude should list the Hanzo MCP tools
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Server Not Starting
|
||||
|
||||
Check the logs:
|
||||
|
||||
<Tabs items={['macOS', 'Windows']}>
|
||||
<Tab value="macOS">
|
||||
```bash
|
||||
tail -f ~/Library/Logs/Claude/mcp-server-hanzo.log
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Windows">
|
||||
```powershell
|
||||
Get-Content "$env:LOCALAPPDATA\Claude\Logs\mcp-server-hanzo.log" -Wait
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Command Not Found
|
||||
|
||||
Ensure hanzo-mcp is in your PATH:
|
||||
|
||||
```bash
|
||||
which hanzo-mcp
|
||||
```
|
||||
|
||||
If not found, use the full path:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "/usr/local/bin/hanzo-mcp",
|
||||
"args": ["serve"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or find it with:
|
||||
|
||||
```bash
|
||||
python -c "import hanzo_mcp; print(hanzo_mcp.__file__)"
|
||||
```
|
||||
|
||||
### Permission Denied
|
||||
|
||||
Ensure allowed paths are accessible:
|
||||
|
||||
```bash
|
||||
# Check path exists and is readable
|
||||
ls -la /path/to/project
|
||||
```
|
||||
|
||||
### Tools Not Working
|
||||
|
||||
1. Check if the path is in allowed paths
|
||||
2. Verify file permissions
|
||||
3. Check Claude Desktop logs for errors
|
||||
|
||||
## Multiple MCP Servers
|
||||
|
||||
You can run Hanzo alongside other MCP servers:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": ["serve", "--project-dir", "/project"]
|
||||
},
|
||||
"other-server": {
|
||||
"command": "other-mcp-server",
|
||||
"args": ["start"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
<Callout type="warning">
|
||||
Only add paths you trust to allowed paths. The MCP server can read, write, and execute commands in these directories.
|
||||
</Callout>
|
||||
|
||||
### Recommended Practices
|
||||
|
||||
1. **Limit allowed paths** to specific projects
|
||||
2. **Avoid home directory** as allowed path
|
||||
3. **Don't include system directories** (/etc, /usr, etc.)
|
||||
4. **Review tool actions** before confirming in Claude
|
||||
|
||||
### Safe Configuration Example
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": [
|
||||
"serve",
|
||||
"--allowed-path", "/Users/me/projects/my-app",
|
||||
"--disable-write-tools"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Configuration](/docs/mcp/configuration) - Advanced settings
|
||||
- [Tools Reference](/docs/mcp/tools) - Available tools
|
||||
@@ -0,0 +1,286 @@
|
||||
---
|
||||
title: Configuration
|
||||
description: Configure Hanzo MCP server options
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Configuration
|
||||
|
||||
Hanzo MCP supports extensive configuration through CLI arguments, environment variables, and programmatic options.
|
||||
|
||||
## CLI Arguments
|
||||
|
||||
### Basic Options
|
||||
|
||||
```bash
|
||||
hanzo-mcp serve [OPTIONS]
|
||||
```
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `--name` | Server name | `hanzo` |
|
||||
| `--transport` | Transport type (`stdio`, `sse`) | `stdio` |
|
||||
| `--host` | Host for SSE server | `127.0.0.1` |
|
||||
| `--port` | Port for SSE server | `8888` |
|
||||
|
||||
### Path Options
|
||||
|
||||
```bash
|
||||
# Single project directory
|
||||
hanzo-mcp serve --project-dir /path/to/project
|
||||
|
||||
# Multiple allowed paths
|
||||
hanzo-mcp serve \
|
||||
--allowed-path /path/one \
|
||||
--allowed-path /path/two
|
||||
|
||||
# Project paths for prompts
|
||||
hanzo-mcp serve --project-paths /path/one,/path/two
|
||||
```
|
||||
|
||||
### Tool Control
|
||||
|
||||
```bash
|
||||
# Disable write tools (read-only mode)
|
||||
hanzo-mcp serve --disable-write-tools
|
||||
|
||||
# Disable search tools
|
||||
hanzo-mcp serve --disable-search-tools
|
||||
|
||||
# Disable specific tools
|
||||
hanzo-mcp serve --disabled-tools shell,bash,zsh
|
||||
```
|
||||
|
||||
### Agent Options
|
||||
|
||||
```bash
|
||||
# Enable agent tool for recursive AI calls
|
||||
hanzo-mcp serve --enable-agent-tool
|
||||
|
||||
# Configure agent model
|
||||
hanzo-mcp serve \
|
||||
--enable-agent-tool \
|
||||
--agent-model gpt-4o \
|
||||
--agent-max-tokens 4096 \
|
||||
--agent-max-iterations 10
|
||||
```
|
||||
|
||||
### Timeout Configuration
|
||||
|
||||
```bash
|
||||
# Command timeout (seconds)
|
||||
hanzo-mcp serve --command-timeout 300
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
|----------|-------------|---------|
|
||||
| `HANZO_MCP_ALLOWED_PATHS` | Colon-separated allowed paths | None |
|
||||
| `HANZO_MCP_PROJECT_DIR` | Default project directory | None |
|
||||
| `HANZO_MCP_TRANSPORT` | Transport type | `stdio` |
|
||||
| `HANZO_MCP_HOST` | SSE host | `127.0.0.1` |
|
||||
| `HANZO_MCP_PORT` | SSE port | `8888` |
|
||||
| `HANZO_MCP_TOKEN` | Authentication token | Auto-generated |
|
||||
| `HANZO_API_KEY` | Hanzo API key | None |
|
||||
| `HANZO_QUIET` | Suppress startup messages | None |
|
||||
|
||||
### Example
|
||||
|
||||
```bash
|
||||
export HANZO_MCP_ALLOWED_PATHS="/home/user/projects:/home/user/docs"
|
||||
export HANZO_MCP_PROJECT_DIR="/home/user/projects/my-app"
|
||||
export HANZO_API_KEY="your-api-key"
|
||||
|
||||
hanzo-mcp serve
|
||||
```
|
||||
|
||||
## Programmatic Configuration
|
||||
|
||||
Use Hanzo MCP as a library:
|
||||
|
||||
```python
|
||||
from hanzo_mcp import HanzoMCPServer
|
||||
|
||||
server = HanzoMCPServer(
|
||||
name="my-server",
|
||||
allowed_paths=["/path/to/project"],
|
||||
project_dir="/path/to/project",
|
||||
project_paths=["/path/to/project"],
|
||||
|
||||
# Tool control
|
||||
disable_write_tools=False,
|
||||
disable_search_tools=False,
|
||||
enabled_tools={"shell": True, "bash": True},
|
||||
disabled_tools=["dangerous_tool"],
|
||||
|
||||
# Agent configuration
|
||||
enable_agent_tool=True,
|
||||
agent_model="gpt-4o",
|
||||
agent_max_tokens=4096,
|
||||
agent_api_key="your-key",
|
||||
agent_base_url="https://api.hanzo.ai",
|
||||
agent_max_iterations=10,
|
||||
agent_max_tool_uses=30,
|
||||
|
||||
# Timeouts
|
||||
command_timeout=120.0,
|
||||
|
||||
# Network
|
||||
host="127.0.0.1",
|
||||
port=8888,
|
||||
)
|
||||
|
||||
server.run(transport="stdio")
|
||||
```
|
||||
|
||||
## Tool Configuration
|
||||
|
||||
### Enabling/Disabling Tools
|
||||
|
||||
```python
|
||||
server = HanzoMCPServer(
|
||||
enabled_tools={
|
||||
"read": True,
|
||||
"write": True,
|
||||
"edit": True,
|
||||
"shell": False, # Disable shell
|
||||
},
|
||||
disabled_tools=["bash", "zsh"], # Also disable these
|
||||
)
|
||||
```
|
||||
|
||||
### Tool Categories
|
||||
|
||||
| Category | Tools |
|
||||
|----------|-------|
|
||||
| File Tools | `read`, `write`, `edit`, `multi_edit`, `tree`, `find` |
|
||||
| Shell Tools | `shell`, `bash`, `zsh`, `npx`, `uvx`, `process` |
|
||||
| Search Tools | `search`, `ast` |
|
||||
| Memory Tools | `recall_memories`, `create_memories`, `store_facts`, etc. |
|
||||
| Code Tools | `lsp`, `refactor` |
|
||||
| Thinking Tools | `think`, `critic` |
|
||||
|
||||
## Permission System
|
||||
|
||||
### Default Allowed Paths
|
||||
|
||||
By default, these paths are allowed:
|
||||
- `/tmp` (Unix)
|
||||
- `/var` (Unix)
|
||||
- `C:\Users\...\AppData\Local\Temp` (Windows)
|
||||
- `~/work` (if exists)
|
||||
|
||||
### Excluded Patterns
|
||||
|
||||
These patterns are always excluded:
|
||||
- `.ssh`, `.gnupg` (security)
|
||||
- `node_modules`, `__pycache__`, `.venv` (dependencies)
|
||||
- `.env`, `*.key`, `*.pem` (secrets)
|
||||
- `*.sqlite`, `*.db` (databases)
|
||||
|
||||
### Custom Exclusions
|
||||
|
||||
```python
|
||||
from hanzo_mcp.tools.common.permissions import PermissionManager
|
||||
|
||||
pm = PermissionManager()
|
||||
pm.add_exclusion_pattern("*.secret")
|
||||
pm.exclude_path("/path/to/sensitive")
|
||||
```
|
||||
|
||||
## Transport Configuration
|
||||
|
||||
### stdio (Default)
|
||||
|
||||
For Claude Desktop and command-line usage:
|
||||
|
||||
```bash
|
||||
hanzo-mcp serve --transport stdio
|
||||
```
|
||||
|
||||
### SSE
|
||||
|
||||
For web clients and custom integrations:
|
||||
|
||||
```bash
|
||||
hanzo-mcp serve \
|
||||
--transport sse \
|
||||
--host 0.0.0.0 \
|
||||
--port 8888
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
### Log Levels
|
||||
|
||||
```bash
|
||||
# Via environment variable
|
||||
export HANZO_MCP_LOG_LEVEL=DEBUG
|
||||
hanzo-mcp serve
|
||||
|
||||
# Or via Python logging
|
||||
import logging
|
||||
logging.getLogger("hanzo_mcp").setLevel(logging.DEBUG)
|
||||
```
|
||||
|
||||
### Log Levels
|
||||
|
||||
| Level | Description |
|
||||
|-------|-------------|
|
||||
| `DEBUG` | Verbose debugging info |
|
||||
| `INFO` | General information |
|
||||
| `WARNING` | Warnings only |
|
||||
| `ERROR` | Errors only |
|
||||
|
||||
## Security Configuration
|
||||
|
||||
### Read-Only Mode
|
||||
|
||||
```bash
|
||||
hanzo-mcp serve --disable-write-tools
|
||||
```
|
||||
|
||||
### Restricted Shell Access
|
||||
|
||||
```bash
|
||||
hanzo-mcp serve --disabled-tools shell,bash,zsh,npx,uvx
|
||||
```
|
||||
|
||||
### Authentication Token
|
||||
|
||||
```bash
|
||||
# Set explicit token
|
||||
export HANZO_MCP_TOKEN="your-secure-token"
|
||||
hanzo-mcp serve
|
||||
|
||||
# Or generate automatically (logged on startup)
|
||||
hanzo-mcp serve
|
||||
# Logs: "Generated token: abc123..."
|
||||
```
|
||||
|
||||
## Multiple Server Instances
|
||||
|
||||
Run different configurations for different projects:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo-project-a": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": ["serve", "--project-dir", "/project-a", "--name", "project-a"]
|
||||
},
|
||||
"hanzo-project-b": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": ["serve", "--project-dir", "/project-b", "--name", "project-b"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Tools Reference](/docs/mcp/tools) - All available tools
|
||||
- [Claude Desktop](/docs/mcp/claude-desktop) - Claude integration
|
||||
@@ -0,0 +1,220 @@
|
||||
---
|
||||
title: Hanzo MCP
|
||||
description: Model Context Protocol tools for AI assistants - 29 tools across 12 packages
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Hanzo MCP
|
||||
|
||||
Hanzo MCP is a comprehensive implementation of the [Model Context Protocol](https://modelcontextprotocol.io/) that provides powerful tools for AI assistants like Claude Desktop, Cursor, and other MCP-compatible clients.
|
||||
|
||||
<Callout type="info">
|
||||
**Version 0.10.21** - 29 tools across 12 modular packages
|
||||
</Callout>
|
||||
|
||||
## Overview
|
||||
|
||||
MCP (Model Context Protocol) is a standard for providing tools and context to AI models. Hanzo MCP implements this protocol with a carefully curated set of tools organized into modular packages.
|
||||
|
||||
## Tool Categories
|
||||
|
||||
| Package | Tools | Description |
|
||||
|---------|-------|-------------|
|
||||
| **filesystem** | `read`, `write`, `edit`, `tree`, `find`, `search`, `ast` | File operations and code search |
|
||||
| **shell** | `dag`, `ps`, `zsh`, `shell`, `npx`, `uvx`, `open` | Command execution and process management |
|
||||
| **browser** | `browser` | Full Playwright automation (70+ actions) |
|
||||
| **memory** | `memory` | Persistent memory across sessions |
|
||||
| **todo** | `todo` | Task tracking and management |
|
||||
| **reasoning** | `think`, `critic` | Structured thinking and analysis |
|
||||
| **lsp** | `lsp` | Language Server Protocol integration |
|
||||
| **refactor** | `refactor` | Code refactoring operations |
|
||||
| **llm** | `llm`, `consensus` | Multi-model LLM access |
|
||||
| **config** | `config`, `mode` | Configuration and modes |
|
||||
| **agent** | `agent`, `iching`, `review` | AI agent orchestration |
|
||||
| **computer** | `computer` | Mac computer automation |
|
||||
|
||||
## Key Features
|
||||
|
||||
### DAG Execution Engine
|
||||
|
||||
The `dag` tool replaces traditional shell commands with a powerful DAG (Directed Acyclic Graph) execution engine:
|
||||
|
||||
```python
|
||||
# Serial execution (default)
|
||||
dag(["git status", "git diff", "git log -5"])
|
||||
|
||||
# Parallel execution
|
||||
dag(["npm install", "cargo build"], parallel=True)
|
||||
|
||||
# Mixed DAG with dependencies
|
||||
dag([
|
||||
"mkdir -p dist",
|
||||
{"parallel": ["cp a.txt dist/", "cp b.txt dist/"]},
|
||||
"zip -r out.zip dist/"
|
||||
])
|
||||
```
|
||||
|
||||
### Auto-Backgrounding
|
||||
|
||||
Long-running commands automatically continue in the background after 60 seconds:
|
||||
|
||||
```python
|
||||
dag(["npm run dev"]) # Auto-backgrounds after 60s
|
||||
ps() # Check background processes
|
||||
ps(logs="abc123") # View process logs
|
||||
ps(kill="abc123") # Kill process
|
||||
```
|
||||
|
||||
### Multi-Engine Search
|
||||
|
||||
The `search` tool runs multiple search engines in parallel:
|
||||
|
||||
```python
|
||||
search(pattern="UserService", path="./src")
|
||||
# Runs: grep, AST, LSP, file, git - all in parallel
|
||||
```
|
||||
|
||||
### Full Playwright Browser
|
||||
|
||||
70+ browser actions for complete web automation:
|
||||
|
||||
```python
|
||||
browser(action="navigate", url="https://example.com")
|
||||
browser(action="click", selector="button.submit")
|
||||
browser(action="screenshot", full_page=True)
|
||||
browser(action="expect_visible", selector=".modal")
|
||||
```
|
||||
|
||||
### Unified Memory
|
||||
|
||||
Persistent memory that survives across sessions:
|
||||
|
||||
```python
|
||||
memory(action="store", content="User prefers dark mode")
|
||||
memory(action="recall", query="user preferences")
|
||||
```
|
||||
|
||||
### 701 Personality Modes
|
||||
|
||||
Switch between 701 programmer personas from `hanzo-persona`:
|
||||
|
||||
```python
|
||||
mode(action="list") # List all modes
|
||||
mode(action="activate", name="guido") # Activate Guido van Rossum mode
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Installation
|
||||
|
||||
<Tabs items={['pip', 'uv', 'pipx']}>
|
||||
<Tab value="pip">
|
||||
```bash
|
||||
pip install hanzo-mcp
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="uv">
|
||||
```bash
|
||||
uv pip install hanzo-mcp
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pipx">
|
||||
```bash
|
||||
pipx install hanzo-mcp
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Start the Server
|
||||
|
||||
```bash
|
||||
# Start MCP server (stdio transport)
|
||||
hanzo-mcp serve
|
||||
|
||||
# With specific allowed paths
|
||||
hanzo-mcp serve --allowed-path /path/to/project
|
||||
```
|
||||
|
||||
### Install to Claude Desktop
|
||||
|
||||
```bash
|
||||
# Auto-install to Claude Desktop config
|
||||
hanzo-mcp install-desktop
|
||||
|
||||
# Or manually add to ~/Library/Application Support/Claude/claude_desktop_config.json
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Claude Desktop / Cursor / MCP Client │
|
||||
└──────────────────────────┬──────────────────────────────────┘
|
||||
│ MCP Protocol (stdio/SSE/HTTP)
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Hanzo MCP Server │
|
||||
│ ┌───────────────────────────────────────────────────────┐ │
|
||||
│ │ Entry Point Loader │ │
|
||||
│ │ Discovers tools from hanzo-tools-* packages │ │
|
||||
│ └───────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
||||
│ │ filesystem │ │ shell │ │ browser │ │
|
||||
│ │ 7 tools │ │ 7 tools │ │ 70+ actions │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
||||
│ │ reasoning │ │ memory │ │ lsp/refactor │ │
|
||||
│ │ 2 tools │ │ 1 tool │ │ 2 tools │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
|
||||
│ │ llm │ │ config │ │ agent │ │
|
||||
│ │ 2 tools │ │ 2 tools │ │ 3 tools │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Modular Packages
|
||||
|
||||
Hanzo MCP uses a modular architecture where tools are organized into independent packages:
|
||||
|
||||
```bash
|
||||
hanzo-mcp # Core server (thin wrapper)
|
||||
├── hanzo-tools-core # Base classes and utilities
|
||||
├── hanzo-tools-filesystem # File operations
|
||||
├── hanzo-tools-shell # Command execution
|
||||
├── hanzo-tools-browser # Playwright automation
|
||||
├── hanzo-tools-memory # Persistent memory
|
||||
├── hanzo-tools-todo # Task management
|
||||
├── hanzo-tools-reasoning # Think and critic
|
||||
├── hanzo-tools-lsp # Language server
|
||||
├── hanzo-tools-refactor # Code refactoring
|
||||
├── hanzo-tools-llm # Multi-model LLM
|
||||
├── hanzo-tools-config # Configuration
|
||||
├── hanzo-tools-agent # Agent orchestration
|
||||
└── hanzo-tools-computer # Mac automation
|
||||
```
|
||||
|
||||
## Supported Transports
|
||||
|
||||
- **stdio** - Standard input/output (default for Claude Desktop)
|
||||
- **SSE** - Server-Sent Events for web clients
|
||||
- **HTTP** - REST API endpoint
|
||||
|
||||
## Essential System Tools
|
||||
|
||||
These tools are always enabled regardless of mode:
|
||||
|
||||
- `llm`, `consensus` - LLM access
|
||||
- `config`, `mode` - Configuration
|
||||
- `memory` - Persistent memory
|
||||
- `version`, `stats` - System info
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Installation](/docs/mcp/installation) - Install and configure
|
||||
- [Tools Reference](/docs/mcp/tools) - All 29 tools documented
|
||||
- [Claude Desktop](/docs/mcp/claude-desktop) - Set up with Claude
|
||||
- [Configuration](/docs/mcp/configuration) - Advanced settings
|
||||
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: Installation
|
||||
description: Install and set up Hanzo MCP
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Installation
|
||||
|
||||
Install Hanzo MCP using your preferred package manager.
|
||||
|
||||
## Quick Install
|
||||
|
||||
<Tabs items={['pip', 'uv', 'pipx']}>
|
||||
<Tab value="pip">
|
||||
```bash
|
||||
pip install hanzo-mcp
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="uv">
|
||||
```bash
|
||||
uv pip install hanzo-mcp
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="pipx">
|
||||
```bash
|
||||
pipx install hanzo-mcp
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Verify Installation
|
||||
|
||||
```bash
|
||||
hanzo-mcp --version
|
||||
```
|
||||
|
||||
## Start the Server
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```bash
|
||||
# Start with stdio transport (for Claude Desktop)
|
||||
hanzo-mcp serve
|
||||
```
|
||||
|
||||
### With Allowed Paths
|
||||
|
||||
```bash
|
||||
# Restrict to specific directories
|
||||
hanzo-mcp serve --allowed-path /path/to/project
|
||||
|
||||
# Multiple paths
|
||||
hanzo-mcp serve \
|
||||
--allowed-path /home/user/projects \
|
||||
--allowed-path /home/user/documents
|
||||
```
|
||||
|
||||
### With SSE Transport
|
||||
|
||||
```bash
|
||||
# Start SSE server for web clients
|
||||
hanzo-mcp serve --transport sse --port 8888
|
||||
```
|
||||
|
||||
## Claude Desktop Integration
|
||||
|
||||
### Automatic Installation
|
||||
|
||||
```bash
|
||||
# Auto-configure Claude Desktop
|
||||
hanzo-mcp install-desktop
|
||||
```
|
||||
|
||||
This automatically updates your Claude Desktop configuration file.
|
||||
|
||||
### Manual Configuration
|
||||
|
||||
<Tabs items={['macOS', 'Windows', 'Linux']}>
|
||||
<Tab value="macOS">
|
||||
Edit `~/Library/Application Support/Claude/claude_desktop_config.json`:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": ["serve"],
|
||||
"env": {
|
||||
"HANZO_MCP_ALLOWED_PATHS": "/Users/you/projects"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Windows">
|
||||
Edit `%APPDATA%\Claude\claude_desktop_config.json`:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": ["serve"],
|
||||
"env": {
|
||||
"HANZO_MCP_ALLOWED_PATHS": "C:\\Users\\you\\projects"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Linux">
|
||||
Edit `~/.config/Claude/claude_desktop_config.json`:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"hanzo": {
|
||||
"command": "hanzo-mcp",
|
||||
"args": ["serve"],
|
||||
"env": {
|
||||
"HANZO_MCP_ALLOWED_PATHS": "/home/you/projects"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Using with Python
|
||||
|
||||
You can also use Hanzo MCP programmatically:
|
||||
|
||||
```python
|
||||
from hanzo_mcp import HanzoMCPServer
|
||||
|
||||
# Create server
|
||||
server = HanzoMCPServer(
|
||||
name="my-mcp-server",
|
||||
allowed_paths=["/path/to/project"],
|
||||
project_dir="/path/to/project"
|
||||
)
|
||||
|
||||
# Run the server
|
||||
server.run(transport="stdio")
|
||||
```
|
||||
|
||||
## Docker Installation
|
||||
|
||||
```dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
RUN pip install hanzo-mcp
|
||||
|
||||
CMD ["hanzo-mcp", "serve"]
|
||||
```
|
||||
|
||||
```bash
|
||||
docker build -t hanzo-mcp .
|
||||
docker run -v /your/project:/project hanzo-mcp \
|
||||
hanzo-mcp serve --allowed-path /project
|
||||
```
|
||||
|
||||
## Development Installation
|
||||
|
||||
For contributing to Hanzo MCP:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/hanzoai/python-sdk
|
||||
cd python-sdk/pkg/hanzo-mcp
|
||||
|
||||
# Install in development mode
|
||||
uv sync --all-extras
|
||||
|
||||
# Run tests
|
||||
uv run pytest
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python 3.9 or higher
|
||||
- For shell tools: bash or zsh
|
||||
- For LSP tools: Language servers (optional, auto-installed)
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Tools Reference](/docs/mcp/tools) - Explore available tools
|
||||
- [Claude Desktop](/docs/mcp/claude-desktop) - Full Claude setup guide
|
||||
- [Configuration](/docs/mcp/configuration) - Advanced options
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"title": "MCP Tools",
|
||||
"description": "Model Context Protocol implementation for AI tools",
|
||||
"pages": [
|
||||
"index",
|
||||
"installation",
|
||||
"tools",
|
||||
"claude-desktop",
|
||||
"configuration"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
---
|
||||
title: Tools Reference
|
||||
description: Complete reference for all Hanzo MCP tools
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Tools Reference
|
||||
|
||||
Hanzo MCP provides 260+ tools organized into categories.
|
||||
|
||||
## File Operations
|
||||
|
||||
### read
|
||||
|
||||
Read files from the filesystem.
|
||||
|
||||
```python
|
||||
read(file_path="/path/to/file.py")
|
||||
read(file_path="/path/to/file.py", offset=100, limit=50) # Lines 100-150
|
||||
```
|
||||
|
||||
### write
|
||||
|
||||
Write or overwrite files.
|
||||
|
||||
```python
|
||||
write(file_path="/path/to/file.py", content="# New content")
|
||||
```
|
||||
|
||||
### edit
|
||||
|
||||
Make precise edits to files.
|
||||
|
||||
```python
|
||||
edit(
|
||||
file_path="/path/to/file.py",
|
||||
old_string="def old_function():",
|
||||
new_string="def new_function():"
|
||||
)
|
||||
```
|
||||
|
||||
### multi_edit
|
||||
|
||||
Multiple edits to one file atomically.
|
||||
|
||||
```python
|
||||
multi_edit(
|
||||
file_path="/path/to/file.py",
|
||||
edits=[
|
||||
{"old_string": "foo", "new_string": "bar"},
|
||||
{"old_string": "baz", "new_string": "qux"}
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
### tree
|
||||
|
||||
View directory structure.
|
||||
|
||||
```python
|
||||
tree(path="/project", depth=3)
|
||||
tree(path="/project", include_filtered=True) # Include node_modules, etc.
|
||||
```
|
||||
|
||||
### find
|
||||
|
||||
Find files by pattern.
|
||||
|
||||
```python
|
||||
find(pattern="*.py", path="/project")
|
||||
find(pattern="test_", type="file", modified_after="1 day ago")
|
||||
find(pattern="config", min_size="1KB", max_size="1MB")
|
||||
```
|
||||
|
||||
## Shell Tools
|
||||
|
||||
### shell
|
||||
|
||||
Smart shell execution (prefers zsh).
|
||||
|
||||
```python
|
||||
shell(command="ls -la")
|
||||
shell(command="npm run build", cwd="/project")
|
||||
```
|
||||
|
||||
### bash
|
||||
|
||||
Execute with bash explicitly.
|
||||
|
||||
```python
|
||||
bash(command="echo $BASH_VERSION")
|
||||
bash(command="./script.sh", timeout=300)
|
||||
```
|
||||
|
||||
### zsh
|
||||
|
||||
Execute with zsh explicitly.
|
||||
|
||||
```python
|
||||
zsh(command="echo $ZSH_VERSION")
|
||||
zsh(command="source ~/.zshrc && mycmd")
|
||||
```
|
||||
|
||||
### npx
|
||||
|
||||
Run Node packages.
|
||||
|
||||
```python
|
||||
npx(package="create-react-app", args="my-app")
|
||||
npx(package="prettier", args="--write .")
|
||||
```
|
||||
|
||||
### uvx
|
||||
|
||||
Run Python packages.
|
||||
|
||||
```python
|
||||
uvx(package="ruff", args="check .")
|
||||
uvx(package="black", args="--check src/")
|
||||
```
|
||||
|
||||
### process
|
||||
|
||||
Manage background processes.
|
||||
|
||||
```python
|
||||
process() # List all
|
||||
process(action="logs", id="bash_abc123")
|
||||
process(action="kill", id="npx_def456")
|
||||
```
|
||||
|
||||
## Search Tools
|
||||
|
||||
### search
|
||||
|
||||
Unified intelligent search.
|
||||
|
||||
```python
|
||||
search(pattern="error handling", path="/project")
|
||||
search(
|
||||
pattern="UserService",
|
||||
enable_ast=True, # Code structure
|
||||
enable_symbol=True, # Definitions
|
||||
enable_text=True # Text matches
|
||||
)
|
||||
```
|
||||
|
||||
### ast
|
||||
|
||||
AST-based code search.
|
||||
|
||||
```python
|
||||
ast(pattern="class.*Service", path="/project/src")
|
||||
ast(pattern="def test_", path="/project/tests", line_number=True)
|
||||
```
|
||||
|
||||
### grep (via search)
|
||||
|
||||
Text pattern search.
|
||||
|
||||
```python
|
||||
search(pattern="TODO|FIXME", path="/project", enable_ast=False)
|
||||
```
|
||||
|
||||
## Memory Tools
|
||||
|
||||
### recall_memories
|
||||
|
||||
Recall stored memories.
|
||||
|
||||
```python
|
||||
recall_memories(queries=["user preferences", "previous decisions"])
|
||||
recall_memories(queries=["project config"], scope="project")
|
||||
```
|
||||
|
||||
### create_memories
|
||||
|
||||
Store new memories.
|
||||
|
||||
```python
|
||||
create_memories(statements=[
|
||||
"User prefers TypeScript over JavaScript",
|
||||
"Project uses PostgreSQL database"
|
||||
])
|
||||
```
|
||||
|
||||
### recall_facts
|
||||
|
||||
Query knowledge bases.
|
||||
|
||||
```python
|
||||
recall_facts(queries=["API authentication"], kb_name="api_docs")
|
||||
```
|
||||
|
||||
### store_facts
|
||||
|
||||
Store structured facts.
|
||||
|
||||
```python
|
||||
store_facts(
|
||||
facts=["API uses JWT tokens", "Rate limit is 100/hour"],
|
||||
kb_name="api_docs"
|
||||
)
|
||||
```
|
||||
|
||||
## Code Intelligence
|
||||
|
||||
### lsp
|
||||
|
||||
Language Server Protocol operations.
|
||||
|
||||
```python
|
||||
lsp(action="definition", file="/path/file.py", line=10, character=5)
|
||||
lsp(action="references", file="/path/file.py", line=10, character=5)
|
||||
lsp(action="hover", file="/path/file.py", line=10, character=5)
|
||||
lsp(action="diagnostics", file="/path/file.py")
|
||||
```
|
||||
|
||||
Supported languages: Go, Python, TypeScript, JavaScript, Rust, Java, C/C++, Ruby, Lua
|
||||
|
||||
### refactor
|
||||
|
||||
Code refactoring operations.
|
||||
|
||||
```python
|
||||
# Rename symbol across codebase
|
||||
refactor(
|
||||
action="rename",
|
||||
file="/path/file.py",
|
||||
line=10,
|
||||
column=5,
|
||||
new_name="newFunctionName"
|
||||
)
|
||||
|
||||
# Find all references
|
||||
refactor(
|
||||
action="find_references",
|
||||
file="/path/file.py",
|
||||
line=10,
|
||||
column=5
|
||||
)
|
||||
```
|
||||
|
||||
## Thinking Tools
|
||||
|
||||
### think
|
||||
|
||||
Structured reasoning.
|
||||
|
||||
```python
|
||||
think(thought="""
|
||||
Analyzing the authentication flow:
|
||||
1. User submits credentials
|
||||
2. Server validates against database
|
||||
3. JWT token is generated
|
||||
4. Token returned to client
|
||||
5. Client stores in localStorage
|
||||
|
||||
Potential issues:
|
||||
- No rate limiting on login attempts
|
||||
- Token doesn't expire
|
||||
""")
|
||||
```
|
||||
|
||||
### critic
|
||||
|
||||
Critical analysis and code review.
|
||||
|
||||
```python
|
||||
critic(analysis="""
|
||||
Code Review:
|
||||
- No error handling for network failures
|
||||
- Missing input validation
|
||||
- SQL injection vulnerability in query construction
|
||||
- No unit tests for edge cases
|
||||
|
||||
Recommendations:
|
||||
1. Add try/catch blocks
|
||||
2. Validate user input
|
||||
3. Use parameterized queries
|
||||
4. Add comprehensive tests
|
||||
""")
|
||||
```
|
||||
|
||||
## Todo Management
|
||||
|
||||
### todo
|
||||
|
||||
Manage task lists.
|
||||
|
||||
```python
|
||||
todo() # List all
|
||||
todo(action="add", content="Fix authentication bug")
|
||||
todo(action="update", id="abc123", status="completed")
|
||||
todo(action="remove", id="abc123")
|
||||
```
|
||||
|
||||
## Web Tools
|
||||
|
||||
### open
|
||||
|
||||
Open files or URLs.
|
||||
|
||||
```python
|
||||
open(path="https://example.com")
|
||||
open(path="/path/to/document.pdf")
|
||||
```
|
||||
|
||||
## Batch Operations
|
||||
|
||||
### batch
|
||||
|
||||
Execute multiple tools in parallel.
|
||||
|
||||
```python
|
||||
batch(
|
||||
description="Read multiple files",
|
||||
invocations=[
|
||||
{"tool_name": "read", "input": {"file_path": "/file1.py"}},
|
||||
{"tool_name": "read", "input": {"file_path": "/file2.py"}},
|
||||
{"tool_name": "tree", "input": {"path": "/src", "depth": 2}}
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
## Tool Comparison
|
||||
|
||||
| Need | Tool | Why |
|
||||
|------|------|-----|
|
||||
| Read a file | `read` | Direct file access |
|
||||
| Edit specific text | `edit` | Precise replacements |
|
||||
| Multiple edits | `multi_edit` | Atomic batch edits |
|
||||
| Find files | `find` | Pattern matching, filters |
|
||||
| Search code | `search` | Multi-modal search |
|
||||
| Code structure | `ast` | AST-aware search |
|
||||
| Run command | `shell` | Smart shell selection |
|
||||
| Background tasks | `process` | Manage long-running |
|
||||
| Store info | `create_memories` | Persistent memory |
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Claude Desktop](/docs/mcp/claude-desktop) - Set up with Claude
|
||||
- [Configuration](/docs/mcp/configuration) - Customize tools
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"title": "Hanzo Python SDK",
|
||||
"description": "The official Python SDK for Hanzo AI",
|
||||
"pages": [
|
||||
"index",
|
||||
"installation",
|
||||
"quickstart",
|
||||
"---SDK---",
|
||||
"client",
|
||||
"chat",
|
||||
"embeddings",
|
||||
"models",
|
||||
"files",
|
||||
"---Agent SDK---",
|
||||
"...agents",
|
||||
"---MCP---",
|
||||
"...mcp",
|
||||
"---Tools---",
|
||||
"...tools"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
---
|
||||
title: Models
|
||||
description: List and manage available models
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Models
|
||||
|
||||
The Models API allows you to list and retrieve information about available models.
|
||||
|
||||
## List Models
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
models = client.models.list()
|
||||
|
||||
for model in models.data:
|
||||
print(f"{model.id}: {model.owned_by}")
|
||||
```
|
||||
|
||||
## Retrieve a Model
|
||||
|
||||
```python
|
||||
model = client.model.retrieve("gpt-4o")
|
||||
|
||||
print(f"ID: {model.id}")
|
||||
print(f"Owner: {model.owned_by}")
|
||||
print(f"Created: {model.created}")
|
||||
```
|
||||
|
||||
## Available Providers
|
||||
|
||||
Hanzo provides unified access to 100+ LLM providers:
|
||||
|
||||
### OpenAI
|
||||
|
||||
```python
|
||||
# GPT-4 Family
|
||||
client.chat.completions.create(model="gpt-4o", ...)
|
||||
client.chat.completions.create(model="gpt-4o-mini", ...)
|
||||
client.chat.completions.create(model="gpt-4-turbo", ...)
|
||||
|
||||
# GPT-3.5
|
||||
client.chat.completions.create(model="gpt-3.5-turbo", ...)
|
||||
```
|
||||
|
||||
### Anthropic Claude
|
||||
|
||||
```python
|
||||
# Claude 3.5
|
||||
client.chat.completions.create(model="claude-3-5-sonnet-20241022", ...)
|
||||
|
||||
# Claude 3
|
||||
client.chat.completions.create(model="claude-3-opus-20240229", ...)
|
||||
client.chat.completions.create(model="claude-3-sonnet-20240229", ...)
|
||||
client.chat.completions.create(model="claude-3-haiku-20240307", ...)
|
||||
```
|
||||
|
||||
### Google
|
||||
|
||||
```python
|
||||
# Gemini
|
||||
client.chat.completions.create(model="gemini/gemini-1.5-pro", ...)
|
||||
client.chat.completions.create(model="gemini/gemini-1.5-flash", ...)
|
||||
client.chat.completions.create(model="gemini/gemini-2.0-flash-exp", ...)
|
||||
```
|
||||
|
||||
### Meta Llama
|
||||
|
||||
```python
|
||||
# Via Together AI
|
||||
client.chat.completions.create(
|
||||
model="together_ai/meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
|
||||
...
|
||||
)
|
||||
client.chat.completions.create(
|
||||
model="together_ai/meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
### Mistral
|
||||
|
||||
```python
|
||||
client.chat.completions.create(model="mistral/mistral-large-latest", ...)
|
||||
client.chat.completions.create(model="mistral/mistral-medium-latest", ...)
|
||||
client.chat.completions.create(model="mistral/mistral-small-latest", ...)
|
||||
```
|
||||
|
||||
### Cohere
|
||||
|
||||
```python
|
||||
client.chat.completions.create(model="cohere/command-r-plus", ...)
|
||||
client.chat.completions.create(model="cohere/command-r", ...)
|
||||
```
|
||||
|
||||
## Model Selection by Task
|
||||
|
||||
<Callout type="info">
|
||||
Choose models based on your specific needs:
|
||||
</Callout>
|
||||
|
||||
| Task | Recommended Models |
|
||||
|------|-------------------|
|
||||
| Complex reasoning | `gpt-4o`, `claude-3-opus`, `gemini-1.5-pro` |
|
||||
| General chat | `gpt-4o-mini`, `claude-3-5-sonnet`, `gemini-1.5-flash` |
|
||||
| Code generation | `gpt-4o`, `claude-3-5-sonnet` |
|
||||
| Fast responses | `gpt-3.5-turbo`, `claude-3-haiku`, `gemini-flash` |
|
||||
| Long context | `claude-3-opus` (200k), `gemini-1.5-pro` (2M) |
|
||||
| Cost efficient | `gpt-4o-mini`, `claude-3-haiku` |
|
||||
|
||||
## Model Info Structure
|
||||
|
||||
```python
|
||||
model = client.model.retrieve("gpt-4o")
|
||||
|
||||
print(model.id) # Model identifier
|
||||
print(model.object) # "model"
|
||||
print(model.created) # Creation timestamp
|
||||
print(model.owned_by) # Owner/provider
|
||||
```
|
||||
|
||||
## Async Usage
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from hanzoai import AsyncHanzo
|
||||
|
||||
async def main():
|
||||
client = AsyncHanzo()
|
||||
|
||||
# List models
|
||||
models = await client.models.list()
|
||||
print(f"Found {len(models.data)} models")
|
||||
|
||||
# Retrieve specific model
|
||||
model = await client.model.retrieve("gpt-4o")
|
||||
print(f"Model: {model.id}")
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Filter by Provider
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
# Get all models
|
||||
models = client.models.list()
|
||||
|
||||
# Filter by provider
|
||||
openai_models = [m for m in models.data if m.owned_by == "openai"]
|
||||
anthropic_models = [m for m in models.data if m.owned_by == "anthropic"]
|
||||
|
||||
print(f"OpenAI models: {len(openai_models)}")
|
||||
print(f"Anthropic models: {len(anthropic_models)}")
|
||||
```
|
||||
|
||||
## Model Capabilities
|
||||
|
||||
Different models have different capabilities:
|
||||
|
||||
```python
|
||||
# Vision-capable models
|
||||
vision_models = ["gpt-4o", "gpt-4-turbo", "claude-3-opus", "gemini-1.5-pro"]
|
||||
|
||||
# Tool/Function calling
|
||||
tool_models = ["gpt-4o", "gpt-4-turbo", "claude-3-5-sonnet", "gemini-1.5-pro"]
|
||||
|
||||
# JSON mode
|
||||
json_models = ["gpt-4o", "gpt-4-turbo", "gpt-3.5-turbo"]
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Files](/docs/python-sdk/files) - Upload and manage files
|
||||
- [Chat](/docs/python-sdk/chat) - Use models for chat completions
|
||||
- [Embeddings](/docs/python-sdk/embeddings) - Generate text embeddings
|
||||
@@ -0,0 +1,155 @@
|
||||
---
|
||||
title: Quickstart
|
||||
description: Get up and running with the Hanzo Python SDK in minutes
|
||||
---
|
||||
|
||||
import { Callout } from 'fumadocs-ui/components/callout'
|
||||
import { Tab, Tabs } from 'fumadocs-ui/components/tabs'
|
||||
|
||||
# Quickstart
|
||||
|
||||
This guide will help you make your first API call with the Hanzo Python SDK.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.9 or higher
|
||||
- A Hanzo API key (get one at [hanzo.ai](https://hanzo.ai))
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install hanzoai
|
||||
```
|
||||
|
||||
## Set Your API Key
|
||||
|
||||
<Tabs items={['Environment Variable', 'Direct']}>
|
||||
<Tab value="Environment Variable">
|
||||
```bash
|
||||
export HANZO_API_KEY="your-api-key"
|
||||
```
|
||||
</Tab>
|
||||
<Tab value="Direct">
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo(api_key="your-api-key")
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Your First API Call
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[
|
||||
{"role": "user", "content": "Hello, how are you?"}
|
||||
]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
## Using Different Models
|
||||
|
||||
Hanzo provides access to 100+ LLM providers through a unified API:
|
||||
|
||||
```python
|
||||
# OpenAI GPT-4
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
|
||||
# Anthropic Claude
|
||||
response = client.chat.completions.create(
|
||||
model="claude-3-5-sonnet-20241022",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
|
||||
# Google Gemini
|
||||
response = client.chat.completions.create(
|
||||
model="gemini/gemini-1.5-pro",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
|
||||
# Open source models
|
||||
response = client.chat.completions.create(
|
||||
model="together_ai/meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
```
|
||||
|
||||
## Async Usage
|
||||
|
||||
For async applications, use the `AsyncHanzo` client:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from hanzoai import AsyncHanzo
|
||||
|
||||
async def main():
|
||||
client = AsyncHanzo()
|
||||
|
||||
response = await client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## Streaming Responses
|
||||
|
||||
Stream responses for real-time output:
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
stream = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Write a short poem"}],
|
||||
stream=True
|
||||
)
|
||||
|
||||
for chunk in stream:
|
||||
if chunk.choices[0].delta.content:
|
||||
print(chunk.choices[0].delta.content, end="")
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
Handle errors gracefully:
|
||||
|
||||
```python
|
||||
from hanzoai import Hanzo
|
||||
from hanzoai._exceptions import APIError, RateLimitError
|
||||
|
||||
client = Hanzo()
|
||||
|
||||
try:
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o",
|
||||
messages=[{"role": "user", "content": "Hello!"}]
|
||||
)
|
||||
except RateLimitError:
|
||||
print("Rate limited, please wait and retry")
|
||||
except APIError as e:
|
||||
print(f"API error: {e.message}")
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Client Configuration](/docs/python-sdk/client) - Learn about client options
|
||||
- [Chat Completions](/docs/python-sdk/chat) - Deep dive into chat API
|
||||
- [Embeddings](/docs/python-sdk/embeddings) - Generate text embeddings
|
||||
- [Models](/docs/python-sdk/models) - List and manage models
|
||||
@@ -0,0 +1,332 @@
|
||||
---
|
||||
title: Agent Tool
|
||||
description: Spawn and manage CLI AI agents with YOLO mode
|
||||
---
|
||||
|
||||
The `hanzo-tools-agent` package provides tools for running various CLI AI agents.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install hanzo-tools-agent
|
||||
|
||||
# With API mode support
|
||||
pip install hanzo-tools-agent[api]
|
||||
|
||||
# With high-performance async
|
||||
pip install hanzo-tools-agent[perf]
|
||||
|
||||
# All features
|
||||
pip install hanzo-tools-agent[full]
|
||||
```
|
||||
|
||||
## Agent Tool
|
||||
|
||||
The unified `agent` tool runs multiple CLI agents with auto-detection and YOLO mode.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```python
|
||||
# Run with default agent (claude when in Claude Code)
|
||||
agent(action="run", prompt="Explain this code")
|
||||
|
||||
# Run specific agent
|
||||
agent(action="run", name="gemini", prompt="Review this PR")
|
||||
|
||||
# Run with system prompt (claude only)
|
||||
agent(action="run", name="claude", prompt="Fix this bug", system_prompt="Be concise")
|
||||
|
||||
# List available agents
|
||||
agent(action="list")
|
||||
|
||||
# Check agent status
|
||||
agent(action="status")
|
||||
|
||||
# Show configuration
|
||||
agent(action="config")
|
||||
```
|
||||
|
||||
### Available Agents
|
||||
|
||||
| Agent | Command | Auth | YOLO Flags |
|
||||
|-------|---------|------|------------|
|
||||
| `claude` | `claude` | OAuth | `--dangerously-skip-permissions --print` |
|
||||
| `codex` | `codex` | OAuth | `--full-auto` |
|
||||
| `gemini` | `gemini` | `GOOGLE_API_KEY` | `-y -p <prompt>` |
|
||||
| `grok` | `grok` | `XAI_API_KEY` | `-y` |
|
||||
| `qwen` | `qwen` | `DASHSCOPE_API_KEY` | `--approval-mode yolo` |
|
||||
| `vibe` | `vibe` | - | `--auto-approve -p <prompt>` |
|
||||
| `code` | `hanzo-code` | - | - |
|
||||
| `dev` | `hanzo-dev` | - | `-y` |
|
||||
|
||||
### YOLO Mode
|
||||
|
||||
YOLO mode flags are automatically applied for supported agents:
|
||||
|
||||
- **Claude**: `--dangerously-skip-permissions -p` (skip all permission prompts, print output)
|
||||
- **Codex**: `--full-auto` (fully automatic mode)
|
||||
|
||||
This enables agents to run without user interaction.
|
||||
|
||||
### OAuth Authentication
|
||||
|
||||
Claude and Codex use browser-based OAuth authentication:
|
||||
|
||||
- No API keys required
|
||||
- Login once via browser
|
||||
- Credentials stored securely
|
||||
|
||||
### System Prompts
|
||||
|
||||
For Claude, you can inject system prompts:
|
||||
|
||||
```python
|
||||
agent(
|
||||
action="run",
|
||||
name="claude",
|
||||
prompt="Refactor this function",
|
||||
system_prompt="Follow the project's code style. Be concise."
|
||||
)
|
||||
```
|
||||
|
||||
This uses the `--append-system-prompt` flag.
|
||||
|
||||
### MCP Config Sharing
|
||||
|
||||
When spawning agents, configuration is shared:
|
||||
|
||||
```python
|
||||
# These environment variables are passed to child agents:
|
||||
HANZO_MCP_MODE # Current mode
|
||||
HANZO_MCP_ALLOWED_PATHS # Allowed paths
|
||||
HANZO_MCP_ENABLED_TOOLS # Enabled tools
|
||||
HANZO_MCP_PERSONA # Active persona
|
||||
HANZO_AGENT_PARENT=true # Indicates spawned agent
|
||||
HANZO_AGENT_NAME=<name> # Agent name
|
||||
```
|
||||
|
||||
### Working Directory
|
||||
|
||||
Specify the working directory for the agent:
|
||||
|
||||
```python
|
||||
agent(
|
||||
action="run",
|
||||
name="claude",
|
||||
prompt="Run the tests",
|
||||
cwd="/path/to/project"
|
||||
)
|
||||
```
|
||||
|
||||
### Timeout
|
||||
|
||||
Set execution timeout (default 300 seconds):
|
||||
|
||||
```python
|
||||
agent(
|
||||
action="run",
|
||||
prompt="Long running task",
|
||||
timeout=600 # 10 minutes
|
||||
)
|
||||
```
|
||||
|
||||
## Telemetry Capture
|
||||
|
||||
For Claude agents, OpenTelemetry data is automatically captured and returned:
|
||||
|
||||
```python
|
||||
from hanzo_tools.agent import AgentTool, Result, Telemetry
|
||||
|
||||
tool = AgentTool()
|
||||
result = await tool._exec("claude", "Hello", None, 30)
|
||||
|
||||
# Clean output (telemetry filtered out)
|
||||
print(result.output) # "Hello!"
|
||||
|
||||
# Captured telemetry data
|
||||
if result.telemetry:
|
||||
t = result.telemetry
|
||||
print(f"Input tokens: {t.input_tokens}")
|
||||
print(f"Output tokens: {t.output_tokens}")
|
||||
print(f"Cache read: {t.cache_read_input_tokens}")
|
||||
print(f"Cost: ${t.cost_usd:.4f}")
|
||||
print(f"Latency: {t.duration_ms}ms")
|
||||
print(f"Model: {t.model}")
|
||||
|
||||
# Full raw data for dataset annotation
|
||||
print(t.raw) # Dict with all OTEL fields
|
||||
```
|
||||
|
||||
### Telemetry Fields
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `input_tokens` | int | Input tokens used |
|
||||
| `output_tokens` | int | Output tokens generated |
|
||||
| `cache_read_input_tokens` | int | Tokens read from cache |
|
||||
| `cache_creation_input_tokens` | int | Tokens used to create cache |
|
||||
| `cost_usd` | float | Total cost in USD |
|
||||
| `duration_ms` | int | Request duration |
|
||||
| `model` | str | Model used |
|
||||
| `service_name` | str | Service name (claude-code) |
|
||||
| `service_version` | str | CLI version |
|
||||
| `raw` | dict | Full OTEL data for datasets |
|
||||
|
||||
The telemetry is valuable for:
|
||||
- Cost tracking and optimization
|
||||
- Performance monitoring
|
||||
- Dataset annotation and training data collection
|
||||
- Usage analytics
|
||||
|
||||
## I Ching Tool
|
||||
|
||||
Get wisdom from the I Ching for decision making:
|
||||
|
||||
```python
|
||||
iching(challenge="How should I approach this refactoring?")
|
||||
```
|
||||
|
||||
## Review Tool
|
||||
|
||||
Request a code review:
|
||||
|
||||
```python
|
||||
review(
|
||||
focus="FUNCTIONALITY",
|
||||
work_description="Implemented auto-import feature",
|
||||
file_paths=["/path/to/file.py"]
|
||||
)
|
||||
```
|
||||
|
||||
Focus options:
|
||||
- `FUNCTIONALITY` - Functional correctness
|
||||
- `SECURITY` - Security issues
|
||||
- `PERFORMANCE` - Performance concerns
|
||||
- `STYLE` - Code style and conventions
|
||||
|
||||
## Direct API Mode
|
||||
|
||||
Configure agents for direct API calls without CLI:
|
||||
|
||||
```json
|
||||
// ~/.hanzo/agents/custom.json
|
||||
{
|
||||
"endpoint": "https://api.openai.com/v1/chat/completions",
|
||||
"api_type": "openai",
|
||||
"model": "gpt-4",
|
||||
"env_key": "OPENAI_API_KEY",
|
||||
"system_prompt": "You are a helpful assistant"
|
||||
}
|
||||
```
|
||||
|
||||
Requires `pip install hanzo-tools-agent[api]`.
|
||||
|
||||
## Auto-backgrounding
|
||||
|
||||
Long-running agents automatically background after timeout:
|
||||
|
||||
```python
|
||||
# Start long task
|
||||
agent(action="run", prompt="Complex analysis", timeout=60)
|
||||
|
||||
# If times out, process runs in background
|
||||
# Check status with ps tool
|
||||
ps() # List all processes
|
||||
ps(logs="agent_xxx") # View output
|
||||
ps(kill="agent_xxx") # Stop process
|
||||
```
|
||||
|
||||
## Consensus Mode
|
||||
|
||||
Multi-agent consensus using the Metastable protocol:
|
||||
|
||||
```python
|
||||
agent(
|
||||
action="consensus",
|
||||
prompt="What's the best approach for handling auth?",
|
||||
agents=["claude", "gemini", "codex"],
|
||||
rounds=3,
|
||||
k=3,
|
||||
alpha=0.6,
|
||||
beta_1=0.5,
|
||||
beta_2=0.8,
|
||||
)
|
||||
```
|
||||
|
||||
### How Consensus Works
|
||||
|
||||
1. **Phase I (Sampling)**: Each agent samples k peers and builds confidence
|
||||
2. **Phase II (Finality)**: Threshold aggregation determines winner
|
||||
3. **Synthesis**: Winning agent provides final synthesis
|
||||
|
||||
### Agent-to-Agent Communication
|
||||
|
||||
During consensus, agents can communicate via MCP:
|
||||
|
||||
```python
|
||||
# Agents automatically get hanzo-mcp configured
|
||||
# System prompt enables these tools:
|
||||
agent(action="run", name="gemini", prompt="What's your view?")
|
||||
think(thought="Analyzing gemini's response...")
|
||||
critic(analysis="Claude proposed X, but consider Y...")
|
||||
```
|
||||
|
||||
Each spawned Claude agent receives:
|
||||
- `--mcp-config` pointing to hanzo-mcp
|
||||
- System prompt explaining MCP tools
|
||||
- List of other consensus participants
|
||||
|
||||
### Consensus Parameters
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|-----------|---------|-------------|
|
||||
| `rounds` | 3 | Number of consensus rounds |
|
||||
| `k` | 3 | Peers sampled per round |
|
||||
| `alpha` | 0.6 | Confidence increment on agreement |
|
||||
| `beta_1` | 0.5 | Phase I to Phase II threshold |
|
||||
| `beta_2` | 0.8 | Finality threshold |
|
||||
|
||||
Reference: [Metastable Consensus Protocol](https://github.com/luxfi/consensus)
|
||||
|
||||
## Swarm Mode
|
||||
|
||||
Distribute work across parallel agents:
|
||||
|
||||
```python
|
||||
agent(
|
||||
action="swarm",
|
||||
items=["file1.py", "file2.py", "file3.py"],
|
||||
template="Review {item} for security issues",
|
||||
max_concurrent=10,
|
||||
)
|
||||
```
|
||||
|
||||
## DAG Mode
|
||||
|
||||
Execute agents with dependencies:
|
||||
|
||||
```python
|
||||
agent(
|
||||
action="dag",
|
||||
tasks=[
|
||||
{"id": "analyze", "prompt": "Analyze the codebase"},
|
||||
{"id": "plan", "prompt": "Create implementation plan", "after": ["analyze"]},
|
||||
{"id": "implement", "prompt": "Implement the plan", "after": ["plan"]},
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
## Dispatch Mode
|
||||
|
||||
Different agents for different tasks:
|
||||
|
||||
```python
|
||||
agent(
|
||||
action="dispatch",
|
||||
tasks=[
|
||||
{"agent": "claude", "prompt": "Review the architecture"},
|
||||
{"agent": "gemini", "prompt": "Check for performance issues"},
|
||||
{"agent": "codex", "prompt": "Suggest optimizations"},
|
||||
],
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,207 @@
|
||||
---
|
||||
title: Browser Tools
|
||||
description: Playwright-based browser automation with 70+ actions
|
||||
---
|
||||
|
||||
The `hanzo-tools-browser` package provides comprehensive browser automation using Playwright.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install hanzo-tools-browser
|
||||
playwright install chromium
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Navigation
|
||||
|
||||
```python
|
||||
browser(action="navigate", url="https://example.com")
|
||||
browser(action="go_back")
|
||||
browser(action="go_forward")
|
||||
browser(action="reload")
|
||||
```
|
||||
|
||||
### Page Content
|
||||
|
||||
```python
|
||||
# Get page URL and title
|
||||
browser(action="url")
|
||||
browser(action="title")
|
||||
|
||||
# Get page content
|
||||
browser(action="content") # Full HTML
|
||||
browser(action="get_text", selector="h1")
|
||||
browser(action="get_html", selector=".container")
|
||||
```
|
||||
|
||||
### Screenshots
|
||||
|
||||
```python
|
||||
browser(action="screenshot")
|
||||
browser(action="screenshot", selector=".modal", full_page=False)
|
||||
```
|
||||
|
||||
## Element Interaction
|
||||
|
||||
### Clicking
|
||||
|
||||
```python
|
||||
browser(action="click", selector="button.submit")
|
||||
browser(action="click", selector="a.nav-link", button="right")
|
||||
browser(action="dblclick", selector=".item")
|
||||
```
|
||||
|
||||
### Typing
|
||||
|
||||
```python
|
||||
browser(action="fill", selector="input[name='email']", text="[email protected]")
|
||||
browser(action="type", selector="textarea", text="Hello world", delay=50)
|
||||
browser(action="clear", selector="input")
|
||||
browser(action="press", selector="input", key="Enter")
|
||||
```
|
||||
|
||||
### Forms
|
||||
|
||||
```python
|
||||
browser(action="select_option", selector="select#country", value="US")
|
||||
browser(action="check", selector="input[type='checkbox']")
|
||||
browser(action="uncheck", selector="input[type='checkbox']")
|
||||
browser(action="upload", selector="input[type='file']", files=["/path/to/file.pdf"])
|
||||
```
|
||||
|
||||
## Mouse Actions
|
||||
|
||||
```python
|
||||
browser(action="hover", selector=".dropdown")
|
||||
browser(action="drag", source=".draggable", target=".dropzone")
|
||||
browser(action="mouse_move", x=100, y=200)
|
||||
browser(action="mouse_wheel", delta_y=-500) # Scroll
|
||||
browser(action="scroll", selector=".container", delta_y=300)
|
||||
```
|
||||
|
||||
## Touch / Mobile
|
||||
|
||||
```python
|
||||
browser(action="tap", selector=".button")
|
||||
browser(action="swipe", selector=".carousel", direction="left")
|
||||
browser(action="pinch", selector=".map", scale=0.5) # Zoom out
|
||||
```
|
||||
|
||||
## Device Emulation
|
||||
|
||||
```python
|
||||
# User-friendly presets
|
||||
browser(action="new_context", device="mobile") # iPhone-like
|
||||
browser(action="new_context", device="tablet") # iPad-like
|
||||
browser(action="new_context", device="laptop") # MacBook-like
|
||||
|
||||
# Specific devices
|
||||
browser(action="new_context", device="iphone_14")
|
||||
browser(action="new_context", device="pixel_7")
|
||||
browser(action="new_context", device="ipad_pro")
|
||||
```
|
||||
|
||||
## Assertions
|
||||
|
||||
```python
|
||||
# Page assertions
|
||||
browser(action="expect_url", expected="*/dashboard*")
|
||||
browser(action="expect_title", expected="Dashboard")
|
||||
|
||||
# Element assertions
|
||||
browser(action="expect_visible", selector=".modal")
|
||||
browser(action="expect_hidden", selector=".loading")
|
||||
browser(action="expect_text", selector="h1", expected="Welcome")
|
||||
browser(action="expect_count", selector=".items", index=5)
|
||||
|
||||
# Negative assertions
|
||||
browser(action="expect_visible", selector=".loading", not_=True)
|
||||
```
|
||||
|
||||
## Locator Composition
|
||||
|
||||
```python
|
||||
# Get specific elements
|
||||
browser(action="first", selector=".item")
|
||||
browser(action="last", selector=".item")
|
||||
browser(action="nth", selector=".item", index=2)
|
||||
|
||||
# Filter and find
|
||||
browser(action="filter", selector=".card", has_text="Premium")
|
||||
browser(action="all", selector=".list-item")
|
||||
browser(action="count", selector=".results")
|
||||
```
|
||||
|
||||
## Waits
|
||||
|
||||
```python
|
||||
browser(action="wait_for_selector", selector=".loaded")
|
||||
browser(action="wait_for_url", url="*/success*")
|
||||
browser(action="wait_for_load_state", state="networkidle")
|
||||
browser(action="wait_for_event", event="download")
|
||||
```
|
||||
|
||||
## Storage & State
|
||||
|
||||
```python
|
||||
# Cookies
|
||||
browser(action="cookies")
|
||||
browser(action="clear_cookies")
|
||||
|
||||
# Storage
|
||||
browser(action="storage") # LocalStorage + SessionStorage
|
||||
browser(action="storage_state") # Full browser state for persistence
|
||||
```
|
||||
|
||||
## Tabs & Windows
|
||||
|
||||
```python
|
||||
browser(action="new_tab", url="https://example.com")
|
||||
browser(action="close_tab")
|
||||
```
|
||||
|
||||
## Headless Control
|
||||
|
||||
```python
|
||||
browser(action="set_headless", headless=False) # Show browser
|
||||
browser(action="set_headless", headless=True) # Hide browser
|
||||
```
|
||||
|
||||
## Parallel Contexts
|
||||
|
||||
For multi-agent workflows, each agent can have isolated sessions:
|
||||
|
||||
```python
|
||||
# Agent 1
|
||||
browser(action="new_context", context_id="agent1")
|
||||
browser(action="navigate", url="https://app.com/login", context_id="agent1")
|
||||
|
||||
# Agent 2 (separate cookies/storage)
|
||||
browser(action="new_context", context_id="agent2")
|
||||
browser(action="navigate", url="https://app.com/login", context_id="agent2")
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Login Flow
|
||||
|
||||
```python
|
||||
browser(action="navigate", url="https://app.com/login")
|
||||
browser(action="fill", selector="input[name='email']", text="[email protected]")
|
||||
browser(action="fill", selector="input[name='password']", text="secret")
|
||||
browser(action="click", selector="button[type='submit']")
|
||||
browser(action="wait_for_url", url="*/dashboard*")
|
||||
browser(action="expect_text", selector="h1", expected="Dashboard")
|
||||
```
|
||||
|
||||
### Mobile Testing
|
||||
|
||||
```python
|
||||
browser(action="new_context", device="mobile")
|
||||
browser(action="navigate", url="https://app.com")
|
||||
browser(action="tap", selector=".hamburger-menu")
|
||||
browser(action="expect_visible", selector=".mobile-nav")
|
||||
browser(action="swipe", selector=".carousel", direction="left")
|
||||
```
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
title: Consensus
|
||||
description: Multi-model consensus using Metastable protocol
|
||||
---
|
||||
|
||||
The `hanzo-consensus` package implements a consensus protocol for multi-model decision making.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install hanzo-consensus
|
||||
```
|
||||
|
||||
Or with hanzo-tools-llm:
|
||||
|
||||
```bash
|
||||
pip install hanzo-tools-llm
|
||||
```
|
||||
|
||||
## Overview
|
||||
|
||||
The Metastable consensus protocol enables multiple AI models to reach agreement through iterative sampling and confidence accumulation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```python
|
||||
from hanzo_consensus import run, Result
|
||||
|
||||
async def execute(participant_id: str, prompt: str) -> Result:
|
||||
# Call your LLM here
|
||||
response = await call_llm(participant_id, prompt)
|
||||
return Result(
|
||||
participant=participant_id,
|
||||
response=response,
|
||||
confidence=0.8
|
||||
)
|
||||
|
||||
state = await run(
|
||||
prompt="What's the best approach for handling authentication?",
|
||||
participants=["gpt-4", "claude-3-5-sonnet", "gemini-pro"],
|
||||
execute=execute,
|
||||
rounds=3,
|
||||
k=3,
|
||||
alpha=0.6,
|
||||
beta_1=0.5,
|
||||
beta_2=0.8,
|
||||
)
|
||||
|
||||
print(f"Winner: {state.winner}")
|
||||
print(f"Finalized: {state.finalized}")
|
||||
print(f"Synthesis: {state.synthesis}")
|
||||
```
|
||||
|
||||
## Protocol Phases
|
||||
|
||||
### Phase I: Sampling
|
||||
|
||||
1. Each participant samples k peers
|
||||
2. Responses are compared for agreement
|
||||
3. Confidence accumulates based on agreement
|
||||
4. β₁ threshold triggers Phase II
|
||||
|
||||
### Phase II: Finality
|
||||
|
||||
1. Threshold aggregation of responses
|
||||
2. β₂ finality threshold checked
|
||||
3. Winner determined by highest confidence
|
||||
4. Synthesis generated from winning response
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Default | Description |
|
||||
|-----------|---------|-------------|
|
||||
| `rounds` | 3 | Number of consensus rounds |
|
||||
| `k` | 3 | Peers sampled per round |
|
||||
| `alpha` | 0.6 | Confidence increment on agreement |
|
||||
| `beta_1` | 0.5 | Phase I to Phase II threshold |
|
||||
| `beta_2` | 0.8 | Finality threshold |
|
||||
|
||||
## State Object
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class State:
|
||||
winner: str | None # Winning participant ID
|
||||
finalized: bool # Whether consensus reached
|
||||
synthesis: str | None # Synthesized response
|
||||
round: int # Current round number
|
||||
participants: dict # Participant states
|
||||
```
|
||||
|
||||
## MCP Tool Usage
|
||||
|
||||
With hanzo-tools-llm:
|
||||
|
||||
```python
|
||||
consensus(
|
||||
prompt="Should we use microservices or monolith?",
|
||||
models=["gpt-4", "claude-3-5-sonnet"],
|
||||
rounds=3
|
||||
)
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Custom Consensus Class
|
||||
|
||||
```python
|
||||
from hanzo_consensus import Consensus
|
||||
|
||||
consensus = Consensus(
|
||||
participants=["model-a", "model-b", "model-c"],
|
||||
k=2,
|
||||
alpha=0.7,
|
||||
beta_1=0.4,
|
||||
beta_2=0.9,
|
||||
)
|
||||
|
||||
for round in range(5):
|
||||
results = await gather_responses(consensus.participants)
|
||||
consensus.update(results)
|
||||
|
||||
if consensus.state.finalized:
|
||||
break
|
||||
|
||||
print(consensus.state.synthesis)
|
||||
```
|
||||
|
||||
### Weighted Participants
|
||||
|
||||
```python
|
||||
state = await run(
|
||||
prompt="Technical decision",
|
||||
participants=[
|
||||
{"id": "expert", "weight": 2.0},
|
||||
{"id": "gpt-4", "weight": 1.0},
|
||||
{"id": "claude", "weight": 1.0},
|
||||
],
|
||||
execute=execute,
|
||||
)
|
||||
```
|
||||
|
||||
## Use Cases
|
||||
|
||||
1. **Technical Decisions** - Get consensus on architecture choices
|
||||
2. **Code Review** - Multiple models review code
|
||||
3. **Content Generation** - Best response from multiple attempts
|
||||
4. **Fact Verification** - Cross-check information across models
|
||||
|
||||
## Reference
|
||||
|
||||
Based on the Metastable consensus protocol: https://github.com/luxfi/consensus
|
||||
@@ -0,0 +1,180 @@
|
||||
---
|
||||
title: Filesystem Tools
|
||||
description: File operations - read, write, edit, search, and navigate
|
||||
---
|
||||
|
||||
The `hanzo-tools-fs` package provides comprehensive filesystem operations.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install hanzo-tools-fs
|
||||
```
|
||||
|
||||
## read
|
||||
|
||||
Read files with line numbers.
|
||||
|
||||
```python
|
||||
# Read entire file
|
||||
read(file_path="/path/to/file.py")
|
||||
|
||||
# Read specific lines
|
||||
read(file_path="/path/to/file.py", offset=100, limit=50) # Lines 100-150
|
||||
```
|
||||
|
||||
## write
|
||||
|
||||
Write or overwrite files.
|
||||
|
||||
```python
|
||||
write(file_path="/path/to/file.py", content="# New content\n")
|
||||
```
|
||||
|
||||
## edit
|
||||
|
||||
Make precise text replacements.
|
||||
|
||||
```python
|
||||
edit(
|
||||
file_path="/path/to/file.py",
|
||||
old_string="def old_function():",
|
||||
new_string="def new_function():"
|
||||
)
|
||||
```
|
||||
|
||||
### Replace All Occurrences
|
||||
|
||||
```python
|
||||
edit(
|
||||
file_path="/path/to/file.py",
|
||||
old_string="foo",
|
||||
new_string="bar",
|
||||
replace_all=True
|
||||
)
|
||||
```
|
||||
|
||||
## multi_edit
|
||||
|
||||
Multiple edits to one file atomically.
|
||||
|
||||
```python
|
||||
multi_edit(
|
||||
file_path="/path/to/file.py",
|
||||
edits=[
|
||||
{"old_string": "foo", "new_string": "bar"},
|
||||
{"old_string": "baz", "new_string": "qux"}
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
## tree
|
||||
|
||||
View directory structure.
|
||||
|
||||
```python
|
||||
# Basic tree
|
||||
tree(path="/project", depth=3)
|
||||
|
||||
# Include filtered directories (node_modules, .git)
|
||||
tree(path="/project", include_filtered=True)
|
||||
```
|
||||
|
||||
## find
|
||||
|
||||
Find files by pattern.
|
||||
|
||||
```python
|
||||
# Find by extension
|
||||
find(pattern="*.py", path="/project")
|
||||
|
||||
# Find by name prefix
|
||||
find(pattern="test_*", type="file")
|
||||
|
||||
# Find by modification time
|
||||
find(pattern="*", modified_after="1 day ago")
|
||||
|
||||
# Find by size
|
||||
find(pattern="*", min_size="1KB", max_size="1MB")
|
||||
```
|
||||
|
||||
## search
|
||||
|
||||
Unified multi-modal search.
|
||||
|
||||
```python
|
||||
# Text search
|
||||
search(pattern="error handling", path="/project")
|
||||
|
||||
# With AST analysis
|
||||
search(
|
||||
pattern="UserService",
|
||||
enable_ast=True,
|
||||
enable_symbol=True,
|
||||
enable_text=True
|
||||
)
|
||||
```
|
||||
|
||||
## ast
|
||||
|
||||
AST-based code structure search.
|
||||
|
||||
```python
|
||||
# Find class definitions
|
||||
ast(pattern="class.*Service", path="/project/src")
|
||||
|
||||
# Find test functions with line numbers
|
||||
ast(pattern="def test_", path="/project/tests", line_number=True)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always read before edit** - Understand the file content first
|
||||
2. **Use edit for precision** - More reliable than write for modifications
|
||||
3. **Use multi_edit for related changes** - Atomic and efficient
|
||||
4. **Use search over find for code** - AST-aware searching is more accurate
|
||||
|
||||
## Examples
|
||||
|
||||
### Safe File Modification
|
||||
|
||||
```python
|
||||
# 1. Read current content
|
||||
content = read(file_path="/config.py")
|
||||
|
||||
# 2. Make precise edit
|
||||
edit(
|
||||
file_path="/config.py",
|
||||
old_string='DEBUG = False',
|
||||
new_string='DEBUG = True'
|
||||
)
|
||||
```
|
||||
|
||||
### Find and Replace Across Codebase
|
||||
|
||||
```python
|
||||
# 1. Find all occurrences
|
||||
results = search(pattern="old_api_call", path="/src")
|
||||
|
||||
# 2. Edit each file
|
||||
for file in results.files:
|
||||
edit(
|
||||
file_path=file,
|
||||
old_string="old_api_call",
|
||||
new_string="new_api_call",
|
||||
replace_all=True
|
||||
)
|
||||
```
|
||||
|
||||
### Project Structure Analysis
|
||||
|
||||
```python
|
||||
# Get directory tree
|
||||
tree(path="/project", depth=2)
|
||||
|
||||
# Find all Python files
|
||||
find(pattern="*.py", path="/project", type="file")
|
||||
|
||||
# Search for specific patterns
|
||||
search(pattern="TODO|FIXME|HACK", path="/project")
|
||||
```
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: Hanzo Tools
|
||||
description: Modular tool packages for AI agents and MCP servers
|
||||
---
|
||||
|
||||
Hanzo Tools is a collection of modular Python packages that provide tools for AI agents. Each package can be installed independently and registered with MCP servers.
|
||||
|
||||
## Installation
|
||||
|
||||
Install all tools:
|
||||
|
||||
```bash
|
||||
pip install hanzo-tools[all]
|
||||
```
|
||||
|
||||
Or install specific packages:
|
||||
|
||||
```bash
|
||||
pip install hanzo-tools-shell # Shell execution (dag, ps, zsh)
|
||||
pip install hanzo-tools-fs # Filesystem operations
|
||||
pip install hanzo-tools-browser # Playwright browser automation
|
||||
pip install hanzo-tools-memory # Memory and knowledge management
|
||||
pip install hanzo-tools-reasoning # Think and critic tools
|
||||
pip install hanzo-tools-lsp # Language Server Protocol
|
||||
pip install hanzo-tools-database # SQL and graph databases
|
||||
```
|
||||
|
||||
## Package Overview
|
||||
|
||||
| Package | Tools | Description |
|
||||
|---------|-------|-------------|
|
||||
| `hanzo-tools-core` | BaseTool, Registry | Core infrastructure |
|
||||
| `hanzo-tools-shell` | dag, ps, zsh, shell | Command execution |
|
||||
| `hanzo-tools-fs` | read, write, edit, tree, find, search | Filesystem operations |
|
||||
| `hanzo-tools-browser` | browser (70+ actions) | Playwright automation |
|
||||
| `hanzo-tools-memory` | memory (unified) | Memory management |
|
||||
| `hanzo-tools-reasoning` | think, critic | Structured reasoning |
|
||||
| `hanzo-tools-lsp` | lsp | Code intelligence |
|
||||
| `hanzo-tools-refactor` | refactor | Code refactoring |
|
||||
| `hanzo-tools-database` | sql_*, graph_* | Database operations |
|
||||
| `hanzo-tools-agent` | agent, iching, review | CLI agent runners |
|
||||
| `hanzo-tools-llm` | llm, consensus | LLM operations |
|
||||
| `hanzo-tools-vector` | vector_index, vector_search | Embeddings |
|
||||
| `hanzo-tools-todo` | todo | Task management |
|
||||
| `hanzo-tools-jupyter` | jupyter | Notebook editing |
|
||||
| `hanzo-tools-editor` | neovim_* | Editor integration |
|
||||
|
||||
## Architecture
|
||||
|
||||
All tools follow a common pattern:
|
||||
|
||||
```python
|
||||
from hanzo_tools.core import BaseTool
|
||||
|
||||
class MyTool(BaseTool):
|
||||
name = "my_tool"
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return "Tool description"
|
||||
|
||||
async def call(self, ctx, **params) -> str:
|
||||
# Implementation
|
||||
return "result"
|
||||
```
|
||||
|
||||
Tools are discovered via entry points:
|
||||
|
||||
```toml
|
||||
[project.entry-points."hanzo.tools"]
|
||||
shell = "hanzo_tools.shell:TOOLS"
|
||||
```
|
||||
|
||||
## Usage with MCP
|
||||
|
||||
Tools automatically register with hanzo-mcp:
|
||||
|
||||
```bash
|
||||
# Install hanzo-mcp with tools
|
||||
pip install hanzo-mcp[tools-all]
|
||||
|
||||
# Run MCP server
|
||||
hanzo-mcp
|
||||
```
|
||||
|
||||
Or use tools directly:
|
||||
|
||||
```python
|
||||
from hanzo_tools.shell import DagTool
|
||||
|
||||
dag = DagTool()
|
||||
result = await dag.call(ctx, commands=["ls", "pwd"])
|
||||
```
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
title: Memory Tools
|
||||
description: Persistent memory and knowledge management for AI agents
|
||||
---
|
||||
|
||||
The `hanzo-tools-memory` package provides unified memory management.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install hanzo-tools-memory
|
||||
```
|
||||
|
||||
## Unified Memory Tool
|
||||
|
||||
All memory operations through a single tool with actions:
|
||||
|
||||
### Recall Memories
|
||||
|
||||
```python
|
||||
memory(action="recall", query="user preferences")
|
||||
memory(action="recall", query="previous decisions", scope="project")
|
||||
```
|
||||
|
||||
### Create Memories
|
||||
|
||||
```python
|
||||
memory(action="create", data={
|
||||
"statements": [
|
||||
"User prefers TypeScript over JavaScript",
|
||||
"Project uses PostgreSQL database"
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Update Memories
|
||||
|
||||
```python
|
||||
memory(action="update", data={
|
||||
"id": "mem_123",
|
||||
"content": "User now prefers Python over TypeScript"
|
||||
})
|
||||
```
|
||||
|
||||
### Delete Memories
|
||||
|
||||
```python
|
||||
memory(action="delete", data={"id": "mem_123"})
|
||||
```
|
||||
|
||||
### Manage Memories
|
||||
|
||||
```python
|
||||
memory(action="manage") # List all memory operations
|
||||
```
|
||||
|
||||
## Knowledge Base Operations
|
||||
|
||||
### Recall Facts
|
||||
|
||||
Query structured knowledge bases:
|
||||
|
||||
```python
|
||||
memory(action="facts", query="API authentication", kb_name="api_docs")
|
||||
```
|
||||
|
||||
### Store Facts
|
||||
|
||||
```python
|
||||
memory(action="store", data={
|
||||
"facts": ["API uses JWT tokens", "Rate limit is 100/hour"],
|
||||
"kb_name": "api_docs"
|
||||
})
|
||||
```
|
||||
|
||||
### Summarize to Memory
|
||||
|
||||
Condense content into memory:
|
||||
|
||||
```python
|
||||
memory(action="summarize", data={
|
||||
"content": "Long document text...",
|
||||
"key_points": True
|
||||
})
|
||||
```
|
||||
|
||||
### Knowledge Base Management
|
||||
|
||||
```python
|
||||
memory(action="kb", data={
|
||||
"action": "list" # List all knowledge bases
|
||||
})
|
||||
|
||||
memory(action="kb", data={
|
||||
"action": "create",
|
||||
"name": "project_docs",
|
||||
"description": "Project documentation"
|
||||
})
|
||||
```
|
||||
|
||||
## Memory Scopes
|
||||
|
||||
Memories can be scoped:
|
||||
|
||||
- **global** - Available across all sessions
|
||||
- **project** - Specific to current project
|
||||
- **session** - Current conversation only
|
||||
|
||||
```python
|
||||
memory(action="recall", query="preferences", scope="global")
|
||||
memory(action="recall", query="architecture", scope="project")
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use semantic queries** - Natural language works better than keywords
|
||||
2. **Scope appropriately** - Project-specific info shouldn't be global
|
||||
3. **Store structured facts** - Use knowledge bases for reference data
|
||||
4. **Summarize long content** - Use summarize for large documents
|
||||
|
||||
## Examples
|
||||
|
||||
### Project Context
|
||||
|
||||
```python
|
||||
# Store project decisions
|
||||
memory(action="create", data={
|
||||
"statements": [
|
||||
"Using React 18 with TypeScript",
|
||||
"State management with Zustand",
|
||||
"API uses REST with OpenAPI spec"
|
||||
],
|
||||
"scope": "project"
|
||||
})
|
||||
|
||||
# Recall later
|
||||
memory(action="recall", query="what framework", scope="project")
|
||||
```
|
||||
|
||||
### API Documentation
|
||||
|
||||
```python
|
||||
# Build knowledge base
|
||||
memory(action="store", data={
|
||||
"kb_name": "api_endpoints",
|
||||
"facts": [
|
||||
"POST /users creates a new user",
|
||||
"GET /users/:id returns user details",
|
||||
"PUT /users/:id updates user",
|
||||
"DELETE /users/:id removes user"
|
||||
]
|
||||
})
|
||||
|
||||
# Query later
|
||||
memory(action="facts", query="how to create user", kb_name="api_endpoints")
|
||||
```
|
||||
|
||||
### Conversation Context
|
||||
|
||||
```python
|
||||
# Remember user preferences
|
||||
memory(action="create", data={
|
||||
"statements": [
|
||||
"User prefers concise responses",
|
||||
"User is experienced with Python",
|
||||
"Working on a FastAPI project"
|
||||
]
|
||||
})
|
||||
|
||||
# Recall for context
|
||||
memory(action="recall", query="user expertise level")
|
||||
```
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "Tools",
|
||||
"pages": [
|
||||
"index",
|
||||
"shell",
|
||||
"filesystem",
|
||||
"browser",
|
||||
"memory",
|
||||
"reasoning",
|
||||
"agent",
|
||||
"consensus"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
---
|
||||
title: Reasoning Tools
|
||||
description: Structured thinking and critical analysis for AI agents
|
||||
---
|
||||
|
||||
The `hanzo-tools-reasoning` package provides tools for structured reasoning.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install hanzo-tools-reasoning
|
||||
```
|
||||
|
||||
## think
|
||||
|
||||
Structured reasoning and analysis. Use this to work through complex problems step by step.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```python
|
||||
think(thought="""
|
||||
Analyzing the authentication flow:
|
||||
1. User submits credentials
|
||||
2. Server validates against database
|
||||
3. JWT token is generated
|
||||
4. Token returned to client
|
||||
5. Client stores in localStorage
|
||||
|
||||
This follows standard OAuth 2.0 patterns.
|
||||
""")
|
||||
```
|
||||
|
||||
### Problem Analysis
|
||||
|
||||
```python
|
||||
think(thought="""
|
||||
Problem: API response times are slow
|
||||
|
||||
Potential causes:
|
||||
- Database queries not optimized
|
||||
- No caching layer
|
||||
- Network latency
|
||||
- Large payload sizes
|
||||
|
||||
Investigation steps:
|
||||
1. Add query timing logs
|
||||
2. Profile database queries
|
||||
3. Check for N+1 queries
|
||||
4. Measure network latency
|
||||
""")
|
||||
```
|
||||
|
||||
### Architecture Decisions
|
||||
|
||||
```python
|
||||
think(thought="""
|
||||
Decision: Choosing between REST and GraphQL
|
||||
|
||||
REST:
|
||||
+ Simple, well-understood
|
||||
+ Excellent caching
|
||||
+ Mature tooling
|
||||
- Over/under fetching
|
||||
- Multiple round trips
|
||||
|
||||
GraphQL:
|
||||
+ Flexible queries
|
||||
+ Single endpoint
|
||||
+ Strong typing
|
||||
- Complexity overhead
|
||||
- Caching challenges
|
||||
|
||||
Recommendation: REST for this project because:
|
||||
- Team has REST experience
|
||||
- Simple CRUD operations
|
||||
- Caching is important
|
||||
""")
|
||||
```
|
||||
|
||||
## critic
|
||||
|
||||
Critical analysis and code review. Use this to evaluate implementations and identify issues.
|
||||
|
||||
### Code Review
|
||||
|
||||
```python
|
||||
critic(analysis="""
|
||||
Code Review: user_service.py
|
||||
|
||||
Issues Found:
|
||||
- No error handling for database failures (line 45)
|
||||
- SQL injection vulnerability in query construction (line 67)
|
||||
- Missing input validation for email field (line 23)
|
||||
- No unit tests for edge cases
|
||||
|
||||
Security Concerns:
|
||||
- Password stored in plain text (line 89)
|
||||
- No rate limiting on authentication endpoint
|
||||
|
||||
Recommendations:
|
||||
1. Add try/catch for database operations
|
||||
2. Use parameterized queries
|
||||
3. Add email validation with pydantic
|
||||
4. Hash passwords with bcrypt
|
||||
5. Implement rate limiting middleware
|
||||
""")
|
||||
```
|
||||
|
||||
### Architecture Review
|
||||
|
||||
```python
|
||||
critic(analysis="""
|
||||
Architecture Review: Microservices Design
|
||||
|
||||
Strengths:
|
||||
- Clear service boundaries
|
||||
- Independent deployability
|
||||
- Technology flexibility
|
||||
|
||||
Weaknesses:
|
||||
- No service discovery mechanism
|
||||
- Missing circuit breaker patterns
|
||||
- Synchronous inter-service calls create coupling
|
||||
- No distributed tracing
|
||||
|
||||
Recommendations:
|
||||
1. Implement service mesh (Istio/Linkerd)
|
||||
2. Add circuit breakers (resilience4j)
|
||||
3. Use async messaging for non-critical paths
|
||||
4. Deploy distributed tracing (Jaeger)
|
||||
""")
|
||||
```
|
||||
|
||||
### Implementation Critique
|
||||
|
||||
```python
|
||||
critic(analysis="""
|
||||
Implementation Review: Rate Limiter
|
||||
|
||||
Current Implementation:
|
||||
- Uses in-memory counter
|
||||
- Fixed window algorithm
|
||||
- No distributed support
|
||||
|
||||
Problems:
|
||||
1. Lost on restart
|
||||
2. Window boundary issues
|
||||
3. Single server only
|
||||
|
||||
Better Approach:
|
||||
- Use Redis for shared state
|
||||
- Sliding window algorithm
|
||||
- Token bucket for smoother limiting
|
||||
|
||||
Code suggestion:
|
||||
```python
|
||||
from redis import Redis
|
||||
from time import time
|
||||
|
||||
class SlidingWindowRateLimiter:
|
||||
def __init__(self, redis: Redis, limit: int, window: int):
|
||||
self.redis = redis
|
||||
self.limit = limit
|
||||
self.window = window
|
||||
|
||||
def is_allowed(self, key: str) -> bool:
|
||||
now = time()
|
||||
pipe = self.redis.pipeline()
|
||||
pipe.zremrangebyscore(key, 0, now - self.window)
|
||||
pipe.zadd(key, {str(now): now})
|
||||
pipe.zcard(key)
|
||||
pipe.expire(key, self.window)
|
||||
_, _, count, _ = pipe.execute()
|
||||
return count <= self.limit
|
||||
```
|
||||
""")
|
||||
```
|
||||
|
||||
## When to Use
|
||||
|
||||
### think
|
||||
|
||||
- Working through complex problems
|
||||
- Making architectural decisions
|
||||
- Planning implementation steps
|
||||
- Analyzing requirements
|
||||
|
||||
### critic
|
||||
|
||||
- Reviewing code quality
|
||||
- Security analysis
|
||||
- Architecture evaluation
|
||||
- Identifying improvements
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Be specific** - Detailed analysis yields better insights
|
||||
2. **Structure your thoughts** - Use numbered lists and categories
|
||||
3. **Include recommendations** - Always suggest improvements
|
||||
4. **Reference specifics** - Line numbers, file names, concrete examples
|
||||
@@ -0,0 +1,151 @@
|
||||
---
|
||||
title: Shell Tools
|
||||
description: Command execution with DAG support and auto-backgrounding
|
||||
---
|
||||
|
||||
The `hanzo-tools-shell` package provides command execution tools with DAG (Directed Acyclic Graph) support for parallel execution.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install hanzo-tools-shell
|
||||
```
|
||||
|
||||
## dag
|
||||
|
||||
Execute commands in serial, parallel, or DAG patterns.
|
||||
|
||||
### Serial Execution
|
||||
|
||||
```python
|
||||
dag(commands=["ls", "pwd", "git status"])
|
||||
```
|
||||
|
||||
### Parallel Execution
|
||||
|
||||
```python
|
||||
dag(commands=["npm install", "cargo build"], parallel=True)
|
||||
```
|
||||
|
||||
### Mixed DAG
|
||||
|
||||
```python
|
||||
dag(commands=[
|
||||
"mkdir -p dist",
|
||||
{"parallel": ["cp a.txt dist/", "cp b.txt dist/"]},
|
||||
"zip -r out.zip dist/"
|
||||
])
|
||||
```
|
||||
|
||||
### Named Steps with Dependencies
|
||||
|
||||
```python
|
||||
dag(commands=[
|
||||
{"id": "build", "run": "make build"},
|
||||
{"id": "test", "run": "make test", "after": ["build"]},
|
||||
])
|
||||
```
|
||||
|
||||
### Tool Invocations
|
||||
|
||||
Execute other tools within the DAG:
|
||||
|
||||
```python
|
||||
dag(commands=[
|
||||
{"tool": "search", "input": {"pattern": "TODO"}},
|
||||
{"tool": "tree", "input": {"path": ".", "depth": 2}},
|
||||
], parallel=True)
|
||||
```
|
||||
|
||||
## ps
|
||||
|
||||
Manage background processes.
|
||||
|
||||
### List Processes
|
||||
|
||||
```python
|
||||
ps() # List all background processes
|
||||
```
|
||||
|
||||
### Get Process Info
|
||||
|
||||
```python
|
||||
ps(id="abc123") # Get specific process info
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```python
|
||||
ps(logs="abc123", n=50) # Last 50 lines of output
|
||||
```
|
||||
|
||||
### Kill Process
|
||||
|
||||
```python
|
||||
ps(kill="abc123") # SIGTERM
|
||||
ps(kill="abc123", sig=9) # SIGKILL
|
||||
```
|
||||
|
||||
## zsh / shell
|
||||
|
||||
Direct shell execution.
|
||||
|
||||
### zsh
|
||||
|
||||
```python
|
||||
zsh(command="echo $ZSH_VERSION")
|
||||
zsh(command="source ~/.zshrc && mycmd")
|
||||
```
|
||||
|
||||
### shell
|
||||
|
||||
Smart shell that prefers zsh:
|
||||
|
||||
```python
|
||||
shell(command="ls -la")
|
||||
shell(command="npm run build", cwd="/project", timeout=300)
|
||||
```
|
||||
|
||||
## Auto-Backgrounding
|
||||
|
||||
Commands automatically background after 60 seconds:
|
||||
|
||||
1. Command starts executing
|
||||
2. Waits for completion with timeout
|
||||
3. If timeout: registers with ProcessManager, continues in background
|
||||
4. Monitor with `ps(logs="id")` or kill with `ps(kill="id")`
|
||||
|
||||
Configure timeout:
|
||||
|
||||
```python
|
||||
dag(commands=["long-running-task"], timeout=120) # 2 minutes
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use dag for multiple commands** - Better than chaining with `&&`
|
||||
2. **Parallel when possible** - Independent tasks should run in parallel
|
||||
3. **Named steps for complex flows** - Makes dependencies explicit
|
||||
4. **Monitor long tasks** - Use `ps` to check status
|
||||
|
||||
## Examples
|
||||
|
||||
### Build Pipeline
|
||||
|
||||
```python
|
||||
dag(commands=[
|
||||
{"id": "deps", "run": "npm install"},
|
||||
{"id": "lint", "run": "npm run lint", "after": ["deps"]},
|
||||
{"id": "test", "run": "npm test", "after": ["deps"]},
|
||||
{"id": "build", "run": "npm run build", "after": ["lint", "test"]},
|
||||
])
|
||||
```
|
||||
|
||||
### Project Analysis
|
||||
|
||||
```python
|
||||
dag(commands=[
|
||||
{"tool": "tree", "input": {"path": ".", "depth": 3}},
|
||||
{"tool": "search", "input": {"pattern": "TODO|FIXME"}},
|
||||
], parallel=True)
|
||||
```
|
||||
@@ -0,0 +1,6 @@
|
||||
import { docs } from '@/.source/server';
|
||||
import { loader } from 'fumadocs-core/source';
|
||||
|
||||
export const source = loader(docs.toFumadocsSource(), {
|
||||
baseUrl: '/docs',
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import defaultMdxComponents from 'fumadocs-ui/mdx';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function useMDXComponents(components: any): any {
|
||||
return {
|
||||
...defaultMdxComponents,
|
||||
...components,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { createMDX } from 'fumadocs-mdx/next';
|
||||
|
||||
const withMDX = createMDX();
|
||||
|
||||
const config = {
|
||||
reactStrictMode: true,
|
||||
output: 'export' as const,
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
basePath: process.env.NODE_ENV === 'production' ? '/python-sdk' : '',
|
||||
};
|
||||
|
||||
export default withMDX(config);
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "@hanzo/python-sdk-docs",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint .",
|
||||
"postinstall": "fumadocs-mdx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hanzo/mdx": "^14.2.5",
|
||||
"@hanzo/ui": "^5.0.0",
|
||||
"fumadocs-core": "^16.2.6",
|
||||
"fumadocs-mdx": "^14.2.3",
|
||||
"fumadocs-ui": "^16.2.6",
|
||||
"lucide-react": "^0.468.0",
|
||||
"next": "^16.1.1",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"shiki": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-dom": "^19.0.0",
|
||||
"postcss": "^8.5.0",
|
||||
"tailwindcss": "^4.1.0",
|
||||
"typescript": "^5.7.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
};
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 366 B |
@@ -0,0 +1,10 @@
|
||||
<svg viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="64" height="64" rx="8" fill="#000000"/>
|
||||
<g transform="translate(8, 8) scale(0.716)">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 535 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
@@ -0,0 +1,9 @@
|
||||
<svg viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 576 B |
@@ -0,0 +1,9 @@
|
||||
<svg viewBox="0 0 67 67" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M22.21 67V44.6369H0V67H22.21Z" fill="#ffffff"/>
|
||||
<path d="M0 44.6369L22.21 46.8285V44.6369H0Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7038 22.3184H22.2534L0.0878906 44.6367H44.4634L66.7038 22.3184Z" fill="#ffffff"/>
|
||||
<path d="M22.21 0H0V22.3184H22.21V0Z" fill="#ffffff"/>
|
||||
<path d="M66.7198 0H44.5098V22.3184H66.7198V0Z" fill="#ffffff"/>
|
||||
<path d="M66.6753 22.3185L44.5098 20.0822V22.3185H66.6753Z" fill="#DDDDDD"/>
|
||||
<path d="M66.7198 67V44.6369H44.5098V67H66.7198Z" fill="#ffffff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 576 B |
@@ -0,0 +1,18 @@
|
||||
import { defineDocs, defineConfig } from 'fumadocs-mdx/config';
|
||||
|
||||
export const docs = defineDocs({
|
||||
docs: {
|
||||
async: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default defineConfig({
|
||||
mdxOptions: {
|
||||
rehypeCodeOptions: {
|
||||
themes: {
|
||||
light: 'github-light',
|
||||
dark: 'github-dark',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./*"
|
||||
]
|
||||
},
|
||||
"target": "ES2017"
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||