同步完整源码 - 2026-05-24
|
|
@ -0,0 +1,117 @@
|
|||
---
|
||||
name: honcho-cli
|
||||
description: Inspect and debug Honcho workspaces via the `honcho` CLI. Use when investigating peer representations, memory state, session context, queue status, or dialectic quality — any task that requires introspection of a Honcho deployment.
|
||||
allowed-tools: Bash(honcho:*), Bash(jq:*), Read, Grep
|
||||
---
|
||||
|
||||
# Honcho CLI
|
||||
|
||||
`honcho` wraps the Honcho Python SDK with agent-friendly defaults: JSON output, structured errors, input validation. Use it to inspect workspace state, debug peer memory, and diagnose the dialectic.
|
||||
|
||||
## Output & config
|
||||
|
||||
- **TTY**: human-readable tables (default when interactive)
|
||||
- **Piped / `--json`**: JSON — collection commands emit arrays, single-resource commands emit objects
|
||||
- **Exit codes**: `0` success · `1` client error (bad input, not found) · `2` server error · `3` auth error
|
||||
- **Config**: `~/.honcho/config.json` (shared with other Honcho tools). The CLI owns `apiKey` and `environmentUrl` at the top level; run `honcho init` to confirm or set them. Per-command scope (workspace / peer / session) is via `-w` / `-p` / `-s` flags or `HONCHO_*` env vars.
|
||||
|
||||
## Command groups
|
||||
|
||||
- `honcho config` — CLI configuration
|
||||
- `honcho workspace` — inspect, delete, search
|
||||
- `honcho peer` — inspect, card, chat, search
|
||||
- `honcho session` — inspect, messages, context, summaries
|
||||
- `honcho message` — list and get
|
||||
- `honcho conclusion` — list, search, create, delete
|
||||
|
||||
## Rules
|
||||
|
||||
- Always pass `--json` when processing output programmatically.
|
||||
- Run `honcho peer inspect` before `honcho peer chat` to understand context.
|
||||
- Use `honcho session context` to see exactly what an agent receives.
|
||||
- Never run `honcho workspace delete` without `honcho workspace inspect` first.
|
||||
- Check queue status when derivation seems stalled.
|
||||
- Compare peer card with conclusions to understand memory state.
|
||||
|
||||
## Inspection tour
|
||||
|
||||
When orienting to a Honcho deployment, walk outside-in:
|
||||
|
||||
### 1. Understand the workspace
|
||||
|
||||
```bash
|
||||
honcho workspace inspect --json
|
||||
```
|
||||
|
||||
### 2. Find the peer
|
||||
|
||||
```bash
|
||||
honcho peer list --json
|
||||
honcho peer inspect <peer_id> --json
|
||||
```
|
||||
|
||||
### 3. Check peer's memory
|
||||
|
||||
```bash
|
||||
honcho peer card <peer_id> --json
|
||||
honcho conclusion list --observer <peer_id> --json
|
||||
honcho conclusion search "topic" --observer <peer_id> --json
|
||||
```
|
||||
|
||||
### 4. Debug a session
|
||||
|
||||
```bash
|
||||
honcho session inspect <session_id> --json
|
||||
honcho message list <session_id> --last 20 --json
|
||||
honcho session context <session_id> --json
|
||||
honcho session summaries <session_id> --json
|
||||
```
|
||||
|
||||
### 5. Search across workspace
|
||||
|
||||
```bash
|
||||
honcho workspace search "query" --json
|
||||
honcho peer search <peer_id> "query" --json
|
||||
```
|
||||
|
||||
## Debugging playbook
|
||||
|
||||
### Peer not learning?
|
||||
|
||||
```bash
|
||||
# Is observation enabled?
|
||||
honcho peer inspect <peer_id> --json | jq '.configuration'
|
||||
|
||||
# Is the deriver queue processing messages?
|
||||
honcho workspace queue-status --json
|
||||
|
||||
# What conclusions exist?
|
||||
honcho conclusion list --observer <peer_id> --json
|
||||
honcho conclusion search "expected topic" --observer <peer_id> --json
|
||||
```
|
||||
|
||||
### Session context looks wrong?
|
||||
|
||||
```bash
|
||||
# Raw context an agent would receive
|
||||
honcho session context <session_id> --json
|
||||
|
||||
# Summaries feeding the context
|
||||
honcho session summaries <session_id> --json
|
||||
|
||||
# Recent message history
|
||||
honcho message list <session_id> --last 50 --json
|
||||
```
|
||||
|
||||
### Dialectic giving bad answers?
|
||||
|
||||
```bash
|
||||
# What the peer card says
|
||||
honcho peer card <peer_id> --json
|
||||
|
||||
# Conclusions on the specific topic
|
||||
honcho conclusion search "topic" --observer <peer_id> --json
|
||||
|
||||
# Exercise the dialectic directly
|
||||
honcho peer chat <peer_id> "what do you know about X?" --json
|
||||
```
|
||||
|
|
@ -0,0 +1,554 @@
|
|||
---
|
||||
name: honcho-integration
|
||||
description: Integrate Honcho memory and social cognition into existing Python or TypeScript codebases. Use when adding Honcho SDK, setting up peers, configuring sessions, implementing the dialectic chat endpoint for AI agents, or wiring Honcho into bot frameworks (nanobot, openclaw, picoclaw, etc).
|
||||
allowed-tools: Read, Glob, Grep, Bash(uv:*), Bash(bun:*), Bash(npm:*), Edit, Write, WebFetch, AskUserQuestion
|
||||
---
|
||||
|
||||
# Honcho Integration Guide
|
||||
|
||||
## What is Honcho
|
||||
|
||||
Honcho is an open source memory library for building stateful agents. It works with any model, framework, or architecture. You send Honcho the messages from your conversations, and custom reasoning models process them in the background — extracting premises, drawing conclusions, and building rich representations of each participant over time. Your agent can then query those representations on-demand ("What does this user care about?", "How technical is this person?") and get grounded, reasoned answers.
|
||||
|
||||
The key mental model: **Peers** are any participant — human or AI. Both are represented the same way. Observation settings (`observe_me`, `observe_others`) control which peers Honcho reasons about. Typically you want Honcho to model your users (`observe_me=True`) but not your AI assistant (`observe_me=False`). **Sessions** scope conversations between peers. **Messages** are the raw data you feed in — Honcho reasons about them asynchronously and stores the results as the peer's **representation**. No messages means no reasoning means no memory.
|
||||
|
||||
Your agent accesses this memory through `peer.chat(query)` (ask a natural language question, get a reasoned answer), `session.context()` (get formatted conversation history + representations), or both.
|
||||
|
||||
## Integration Workflow
|
||||
|
||||
Follow these phases in order:
|
||||
|
||||
### Phase 1: Codebase Exploration
|
||||
|
||||
Before asking the user anything, explore the codebase to understand:
|
||||
|
||||
1. **Language & Framework**: Is this Python or TypeScript? What frameworks are used (FastAPI, Express, Next.js, etc.)?
|
||||
2. **Existing AI/LLM code**: Search for existing LLM integrations (OpenAI, Anthropic, LangChain, etc.)
|
||||
3. **Entity structure**: Identify users, agents, bots, or other entities that interact
|
||||
4. **Session/conversation handling**: How does the app currently manage conversations?
|
||||
5. **Message flow**: Where are messages sent/received? What's the request/response cycle?
|
||||
|
||||
Use Glob and Grep to find:
|
||||
|
||||
- `**/*.py` or `**/*.ts` files with "openai", "anthropic", "llm", "chat", "message"
|
||||
- User/session models or types
|
||||
- API routes handling chat or conversation endpoints
|
||||
|
||||
> **Bot framework detected?** If the codebase is built around an agent loop, tool registry, session manager, and message bus (e.g., nanobot, openclaw, picoclaw), read `{baseDir}/references/bot-frameworks.md` for framework-specific integration guidance and check `{baseDir}/references/bot-frameworks/<framework>/` for concrete reference implementations.
|
||||
|
||||
### Phase 2: Interview (REQUIRED)
|
||||
|
||||
After exploring the codebase, use the **AskUserQuestion** tool to clarify integration requirements. Ask these questions (adapt based on what you learned in Phase 1):
|
||||
|
||||
#### Question Set 1 - Entities & Peers
|
||||
|
||||
Ask about which entities should be Honcho peers:
|
||||
|
||||
- header: "Peers"
|
||||
- question: "Which entities should Honcho track and build representations for?"
|
||||
- options based on what you found (e.g., "End users only", "Users + AI assistant", "Users + multiple AI agents", "All participants including third-party services")
|
||||
- Include a follow-up if they have multiple AI agents: should any AI peers be observed?
|
||||
|
||||
#### Question Set 2 - Integration Pattern
|
||||
|
||||
Ask how they want to use Honcho context:
|
||||
|
||||
- header: "Pattern"
|
||||
- question: "How should your AI access Honcho's user context?"
|
||||
- options:
|
||||
- "Tool call (Recommended)" - "Agent queries Honcho on-demand via function calling"
|
||||
- "Pre-fetch" - "Fetch user context before each LLM call with predefined queries"
|
||||
- "context()" - "Include conversation history and representations in prompt"
|
||||
- "Multiple patterns" - "Combine approaches for different use cases"
|
||||
|
||||
#### Question Set 3 - Session Structure
|
||||
|
||||
Ask about conversation structure:
|
||||
|
||||
- header: "Sessions"
|
||||
- question: "How should conversations map to Honcho sessions?"
|
||||
- options based on their app (e.g., "One session per chat thread", "One session per user", "Multiple users per session (group chat)", "Custom session logic")
|
||||
|
||||
#### Question Set 4 - Specific Queries (if using pre-fetch pattern)
|
||||
|
||||
If they chose pre-fetch, ask what context matters:
|
||||
|
||||
- header: "Context"
|
||||
- question: "What user context should be fetched for the AI?"
|
||||
- multiSelect: true
|
||||
- options: "Communication style", "Expertise level", "Goals/priorities", "Preferences", "Recent activity summary", "Custom queries"
|
||||
|
||||
### Phase 3: Implementation
|
||||
|
||||
Based on interview responses, implement the integration:
|
||||
|
||||
1. Install the SDK
|
||||
2. Create Honcho client initialization
|
||||
3. Set up peer creation for identified entities
|
||||
4. Implement the chosen integration pattern(s)
|
||||
5. Add message storage after exchanges
|
||||
6. Update any existing conversation handlers
|
||||
|
||||
### Phase 4: Verification
|
||||
|
||||
- If the Honcho CLI is available, run `honcho doctor` to confirm connectivity before testing the integration code
|
||||
- Use `honcho peer list` and `honcho peer chat` to verify peers exist and the dialectic endpoint works independently of the integration
|
||||
- Ensure all message exchanges are stored to Honcho
|
||||
- Verify AI peers have `observe_me=False` (unless user specifically wants AI observation)
|
||||
- Check that the workspace ID is consistent across the codebase
|
||||
- Confirm environment variable for API key is documented
|
||||
|
||||
---
|
||||
|
||||
## Before You Start
|
||||
|
||||
1. **Check the latest SDK versions** at <https://honcho.dev/docs/changelog/introduction>
|
||||
- Python SDK: `honcho-ai`
|
||||
- TypeScript SDK: `@honcho-ai/sdk`
|
||||
|
||||
2. **Get an API key** ask the user to get a Honcho API key from <https://app.honcho.dev> and add it to the environment.
|
||||
|
||||
3. **Verify with the CLI** (optional but recommended). If the user has the Honcho CLI installed (`pip install honcho-cli`), they can validate their setup before writing any integration code:
|
||||
|
||||
```bash
|
||||
honcho init # persist API key + URL to ~/.honcho/config.json
|
||||
honcho doctor # verify connectivity, config, workspace health
|
||||
honcho peer chat # test the dialectic endpoint interactively
|
||||
```
|
||||
|
||||
This is the fastest way to confirm the API key and URL are correct before debugging SDK code.
|
||||
|
||||
## Installation
|
||||
|
||||
### Python (use uv)
|
||||
|
||||
```bash
|
||||
uv add honcho-ai
|
||||
```
|
||||
|
||||
### TypeScript (use bun)
|
||||
|
||||
```bash
|
||||
bun add @honcho-ai/sdk
|
||||
```
|
||||
|
||||
## Sync vs Async
|
||||
|
||||
**TypeScript** — The SDK is async by default. All methods return promises. No separate sync API.
|
||||
|
||||
**Python** — The SDK provides both sync and async interfaces:
|
||||
|
||||
- **Sync** (default): `from honcho import Honcho` — use in sync frameworks (Flask, Django, CLI scripts)
|
||||
- **Async**: `from honcho import Honcho` with `.aio` namespace — use in async frameworks (FastAPI, Starlette, async workers)
|
||||
|
||||
```python
|
||||
# Sync usage (Flask, Django, scripts)
|
||||
from honcho import Honcho
|
||||
honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"])
|
||||
peer = honcho.peer("user-123")
|
||||
response = peer.chat("What does this user prefer?")
|
||||
|
||||
# Async usage (FastAPI, Starlette)
|
||||
from honcho import Honcho
|
||||
honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"])
|
||||
peer = await honcho.aio.peer("user-123")
|
||||
response = await peer.aio.chat("What does this user prefer?")
|
||||
```
|
||||
|
||||
Match the client to the framework — check whether the codebase uses `async def` handlers or sync `def` handlers and choose accordingly. The rest of this skill shows sync Python examples; swap to `.aio` equivalents for async codebases.
|
||||
|
||||
## Core Integration Patterns
|
||||
|
||||
### 1. Initialize with a Single Workspace
|
||||
|
||||
Use ONE workspace for your entire application. The workspace name should reflect your app/product.
|
||||
|
||||
**Python:**
|
||||
|
||||
```python
|
||||
from honcho import Honcho
|
||||
import os
|
||||
|
||||
# Sync client (Flask, Django, scripts)
|
||||
honcho = Honcho(
|
||||
workspace_id="your-app-name",
|
||||
api_key=os.environ["HONCHO_API_KEY"],
|
||||
environment="production"
|
||||
)
|
||||
|
||||
# Async client (FastAPI, Starlette) — use honcho.aio for all operations
|
||||
# honcho.aio.peer(), honcho.aio.session(), etc.
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
|
||||
```typescript
|
||||
import { Honcho } from '@honcho-ai/sdk';
|
||||
|
||||
// All methods are async by default
|
||||
const honcho = new Honcho({
|
||||
workspaceId: "your-app-name",
|
||||
apiKey: process.env.HONCHO_API_KEY,
|
||||
environment: "production"
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Create Peers for ALL Entities
|
||||
|
||||
Create peers for **every entity** in your business logic - users AND AI assistants.
|
||||
|
||||
**Python:**
|
||||
|
||||
```python
|
||||
from honcho.api_types import PeerConfig
|
||||
|
||||
# Human users
|
||||
user = honcho.peer("user-123")
|
||||
|
||||
# AI assistants - set observe_me=False so Honcho doesn't model the AI
|
||||
assistant = honcho.peer("assistant", configuration=PeerConfig(observe_me=False))
|
||||
support_bot = honcho.peer("support-bot", configuration=PeerConfig(observe_me=False))
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
|
||||
```typescript
|
||||
// Human users
|
||||
const user = await honcho.peer("user-123");
|
||||
|
||||
// AI assistants - set observeMe=false so Honcho doesn't model the AI
|
||||
const assistant = await honcho.peer("assistant", { configuration: { observeMe: false } });
|
||||
const supportBot = await honcho.peer("support-bot", { configuration: { observeMe: false } });
|
||||
```
|
||||
|
||||
### 3. Multi-Peer Sessions
|
||||
|
||||
Sessions can have multiple participants. Configure observation settings per-peer.
|
||||
|
||||
**Python:**
|
||||
|
||||
```python
|
||||
from honcho.api_types import SessionPeerConfig
|
||||
|
||||
session = honcho.session("conversation-123")
|
||||
|
||||
# User is observed (Honcho builds a model of them)
|
||||
user_config = SessionPeerConfig(observe_me=True, observe_others=True)
|
||||
|
||||
# AI is NOT observed (no model built of the AI)
|
||||
ai_config = SessionPeerConfig(observe_me=False, observe_others=True)
|
||||
|
||||
session.add_peers([
|
||||
(user, user_config),
|
||||
(assistant, ai_config)
|
||||
])
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
|
||||
```typescript
|
||||
const session = await honcho.session("conversation-123");
|
||||
|
||||
await session.addPeers([
|
||||
[user, { observeMe: true, observeOthers: true }],
|
||||
[assistant, { observeMe: false, observeOthers: true }]
|
||||
]);
|
||||
```
|
||||
|
||||
### 4. Add Messages to Sessions
|
||||
|
||||
**Python:**
|
||||
|
||||
```python
|
||||
session.add_messages([
|
||||
user.message("I'm having trouble with my account"),
|
||||
assistant.message("I'd be happy to help. What seems to be the issue?"),
|
||||
user.message("I can't reset my password")
|
||||
])
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
|
||||
```typescript
|
||||
await session.addMessages([
|
||||
user.message("I'm having trouble with my account"),
|
||||
assistant.message("I'd be happy to help. What seems to be the issue?"),
|
||||
user.message("I can't reset my password")
|
||||
]);
|
||||
```
|
||||
|
||||
## Using Honcho for AI Agents
|
||||
|
||||
### Pattern A: Dialectic Chat as a Tool Call (Recommended for Agents)
|
||||
|
||||
Make Honcho's chat endpoint available as a **tool** for your AI agent. This lets the agent query user context on-demand.
|
||||
|
||||
**Python (OpenAI function calling):**
|
||||
|
||||
```python
|
||||
import openai
|
||||
from honcho import Honcho
|
||||
|
||||
honcho = Honcho(workspace_id="my-app", api_key=os.environ["HONCHO_API_KEY"])
|
||||
|
||||
# Define the tool for your agent
|
||||
honcho_tool = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "query_user_context",
|
||||
"description": "Query Honcho to retrieve relevant context about the user based on their history and preferences. Use this when you need to understand the user's background, preferences, past interactions, or goals.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "A natural language question about the user, e.g. 'What are this user's main goals?' or 'What communication style does this user prefer?'"
|
||||
}
|
||||
},
|
||||
"required": ["query"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def handle_honcho_tool_call(user_id: str, query: str) -> str:
|
||||
"""Execute the Honcho chat tool call."""
|
||||
peer = honcho.peer(user_id)
|
||||
return peer.chat(query)
|
||||
|
||||
# Use in your agent loop
|
||||
def run_agent(user_id: str, user_message: str):
|
||||
messages = [{"role": "user", "content": user_message}]
|
||||
|
||||
response = openai.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=messages,
|
||||
tools=[honcho_tool]
|
||||
)
|
||||
|
||||
# Handle tool calls
|
||||
if response.choices[0].message.tool_calls:
|
||||
for tool_call in response.choices[0].message.tool_calls:
|
||||
if tool_call.function.name == "query_user_context":
|
||||
import json
|
||||
args = json.loads(tool_call.function.arguments)
|
||||
result = handle_honcho_tool_call(user_id, args["query"])
|
||||
# Continue conversation with tool result...
|
||||
```
|
||||
|
||||
**TypeScript (OpenAI function calling):**
|
||||
|
||||
```typescript
|
||||
import OpenAI from 'openai';
|
||||
import { Honcho } from '@honcho-ai/sdk';
|
||||
|
||||
const honcho = new Honcho({
|
||||
workspaceId: "my-app",
|
||||
apiKey: process.env.HONCHO_API_KEY
|
||||
});
|
||||
|
||||
const honchoTool: OpenAI.ChatCompletionTool = {
|
||||
type: "function",
|
||||
function: {
|
||||
name: "query_user_context",
|
||||
description: "Query Honcho to retrieve relevant context about the user based on their history and preferences.",
|
||||
parameters: {
|
||||
type: "object",
|
||||
properties: {
|
||||
query: {
|
||||
type: "string",
|
||||
description: "A natural language question about the user"
|
||||
}
|
||||
},
|
||||
required: ["query"]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
async function handleHonchoToolCall(userId: string, query: string): Promise<string> {
|
||||
const peer = await honcho.peer(userId);
|
||||
return await peer.chat(query);
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern B: Pre-fetch Context with Targeted Queries
|
||||
|
||||
For simpler integrations, fetch user context before the LLM call using pre-defined queries.
|
||||
|
||||
**Python:**
|
||||
|
||||
```python
|
||||
def get_user_context_for_prompt(user_id: str) -> dict:
|
||||
"""Fetch key user attributes via targeted Honcho queries."""
|
||||
peer = honcho.peer(user_id)
|
||||
|
||||
return {
|
||||
"communication_style": peer.chat("What communication style does this user prefer? Be concise."),
|
||||
"expertise_level": peer.chat("What is this user's technical expertise level? Be concise."),
|
||||
"current_goals": peer.chat("What are this user's current goals or priorities? Be concise."),
|
||||
"preferences": peer.chat("What key preferences should I know about this user? Be concise.")
|
||||
}
|
||||
|
||||
def build_system_prompt(user_context: dict) -> str:
|
||||
return f"""You are a helpful assistant. Here's what you know about this user:
|
||||
|
||||
Communication style: {user_context['communication_style']}
|
||||
Expertise level: {user_context['expertise_level']}
|
||||
Current goals: {user_context['current_goals']}
|
||||
Key preferences: {user_context['preferences']}
|
||||
|
||||
Tailor your responses accordingly."""
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
|
||||
```typescript
|
||||
async function getUserContextForPrompt(userId: string): Promise<Record<string, string>> {
|
||||
const peer = await honcho.peer(userId);
|
||||
|
||||
const [style, expertise, goals, preferences] = await Promise.all([
|
||||
peer.chat("What communication style does this user prefer? Be concise."),
|
||||
peer.chat("What is this user's technical expertise level? Be concise."),
|
||||
peer.chat("What are this user's current goals or priorities? Be concise."),
|
||||
peer.chat("What key preferences should I know about this user? Be concise.")
|
||||
]);
|
||||
|
||||
return {
|
||||
communicationStyle: style,
|
||||
expertiseLevel: expertise,
|
||||
currentGoals: goals,
|
||||
preferences: preferences
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern C: Get Context for LLM Integration
|
||||
|
||||
Use `context()` for conversation history with built-in LLM formatting.
|
||||
|
||||
**Python:**
|
||||
|
||||
```python
|
||||
import openai
|
||||
|
||||
session = honcho.session("conversation-123")
|
||||
user = honcho.peer("user-123")
|
||||
assistant = honcho.peer("assistant", configuration=PeerConfig(observe_me=False))
|
||||
|
||||
# Get context formatted for your LLM
|
||||
context = session.context(
|
||||
tokens=2000,
|
||||
peer_target=user.id, # Include representation of this user
|
||||
summary=True # Include conversation summaries
|
||||
)
|
||||
|
||||
# Convert to OpenAI format
|
||||
messages = context.to_openai(assistant=assistant)
|
||||
|
||||
# Or Anthropic format
|
||||
# messages = context.to_anthropic(assistant=assistant)
|
||||
|
||||
# Add the new user message
|
||||
messages.append({"role": "user", "content": "What should I focus on today?"})
|
||||
|
||||
response = openai.chat.completions.create(
|
||||
model="gpt-4",
|
||||
messages=messages
|
||||
)
|
||||
|
||||
# Store the exchange
|
||||
session.add_messages([
|
||||
user.message("What should I focus on today?"),
|
||||
assistant.message(response.choices[0].message.content)
|
||||
])
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
|
||||
```typescript
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const session = await honcho.session("conversation-123");
|
||||
const user = await honcho.peer("user-123");
|
||||
const assistant = await honcho.peer("assistant", { configuration: { observeMe: false } });
|
||||
|
||||
// Get context formatted for your LLM
|
||||
const context = await session.context({
|
||||
tokens: 2000,
|
||||
peerTarget: user.id, // Include representation of this user
|
||||
summary: true // Include conversation summaries
|
||||
});
|
||||
|
||||
// Convert to OpenAI format
|
||||
const messages = context.toOpenAI(assistant);
|
||||
|
||||
// Or Anthropic format
|
||||
// const messages = context.toAnthropic(assistant);
|
||||
|
||||
// Add the new user message
|
||||
messages.push({ role: "user", content: "What should I focus on today?" });
|
||||
|
||||
const openai = new OpenAI();
|
||||
const response = await openai.chat.completions.create({
|
||||
model: "gpt-4",
|
||||
messages
|
||||
});
|
||||
|
||||
// Store the exchange
|
||||
await session.addMessages([
|
||||
user.message("What should I focus on today?"),
|
||||
assistant.message(response.choices[0].message.content!)
|
||||
]);
|
||||
```
|
||||
|
||||
## Streaming Responses
|
||||
|
||||
**Python:**
|
||||
|
||||
```python
|
||||
stream = peer.chat_stream("What do we know about this user?")
|
||||
|
||||
for chunk in stream:
|
||||
print(chunk, end="", flush=True)
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
|
||||
```typescript
|
||||
const stream = await peer.chatStream("What do we know about this user?");
|
||||
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk);
|
||||
}
|
||||
```
|
||||
|
||||
## Integration Checklist
|
||||
|
||||
When integrating Honcho into an existing codebase:
|
||||
|
||||
- [ ] Install SDK with `uv add honcho-ai` (Python) or `bun add @honcho-ai/sdk` (TypeScript)
|
||||
- [ ] Set up `HONCHO_API_KEY` environment variable
|
||||
- [ ] Initialize Honcho client with a single workspace ID
|
||||
- [ ] Create peers for all entities (users AND AI assistants)
|
||||
- [ ] Set `observe_me=False` for AI peers
|
||||
- [ ] Configure sessions with appropriate peer observation settings
|
||||
- [ ] Choose integration pattern:
|
||||
- [ ] Tool call pattern for agentic systems
|
||||
- [ ] Pre-fetch pattern for simpler integrations
|
||||
- [ ] context() for conversation history
|
||||
- [ ] Store messages after each exchange to build user models
|
||||
- [ ] (Optional) Run `honcho doctor` to verify connectivity before testing integration code
|
||||
- [ ] (Optional) Use `honcho peer chat` to test dialectic queries independently
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
1. **Multiple workspaces**: Use ONE workspace per application
|
||||
2. **Forgetting AI peers**: Create peers for AI assistants, not just users
|
||||
3. **Observing AI peers**: Set `observe_me=False` for AI peers unless you specifically want Honcho to model your AI's behavior
|
||||
4. **Not storing messages**: Always call `add_messages()` to feed Honcho's reasoning engine
|
||||
5. **Blocking on processing**: Messages are processed asynchronously — don't poll or wait for reasoning to complete before continuing
|
||||
|
||||
## Resources
|
||||
|
||||
- Documentation: <https://honcho.dev/docs>
|
||||
- Latest SDK versions: <https://honcho.dev/docs/changelog/introduction>
|
||||
- API Reference: <https://honcho.dev/docs/v3/api-reference/introduction>
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
# Honcho Integration for Bot Frameworks
|
||||
|
||||
This reference extends the main honcho-integration skill for **bot frameworks** — applications built around an agent loop, session manager, tool registry, and message bus (e.g., nanobot, openclaw, picoclaw).
|
||||
|
||||
## Supported Frameworks
|
||||
|
||||
When a known framework is detected, use concrete reference implementations from `{baseDir}/references/bot-frameworks/<framework>/`.
|
||||
|
||||
| Framework | Status | Reference Dir |
|
||||
|-----------|--------|---------------|
|
||||
| [nanobot](https://github.com/HKUDS/nanobot) | concrete references | `bot-frameworks/nanobot/` |
|
||||
| openclaw | planned | -- |
|
||||
| picoclaw | planned | -- |
|
||||
|
||||
For unknown frameworks, adapt the general pattern below to the codebase's architecture.
|
||||
|
||||
## Phase 1: Explore (bot-specific)
|
||||
|
||||
In addition to the main skill's Phase 1, identify these bot-specific components:
|
||||
|
||||
1. **Agent loop**: Where messages are processed (look for `while` loops calling an LLM)
|
||||
2. **Session manager**: How conversation history is stored (JSONL files, database, in-memory)
|
||||
3. **Tool registry**: How tools/functions are registered for the LLM to call
|
||||
4. **Message bus**: How inbound/outbound messages are routed between channels and the agent
|
||||
5. **Config system**: How the bot loads configuration (JSON, YAML, env vars, pydantic models, zod schemas)
|
||||
6. **CLI entry points**: How the bot is started (commands, gateway, agent modes)
|
||||
|
||||
If the framework matches a known one (e.g., nanobot), pull the concrete references from `{baseDir}/references/bot-frameworks/<framework>/` and use them as the implementation target.
|
||||
|
||||
## Phase 2: Interview (bot-specific)
|
||||
|
||||
In addition to the main skill's interview questions, ask about:
|
||||
|
||||
- **Peer model**: Who are the participants? (typically: one user peer per channel:chat_id, one shared assistant peer)
|
||||
- **Session granularity**: One session per chat? Per user? Per channel?
|
||||
- **Workspace ID**: What namespace for this bot's Honcho data?
|
||||
- **Feature flag**: Should Honcho be opt-in (default `false`) or opt-out (default `true`)?
|
||||
|
||||
## Phase 3: Implement (bot-specific)
|
||||
|
||||
### Step 1: Add dependency
|
||||
|
||||
**Python:** Add `honcho-ai>=2.0.1`. If the framework supports optional dependencies, make it optional:
|
||||
|
||||
```toml
|
||||
[project.optional-dependencies]
|
||||
honcho = ["honcho-ai>=2.0.1"]
|
||||
```
|
||||
|
||||
**TypeScript:** Add `@honcho-ai/sdk`:
|
||||
|
||||
```bash
|
||||
bun add @honcho-ai/sdk
|
||||
# or npm install @honcho-ai/sdk
|
||||
```
|
||||
|
||||
If the framework supports optional peer dependencies:
|
||||
|
||||
```json
|
||||
{
|
||||
"peerDependencies": {
|
||||
"@honcho-ai/sdk": ">=2.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@honcho-ai/sdk": { "optional": true }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Add config schema
|
||||
|
||||
Add a Honcho config section to the bot's configuration system:
|
||||
|
||||
**Python:**
|
||||
|
||||
```python
|
||||
class HonchoConfig(BaseModel):
|
||||
"""Honcho AI-native memory integration (optional feature flag)."""
|
||||
enabled: bool = False # or True for Honcho-first deployments
|
||||
workspace_id: str = "default"
|
||||
prefetch: bool = True # inject user context into system prompts
|
||||
context_tokens: int | None = None
|
||||
environment: str = "production"
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
|
||||
```typescript
|
||||
interface HonchoConfig {
|
||||
/** Honcho AI-native memory integration (optional feature flag). */
|
||||
enabled: boolean; // default: false, or true for Honcho-first deployments
|
||||
workspaceId: string; // default: "default"
|
||||
prefetch: boolean; // default: true — inject user context into system prompts
|
||||
contextTokens?: number;
|
||||
environment: string; // default: "production"
|
||||
}
|
||||
|
||||
const defaultHonchoConfig: HonchoConfig = {
|
||||
enabled: false,
|
||||
workspaceId: "default",
|
||||
prefetch: true,
|
||||
environment: "production",
|
||||
};
|
||||
```
|
||||
|
||||
### Step 3: Create the honcho package
|
||||
|
||||
Create a honcho integration package with:
|
||||
|
||||
- **Client singleton** (`client.py` / `client.ts`): Lazy initialization, deferred imports, `getHonchoClient()` factory
|
||||
- **Session manager** (`session.py` / `session.ts`): Maps bot sessions to Honcho sessions with peer configuration
|
||||
- **Agent tool** (`honcho_tool.py` / `honchoTool.ts`): Tool the agent can call to query user context via `peer.chat()`
|
||||
|
||||
Key patterns (Python):
|
||||
|
||||
- `from __future__ import annotations` + `TYPE_CHECKING` for all honcho imports
|
||||
- Runtime imports inside functions (never top-level) so the bot doesn't crash without `honcho-ai`
|
||||
- Wrap in `try/except ImportError` for graceful degradation
|
||||
|
||||
Key patterns (TypeScript):
|
||||
|
||||
- Use dynamic `import()` for honcho SDK (never top-level `import ... from`) so the bot doesn't crash without `@honcho-ai/sdk`
|
||||
- Use `import type { ... }` for type-only imports that are erased at runtime
|
||||
- Wrap in `try/catch` for graceful degradation when the SDK is missing
|
||||
|
||||
Key patterns (shared):
|
||||
|
||||
- IDs sanitized to `^[a-zA-Z0-9_-]+` (Honcho requirement)
|
||||
- User peer: `observe_me=True, observe_others=True`
|
||||
- Assistant peer: `observe_me=False, observe_others=True`
|
||||
|
||||
If references exist for this framework, use them directly from `{baseDir}/references/bot-frameworks/<framework>/`.
|
||||
|
||||
### Step 4: Wire into the agent loop
|
||||
|
||||
Add these integration points to the agent loop:
|
||||
|
||||
1. **Tool registration** (at startup): If `honcho.enabled` and `HONCHO_API_KEY` set, initialize client + register Honcho tools.
|
||||
|
||||
**Python:** Wrap in `try/except ImportError` for graceful degradation.
|
||||
**TypeScript:** Use dynamic `import()` inside a `try/catch` block.
|
||||
|
||||
2. **Context setup** (per message): Set session context on Honcho tools, ensure Honcho session exists.
|
||||
|
||||
3. **Prefetch** (per message): Call `session.context()` to get user representation and inject into system prompt before the LLM call.
|
||||
|
||||
4. **Sync** (after response): After saving to local session, sync the user+assistant message pair to Honcho.
|
||||
|
||||
**Python:**
|
||||
|
||||
```python
|
||||
session.add_messages([
|
||||
user_peer.message(user_input),
|
||||
assistant_peer.message(assistant_response),
|
||||
])
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
|
||||
```typescript
|
||||
await session.addMessages([
|
||||
userPeer.message(userInput),
|
||||
assistantPeer.message(assistantResponse),
|
||||
]);
|
||||
```
|
||||
|
||||
5. **Migration** (on first activation): If Honcho session is empty but local session has history, upload prior messages as a file via `session.upload_file()` (Python) or `session.uploadFile()` (TypeScript). Also upload `MEMORY.md` and `HISTORY.md` if they exist (from frameworks with local memory consolidation). Archive originals after successful upload.
|
||||
|
||||
### Step 5: Pass config through CLI
|
||||
|
||||
Pass `honcho_config` to every agent loop instantiation in the CLI commands.
|
||||
|
||||
### Step 6: Migration support
|
||||
|
||||
When Honcho activates on an instance with existing local data, migrate automatically:
|
||||
|
||||
- **Session messages** (JSONL files): Format as XML transcript, upload via `session.upload_file()` (Python) or `session.uploadFile()` (TypeScript)
|
||||
- **Consolidated memory** (MEMORY.md, HISTORY.md): Upload as tagged files with context annotations
|
||||
- **Archive originals**: Move to `migrated/` subdirectory after successful upload
|
||||
- **Idempotent**: Skip if Honcho session already has messages
|
||||
|
||||
## Phase 4: Verify (bot-specific)
|
||||
|
||||
After integration, verify:
|
||||
|
||||
- [ ] Bot starts normally without the Honcho SDK installed (no import errors)
|
||||
- [ ] Bot starts normally with the SDK but without `HONCHO_API_KEY` (graceful skip)
|
||||
- [ ] With both present and `enabled=true`, logs show "Honcho tools registered"
|
||||
- [ ] User context is prefetched and visible in system prompts
|
||||
- [ ] Messages sync to Honcho after each exchange
|
||||
- [ ] Local session migration works on first Honcho activation
|
||||
- [ ] Memory file migration works for MEMORY.md/HISTORY.md (if applicable)
|
||||
|
||||
## Bot-Specific Patterns
|
||||
|
||||
- **Lazy imports everywhere**:
|
||||
- Python: `from __future__ import annotations` + `TYPE_CHECKING` for type hints, runtime imports inside functions
|
||||
- TypeScript: `import type { ... }` for type-only imports, dynamic `import()` for runtime access
|
||||
- **Feature flag gating**: Always check `config.enabled` AND `HONCHO_API_KEY` / `process.env.HONCHO_API_KEY` before touching Honcho
|
||||
- **Graceful degradation**:
|
||||
- Python: `try/except ImportError` and generic `Exception` catches with logger warnings, never crash the bot
|
||||
- TypeScript: `try/catch` around dynamic `import()` with logger warnings, never crash the bot
|
||||
- **Sanitize IDs**: Honcho requires `^[a-zA-Z0-9_-]+` — replace colons, dots, spaces with dashes
|
||||
- **Sync after success**: Only mark messages as synced after the API call succeeds, not before
|
||||
- **Cache consistency**: When creating aliased sessions, store under both original and derived keys
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
"""Honcho client initialization and configuration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from loguru import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from honcho import Honcho
|
||||
|
||||
|
||||
@dataclass
|
||||
class HonchoConfig:
|
||||
"""Configuration for Honcho client."""
|
||||
|
||||
workspace_id: str = "nanobot"
|
||||
api_key: str | None = None
|
||||
environment: str = "production"
|
||||
|
||||
@classmethod
|
||||
def from_env(cls, workspace_id: str = "nanobot") -> HonchoConfig:
|
||||
"""Create config from environment variables."""
|
||||
return cls(
|
||||
workspace_id=workspace_id,
|
||||
api_key=os.environ.get("HONCHO_API_KEY"),
|
||||
environment=os.environ.get("HONCHO_ENVIRONMENT", "production"),
|
||||
)
|
||||
|
||||
|
||||
_honcho_client: Honcho | None = None
|
||||
|
||||
|
||||
def get_honcho_client(config: HonchoConfig | None = None) -> Honcho:
|
||||
"""
|
||||
Get or create the Honcho client singleton.
|
||||
|
||||
Args:
|
||||
config: Optional config. If not provided, uses environment variables.
|
||||
|
||||
Returns:
|
||||
Configured Honcho client.
|
||||
|
||||
Raises:
|
||||
ValueError: If HONCHO_API_KEY is not set.
|
||||
"""
|
||||
global _honcho_client
|
||||
|
||||
if _honcho_client is not None:
|
||||
return _honcho_client
|
||||
|
||||
if config is None:
|
||||
config = HonchoConfig.from_env()
|
||||
|
||||
if not config.api_key:
|
||||
raise ValueError(
|
||||
"HONCHO_API_KEY environment variable is required. "
|
||||
"Get an API key from https://app.honcho.dev"
|
||||
)
|
||||
|
||||
try:
|
||||
from honcho import Honcho
|
||||
except ImportError:
|
||||
raise ImportError(
|
||||
"honcho-ai is required for Honcho integration. "
|
||||
"Install it with: nanobot honcho enable --api-key YOUR_KEY"
|
||||
)
|
||||
|
||||
logger.info(f"Initializing Honcho client (workspace: {config.workspace_id})")
|
||||
|
||||
_honcho_client = Honcho(
|
||||
workspace_id=config.workspace_id,
|
||||
api_key=config.api_key,
|
||||
environment=config.environment,
|
||||
)
|
||||
|
||||
return _honcho_client
|
||||
|
||||
|
||||
def reset_honcho_client() -> None:
|
||||
"""Reset the Honcho client singleton (useful for testing)."""
|
||||
global _honcho_client
|
||||
_honcho_client = None
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
"""Honcho tool for querying user context."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from nanobot.agent.tools.base import Tool
|
||||
|
||||
|
||||
class HonchoTool(Tool):
|
||||
"""
|
||||
Tool for querying Honcho's AI-native memory.
|
||||
|
||||
Allows the agent to retrieve relevant context about users
|
||||
based on their history and learned preferences.
|
||||
"""
|
||||
|
||||
def __init__(self, session_manager: "HonchoSessionManager"):
|
||||
"""
|
||||
Initialize the Honcho tool.
|
||||
|
||||
Args:
|
||||
session_manager: The HonchoSessionManager instance.
|
||||
"""
|
||||
self._session_manager = session_manager
|
||||
self._current_session_key: str | None = None
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "query_user_context"
|
||||
|
||||
@property
|
||||
def description(self) -> str:
|
||||
return (
|
||||
"Query Honcho to retrieve relevant context about the user based on their "
|
||||
"history and preferences. Use this when you need to understand the user's "
|
||||
"background, preferences, past interactions, or goals. This helps you "
|
||||
"personalize your responses and provide more relevant assistance."
|
||||
)
|
||||
|
||||
@property
|
||||
def parameters(self) -> dict[str, Any]:
|
||||
return {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": (
|
||||
"A natural language question about the user. Examples: "
|
||||
"'What are this user's main goals?', "
|
||||
"'What communication style does this user prefer?', "
|
||||
"'What topics has this user discussed recently?', "
|
||||
"'What is this user's technical expertise level?'"
|
||||
),
|
||||
}
|
||||
},
|
||||
"required": ["query"],
|
||||
}
|
||||
|
||||
def set_context(self, session_key: str) -> None:
|
||||
"""
|
||||
Set the current session context.
|
||||
|
||||
Args:
|
||||
session_key: The session key (channel:chat_id).
|
||||
"""
|
||||
self._current_session_key = session_key
|
||||
|
||||
async def execute(self, query: str) -> str:
|
||||
"""
|
||||
Execute the Honcho context query.
|
||||
|
||||
Args:
|
||||
query: Natural language question about the user.
|
||||
|
||||
Returns:
|
||||
Honcho's response about the user.
|
||||
"""
|
||||
if not self._current_session_key:
|
||||
return "Error: No session context set. Unable to query user information."
|
||||
|
||||
try:
|
||||
result = self._session_manager.get_user_context(
|
||||
self._current_session_key, query
|
||||
)
|
||||
return result
|
||||
except Exception as e:
|
||||
return f"Error querying user context: {str(e)}"
|
||||
|
|
@ -0,0 +1,576 @@
|
|||
"""Honcho-based session management for conversation history."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Any, TYPE_CHECKING
|
||||
|
||||
from loguru import logger
|
||||
|
||||
from nanobot.honcho.client import get_honcho_client
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from honcho import Honcho
|
||||
from honcho.api_types import SessionPeerConfig
|
||||
|
||||
|
||||
@dataclass
|
||||
class HonchoSession:
|
||||
"""
|
||||
A conversation session backed by Honcho.
|
||||
|
||||
Provides the same interface as the original Session class
|
||||
but stores messages in Honcho for AI-native memory.
|
||||
"""
|
||||
|
||||
key: str # channel:chat_id
|
||||
user_peer_id: str # Honcho peer ID for the user
|
||||
assistant_peer_id: str # Honcho peer ID for the assistant
|
||||
honcho_session_id: str # Honcho session ID
|
||||
messages: list[dict[str, Any]] = field(default_factory=list)
|
||||
created_at: datetime = field(default_factory=datetime.now)
|
||||
updated_at: datetime = field(default_factory=datetime.now)
|
||||
metadata: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def add_message(self, role: str, content: str, **kwargs: Any) -> None:
|
||||
"""Add a message to the local cache."""
|
||||
msg = {
|
||||
"role": role,
|
||||
"content": content,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
**kwargs,
|
||||
}
|
||||
self.messages.append(msg)
|
||||
self.updated_at = datetime.now()
|
||||
|
||||
def get_history(self, max_messages: int = 50) -> list[dict[str, Any]]:
|
||||
"""
|
||||
Get message history for LLM context.
|
||||
|
||||
Args:
|
||||
max_messages: Maximum messages to return.
|
||||
|
||||
Returns:
|
||||
List of messages in LLM format.
|
||||
"""
|
||||
recent = (
|
||||
self.messages[-max_messages:]
|
||||
if len(self.messages) > max_messages
|
||||
else self.messages
|
||||
)
|
||||
return [{"role": m["role"], "content": m["content"]} for m in recent]
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Clear all messages in the session."""
|
||||
self.messages = []
|
||||
self.updated_at = datetime.now()
|
||||
|
||||
|
||||
class HonchoSessionManager:
|
||||
"""
|
||||
Manages conversation sessions using Honcho.
|
||||
|
||||
Replaces the file-based SessionManager with Honcho's
|
||||
AI-native memory system for user modeling.
|
||||
"""
|
||||
|
||||
def __init__(self, honcho: Honcho | None = None, context_tokens: int | None = None):
|
||||
"""
|
||||
Initialize the session manager.
|
||||
|
||||
Args:
|
||||
honcho: Optional Honcho client. If not provided, uses the singleton.
|
||||
context_tokens: Max tokens for context() calls (None = Honcho default).
|
||||
"""
|
||||
self._honcho = honcho
|
||||
self._context_tokens = context_tokens
|
||||
self._cache: dict[str, HonchoSession] = {}
|
||||
self._peers_cache: dict[str, Any] = {}
|
||||
self._sessions_cache: dict[str, Any] = {}
|
||||
|
||||
@property
|
||||
def honcho(self) -> Honcho:
|
||||
"""Get the Honcho client, initializing if needed."""
|
||||
if self._honcho is None:
|
||||
self._honcho = get_honcho_client()
|
||||
return self._honcho
|
||||
|
||||
def _get_or_create_peer(self, peer_id: str) -> Any:
|
||||
"""
|
||||
Get or create a Honcho peer.
|
||||
|
||||
As of v2.1.0, peer() always makes a get-or-create API call.
|
||||
Observation settings are controlled per-session via SessionPeerConfig.
|
||||
|
||||
Args:
|
||||
peer_id: The peer identifier.
|
||||
|
||||
Returns:
|
||||
The Honcho peer object.
|
||||
"""
|
||||
if peer_id in self._peers_cache:
|
||||
return self._peers_cache[peer_id]
|
||||
|
||||
peer = self.honcho.peer(peer_id)
|
||||
self._peers_cache[peer_id] = peer
|
||||
return peer
|
||||
|
||||
def _get_or_create_honcho_session(
|
||||
self, session_id: str, user_peer: Any, assistant_peer: Any
|
||||
) -> Any:
|
||||
"""
|
||||
Get or create a Honcho session with peers configured.
|
||||
|
||||
Args:
|
||||
session_id: The session identifier.
|
||||
user_peer: The user peer object.
|
||||
assistant_peer: The assistant peer object.
|
||||
|
||||
Returns:
|
||||
The Honcho session object.
|
||||
"""
|
||||
if session_id in self._sessions_cache:
|
||||
logger.debug(f"Honcho session '{session_id}' retrieved from cache")
|
||||
return self._sessions_cache[session_id], []
|
||||
|
||||
session = self.honcho.session(session_id)
|
||||
|
||||
# Configure peer observation settings
|
||||
from honcho.api_types import SessionPeerConfig
|
||||
user_config = SessionPeerConfig(observe_me=True, observe_others=True)
|
||||
ai_config = SessionPeerConfig(observe_me=False, observe_others=True)
|
||||
|
||||
session.add_peers([(user_peer, user_config), (assistant_peer, ai_config)])
|
||||
|
||||
# Load existing messages via context() - single call for messages + metadata
|
||||
existing_messages = []
|
||||
try:
|
||||
ctx = session.context(summary=True, tokens=self._context_tokens)
|
||||
existing_messages = ctx.messages or []
|
||||
|
||||
# Verify chronological ordering
|
||||
if existing_messages and len(existing_messages) > 1:
|
||||
timestamps = [m.created_at for m in existing_messages if m.created_at]
|
||||
if timestamps and timestamps != sorted(timestamps):
|
||||
logger.warning(
|
||||
f"Honcho messages not chronologically ordered for session '{session_id}', sorting"
|
||||
)
|
||||
existing_messages = sorted(
|
||||
existing_messages,
|
||||
key=lambda m: m.created_at or datetime.min,
|
||||
)
|
||||
|
||||
if existing_messages:
|
||||
logger.info(f"Honcho session '{session_id}' retrieved ({len(existing_messages)} existing messages)")
|
||||
else:
|
||||
logger.info(f"Honcho session '{session_id}' created (new)")
|
||||
except Exception as e:
|
||||
logger.warning(f"Honcho session '{session_id}' loaded (failed to fetch context: {e})")
|
||||
|
||||
self._sessions_cache[session_id] = session
|
||||
return session, existing_messages
|
||||
|
||||
def _sanitize_id(self, id_str: str) -> str:
|
||||
"""Sanitize an ID to match Honcho's pattern: ^[a-zA-Z0-9_-]+"""
|
||||
return re.sub(r'[^a-zA-Z0-9_-]', '-', id_str)
|
||||
|
||||
def get_or_create(self, key: str) -> HonchoSession:
|
||||
"""
|
||||
Get an existing session or create a new one.
|
||||
|
||||
Args:
|
||||
key: Session key (usually channel:chat_id).
|
||||
|
||||
Returns:
|
||||
The session.
|
||||
"""
|
||||
if key in self._cache:
|
||||
logger.debug(f"Local session cache hit: {key}")
|
||||
return self._cache[key]
|
||||
|
||||
# Parse key to extract user identifier
|
||||
# Format: channel:chat_id (e.g., "telegram:123456789")
|
||||
parts = key.split(":", 1)
|
||||
channel = parts[0] if len(parts) > 1 else "default"
|
||||
chat_id = parts[1] if len(parts) > 1 else key
|
||||
|
||||
# Create peer IDs (sanitized for Honcho's ID pattern)
|
||||
user_peer_id = self._sanitize_id(f"user-{channel}-{chat_id}")
|
||||
assistant_peer_id = "nanobot-assistant"
|
||||
|
||||
# Sanitize session ID for Honcho
|
||||
honcho_session_id = self._sanitize_id(key)
|
||||
|
||||
# Get or create peers
|
||||
user_peer = self._get_or_create_peer(user_peer_id)
|
||||
assistant_peer = self._get_or_create_peer(assistant_peer_id)
|
||||
|
||||
# Get or create Honcho session
|
||||
honcho_session, existing_messages = self._get_or_create_honcho_session(
|
||||
honcho_session_id, user_peer, assistant_peer
|
||||
)
|
||||
|
||||
# Convert Honcho messages to local format
|
||||
local_messages = []
|
||||
for msg in existing_messages:
|
||||
role = "assistant" if msg.peer_id == assistant_peer_id else "user"
|
||||
local_messages.append({
|
||||
"role": role,
|
||||
"content": msg.content,
|
||||
"timestamp": msg.created_at.isoformat() if msg.created_at else "",
|
||||
"_synced": True, # Already in Honcho
|
||||
})
|
||||
|
||||
# Create local session wrapper with existing messages
|
||||
session = HonchoSession(
|
||||
key=key,
|
||||
user_peer_id=user_peer_id,
|
||||
assistant_peer_id=assistant_peer_id,
|
||||
honcho_session_id=honcho_session_id,
|
||||
messages=local_messages,
|
||||
)
|
||||
|
||||
self._cache[key] = session
|
||||
return session
|
||||
|
||||
def save(self, session: HonchoSession) -> None:
|
||||
"""
|
||||
Save messages to Honcho.
|
||||
|
||||
This syncs the local message cache to Honcho's storage.
|
||||
|
||||
Args:
|
||||
session: The session to save.
|
||||
"""
|
||||
if not session.messages:
|
||||
return
|
||||
|
||||
# Get the Honcho session and peers
|
||||
user_peer = self._get_or_create_peer(session.user_peer_id)
|
||||
assistant_peer = self._get_or_create_peer(session.assistant_peer_id)
|
||||
honcho_session = self._sessions_cache.get(session.honcho_session_id)
|
||||
|
||||
if not honcho_session:
|
||||
honcho_session, _ = self._get_or_create_honcho_session(
|
||||
session.honcho_session_id, user_peer, assistant_peer
|
||||
)
|
||||
|
||||
# Convert messages to Honcho format and send
|
||||
# Only send new messages (those without a 'synced' flag)
|
||||
new_messages = [m for m in session.messages if not m.get("_synced")]
|
||||
|
||||
if not new_messages:
|
||||
return
|
||||
|
||||
honcho_messages = []
|
||||
for msg in new_messages:
|
||||
peer = user_peer if msg["role"] == "user" else assistant_peer
|
||||
honcho_messages.append(peer.message(msg["content"]))
|
||||
|
||||
try:
|
||||
honcho_session.add_messages(honcho_messages)
|
||||
for msg in new_messages:
|
||||
msg["_synced"] = True
|
||||
logger.debug(f"Synced {len(honcho_messages)} messages to Honcho for {session.key}")
|
||||
except Exception as e:
|
||||
for msg in new_messages:
|
||||
msg["_synced"] = False
|
||||
logger.error(f"Failed to sync messages to Honcho: {e}")
|
||||
|
||||
# Update cache
|
||||
self._cache[session.key] = session
|
||||
|
||||
def delete(self, key: str) -> bool:
|
||||
"""
|
||||
Delete a session from local cache.
|
||||
|
||||
Args:
|
||||
key: Session key.
|
||||
|
||||
Returns:
|
||||
True if deleted from cache, False if not found.
|
||||
"""
|
||||
if key in self._cache:
|
||||
del self._cache[key]
|
||||
return True
|
||||
return False
|
||||
|
||||
def new_session(self, key: str) -> HonchoSession:
|
||||
"""
|
||||
Create a new session, preserving the old one for user modeling.
|
||||
|
||||
This creates a fresh session with a new ID while keeping the old
|
||||
session's data in Honcho for continued user modeling.
|
||||
|
||||
Args:
|
||||
key: Original session key (e.g., "discord:123456").
|
||||
|
||||
Returns:
|
||||
A fresh HonchoSession with no message history.
|
||||
"""
|
||||
import time
|
||||
|
||||
# Remove old session from caches (but don't delete from Honcho)
|
||||
old_session = self._cache.pop(key, None)
|
||||
if old_session:
|
||||
self._sessions_cache.pop(old_session.honcho_session_id, None)
|
||||
|
||||
# Create new session with timestamp suffix
|
||||
# This preserves old session in Honcho while starting fresh
|
||||
timestamp = int(time.time())
|
||||
new_key = f"{key}:{timestamp}"
|
||||
|
||||
# Get or create will create a fresh session
|
||||
session = self.get_or_create(new_key)
|
||||
|
||||
# Cache under both original key (for future lookups) and timestamped
|
||||
# key (so session.key matches a valid cache entry)
|
||||
self._cache[key] = session
|
||||
self._cache[new_key] = session
|
||||
|
||||
logger.info(f"Created new session for {key} (honcho: {session.honcho_session_id})")
|
||||
return session
|
||||
|
||||
def get_user_context(self, session_key: str, query: str) -> str:
|
||||
"""
|
||||
Query Honcho's dialectic chat for user context.
|
||||
|
||||
Args:
|
||||
session_key: The session key to get context for.
|
||||
query: Natural language question about the user.
|
||||
|
||||
Returns:
|
||||
Honcho's response about the user.
|
||||
"""
|
||||
session = self._cache.get(session_key)
|
||||
if not session:
|
||||
return "No session found for this context."
|
||||
|
||||
user_peer = self._get_or_create_peer(session.user_peer_id)
|
||||
|
||||
try:
|
||||
return user_peer.chat(query)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to get user context from Honcho: {e}")
|
||||
return f"Unable to retrieve user context: {e}"
|
||||
|
||||
def get_prefetch_context(self, session_key: str, user_message: str | None = None) -> dict[str, str]:
|
||||
"""
|
||||
Pre-fetch user context using Honcho's context() method.
|
||||
|
||||
This is a single API call that returns the user's representation
|
||||
and peer card, using semantic search based on the user's message.
|
||||
|
||||
Args:
|
||||
session_key: The session key to get context for.
|
||||
user_message: The user's message for semantic search.
|
||||
|
||||
Returns:
|
||||
Dictionary with 'representation' and 'card' keys.
|
||||
"""
|
||||
session = self._cache.get(session_key)
|
||||
if not session:
|
||||
return {}
|
||||
|
||||
honcho_session = self._sessions_cache.get(session.honcho_session_id)
|
||||
if not honcho_session:
|
||||
return {}
|
||||
|
||||
try:
|
||||
# Single API call to get user representation with semantic search
|
||||
ctx = honcho_session.context(
|
||||
summary=False,
|
||||
tokens=self._context_tokens,
|
||||
peer_target=session.user_peer_id,
|
||||
search_query=user_message,
|
||||
)
|
||||
# peer_card is list[str] in SDK v2, join for prompt injection
|
||||
card = ctx.peer_card or []
|
||||
card_str = "\n".join(card) if isinstance(card, list) else str(card)
|
||||
return {
|
||||
"representation": ctx.peer_representation or "",
|
||||
"card": card_str,
|
||||
}
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to fetch context from Honcho: {e}")
|
||||
return {}
|
||||
|
||||
def migrate_local_history(self, session_key: str, messages: list[dict[str, Any]]) -> bool:
|
||||
"""
|
||||
Upload local session history to Honcho as a file.
|
||||
|
||||
Used when Honcho activates mid-conversation to preserve prior context.
|
||||
|
||||
Args:
|
||||
session_key: The session key (e.g., "telegram:123456").
|
||||
messages: Local messages (dicts with role, content, timestamp).
|
||||
|
||||
Returns:
|
||||
True if upload succeeded, False otherwise.
|
||||
"""
|
||||
sanitized = self._sanitize_id(session_key)
|
||||
honcho_session = self._sessions_cache.get(sanitized)
|
||||
if not honcho_session:
|
||||
logger.warning(f"No Honcho session cached for '{session_key}', skipping migration")
|
||||
return False
|
||||
|
||||
# Resolve user peer for attribution
|
||||
parts = session_key.split(":", 1)
|
||||
channel = parts[0] if len(parts) > 1 else "default"
|
||||
chat_id = parts[1] if len(parts) > 1 else session_key
|
||||
user_peer_id = self._sanitize_id(f"user-{channel}-{chat_id}")
|
||||
user_peer = self._peers_cache.get(user_peer_id)
|
||||
if not user_peer:
|
||||
logger.warning(f"No user peer cached for '{user_peer_id}', skipping migration")
|
||||
return False
|
||||
|
||||
content_bytes = self._format_migration_transcript(session_key, messages)
|
||||
first_ts = messages[0].get("timestamp") if messages else None
|
||||
|
||||
try:
|
||||
honcho_session.upload_file(
|
||||
file=("prior_history.txt", content_bytes, "text/plain"),
|
||||
peer=user_peer,
|
||||
metadata={"source": "local_jsonl", "count": len(messages)},
|
||||
created_at=first_ts,
|
||||
)
|
||||
logger.info(f"Migrated {len(messages)} local messages to Honcho for {session_key}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to upload local history to Honcho for {session_key}: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _format_migration_transcript(session_key: str, messages: list[dict[str, Any]]) -> bytes:
|
||||
"""
|
||||
Format local messages as an XML transcript for Honcho file upload.
|
||||
|
||||
Args:
|
||||
session_key: The session key for metadata.
|
||||
messages: Local messages (dicts with role, content, timestamp).
|
||||
|
||||
Returns:
|
||||
UTF-8 encoded transcript bytes.
|
||||
"""
|
||||
timestamps = [m.get("timestamp", "") for m in messages]
|
||||
time_range = f"{timestamps[0]} to {timestamps[-1]}" if timestamps else "unknown"
|
||||
|
||||
lines = [
|
||||
"<prior_conversation_history>",
|
||||
"<context>",
|
||||
"This conversation history occurred BEFORE the Honcho memory system was activated.",
|
||||
"These messages are the preceding elements of this conversation session and should",
|
||||
"be treated as foundational context for all subsequent interactions. The user and",
|
||||
"assistant have already established rapport through these exchanges.",
|
||||
"</context>",
|
||||
"",
|
||||
f'<transcript session_key="{session_key}" message_count="{len(messages)}"',
|
||||
f' time_range="{time_range}">',
|
||||
"",
|
||||
]
|
||||
for msg in messages:
|
||||
ts = msg.get("timestamp", "?")
|
||||
role = msg.get("role", "unknown")
|
||||
content = msg.get("content", "")
|
||||
lines.append(f"[{ts}] {role}: {content}")
|
||||
|
||||
lines.append("")
|
||||
lines.append("</transcript>")
|
||||
lines.append("</prior_conversation_history>")
|
||||
|
||||
return "\n".join(lines).encode("utf-8")
|
||||
|
||||
def migrate_memory_files(self, session_key: str, workspace: Any) -> bool:
|
||||
"""
|
||||
Upload workspace/memory/MEMORY.md and HISTORY.md to Honcho as files.
|
||||
|
||||
Used when Honcho activates on an instance that already has locally
|
||||
consolidated memory (from upstream's _consolidate_memory). Backwards
|
||||
compatible -- skips gracefully if files don't exist.
|
||||
|
||||
Args:
|
||||
session_key: The session key to associate files with.
|
||||
workspace: Path to the workspace directory.
|
||||
|
||||
Returns:
|
||||
True if at least one file was uploaded, False otherwise.
|
||||
"""
|
||||
from pathlib import Path
|
||||
workspace = Path(workspace)
|
||||
memory_dir = workspace / "memory"
|
||||
|
||||
if not memory_dir.exists():
|
||||
return False
|
||||
|
||||
sanitized = self._sanitize_id(session_key)
|
||||
honcho_session = self._sessions_cache.get(sanitized)
|
||||
if not honcho_session:
|
||||
logger.warning(f"No Honcho session cached for '{session_key}', skipping memory migration")
|
||||
return False
|
||||
|
||||
# Resolve user peer for attribution
|
||||
parts = session_key.split(":", 1)
|
||||
channel = parts[0] if len(parts) > 1 else "default"
|
||||
chat_id = parts[1] if len(parts) > 1 else session_key
|
||||
user_peer_id = self._sanitize_id(f"user-{channel}-{chat_id}")
|
||||
user_peer = self._peers_cache.get(user_peer_id)
|
||||
if not user_peer:
|
||||
logger.warning(f"No user peer cached for '{user_peer_id}', skipping memory migration")
|
||||
return False
|
||||
|
||||
uploaded = False
|
||||
files = [
|
||||
("MEMORY.md", "consolidated_memory.md", "Long-term user facts and preferences"),
|
||||
("HISTORY.md", "conversation_history.md", "Chronological conversation summaries"),
|
||||
]
|
||||
|
||||
for filename, upload_name, description in files:
|
||||
filepath = memory_dir / filename
|
||||
if not filepath.exists():
|
||||
continue
|
||||
content = filepath.read_text(encoding="utf-8").strip()
|
||||
if not content:
|
||||
continue
|
||||
|
||||
wrapped = (
|
||||
f"<prior_memory_file>\n"
|
||||
f"<context>\n"
|
||||
f"This file was consolidated from local conversations BEFORE Honcho was activated.\n"
|
||||
f"{description}. Treat as foundational context for this user.\n"
|
||||
f"</context>\n"
|
||||
f"\n"
|
||||
f"{content}\n"
|
||||
f"</prior_memory_file>\n"
|
||||
)
|
||||
|
||||
try:
|
||||
honcho_session.upload_file(
|
||||
file=(upload_name, wrapped.encode("utf-8"), "text/plain"),
|
||||
peer=user_peer,
|
||||
metadata={"source": "local_memory", "original_file": filename},
|
||||
)
|
||||
logger.info(f"Uploaded {filename} to Honcho for {session_key}")
|
||||
uploaded = True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to upload {filename} to Honcho: {e}")
|
||||
|
||||
return uploaded
|
||||
|
||||
def list_sessions(self) -> list[dict[str, Any]]:
|
||||
"""
|
||||
List all cached sessions.
|
||||
|
||||
Returns:
|
||||
List of session info dicts.
|
||||
"""
|
||||
return [
|
||||
{
|
||||
"key": s.key,
|
||||
"created_at": s.created_at.isoformat(),
|
||||
"updated_at": s.updated_at.isoformat(),
|
||||
"message_count": len(s.messages),
|
||||
}
|
||||
for s in self._cache.values()
|
||||
]
|
||||
|
|
@ -0,0 +1,607 @@
|
|||
# Detailed API Changes
|
||||
|
||||
## 1. Async Client Architecture (Major Change)
|
||||
|
||||
The separate `AsyncHoncho`, `AsyncPeer`, and `AsyncSession` classes have been removed. Use the `.aio` accessor instead.
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
from honcho import Honcho, AsyncHoncho, AsyncPeer, AsyncSession
|
||||
|
||||
# Sync client
|
||||
client = Honcho()
|
||||
|
||||
# Async client - separate class
|
||||
async_client = AsyncHoncho()
|
||||
peer = await async_client.peer("user-123")
|
||||
response = await peer.chat("query")
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
from honcho import Honcho
|
||||
|
||||
# Single client with .aio accessor for async operations
|
||||
client = Honcho()
|
||||
|
||||
# Sync operations
|
||||
peer = client.peer("user-123")
|
||||
response = peer.chat("query")
|
||||
|
||||
# Async operations via .aio accessor
|
||||
peer = await client.aio.peer("user-123")
|
||||
response = await peer.aio.chat("query")
|
||||
|
||||
# Async iteration
|
||||
async for p in client.aio.peers():
|
||||
print(p.id)
|
||||
```
|
||||
|
||||
**Migration steps:**
|
||||
|
||||
1. Remove all `AsyncHoncho`, `AsyncPeer`, `AsyncSession` imports
|
||||
2. Replace `AsyncHoncho()` with `Honcho()` and use `.aio` accessor
|
||||
3. Replace `AsyncPeer` type hints with `Peer`
|
||||
4. Replace `AsyncSession` type hints with `Session`
|
||||
5. Access async methods via `.aio` property on instances
|
||||
|
||||
---
|
||||
|
||||
## 2. Observations → Conclusions (Terminology Change)
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
from honcho import Observation, ObservationScope, AsyncObservationScope
|
||||
|
||||
# Access observations
|
||||
scope = peer.observations
|
||||
scope = peer.observations_of("other-peer")
|
||||
|
||||
# List observations
|
||||
obs_list = scope.list()
|
||||
|
||||
# Query observations
|
||||
results = scope.query("preferences")
|
||||
|
||||
# Create observations
|
||||
scope.create([{"content": "User likes dark mode", "session_id": "sess-1"}])
|
||||
|
||||
# Get representation from observations
|
||||
rep = scope.get_representation()
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
from honcho import Conclusion, ConclusionScope, ConclusionScopeAio
|
||||
|
||||
# Access conclusions
|
||||
scope = peer.conclusions
|
||||
scope = peer.conclusions_of("other-peer")
|
||||
|
||||
# List conclusions (now returns SyncPage, not list)
|
||||
conclusions_page = scope.list()
|
||||
for conclusion in conclusions_page:
|
||||
print(conclusion.content)
|
||||
|
||||
# Query conclusions
|
||||
results = scope.query("preferences")
|
||||
|
||||
# Create conclusions
|
||||
scope.create([{"content": "User likes dark mode", "session_id": "sess-1"}])
|
||||
|
||||
# Get representation from conclusions
|
||||
rep = scope.representation() # Returns str, not Representation object
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Representation Type Change (Major Change)
|
||||
|
||||
The `Representation` class has been removed. Representations are now simple strings.
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
from honcho import Representation, ExplicitObservation, DeductiveObservation
|
||||
|
||||
# Get working representation
|
||||
rep: Representation = peer.working_rep()
|
||||
|
||||
# Access explicit and deductive observations
|
||||
for obs in rep.explicit:
|
||||
print(obs.content, obs.created_at)
|
||||
|
||||
for obs in rep.deductive:
|
||||
print(obs.conclusion, obs.premises)
|
||||
|
||||
# Check if empty
|
||||
if rep.is_empty():
|
||||
print("No observations")
|
||||
|
||||
# Merge representations
|
||||
rep.merge_representation(other_rep)
|
||||
|
||||
# Diff representations
|
||||
diff = rep.diff_representation(other_rep)
|
||||
|
||||
# String formatting
|
||||
print(str(rep))
|
||||
print(rep.str_no_timestamps())
|
||||
print(rep.format_as_markdown())
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
# Get representation - now returns str directly
|
||||
rep: str = peer.representation()
|
||||
|
||||
# It's just a string now
|
||||
print(rep)
|
||||
|
||||
# Check if empty
|
||||
if not rep:
|
||||
print("No conclusions")
|
||||
```
|
||||
|
||||
**Removed methods:**
|
||||
|
||||
- `.explicit` property
|
||||
- `.deductive` property
|
||||
- `.is_empty()`
|
||||
- `.merge_representation()`
|
||||
- `.diff_representation()`
|
||||
- `.str_no_timestamps()`
|
||||
- `.format_as_markdown()`
|
||||
|
||||
---
|
||||
|
||||
## 4. Configuration Parameter Rename
|
||||
|
||||
All `config` parameters have been renamed to `configuration`, and configuration types are now strongly typed.
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
# Creating resources with config
|
||||
peer = client.peer("user-1", config={"observe_me": True})
|
||||
session = client.session("sess-1", config={"some_setting": True})
|
||||
|
||||
# Getting/setting config
|
||||
config = peer.get_config()
|
||||
peer.set_config({"observe_me": False})
|
||||
|
||||
config = session.get_config()
|
||||
session.set_config({"some_setting": False})
|
||||
|
||||
config = client.get_config()
|
||||
client.set_config({"workspace_setting": True})
|
||||
|
||||
# Message config parameter
|
||||
msg = peer.message("Hello", config={"reasoning": {"enabled": True}})
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
from honcho.api_types import PeerConfig, SessionConfiguration, WorkspaceConfiguration
|
||||
|
||||
# Creating resources with configuration (typed)
|
||||
peer = client.peer("user-1", configuration=PeerConfig(observe_me=True))
|
||||
session = client.session("sess-1", configuration=SessionConfiguration())
|
||||
|
||||
# Getting/setting configuration (returns typed objects)
|
||||
config: PeerConfig = peer.get_configuration()
|
||||
peer.set_configuration(PeerConfig(observe_me=False))
|
||||
|
||||
config: SessionConfiguration = session.get_configuration()
|
||||
session.set_configuration(SessionConfiguration())
|
||||
|
||||
config: WorkspaceConfiguration = client.get_configuration()
|
||||
client.set_configuration(WorkspaceConfiguration())
|
||||
|
||||
# Message configuration parameter
|
||||
msg = peer.message("Hello", configuration={"reasoning": {"enabled": True}})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Streaming Chat API Change
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
# Streaming via parameter
|
||||
response = peer.chat("query", stream=True)
|
||||
for chunk in response:
|
||||
print(chunk, end="")
|
||||
|
||||
final = response.get_final_response()
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
# Streaming via separate method
|
||||
stream = peer.chat_stream("query")
|
||||
for chunk in stream:
|
||||
print(chunk, end="")
|
||||
|
||||
final = stream.get_final_response()
|
||||
|
||||
# Non-streaming (no stream parameter needed)
|
||||
response = peer.chat("query")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Deriver Status → Queue Status
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
from honcho_core.types import DeriverStatus
|
||||
|
||||
# Get status
|
||||
status: DeriverStatus = client.get_deriver_status()
|
||||
status = session.get_deriver_status()
|
||||
|
||||
# Poll until complete
|
||||
status = client.poll_deriver_status(timeout=300.0)
|
||||
status = session.poll_deriver_status(timeout=300.0)
|
||||
|
||||
# Access fields
|
||||
print(status.pending_work_units)
|
||||
print(status.in_progress_work_units)
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
from honcho.api_types import QueueStatusResponse
|
||||
|
||||
# Get status
|
||||
status: QueueStatusResponse = client.queue_status()
|
||||
status = session.queue_status()
|
||||
|
||||
# Access fields (same as before)
|
||||
print(status.pending_work_units)
|
||||
print(status.in_progress_work_units)
|
||||
|
||||
# poll_deriver_status has been removed - implement polling manually if needed:
|
||||
import time
|
||||
|
||||
def poll_until_complete(client, timeout=300.0):
|
||||
start = time.time()
|
||||
while time.time() - start < timeout:
|
||||
status = client.queue_status()
|
||||
if status.pending_work_units == 0 and status.in_progress_work_units == 0:
|
||||
return status
|
||||
time.sleep(1)
|
||||
raise TimeoutError("Queue processing did not complete in time")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. PeerContext Changes
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
from honcho import PeerContext
|
||||
|
||||
context: PeerContext = peer.get_context()
|
||||
|
||||
# Access representation (was Representation object)
|
||||
rep: Representation = context.representation
|
||||
if rep:
|
||||
print(rep.explicit)
|
||||
print(rep.deductive)
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
from honcho.api_types import PeerContextResponse
|
||||
|
||||
context: PeerContextResponse = peer.context()
|
||||
|
||||
# Access representation (now str)
|
||||
rep: str | None = context.representation
|
||||
if rep:
|
||||
print(rep)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Card Method Return Type Change
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
# card() returned str (joined with newlines)
|
||||
card: str = peer.card()
|
||||
print(card) # "line1\nline2\nline3"
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
# card() returns list[str] | None
|
||||
card: list[str] | None = peer.card()
|
||||
if card:
|
||||
print("\n".join(card)) # Join manually if needed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Message Update Location Change
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
# Update message via client
|
||||
updated = client.update_message(
|
||||
message=msg,
|
||||
metadata={"key": "value"},
|
||||
session="session-id" # Required if message is string ID
|
||||
)
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
# Update message via session
|
||||
updated = session.update_message(
|
||||
message=msg,
|
||||
metadata={"key": "value"}
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. Removed: `core` Property
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
# Access underlying Stainless-generated client
|
||||
core_client = client.core
|
||||
workspace = client.core.workspaces.get_or_create(id="custom-workspace")
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
# The `core` property has been removed
|
||||
# The SDK no longer uses a Stainless-generated client internally
|
||||
# Use the SDK's public API directly
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. Environment Changes
|
||||
|
||||
### Before (v1.6.0)
|
||||
|
||||
```python
|
||||
# Three environments available
|
||||
client = Honcho(environment="local")
|
||||
client = Honcho(environment="production")
|
||||
client = Honcho(environment="demo")
|
||||
```
|
||||
|
||||
### After (v2.0.0)
|
||||
|
||||
```python
|
||||
# Only two environments
|
||||
client = Honcho(environment="local")
|
||||
client = Honcho(environment="production")
|
||||
# "demo" environment has been removed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Reasoning Level Parameter (New Feature)
|
||||
|
||||
The chat method now supports a `reasoning_level` parameter:
|
||||
|
||||
```python
|
||||
# New in v2.0.0
|
||||
response = peer.chat(
|
||||
"complex query",
|
||||
reasoning_level="high" # "minimal", "low", "medium", "high", "max"
|
||||
)
|
||||
|
||||
stream = peer.chat_stream(
|
||||
"complex query",
|
||||
reasoning_level="max"
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Import Changes Summary
|
||||
|
||||
### Removed Imports
|
||||
|
||||
```python
|
||||
# These no longer exist in v2.0.0
|
||||
from honcho import AsyncHoncho # Use Honcho with .aio accessor
|
||||
from honcho import AsyncPeer # Use Peer with .aio accessor
|
||||
from honcho import AsyncSession # Use Session with .aio accessor
|
||||
from honcho import Observation # Renamed to Conclusion
|
||||
from honcho import ObservationScope # Renamed to ConclusionScope
|
||||
from honcho import AsyncObservationScope # Renamed to ConclusionScopeAio
|
||||
from honcho import Representation # Removed (now str)
|
||||
from honcho import ExplicitObservation # Removed
|
||||
from honcho import DeductiveObservation # Removed
|
||||
from honcho import PeerContext # Use PeerContextResponse from api_types
|
||||
```
|
||||
|
||||
### New Imports
|
||||
|
||||
```python
|
||||
from honcho import Conclusion, ConclusionScope
|
||||
from honcho import ConclusionScopeAio
|
||||
from honcho import HonchoAio, PeerAio, SessionAio # For type hints
|
||||
from honcho import MessageCreateParams, Message
|
||||
|
||||
# Typed configuration classes
|
||||
from honcho.api_types import (
|
||||
PeerConfig,
|
||||
SessionConfiguration,
|
||||
WorkspaceConfiguration,
|
||||
SessionPeerConfig,
|
||||
QueueStatusResponse,
|
||||
PeerContextResponse,
|
||||
)
|
||||
```
|
||||
|
||||
### Message Type Import Changes
|
||||
|
||||
```python
|
||||
# Before
|
||||
from honcho_core.types.workspaces.sessions import MessageCreateParam
|
||||
from honcho_core.types.workspaces.sessions.message import Message
|
||||
from honcho.session import SessionPeerConfig
|
||||
|
||||
# After
|
||||
from honcho import Message, MessageCreateParams # Note: plural "Params"
|
||||
from honcho.api_types import SessionPeerConfig
|
||||
```
|
||||
|
||||
**Note:** `MessageCreateParam` (singular) is now `MessageCreateParams` (plural).
|
||||
|
||||
---
|
||||
|
||||
## 14. Card Method Deprecation and set_card (v2.0.1)
|
||||
|
||||
### Before (v2.0.0)
|
||||
|
||||
```python
|
||||
card: list[str] | None = peer.card()
|
||||
```
|
||||
|
||||
### After (v2.0.1+)
|
||||
|
||||
```python
|
||||
# get_card() is the preferred method
|
||||
card: list[str] | None = peer.get_card()
|
||||
|
||||
# card() still works but emits a deprecation warning
|
||||
card = peer.card() # Deprecated
|
||||
|
||||
# New: set_card()
|
||||
updated = peer.set_card(["Fact 1", "Fact 2"])
|
||||
updated = peer.set_card(["Fact 1"], target="other-peer")
|
||||
|
||||
# Async variants
|
||||
card = await peer.aio.get_card()
|
||||
await peer.aio.set_card(["Fact 1"])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 15. Strict Input Validation (v2.0.2)
|
||||
|
||||
All Pydantic input models now use `extra="forbid"`, raising `ValidationError` for unknown fields.
|
||||
|
||||
```python
|
||||
from honcho.api_types import PeerConfig
|
||||
|
||||
# This now raises ValidationError instead of silently ignoring the typo
|
||||
PeerConfig(observe_mee=True) # ValidationError: extra fields not permitted
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 16. peer() and session() Always Make API Calls (v2.1.0)
|
||||
|
||||
### Before (v2.0.x)
|
||||
|
||||
```python
|
||||
# Without options: lazy object, no API call
|
||||
peer = client.peer("user-123")
|
||||
# peer.created_at was None
|
||||
|
||||
# With options: made API call
|
||||
peer = client.peer("user-123", metadata={"key": "value"})
|
||||
```
|
||||
|
||||
### After (v2.1.0+)
|
||||
|
||||
```python
|
||||
# Always makes a get-or-create API call
|
||||
peer = client.peer("user-123")
|
||||
# peer.created_at is now always populated
|
||||
|
||||
# Async
|
||||
peer = await client.aio.peer("user-123")
|
||||
```
|
||||
|
||||
All Peer/Session objects now have `created_at` populated immediately after construction.
|
||||
|
||||
---
|
||||
|
||||
## 17. New Properties: created_at, is_active (v2.1.0)
|
||||
|
||||
```python
|
||||
# Peer
|
||||
peer = client.peer("user-123")
|
||||
print(peer.created_at) # datetime | None
|
||||
|
||||
# Session
|
||||
session = client.session("sess-1")
|
||||
print(session.created_at) # datetime | None
|
||||
print(session.is_active) # bool | None
|
||||
|
||||
# These are refreshed by get_metadata(), get_configuration(), and refresh()
|
||||
peer.refresh()
|
||||
session.refresh()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 18. get_message() on Session (v2.1.0)
|
||||
|
||||
```python
|
||||
# Fetch a single message by ID
|
||||
msg = session.get_message("msg-abc123")
|
||||
print(msg.content, msg.created_at)
|
||||
|
||||
# Async
|
||||
msg = await session.aio.get_message("msg-abc123")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 19. Pagination Parameters (v2.1.0)
|
||||
|
||||
All list methods now accept `page`, `size`, and `reverse`:
|
||||
|
||||
```python
|
||||
# Defaults: page=1, size=50, reverse=False
|
||||
peers_page = client.peers(page=2, size=25, reverse=True)
|
||||
|
||||
# Returns SyncPage / AsyncPage with:
|
||||
print(peers_page.total) # Total items
|
||||
print(peers_page.pages) # Total pages
|
||||
print(peers_page.has_next_page())
|
||||
|
||||
# Works on:
|
||||
# client.peers(), client.sessions()
|
||||
# peer.sessions()
|
||||
# session.messages()
|
||||
# scope.list()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 20. Broader HTTP Retry Logic (v2.1.1)
|
||||
|
||||
The SDK now catches `httpx.NetworkError` and `httpx.RemoteProtocolError` for retry in addition to `httpx.TimeoutException` and `httpx.ConnectError`. This is transparent — no code changes needed.
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
# Migration Checklist
|
||||
|
||||
Use this checklist to track migration progress. Copy into your working notes and check off items as completed.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [ ] Update `honcho` package to v2.1.1
|
||||
- [ ] Remove any `honcho-core` imports
|
||||
|
||||
## Async Architecture Changes
|
||||
|
||||
- [ ] Remove `AsyncHoncho` imports → use `Honcho` with `.aio` accessor
|
||||
- [ ] Remove `AsyncPeer` imports → use `Peer` with `.aio` accessor
|
||||
- [ ] Remove `AsyncSession` imports → use `Session` with `.aio` accessor
|
||||
- [ ] Update all async client usage to use `.aio` accessor pattern
|
||||
- [ ] Update type hints: `AsyncPeer` → `Peer`, `AsyncSession` → `Session`
|
||||
|
||||
## Terminology: Observations → Conclusions
|
||||
|
||||
- [ ] Replace `Observation` import with `Conclusion`
|
||||
- [ ] Replace `ObservationScope` import with `ConclusionScope`
|
||||
- [ ] Replace `AsyncObservationScope` import with `ConclusionScopeAio`
|
||||
- [ ] Replace `.observations` property with `.conclusions`
|
||||
- [ ] Replace `.observations_of()` method with `.conclusions_of()`
|
||||
- [ ] Replace `.get_representation()` with `.representation()`
|
||||
|
||||
## Representation Changes
|
||||
|
||||
- [ ] Remove `Representation` import (now returns `str`)
|
||||
- [ ] Remove `ExplicitObservation` import
|
||||
- [ ] Remove `DeductiveObservation` import
|
||||
- [ ] Replace `working_rep()` with `representation()`
|
||||
- [ ] Update type hints from `Representation` to `str`
|
||||
- [ ] Remove `.explicit` property access
|
||||
- [ ] Remove `.deductive` property access
|
||||
- [ ] Replace `.is_empty()` checks with `not rep`
|
||||
- [ ] Remove `.merge_representation()` calls
|
||||
- [ ] Remove `.diff_representation()` calls
|
||||
- [ ] Remove `.str_no_timestamps()` calls
|
||||
- [ ] Remove `.format_as_markdown()` calls
|
||||
|
||||
## Configuration Changes
|
||||
|
||||
- [ ] Replace all `config=` parameters with `configuration=`
|
||||
- [ ] Replace `.get_config()` with `.get_configuration()`
|
||||
- [ ] Replace `.set_config()` with `.set_configuration()`
|
||||
- [ ] Rename `.get_peer_config()` → `.get_peer_configuration()`
|
||||
- [ ] Rename `.set_peer_config()` → `.set_peer_configuration()`
|
||||
- [ ] Import typed config classes from `honcho.api_types` if needed:
|
||||
- [ ] `PeerConfig`
|
||||
- [ ] `SessionConfiguration`
|
||||
- [ ] `WorkspaceConfiguration`
|
||||
|
||||
## Method Renames
|
||||
|
||||
### Peer Methods
|
||||
|
||||
- [ ] `peer.working_rep()` → `peer.representation()`
|
||||
- [ ] `peer.get_context()` → `peer.context()`
|
||||
- [ ] `peer.get_sessions()` → `peer.sessions()`
|
||||
- [ ] `peer.chat(stream=True)` → `peer.chat_stream()`
|
||||
|
||||
### Session Methods
|
||||
|
||||
- [ ] `session.get_context()` → `session.context()`
|
||||
- [ ] `session.get_summaries()` → `session.summaries()`
|
||||
- [ ] `session.get_messages()` → `session.messages()`
|
||||
- [ ] `session.get_peers()` → `session.peers()`
|
||||
- [ ] `session.get_peer_config()` → `session.get_peer_configuration()`
|
||||
- [ ] `session.set_peer_config()` → `session.set_peer_configuration()`
|
||||
- [ ] `session.working_rep()` → `session.representation()`
|
||||
- [ ] `session.get_deriver_status()` → `session.queue_status()`
|
||||
- [ ] Remove `session.poll_deriver_status()` calls
|
||||
|
||||
### Client Methods
|
||||
|
||||
- [ ] `client.get_peers()` → `client.peers()`
|
||||
- [ ] `client.get_sessions()` → `client.sessions()`
|
||||
- [ ] `client.get_workspaces()` → `client.workspaces()`
|
||||
- [ ] `client.get_deriver_status()` → `client.queue_status()`
|
||||
- [ ] Remove `client.poll_deriver_status()` calls
|
||||
- [ ] Move `client.update_message()` → `session.update_message()`
|
||||
|
||||
## Parameter Renames
|
||||
|
||||
- [ ] `include_most_derived=` → `include_most_frequent=`
|
||||
- [ ] `max_observations=` → `max_conclusions=`
|
||||
- [ ] `last_user_message=` → `search_query=`
|
||||
|
||||
## Return Type Changes
|
||||
|
||||
- [ ] Handle `card()` returning `list[str] | None` instead of `str`
|
||||
- [ ] Handle `.list()` on conclusions returning `SyncPage` instead of `list`
|
||||
|
||||
## Removed Features
|
||||
|
||||
- [ ] Remove any usage of `client.core` property
|
||||
- [ ] Remove usage of `"demo"` environment (only `"local"` and `"production"` remain)
|
||||
- [ ] Implement custom polling if you were using `poll_deriver_status()`
|
||||
|
||||
## Type Import Updates
|
||||
|
||||
- [ ] Replace `PeerContext` import with `PeerContextResponse` from `honcho.api_types`
|
||||
- [ ] Replace `DeriverStatus` import with `QueueStatusResponse` from `honcho.api_types`
|
||||
- [ ] Replace `MessageCreateParam` with `MessageCreateParams` (plural)
|
||||
- [ ] Move `SessionPeerConfig` import from `honcho.session` to `honcho.api_types`
|
||||
|
||||
## Exception Handling (Optional)
|
||||
|
||||
- [ ] Update exception handling to use new exception types if needed:
|
||||
- `HonchoError`, `APIError`, `BadRequestError`, `AuthenticationError`
|
||||
- `PermissionDeniedError`, `NotFoundError`, `ConflictError`
|
||||
- `UnprocessableEntityError`, `RateLimitError`, `ServerError`
|
||||
- `TimeoutError`, `ConnectionError`
|
||||
|
||||
## Card Method Updates (v2.0.1)
|
||||
|
||||
- [ ] Replace `peer.card()` with `peer.get_card()` (card() is deprecated)
|
||||
- [ ] Use `peer.set_card(list[str])` if setting peer cards
|
||||
|
||||
## Strict Validation (v2.0.2)
|
||||
|
||||
- [ ] Verify no input models pass unknown/misspelled fields (now raises `ValidationError`)
|
||||
- [ ] Check for typos in `PeerConfig`, `SessionConfiguration`, `WorkspaceConfiguration` fields
|
||||
|
||||
## peer() / session() API Call Change (v2.1.0)
|
||||
|
||||
- [ ] Update code that relied on lazy `peer()` / `session()` — they now always make API calls
|
||||
- [ ] Add `await` if using async and previously didn't need it for lazy construction
|
||||
|
||||
## New Properties (v2.1.0)
|
||||
|
||||
- [ ] Use `peer.created_at` / `session.created_at` where creation time is needed
|
||||
- [ ] Use `session.is_active` where session active status is needed
|
||||
|
||||
## New Methods (v2.1.0)
|
||||
|
||||
- [ ] Use `session.get_message(message_id)` to fetch single messages by ID
|
||||
|
||||
## Pagination Parameters (v2.1.0)
|
||||
|
||||
- [ ] Add `page`, `size`, `reverse` parameters to list calls where needed:
|
||||
- [ ] `client.peers()`
|
||||
- [ ] `client.sessions()`
|
||||
- [ ] `peer.sessions()`
|
||||
- [ ] `session.messages()`
|
||||
- [ ] `scope.list()`
|
||||
|
||||
## Final Verification
|
||||
|
||||
- [ ] Run type checker (mypy/pyright) with no errors
|
||||
- [ ] Run tests
|
||||
- [ ] Verify async operations work with `.aio` accessor
|
||||
- [ ] Verify streaming functionality works with `chat_stream()`
|
||||
- [ ] Verify configuration changes take effect
|
||||
|
|
@ -0,0 +1,358 @@
|
|||
---
|
||||
name: migrate-honcho
|
||||
description: Migrates Honcho Python SDK code from v1.6.0 to v2.1.1. Use when upgrading honcho package, fixing breaking changes after upgrade, or when errors mention AsyncHoncho, observations, Representation class, .core property, or get_config methods.
|
||||
---
|
||||
|
||||
# Honcho Python SDK Migration (v1.6.0 → v2.1.1)
|
||||
|
||||
## Overview
|
||||
|
||||
This skill migrates code from `honcho` Python SDK v1.6.0 to v2.1.1 (required for Honcho 3.0.0+).
|
||||
|
||||
**Key breaking changes:**
|
||||
|
||||
- `AsyncHoncho`/`AsyncPeer`/`AsyncSession` removed → use `.aio` accessor
|
||||
- "Observation" → "Conclusion" terminology
|
||||
- `Representation` class removed (returns `str` now)
|
||||
- `get_config`/`set_config` → `get_configuration`/`set_configuration`
|
||||
- Streaming via `chat_stream()` instead of `chat(stream=True)`
|
||||
- `poll_deriver_status()` removed
|
||||
- `.core` property removed
|
||||
|
||||
## Quick Migration
|
||||
|
||||
### 1. Update async architecture
|
||||
|
||||
```python
|
||||
# Before
|
||||
from honcho import AsyncHoncho, AsyncPeer, AsyncSession
|
||||
|
||||
async_client = AsyncHoncho()
|
||||
peer = await async_client.peer("user-123")
|
||||
response = await peer.chat("query")
|
||||
|
||||
# After
|
||||
from honcho import Honcho
|
||||
|
||||
client = Honcho()
|
||||
peer = await client.aio.peer("user-123")
|
||||
response = await peer.aio.chat("query")
|
||||
|
||||
# Async iteration
|
||||
async for p in client.aio.peers():
|
||||
print(p.id)
|
||||
```
|
||||
|
||||
### 2. Replace observations with conclusions
|
||||
|
||||
```python
|
||||
# Before
|
||||
from honcho import Observation, ObservationScope, AsyncObservationScope
|
||||
|
||||
scope = peer.observations
|
||||
scope = peer.observations_of("other-peer")
|
||||
rep = scope.get_representation()
|
||||
|
||||
# After
|
||||
from honcho import Conclusion, ConclusionScope, ConclusionScopeAio
|
||||
|
||||
scope = peer.conclusions
|
||||
scope = peer.conclusions_of("other-peer")
|
||||
rep = scope.representation() # Returns str
|
||||
```
|
||||
|
||||
### 3. Update representation handling
|
||||
|
||||
```python
|
||||
# Before
|
||||
from honcho import Representation, ExplicitObservation, DeductiveObservation
|
||||
|
||||
rep: Representation = peer.working_rep()
|
||||
print(rep.explicit)
|
||||
print(rep.deductive)
|
||||
if rep.is_empty():
|
||||
print("No observations")
|
||||
|
||||
# After
|
||||
rep: str = peer.representation()
|
||||
print(rep) # Just a string now
|
||||
if not rep:
|
||||
print("No conclusions")
|
||||
```
|
||||
|
||||
### 4. Rename configuration methods
|
||||
|
||||
```python
|
||||
# Before
|
||||
config = peer.get_config()
|
||||
peer.set_config({"observe_me": False})
|
||||
session.get_config()
|
||||
client.get_config()
|
||||
|
||||
# After
|
||||
from honcho.api_types import PeerConfig, SessionConfiguration, WorkspaceConfiguration
|
||||
|
||||
config = peer.get_configuration()
|
||||
peer.set_configuration(PeerConfig(observe_me=False))
|
||||
session.get_configuration()
|
||||
client.get_configuration()
|
||||
```
|
||||
|
||||
### 5. Update method names
|
||||
|
||||
```python
|
||||
# Before
|
||||
peer.working_rep()
|
||||
peer.get_context()
|
||||
peer.get_sessions()
|
||||
session.get_context()
|
||||
session.get_summaries()
|
||||
session.get_messages()
|
||||
session.get_peers()
|
||||
session.get_peer_config()
|
||||
client.get_peers()
|
||||
client.get_sessions()
|
||||
client.get_workspaces()
|
||||
|
||||
# After
|
||||
peer.representation()
|
||||
peer.context()
|
||||
peer.sessions()
|
||||
session.context()
|
||||
session.summaries()
|
||||
session.messages()
|
||||
session.peers()
|
||||
session.get_peer_configuration()
|
||||
client.peers()
|
||||
client.sessions()
|
||||
client.workspaces()
|
||||
```
|
||||
|
||||
### 6. Update streaming
|
||||
|
||||
```python
|
||||
# Before
|
||||
response = peer.chat("query", stream=True)
|
||||
for chunk in response:
|
||||
print(chunk, end="")
|
||||
|
||||
# After
|
||||
stream = peer.chat_stream("query")
|
||||
for chunk in stream:
|
||||
print(chunk, end="")
|
||||
```
|
||||
|
||||
### 7. Update queue status (formerly deriver)
|
||||
|
||||
```python
|
||||
# Before
|
||||
from honcho_core.types import DeriverStatus
|
||||
|
||||
status = client.get_deriver_status()
|
||||
status = client.poll_deriver_status(timeout=300.0) # Removed!
|
||||
|
||||
# After
|
||||
from honcho.api_types import QueueStatusResponse
|
||||
|
||||
status = client.queue_status()
|
||||
# poll_deriver_status removed - implement polling manually if needed
|
||||
```
|
||||
|
||||
### 8. Update representation parameters
|
||||
|
||||
```python
|
||||
# Before
|
||||
rep = peer.working_rep(
|
||||
include_most_derived=True,
|
||||
max_observations=50
|
||||
)
|
||||
|
||||
# After
|
||||
rep = peer.representation(
|
||||
include_most_frequent=True,
|
||||
max_conclusions=50
|
||||
)
|
||||
```
|
||||
|
||||
### 9. Move update_message to session
|
||||
|
||||
```python
|
||||
# Before
|
||||
updated = client.update_message(message=msg, metadata={"key": "value"}, session="sess-id")
|
||||
|
||||
# After
|
||||
updated = session.update_message(message=msg, metadata={"key": "value"})
|
||||
```
|
||||
|
||||
### 10. Update card() return type and method name
|
||||
|
||||
```python
|
||||
# Before
|
||||
card: str = peer.card() # Returns str
|
||||
|
||||
# After (v2.0.0+)
|
||||
card: list[str] | None = peer.get_card() # Returns list[str] | None
|
||||
if card:
|
||||
print("\n".join(card))
|
||||
|
||||
# peer.card() still works but is deprecated — use get_card()
|
||||
|
||||
# New in v2.0.1: set_card()
|
||||
peer.set_card(["Prefers dark mode", "Located in US"])
|
||||
```
|
||||
|
||||
### 11. Strict input validation (v2.0.2+)
|
||||
|
||||
All input models now reject unknown fields via `extra="forbid"` Pydantic validation. Previously, misspelled or extraneous fields were silently ignored.
|
||||
|
||||
```python
|
||||
# Before (v2.0.1 and earlier) — silently ignored
|
||||
peer = client.peer("user-1", configuration=PeerConfig(observe_mee=True)) # typo silently ignored
|
||||
|
||||
# After (v2.0.2+) — raises ValidationError
|
||||
peer = client.peer("user-1", configuration=PeerConfig(observe_mee=True)) # ValidationError!
|
||||
```
|
||||
|
||||
### 12. peer() and session() always make API calls (v2.1.0+)
|
||||
|
||||
**Breaking**: `peer()` and `session()` now always make a get-or-create API call. Previously, calling without metadata/configuration returned a lazy object with no API call.
|
||||
|
||||
```python
|
||||
# Before (v2.0.x) — no API call without options
|
||||
peer = client.peer("user-123") # Lazy, no network request
|
||||
|
||||
# After (v2.1.0+) — always hits the API
|
||||
peer = client.peer("user-123") # Makes POST to /peers (get-or-create)
|
||||
|
||||
# Async
|
||||
peer = await client.aio.peer("user-123") # Also always hits API
|
||||
```
|
||||
|
||||
### 13. New properties and methods (v2.1.0+)
|
||||
|
||||
```python
|
||||
# created_at on Peer and Session
|
||||
peer = client.peer("user-123")
|
||||
print(peer.created_at) # datetime | None
|
||||
|
||||
session = client.session("sess-1")
|
||||
print(session.created_at) # datetime | None
|
||||
|
||||
# is_active on Session
|
||||
print(session.is_active) # bool | None
|
||||
|
||||
# get_message() on Session
|
||||
msg = session.get_message("msg-id")
|
||||
# Async: msg = await session.aio.get_message("msg-id")
|
||||
```
|
||||
|
||||
### 14. Pagination parameters on list methods (v2.1.0+)
|
||||
|
||||
All list methods now accept `page`, `size`, and `reverse` parameters:
|
||||
|
||||
```python
|
||||
# Before (v2.0.x) — only filters
|
||||
peers_page = client.peers(filters={"metadata": {"role": "admin"}})
|
||||
|
||||
# After (v2.1.0+) — pagination controls
|
||||
peers_page = client.peers(
|
||||
filters={"metadata": {"role": "admin"}},
|
||||
page=2,
|
||||
size=25,
|
||||
reverse=True
|
||||
)
|
||||
|
||||
# Works on: client.peers(), client.sessions(), peer.sessions(),
|
||||
# session.messages(), scope.list()
|
||||
```
|
||||
|
||||
### 15. Broader HTTP retry logic (v2.1.1+)
|
||||
|
||||
The SDK now retries on `httpx.TimeoutException`, `httpx.NetworkError`, and `httpx.RemoteProtocolError` (previously only `httpx.TimeoutException` and `httpx.ConnectError`). These are mapped to the SDK's `TimeoutError` and `ConnectionError` respectively. No code changes needed — this is transparent.
|
||||
|
||||
## Quick Reference Table
|
||||
|
||||
| v1.6.0 | v2.0.0 |
|
||||
|--------|--------|
|
||||
| `AsyncHoncho()` | `Honcho()` + `.aio` accessor |
|
||||
| `AsyncPeer` | `Peer` + `.aio` accessor |
|
||||
| `AsyncSession` | `Session` + `.aio` accessor |
|
||||
| `Observation` | `Conclusion` |
|
||||
| `ObservationScope` | `ConclusionScope` |
|
||||
| `AsyncObservationScope` | `ConclusionScopeAio` |
|
||||
| `Representation` | `str` |
|
||||
| `.observations` | `.conclusions` |
|
||||
| `.observations_of()` | `.conclusions_of()` |
|
||||
| `.get_config()` | `.get_configuration()` |
|
||||
| `.set_config()` | `.set_configuration()` |
|
||||
| `.working_rep()` | `.representation()` |
|
||||
| `.get_context()` | `.context()` |
|
||||
| `.get_sessions()` | `.sessions()` |
|
||||
| `.get_peers()` | `.peers()` |
|
||||
| `.get_messages()` | `.messages()` |
|
||||
| `.get_summaries()` | `.summaries()` |
|
||||
| `.get_deriver_status()` | `.queue_status()` |
|
||||
| `.poll_deriver_status()` | *(removed)* |
|
||||
| `.get_peer_config()` | `.get_peer_configuration()` |
|
||||
| `.set_peer_config()` | `.set_peer_configuration()` |
|
||||
| `client.update_message()` | `session.update_message()` |
|
||||
| `peer.card()` | `peer.get_card()` *(card() deprecated)* |
|
||||
| *(new)* | `peer.set_card(list[str])` |
|
||||
| `chat(stream=True)` | `chat_stream()` |
|
||||
| `include_most_derived=` | `include_most_frequent=` |
|
||||
| `max_observations=` | `max_conclusions=` |
|
||||
| `last_user_message=` | `search_query=` |
|
||||
| `config=` | `configuration=` |
|
||||
| `PeerContext` | `PeerContextResponse` |
|
||||
| `DeriverStatus` | `QueueStatusResponse` |
|
||||
| `client.core` | *(removed)* |
|
||||
| *(new v2.1.0)* | `peer.created_at` / `session.created_at` |
|
||||
| *(new v2.1.0)* | `session.is_active` |
|
||||
| *(new v2.1.0)* | `session.get_message(id)` |
|
||||
| *(new v2.1.0)* | `page=`, `size=`, `reverse=` on list methods |
|
||||
|
||||
## Detailed Reference
|
||||
|
||||
For comprehensive details on each change, see:
|
||||
|
||||
- [DETAILED-CHANGES.md](DETAILED-CHANGES.md) - Full API change documentation
|
||||
- [MIGRATION-CHECKLIST.md](MIGRATION-CHECKLIST.md) - Step-by-step checklist
|
||||
|
||||
## New Exception Types
|
||||
|
||||
```python
|
||||
from honcho import (
|
||||
HonchoError,
|
||||
APIError,
|
||||
BadRequestError,
|
||||
AuthenticationError,
|
||||
PermissionDeniedError,
|
||||
NotFoundError,
|
||||
ConflictError,
|
||||
UnprocessableEntityError,
|
||||
RateLimitError,
|
||||
ServerError,
|
||||
TimeoutError,
|
||||
ConnectionError,
|
||||
)
|
||||
```
|
||||
|
||||
## New Import Locations
|
||||
|
||||
```python
|
||||
# Configuration types
|
||||
from honcho.api_types import (
|
||||
PeerConfig,
|
||||
SessionConfiguration,
|
||||
WorkspaceConfiguration,
|
||||
SessionPeerConfig,
|
||||
QueueStatusResponse,
|
||||
PeerContextResponse,
|
||||
)
|
||||
|
||||
# Async type hints
|
||||
from honcho import HonchoAio, PeerAio, SessionAio
|
||||
|
||||
# Message types (note: Params is plural now)
|
||||
from honcho import Message, MessageCreateParams
|
||||
```
|
||||
|
|
@ -0,0 +1,583 @@
|
|||
# Detailed API Changes
|
||||
|
||||
## Client Changes
|
||||
|
||||
### `.core` Property Removed
|
||||
|
||||
The `.core` property (which exposed the raw `@honcho-ai/core` client) has been removed. Use `.http` for advanced HTTP access.
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const workspace = await client.core.workspaces.getOrCreate({ id: 'my-workspace' })
|
||||
|
||||
// After - SDK handles workspace creation automatically
|
||||
// For advanced usage:
|
||||
const response = await client.http.post('/v3/workspaces', { body: { id: 'my-workspace' } })
|
||||
```
|
||||
|
||||
### Listing Methods Return Type Changes
|
||||
|
||||
- `workspaces()` now returns `Page<string>` instead of `string[]`
|
||||
- `session.peers()` now returns `Peer[]` instead of `Page<Peer>`
|
||||
|
||||
```typescript
|
||||
const workspacePage = await honcho.workspaces()
|
||||
for (const id of workspacePage.items) {
|
||||
console.log(id)
|
||||
}
|
||||
```
|
||||
|
||||
### `updateMessage()` Moved to Session
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
await honcho.updateMessage(message, { key: 'value' }, session)
|
||||
|
||||
// After
|
||||
await session.updateMessage(message, { key: 'value' })
|
||||
```
|
||||
|
||||
### `config` Option Renamed to `configuration`
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const peer = await honcho.peer('user-id', { config: { observe_me: true } })
|
||||
const session = await honcho.session('session-id', { config: { ... } })
|
||||
|
||||
// After
|
||||
const peer = await honcho.peer('user-id', { configuration: { observeMe: true } })
|
||||
const session = await honcho.session('session-id', { configuration: { reasoning: { enabled: true } } })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Peer Changes
|
||||
|
||||
### Streaming API
|
||||
|
||||
The `stream` option on `chat()` has been removed. Use `chatStream()` instead.
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const stream = await peer.chat('Hello', { stream: true })
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk)
|
||||
}
|
||||
|
||||
// After
|
||||
const stream = await peer.chatStream('Hello')
|
||||
for await (const chunk of stream) {
|
||||
process.stdout.write(chunk)
|
||||
}
|
||||
```
|
||||
|
||||
Non-streaming `chat()` now only returns `string | null`:
|
||||
|
||||
```typescript
|
||||
const response = await peer.chat('Hello') // Returns string | null
|
||||
```
|
||||
|
||||
### New `reasoningLevel` Option
|
||||
|
||||
```typescript
|
||||
const response = await peer.chat('Complex question', {
|
||||
reasoningLevel: 'high' // 'minimal' | 'low' | 'medium' | 'high' | 'max'
|
||||
})
|
||||
```
|
||||
|
||||
### `workingRep()` Renamed to `representation()`
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const rep = await peer.workingRep(session, target, options)
|
||||
console.log(rep.toString())
|
||||
console.log(rep.explicit)
|
||||
console.log(rep.deductive)
|
||||
|
||||
// After
|
||||
const rep = await peer.representation({
|
||||
session,
|
||||
target,
|
||||
searchQuery: options?.searchQuery,
|
||||
maxConclusions: options?.maxObservations,
|
||||
includeMostFrequent: options?.includeMostDerived,
|
||||
})
|
||||
console.log(rep) // Returns string directly
|
||||
```
|
||||
|
||||
### `getContext()` Renamed to `context()`
|
||||
|
||||
Options are now passed as a single object:
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const ctx = await peer.getContext(target, options)
|
||||
|
||||
// After
|
||||
const ctx = await peer.context({ target, ...options })
|
||||
```
|
||||
|
||||
### `card()` Return Type Changed
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const card = await peer.card(target) // Returns string
|
||||
|
||||
// After
|
||||
const card = await peer.card(target) // Returns string[] | null
|
||||
```
|
||||
|
||||
### `message()` Options Changed
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const msg = peer.message('Hello', {
|
||||
metadata: { key: 'value' },
|
||||
configuration: { deriver: { enabled: true } },
|
||||
created_at: '2024-01-01T00:00:00Z'
|
||||
})
|
||||
// Returns ValidatedMessageCreate with peer_id, created_at
|
||||
|
||||
// After
|
||||
const msg = peer.message('Hello', {
|
||||
metadata: { key: 'value' },
|
||||
configuration: { reasoning: { enabled: true } },
|
||||
createdAt: '2024-01-01T00:00:00Z'
|
||||
})
|
||||
// Returns MessageInput with peerId, createdAt
|
||||
```
|
||||
|
||||
### `PeerContext.representation` Type Changed
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const ctx = await peer.getContext()
|
||||
if (ctx.representation) {
|
||||
console.log(ctx.representation.explicit) // Representation object
|
||||
console.log(ctx.representation.deductive)
|
||||
}
|
||||
|
||||
// After
|
||||
const ctx = await peer.context()
|
||||
if (ctx.representation) {
|
||||
console.log(ctx.representation) // Now a string
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Changes
|
||||
|
||||
### `getPeers()` Return Type Changed
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const peers = await session.getPeers() // Returns Page<Peer>
|
||||
|
||||
// After
|
||||
const peers = await session.peers() // Returns Peer[]
|
||||
```
|
||||
|
||||
### `getContext()` Renamed to `context()`
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const ctx = await session.getContext({
|
||||
summary: true,
|
||||
peerTarget: user,
|
||||
peerPerspective: assistant,
|
||||
lastUserMessage: "What are my preferences?",
|
||||
representationOptions: {
|
||||
maxObservations: 50,
|
||||
includeMostDerived: true
|
||||
}
|
||||
})
|
||||
|
||||
// After
|
||||
const ctx = await session.context({
|
||||
summary: true,
|
||||
peerTarget: user,
|
||||
peerPerspective: assistant,
|
||||
searchQuery: "What are my preferences?",
|
||||
representationOptions: {
|
||||
maxConclusions: 50,
|
||||
includeMostFrequent: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### `SessionPeerConfig` Uses camelCase and Methods Renamed
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
await session.setPeerConfig(peer, {
|
||||
observe_me: true,
|
||||
observe_others: false
|
||||
})
|
||||
const config = await session.peerConfig(peer)
|
||||
|
||||
// After
|
||||
await session.setPeerConfiguration(peer, {
|
||||
observeMe: true,
|
||||
observeOthers: false
|
||||
})
|
||||
const config = await session.getPeerConfiguration(peer)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Message Changes
|
||||
|
||||
### Message Properties Use camelCase
|
||||
|
||||
```typescript
|
||||
// Before (from @honcho-ai/core)
|
||||
message.peer_id
|
||||
message.session_id
|
||||
message.workspace_id
|
||||
message.created_at
|
||||
message.token_count
|
||||
|
||||
// After
|
||||
message.peerId
|
||||
message.sessionId
|
||||
message.workspaceId
|
||||
message.createdAt
|
||||
message.tokenCount
|
||||
```
|
||||
|
||||
### MessageInput Type
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
interface ValidatedMessageCreate {
|
||||
peer_id: string
|
||||
content: string
|
||||
metadata?: Record<string, unknown>
|
||||
configuration?: Record<string, unknown>
|
||||
created_at?: string
|
||||
}
|
||||
|
||||
// After
|
||||
interface MessageInput {
|
||||
peerId: string
|
||||
content: string
|
||||
metadata?: Record<string, unknown>
|
||||
configuration?: MessageConfiguration
|
||||
createdAt?: string
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Streaming Changes
|
||||
|
||||
### `DialecticStreamDelta` Removed
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
import { DialecticStreamDelta, DialecticStreamChunk } from '@honcho-ai/sdk'
|
||||
|
||||
// After
|
||||
import { DialecticStreamChunk, DialecticStreamResponse } from '@honcho-ai/sdk'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuration Changes
|
||||
|
||||
### Workspace Configuration
|
||||
|
||||
Configurations are now strongly typed objects instead of `Record<string, unknown>`.
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
await honcho.setConfig({
|
||||
deriver: { enabled: true },
|
||||
some_custom_key: 'value'
|
||||
})
|
||||
|
||||
// After
|
||||
await honcho.setConfiguration({
|
||||
reasoning: {
|
||||
enabled: true,
|
||||
customInstructions: 'Be concise'
|
||||
},
|
||||
peerCard: {
|
||||
use: true,
|
||||
create: true
|
||||
},
|
||||
summary: {
|
||||
enabled: true,
|
||||
messagesPerShortSummary: 20,
|
||||
messagesPerLongSummary: 60
|
||||
},
|
||||
dream: {
|
||||
enabled: true
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Peer Configuration
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
await peer.setConfig({ observe_me: false })
|
||||
|
||||
// After
|
||||
await peer.setConfiguration({ observeMe: false })
|
||||
```
|
||||
|
||||
### Message Configuration
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
peer.message('Hello', {
|
||||
configuration: {
|
||||
deriver: { enabled: true }
|
||||
}
|
||||
})
|
||||
|
||||
// After
|
||||
peer.message('Hello', {
|
||||
configuration: {
|
||||
reasoning: {
|
||||
enabled: true,
|
||||
customInstructions: 'Focus on emotions'
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Type Changes
|
||||
|
||||
### Removed Exports
|
||||
|
||||
- `Observation` (use `Conclusion`)
|
||||
- `ObservationScope` (use `ConclusionScope`)
|
||||
- `ObservationData`, `ObservationCreateParam`, `ObservationQueryParams`
|
||||
- `Representation`, `RepresentationData`, `RepresentationOptions` (class removed)
|
||||
- `ExplicitObservation`, `DeductiveObservation`
|
||||
- `DialecticStreamDelta`
|
||||
- `DeriverStatusOptions` (use `QueueStatusOptions`)
|
||||
- `MessageCreate` (use `MessageInput`)
|
||||
- `WorkingRepParams`
|
||||
|
||||
### New Exports
|
||||
|
||||
```typescript
|
||||
import {
|
||||
// Domain classes
|
||||
Conclusion,
|
||||
ConclusionScope,
|
||||
ConclusionCreateParams,
|
||||
|
||||
// Error types
|
||||
HonchoError,
|
||||
AuthenticationError,
|
||||
BadRequestError,
|
||||
NotFoundError,
|
||||
PermissionDeniedError,
|
||||
RateLimitError,
|
||||
ConflictError,
|
||||
UnprocessableEntityError,
|
||||
ServerError,
|
||||
ConnectionError,
|
||||
TimeoutError,
|
||||
|
||||
// Message types
|
||||
Message,
|
||||
MessageInput,
|
||||
|
||||
// Configuration types
|
||||
WorkspaceConfig,
|
||||
SessionConfig,
|
||||
PeerConfig,
|
||||
SessionPeerConfig,
|
||||
MessageConfiguration,
|
||||
ReasoningConfig,
|
||||
PeerCardConfig,
|
||||
SummaryConfig,
|
||||
DreamConfig,
|
||||
|
||||
// API response types
|
||||
QueueStatus,
|
||||
QueueStatusOptions,
|
||||
RepresentationOptions,
|
||||
ConclusionQueryParams,
|
||||
ConclusionResponse,
|
||||
} from '@honcho-ai/sdk'
|
||||
```
|
||||
|
||||
### SummaryData Type Changed
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
interface SummaryData {
|
||||
content: string
|
||||
message_id: string
|
||||
summary_type: string
|
||||
created_at: string
|
||||
token_count: number
|
||||
}
|
||||
|
||||
// After
|
||||
interface SummaryData {
|
||||
content: string
|
||||
messageId: string
|
||||
summaryType: string
|
||||
createdAt: string
|
||||
tokenCount: number
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Post-v2.0.0 Changes
|
||||
|
||||
---
|
||||
|
||||
## Card Method Deprecation and setCard (v2.0.1)
|
||||
|
||||
### Before (v2.0.0)
|
||||
|
||||
```typescript
|
||||
const card = await peer.card(target) // string[] | null
|
||||
```
|
||||
|
||||
### After (v2.0.1+)
|
||||
|
||||
```typescript
|
||||
// getCard() is the preferred method
|
||||
const card = await peer.getCard(target) // string[] | null
|
||||
|
||||
// card() still works but is deprecated
|
||||
const card = await peer.card(target) // Deprecated
|
||||
|
||||
// New: setCard()
|
||||
const updated = await peer.setCard(['Fact 1', 'Fact 2'])
|
||||
const updated = await peer.setCard(['Fact 1'], targetPeer)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Strict Input Validation (v2.0.2)
|
||||
|
||||
Client constructor and all input schemas now use `.strict()` Zod validation.
|
||||
|
||||
```typescript
|
||||
// Before (v2.0.1) — silently ignored
|
||||
const honcho = new Honcho({ baseUrl: 'http://...' }) // typo fell back to default
|
||||
|
||||
// After (v2.0.2+) — ZodError thrown
|
||||
const honcho = new Honcho({ baseUrl: 'http://...' }) // ZodError: Unrecognized key "baseUrl"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## peer() and session() Always Make API Calls (v2.1.0)
|
||||
|
||||
### Before (v2.0.x)
|
||||
|
||||
```typescript
|
||||
// Without options: lazy object, no API call
|
||||
const peer = honcho.peer('user-123')
|
||||
|
||||
// With options: made API call
|
||||
const peer = await honcho.peer('user-123', { metadata: { key: 'value' } })
|
||||
```
|
||||
|
||||
### After (v2.1.0+)
|
||||
|
||||
```typescript
|
||||
// Always makes a get-or-create API call
|
||||
const peer = await honcho.peer('user-123')
|
||||
// peer.createdAt is now always populated
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## New Properties: createdAt, isActive (v2.1.0)
|
||||
|
||||
```typescript
|
||||
// Peer
|
||||
const peer = await honcho.peer('user-123')
|
||||
console.log(peer.createdAt) // string | undefined
|
||||
|
||||
// Session
|
||||
const session = await honcho.session('sess-1')
|
||||
console.log(session.createdAt) // string | undefined
|
||||
console.log(session.isActive) // boolean | undefined
|
||||
|
||||
// Refreshed by getMetadata(), getConfiguration(), and refresh()
|
||||
await session.refresh()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## getMessage() on Session (v2.1.0)
|
||||
|
||||
```typescript
|
||||
// Fetch a single message by ID
|
||||
const msg = await session.getMessage('msg-abc123')
|
||||
console.log(msg.content, msg.createdAt)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pagination Parameters (v2.1.0)
|
||||
|
||||
All list methods now accept `page`, `size`, and `reverse`:
|
||||
|
||||
```typescript
|
||||
// Defaults: page=1, size=50, reverse=false
|
||||
const peersPage = await honcho.peers({
|
||||
filters: { metadata: { role: 'admin' } },
|
||||
page: 2,
|
||||
size: 25,
|
||||
reverse: true
|
||||
})
|
||||
|
||||
// Page<T> properties:
|
||||
console.log(peersPage.total) // Total items
|
||||
console.log(peersPage.pages) // Total pages
|
||||
console.log(peersPage.hasNextPage) // boolean
|
||||
|
||||
// Works on:
|
||||
// honcho.peers(), honcho.sessions(), honcho.workspaces()
|
||||
// peer.sessions()
|
||||
// session.messages()
|
||||
// scope.list()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## searchQuery Moved in context() (v2.1.0)
|
||||
|
||||
### Before (v2.0.x)
|
||||
|
||||
```typescript
|
||||
const ctx = await session.context({
|
||||
searchQuery: 'What are my preferences?',
|
||||
representationOptions: { maxConclusions: 50 }
|
||||
})
|
||||
```
|
||||
|
||||
### After (v2.1.0+)
|
||||
|
||||
```typescript
|
||||
const ctx = await session.context({
|
||||
representationOptions: {
|
||||
searchQuery: 'What are my preferences?',
|
||||
maxConclusions: 50
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Broader Fetch Retry Logic (v2.1.1)
|
||||
|
||||
The SDK now retries on all `TypeError` network failures (connection resets, DNS errors, etc.) instead of only those containing `'fetch'` in the error message. This is transparent — no code changes needed.
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
# Migration Checklist
|
||||
|
||||
Use this checklist to track migration progress. Copy into your working notes and check off items as completed.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [ ] Remove `@honcho-ai/core` from dependencies
|
||||
- [ ] Update `@honcho-ai/sdk` to v2.1.1
|
||||
|
||||
## Client-Level Changes
|
||||
|
||||
- [ ] Replace all `.core` usages with `.http` or remove
|
||||
- [ ] Rename `getConfig()` → `getConfiguration()`
|
||||
- [ ] Rename `setConfig()` → `setConfiguration()`
|
||||
- [ ] Rename `getPeers()` → `peers()`
|
||||
- [ ] Rename `getSessions()` → `sessions()`
|
||||
- [ ] Rename `getWorkspaces()` → `workspaces()` (returns `Page<string>` now)
|
||||
- [ ] Rename `getDeriverStatus()` → `queueStatus()`
|
||||
- [ ] Remove `pollDeriverStatus()` calls entirely (no replacement—do not rely on queue being empty)
|
||||
- [ ] Move `updateMessage()` calls from client to session
|
||||
|
||||
## Peer-Level Changes
|
||||
|
||||
- [ ] Replace `peer.chat(q, { stream: true })` with `peer.chatStream(q)`
|
||||
- [ ] Rename `getSessions()` → `sessions()`
|
||||
- [ ] Rename `getConfig()` → `getConfiguration()`
|
||||
- [ ] Rename `setConfig()` → `setConfiguration()`
|
||||
- [ ] Rename `peerConfig()` → `getPeerConfiguration()`
|
||||
- [ ] Rename `setPeerConfig()` → `setPeerConfiguration()`
|
||||
- [ ] Rename `workingRep()` → `representation()` (returns string now)
|
||||
- [ ] Rename `getContext()` → `context()`
|
||||
- [ ] Replace `observations` → `conclusions`
|
||||
- [ ] Replace `observationsOf()` → `conclusionsOf()`
|
||||
- [ ] Handle `card()` returning `string[] | null` instead of `string`
|
||||
|
||||
## Session-Level Changes
|
||||
|
||||
- [ ] Rename `getPeers()` → `peers()` (returns `Peer[]` now, not `Page<Peer>`)
|
||||
- [ ] Rename `getMessages()` → `messages()`
|
||||
- [ ] Rename `getConfig()` → `getConfiguration()`
|
||||
- [ ] Rename `setConfig()` → `setConfiguration()`
|
||||
- [ ] Rename `getContext()` → `context()`
|
||||
- [ ] Rename `getSummaries()` → `summaries()`
|
||||
- [ ] Rename `getDeriverStatus()` → `queueStatus()`
|
||||
- [ ] Remove `pollDeriverStatus()` calls entirely (no replacement—do not rely on queue being empty)
|
||||
- [ ] Rename `workingRep()` → `representation()` (returns string now)
|
||||
|
||||
## Terminology Changes
|
||||
|
||||
- [ ] Rename `maxObservations` → `maxConclusions`
|
||||
- [ ] Rename `includeMostDerived` → `includeMostFrequent`
|
||||
- [ ] Rename `lastUserMessage` → `searchQuery`
|
||||
- [ ] Rename `Observation` type → `Conclusion`
|
||||
- [ ] Rename `ObservationScope` type → `ConclusionScope`
|
||||
|
||||
## snake_case → camelCase
|
||||
|
||||
- [ ] Update all `{ config: ... }` to `{ configuration: ... }`
|
||||
- [ ] Update `observe_me` → `observeMe`
|
||||
- [ ] Update `observe_others` → `observeOthers`
|
||||
- [ ] Update `created_at` → `createdAt`
|
||||
- [ ] Update message property access:
|
||||
- [ ] `peer_id` → `peerId`
|
||||
- [ ] `session_id` → `sessionId`
|
||||
- [ ] `workspace_id` → `workspaceId`
|
||||
- [ ] `created_at` → `createdAt`
|
||||
- [ ] `token_count` → `tokenCount`
|
||||
- [ ] Update summary property access:
|
||||
- [ ] `message_id` → `messageId`
|
||||
- [ ] `summary_type` → `summaryType`
|
||||
|
||||
## Configuration Objects
|
||||
|
||||
- [ ] Update workspace configuration to typed structure
|
||||
- [ ] Update session configuration to typed structure
|
||||
- [ ] Update peer configuration to typed structure
|
||||
- [ ] Replace `deriver` config with `reasoning` config
|
||||
|
||||
## Error Handling
|
||||
|
||||
- [ ] Update error handling to use new error types if needed
|
||||
|
||||
## Type Imports
|
||||
|
||||
- [ ] Remove imports of deleted types:
|
||||
- `Observation`, `ObservationScope`, `ObservationData`
|
||||
- `Representation`, `RepresentationData`
|
||||
- `ExplicitObservation`, `DeductiveObservation`
|
||||
- `DialecticStreamDelta`
|
||||
- `DeriverStatusOptions`
|
||||
- `MessageCreate`, `ValidatedMessageCreate`
|
||||
- `WorkingRepParams`
|
||||
- [ ] Add imports of new types as needed:
|
||||
- `Conclusion`, `ConclusionScope`
|
||||
- `MessageInput`
|
||||
- `QueueStatusOptions`
|
||||
- Error types
|
||||
|
||||
## Representation Handling
|
||||
|
||||
- [ ] Remove usage of `Representation` class methods (`.explicit`, `.deductive`, `.isEmpty()`, `.diff()`)
|
||||
- [ ] Handle representation as plain string
|
||||
|
||||
## Card Method Updates (v2.0.1)
|
||||
|
||||
- [ ] Replace `peer.card()` with `peer.getCard()` (card() is deprecated)
|
||||
- [ ] Use `peer.setCard(string[])` if setting peer cards
|
||||
|
||||
## Strict Validation (v2.0.2)
|
||||
|
||||
- [ ] Verify no constructor options or input schemas pass unknown/misspelled fields (now throws `ZodError`)
|
||||
- [ ] Check for `baseUrl` vs `baseURL` typo in Honcho constructor
|
||||
|
||||
## peer() / session() API Call Change (v2.1.0)
|
||||
|
||||
- [ ] Update code that relied on lazy `peer()` / `session()` — they now always make API calls
|
||||
- [ ] Ensure all `peer()` and `session()` calls are `await`ed
|
||||
|
||||
## New Properties (v2.1.0)
|
||||
|
||||
- [ ] Use `peer.createdAt` / `session.createdAt` where creation time is needed
|
||||
- [ ] Use `session.isActive` where session active status is needed
|
||||
|
||||
## New Methods (v2.1.0)
|
||||
|
||||
- [ ] Use `session.getMessage(messageId)` to fetch single messages by ID
|
||||
|
||||
## Pagination Parameters (v2.1.0)
|
||||
|
||||
- [ ] Add `page`, `size`, `reverse` parameters to list calls where needed:
|
||||
- [ ] `honcho.peers()`
|
||||
- [ ] `honcho.sessions()`
|
||||
- [ ] `honcho.workspaces()`
|
||||
- [ ] `peer.sessions()`
|
||||
- [ ] `session.messages()`
|
||||
- [ ] `scope.list()`
|
||||
|
||||
## searchQuery Location Change (v2.1.0)
|
||||
|
||||
- [ ] Move `searchQuery` from top-level `context()` options to `representationOptions.searchQuery`
|
||||
|
||||
## Final Verification
|
||||
|
||||
- [ ] Run TypeScript compiler with no errors
|
||||
- [ ] Run tests
|
||||
- [ ] Verify streaming functionality works
|
||||
- [ ] Verify configuration changes take effect
|
||||
|
|
@ -0,0 +1,330 @@
|
|||
---
|
||||
name: migrate-honcho-ts
|
||||
description: Migrates Honcho TypeScript SDK code from v1.6.0 to v2.1.1. Use when upgrading @honcho-ai/sdk, fixing breaking changes after upgrade, or when errors mention removed APIs like .core, getConfig, observations, or snake_case properties.
|
||||
---
|
||||
|
||||
# Honcho TypeScript SDK Migration (v1.6.0 → v2.1.1)
|
||||
|
||||
## Overview
|
||||
|
||||
This skill migrates code from `@honcho-ai/sdk` v1.6.0 to v2.1.1 (required for Honcho 3.0.0+).
|
||||
|
||||
**Key breaking changes:**
|
||||
|
||||
- `@honcho-ai/core` dependency removed
|
||||
- "Observation" → "Conclusion" terminology
|
||||
- "Deriver" → "Queue" terminology
|
||||
- `getConfig`/`setConfig` → `getConfiguration`/`setConfiguration`
|
||||
- `snake_case` → `camelCase` throughout
|
||||
- Streaming via `chatStream()` instead of `chat({ stream: true })`
|
||||
- `Representation` class removed (returns string now)
|
||||
|
||||
## Quick Migration
|
||||
|
||||
### 1. Update dependencies
|
||||
|
||||
Remove `@honcho-ai/core` from package.json. The SDK now has its own HTTP client.
|
||||
|
||||
### 2. Replace `.core` with `.http`
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const workspace = await client.core.workspaces.getOrCreate({ id: 'my-workspace' })
|
||||
|
||||
// After
|
||||
const response = await client.http.post('/v3/workspaces', { body: { id: 'my-workspace' } })
|
||||
```
|
||||
|
||||
### 3. Rename configuration methods
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
await honcho.getConfig()
|
||||
await honcho.setConfig({ key: 'value' })
|
||||
await peer.getConfig()
|
||||
await session.getConfig()
|
||||
|
||||
// After
|
||||
await honcho.getConfiguration()
|
||||
await honcho.setConfiguration({ reasoning: { enabled: true } })
|
||||
await peer.getConfiguration()
|
||||
await session.getConfiguration()
|
||||
```
|
||||
|
||||
### 4. Rename listing methods
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const peers = await honcho.getPeers()
|
||||
const sessions = await honcho.getSessions()
|
||||
const workspaces = await honcho.getWorkspaces() // string[]
|
||||
|
||||
// After
|
||||
const peers = await honcho.peers()
|
||||
const sessions = await honcho.sessions()
|
||||
const workspaces = await honcho.workspaces() // Page<string>
|
||||
```
|
||||
|
||||
### 5. Update streaming
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const stream = await peer.chat('Hello', { stream: true })
|
||||
|
||||
// After
|
||||
const stream = await peer.chatStream('Hello')
|
||||
```
|
||||
|
||||
### 6. Update observations → conclusions
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
peer.observations
|
||||
peer.observationsOf('bob')
|
||||
maxObservations: 50
|
||||
includeMostDerived: true
|
||||
|
||||
// After
|
||||
peer.conclusions
|
||||
peer.conclusionsOf('bob')
|
||||
maxConclusions: 50
|
||||
includeMostFrequent: true
|
||||
```
|
||||
|
||||
### 7. Update queue status methods
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
await honcho.getDeriverStatus({ observer: peer })
|
||||
await honcho.pollDeriverStatus({ timeoutMs: 60000 }) // REMOVE - see note below
|
||||
|
||||
// After
|
||||
await honcho.queueStatus({ observer: peer })
|
||||
// pollDeriverStatus() has no replacement - see note below
|
||||
```
|
||||
|
||||
**Important:** `pollDeriverStatus()` and its polling pattern have been removed entirely. Do not rely on the queue ever being empty. The queue is a continuous processing system—new messages may arrive at any time, and waiting for "completion" is not a valid pattern. If your code previously polled for queue completion, redesign it to work without that assumption.
|
||||
|
||||
### 8. Convert snake_case to camelCase
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
message.peer_id
|
||||
message.session_id
|
||||
message.created_at
|
||||
message.token_count
|
||||
{ observe_me: true, observe_others: false }
|
||||
{ created_at: '2024-01-01' }
|
||||
|
||||
// After
|
||||
message.peerId
|
||||
message.sessionId
|
||||
message.createdAt
|
||||
message.tokenCount
|
||||
{ observeMe: true, observeOthers: false }
|
||||
{ createdAt: '2024-01-01' }
|
||||
```
|
||||
|
||||
### 9. Update representation calls
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const rep = await peer.workingRep(session, target, options)
|
||||
console.log(rep.explicit) // ExplicitObservation[]
|
||||
console.log(rep.deductive) // DeductiveObservation[]
|
||||
|
||||
// After
|
||||
const rep = await peer.representation({ session, target, ...options })
|
||||
console.log(rep) // string
|
||||
```
|
||||
|
||||
### 10. Move updateMessage to session
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
await honcho.updateMessage(message, metadata, session)
|
||||
|
||||
// After
|
||||
await session.updateMessage(message, metadata)
|
||||
```
|
||||
|
||||
### 11. Update card() to getCard() (v2.0.1+)
|
||||
|
||||
```typescript
|
||||
// Before
|
||||
const card = await peer.card(target)
|
||||
|
||||
// After (v2.0.1+)
|
||||
const card = await peer.getCard(target) // Returns string[] | null
|
||||
|
||||
// peer.card() still works but is deprecated — use getCard()
|
||||
|
||||
// New: setPeerCard / setCard
|
||||
await peer.setCard(['Prefers dark mode', 'Located in US'])
|
||||
```
|
||||
|
||||
### 12. Strict input validation (v2.0.2+)
|
||||
|
||||
Client constructor and all input schemas now reject unknown options via `.strict()` Zod validation.
|
||||
|
||||
```typescript
|
||||
// Before (v2.0.1 and earlier) — silently ignored
|
||||
const honcho = new Honcho({ baseUrl: 'http://...' }) // typo: baseUrl vs baseURL — silently fell back to default
|
||||
|
||||
// After (v2.0.2+) — throws ZodError
|
||||
const honcho = new Honcho({ baseUrl: 'http://...' }) // ZodError! Use baseURL
|
||||
```
|
||||
|
||||
### 13. peer() and session() always make API calls (v2.1.0+)
|
||||
|
||||
**Breaking**: `peer()` and `session()` now always make a get-or-create API call. Previously, calling without metadata/configuration returned a lazy object with no API call.
|
||||
|
||||
```typescript
|
||||
// Before (v2.0.x) — no API call without options
|
||||
const session = honcho.session('my-session') // Lazy, no network request
|
||||
|
||||
// After (v2.1.0+) — always hits the API
|
||||
const session = await honcho.session('my-session') // Makes POST to /sessions (get-or-create)
|
||||
```
|
||||
|
||||
### 14. New properties and methods (v2.1.0+)
|
||||
|
||||
```typescript
|
||||
// createdAt on Peer and Session
|
||||
const peer = await honcho.peer('user-123')
|
||||
console.log(peer.createdAt) // string | undefined
|
||||
|
||||
const session = await honcho.session('sess-1')
|
||||
console.log(session.createdAt) // string | undefined
|
||||
|
||||
// isActive on Session
|
||||
console.log(session.isActive) // boolean | undefined
|
||||
|
||||
// getMessage() on Session
|
||||
const msg = await session.getMessage('msg-id')
|
||||
```
|
||||
|
||||
### 15. Pagination parameters on list methods (v2.1.0+)
|
||||
|
||||
All list methods now accept `page`, `size`, and `reverse` parameters:
|
||||
|
||||
```typescript
|
||||
// Before (v2.0.x) — only filters
|
||||
const peers = await honcho.peers({ metadata: { role: 'admin' } })
|
||||
|
||||
// After (v2.1.0+) — pagination controls via options object
|
||||
const peers = await honcho.peers({
|
||||
filters: { metadata: { role: 'admin' } },
|
||||
page: 2,
|
||||
size: 25,
|
||||
reverse: true
|
||||
})
|
||||
|
||||
// Legacy raw-filter form still works:
|
||||
const peers = await honcho.peers({ metadata: { role: 'admin' } })
|
||||
|
||||
// Works on: honcho.peers(), honcho.sessions(), honcho.workspaces(),
|
||||
// peer.sessions(), session.messages(), scope.list()
|
||||
```
|
||||
|
||||
### 16. searchQuery moved in context() (v2.1.0+)
|
||||
|
||||
**Breaking**: `searchQuery` removed from top-level `context()` options. Use `representationOptions.searchQuery` instead.
|
||||
|
||||
```typescript
|
||||
// Before (v2.0.x)
|
||||
await session.context({ searchQuery: '...' })
|
||||
|
||||
// After (v2.1.0+)
|
||||
await session.context({ representationOptions: { searchQuery: '...' } })
|
||||
```
|
||||
|
||||
### 17. Broader fetch retry logic (v2.1.1+)
|
||||
|
||||
The SDK now retries on all `TypeError` network failures (connection resets, DNS errors, etc.) instead of only those with `'fetch'` in the message. No code changes needed — this is transparent.
|
||||
|
||||
## Quick Reference Table
|
||||
|
||||
| v1.6.0 | v2.0.0 |
|
||||
|--------|--------|
|
||||
| `client.core` | `client.http` |
|
||||
| `getConfig()` | `getConfiguration()` |
|
||||
| `setConfig()` | `setConfiguration()` |
|
||||
| `getPeers()` | `peers()` |
|
||||
| `getSessions()` | `sessions()` |
|
||||
| `getWorkspaces()` | `workspaces()` |
|
||||
| `getDeriverStatus()` | `queueStatus()` |
|
||||
| `pollDeriverStatus()` | *Removed - do not poll* |
|
||||
| `peer.chat(q, { stream: true })` | `peer.chatStream(q)` |
|
||||
| `peer.workingRep()` | `peer.representation()` |
|
||||
| `peer.getContext()` | `peer.context()` |
|
||||
| `peer.observations` | `peer.conclusions` |
|
||||
| `peer.observationsOf()` | `peer.conclusionsOf()` |
|
||||
| `session.getPeers()` | `session.peers()` |
|
||||
| `session.getMessages()` | `session.messages()` |
|
||||
| `session.getSummaries()` | `session.summaries()` |
|
||||
| `session.getContext()` | `session.context()` |
|
||||
| `session.workingRep()` | `session.representation()` |
|
||||
| `session.peerConfig()` | `session.getPeerConfiguration()` |
|
||||
| `session.setPeerConfig()` | `session.setPeerConfiguration()` |
|
||||
| `{ timeoutMs: 60000 }` | `{ timeout: 60000 }` |
|
||||
| `{ maxObservations: 50 }` | `{ maxConclusions: 50 }` |
|
||||
| `{ includeMostDerived }` | `{ includeMostFrequent }` |
|
||||
| `{ lastUserMessage }` | `{ searchQuery }` |
|
||||
| `{ config: ... }` | `{ configuration: ... }` |
|
||||
| `message.peer_id` | `message.peerId` |
|
||||
| `message.created_at` | `message.createdAt` |
|
||||
| `peer.card()` | `peer.getCard()` *(card() deprecated)* |
|
||||
| *(new)* | `peer.setCard(string[])` |
|
||||
| `Observation` | `Conclusion` |
|
||||
| `ObservationScope` | `ConclusionScope` |
|
||||
| *(new v2.1.0)* | `peer.createdAt` / `session.createdAt` |
|
||||
| *(new v2.1.0)* | `session.isActive` |
|
||||
| *(new v2.1.0)* | `session.getMessage(id)` |
|
||||
| *(new v2.1.0)* | `page`, `size`, `reverse` on list methods |
|
||||
| `context({ searchQuery })` | `context({ representationOptions: { searchQuery } })` |
|
||||
|
||||
## Detailed Reference
|
||||
|
||||
For comprehensive details on each change, see:
|
||||
|
||||
- [DETAILED-CHANGES.md](DETAILED-CHANGES.md) - Full API change documentation
|
||||
- [MIGRATION-CHECKLIST.md](MIGRATION-CHECKLIST.md) - Step-by-step checklist
|
||||
|
||||
## New Error Types
|
||||
|
||||
```typescript
|
||||
import {
|
||||
HonchoError,
|
||||
AuthenticationError,
|
||||
BadRequestError,
|
||||
NotFoundError,
|
||||
PermissionDeniedError,
|
||||
RateLimitError,
|
||||
ConflictError,
|
||||
UnprocessableEntityError,
|
||||
ServerError,
|
||||
ConnectionError,
|
||||
TimeoutError
|
||||
} from '@honcho-ai/sdk'
|
||||
```
|
||||
|
||||
## New Configuration Types
|
||||
|
||||
Configurations are now strongly typed:
|
||||
|
||||
```typescript
|
||||
await honcho.setConfiguration({
|
||||
reasoning: {
|
||||
enabled: true,
|
||||
customInstructions: 'Be concise'
|
||||
},
|
||||
peerCard: { use: true, create: true },
|
||||
summary: {
|
||||
enabled: true,
|
||||
messagesPerShortSummary: 20,
|
||||
messagesPerLongSummary: 60
|
||||
},
|
||||
dream: { enabled: true }
|
||||
})
|
||||
```
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
fly.toml
|
||||
.env
|
||||
.env.template
|
||||
*.md
|
||||
docs/**
|
||||
.DS_Store
|
||||
supabase/**
|
||||
LICENSE
|
||||
__pycache__
|
||||
docker-compose.yml.example
|
||||
.github/**
|
||||
.vscode/**
|
||||
data/**
|
||||
.venv
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
# Honcho Environment Variables Template
|
||||
# Copy this file to .env and fill in the appropriate values
|
||||
#
|
||||
# Required variables are marked with (REQUIRED)
|
||||
# Optional variables have default values and can be left commented out
|
||||
|
||||
# =============================================================================
|
||||
# Application Settings
|
||||
# =============================================================================
|
||||
LOG_LEVEL=INFO
|
||||
# SESSION_OBSERVERS_LIMIT=10
|
||||
# GET_CONTEXT_MAX_TOKENS=100000
|
||||
# MAX_FILE_SIZE=5242880 # Bytes
|
||||
# MAX_MESSAGE_SIZE=25000 # Characters
|
||||
|
||||
# Embedding settings
|
||||
# EMBED_MESSAGES=true
|
||||
# EMBEDDING_VECTOR_DIMENSIONS=1536
|
||||
# EMBEDDING_MAX_INPUT_TOKENS=8192
|
||||
# EMBEDDING_MAX_TOKENS_PER_REQUEST=300000
|
||||
# EMBEDDING_MODEL_CONFIG__TRANSPORT=openai
|
||||
# EMBEDDING_MODEL_CONFIG__MODEL=text-embedding-3-small
|
||||
# EMBEDDING_MODEL_CONFIG__OVERRIDES__BASE_URL=
|
||||
# EMBEDDING_MODEL_CONFIG__OVERRIDES__API_KEY_ENV=
|
||||
|
||||
# LANGFUSE_HOST=
|
||||
# LANGFUSE_PUBLIC_KEY=
|
||||
|
||||
# COLLECT_METRICS_LOCAL=false
|
||||
# LOCAL_METRICS_FILE=metrics.jsonl
|
||||
# REASONING_TRACES_FILE=traces.jsonl # Path to JSONL file for reasoning traces
|
||||
|
||||
# NAMESPACE="honcho"
|
||||
|
||||
# =============================================================================
|
||||
# Database Settings (REQUIRED)
|
||||
# =============================================================================
|
||||
# Connection URI for PostgreSQL database with pgvector support
|
||||
# Must use postgresql+psycopg prefix for SQLAlchemy compatibility
|
||||
DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@localhost:5432/postgres
|
||||
|
||||
# Optional database settings
|
||||
# DB_SCHEMA=public
|
||||
# DB_POOL_CLASS=default
|
||||
# DB_POOL_SIZE=10
|
||||
# DB_MAX_OVERFLOW=20
|
||||
# DB_POOL_TIMEOUT=30
|
||||
# DB_POOL_RECYCLE=300
|
||||
# DB_POOL_PRE_PING=true
|
||||
# DB_POOL_USE_LIFO=true
|
||||
# DB_SQL_DEBUG=false
|
||||
# DB_TRACING=false
|
||||
|
||||
# =============================================================================
|
||||
# Authentication Settings
|
||||
# =============================================================================
|
||||
# Whether to enable authentication (set to true for production)
|
||||
AUTH_USE_AUTH=false
|
||||
|
||||
# JWT secret key (REQUIRED if AUTH_USE_AUTH=true)
|
||||
# Generate with: python scripts/generate_jwt_secret.py
|
||||
# AUTH_JWT_SECRET=your-secret-key-here
|
||||
|
||||
# =============================================================================
|
||||
# LLM Provider (REQUIRED)
|
||||
# =============================================================================
|
||||
# Honcho uses LLMs for memory extraction, summarization, dialectic chat, and
|
||||
# dream consolidation. The server will fail to start without a provider configured.
|
||||
#
|
||||
# Quick start: set LLM_OPENAI_API_KEY below to use the built-in defaults.
|
||||
# Text-generation features default to transport = "openai" and
|
||||
# model = "gpt-5.4-mini". Embeddings default to transport = "openai" and
|
||||
# model = "text-embedding-3-small". For OpenAI-compatible proxies
|
||||
# (OpenRouter, Together, Fireworks, vLLM, Ollama, LiteLLM), override
|
||||
# MODEL_CONFIG__MODEL and MODEL_CONFIG__OVERRIDES__BASE_URL on each feature
|
||||
# section you want to route through that endpoint.
|
||||
# Models must support tool calling (function calling).
|
||||
#
|
||||
# Supported transports: openai, anthropic, gemini
|
||||
# Each transport picks up its API key from the corresponding LLM_*_API_KEY.
|
||||
# Base URLs are set per-module via MODEL_CONFIG__OVERRIDES__BASE_URL.
|
||||
#
|
||||
LLM_OPENAI_API_KEY=your-api-key-here
|
||||
# LLM_ANTHROPIC_API_KEY=
|
||||
# LLM_GEMINI_API_KEY=
|
||||
|
||||
# =============================================================================
|
||||
# LLM Configuration
|
||||
# =============================================================================
|
||||
# Global LLM settings
|
||||
# LLM_DEFAULT_MAX_TOKENS=2500
|
||||
# LLM_MAX_TOOL_OUTPUT_CHARS=10000 # Max chars for tool output (~2500 tokens)
|
||||
# LLM_MAX_MESSAGE_CONTENT_CHARS=2000 # Max chars per message in tool results
|
||||
|
||||
# =============================================================================
|
||||
# Deriver (Background Worker)
|
||||
# =============================================================================
|
||||
# DERIVER_ENABLED=true
|
||||
# Defaults:
|
||||
# DERIVER_MODEL_CONFIG__TRANSPORT=openai
|
||||
# DERIVER_MODEL_CONFIG__MODEL=gpt-5.4-mini
|
||||
# Optional overrides:
|
||||
# DERIVER_MODEL_CONFIG__MODEL=your-model-here
|
||||
# DERIVER_MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
||||
# DERIVER_WORKERS=1
|
||||
# DERIVER_POLLING_SLEEP_INTERVAL_SECONDS=1.0
|
||||
# DERIVER_STALE_SESSION_TIMEOUT_MINUTES=5
|
||||
# DERIVER_QUEUE_ERROR_RETENTION_SECONDS=2592000 # 30 days
|
||||
# DERIVER_MODEL_CONFIG__TEMPERATURE=
|
||||
# DERIVER_MODEL_CONFIG__THINKING_EFFORT=minimal
|
||||
# DERIVER_MODEL_CONFIG__THINKING_BUDGET_TOKENS=1024 # Gemini/Anthropic only
|
||||
# DERIVER_DEDUPLICATE=true
|
||||
# DERIVER_MODEL_CONFIG__MAX_OUTPUT_TOKENS=4096
|
||||
# DERIVER_LOG_OBSERVATIONS=false
|
||||
# DERIVER_MAX_INPUT_TOKENS=25000
|
||||
# DERIVER_MAX_CUSTOM_INSTRUCTIONS_TOKENS=2000
|
||||
# DERIVER_WORKING_REPRESENTATION_MAX_OBSERVATIONS=100
|
||||
# DERIVER_REPRESENTATION_BATCH_MAX_TOKENS=1024
|
||||
# DERIVER_FLUSH_ENABLED=false # Bypass batch token threshold, process work immediately
|
||||
# DERIVER_MODEL_CONFIG__FALLBACK__MODEL=
|
||||
# DERIVER_MODEL_CONFIG__FALLBACK__TRANSPORT=
|
||||
# DERIVER_MODEL_CONFIG__OVERRIDES__BASE_URL=
|
||||
# DERIVER_MODEL_CONFIG__OVERRIDES__API_KEY_ENV=
|
||||
|
||||
# =============================================================================
|
||||
# Peer Card
|
||||
# =============================================================================
|
||||
# PEER_CARD_ENABLED=true
|
||||
|
||||
# =============================================================================
|
||||
# Dialectic
|
||||
# =============================================================================
|
||||
# DIALECTIC_MAX_OUTPUT_TOKENS=8192
|
||||
# DIALECTIC_MAX_INPUT_TOKENS=100000
|
||||
# DIALECTIC_HISTORY_TOKEN_LIMIT=8192
|
||||
# DIALECTIC_SESSION_HISTORY_MAX_TOKENS=4096
|
||||
#
|
||||
# Per-level settings (reasoning_level parameter in API)
|
||||
# Each level has its own nested MODEL_CONFIG, tool iterations, and max output tokens.
|
||||
# MAX_OUTPUT_TOKENS is optional per level; if not set, uses global DIALECTIC_MAX_OUTPUT_TOKENS.
|
||||
# Defaults:
|
||||
# DIALECTIC_LEVELS__minimal__MODEL_CONFIG__TRANSPORT=openai
|
||||
# DIALECTIC_LEVELS__minimal__MODEL_CONFIG__MODEL=gpt-5.4-mini
|
||||
# DIALECTIC_LEVELS__minimal__MAX_TOOL_ITERATIONS=1
|
||||
# DIALECTIC_LEVELS__minimal__MAX_OUTPUT_TOKENS=250
|
||||
# DIALECTIC_LEVELS__minimal__TOOL_CHOICE=auto
|
||||
# DIALECTIC_LEVELS__low__MODEL_CONFIG__TRANSPORT=openai
|
||||
# DIALECTIC_LEVELS__low__MODEL_CONFIG__MODEL=gpt-5.4-mini
|
||||
# DIALECTIC_LEVELS__low__MAX_TOOL_ITERATIONS=5
|
||||
# DIALECTIC_LEVELS__low__TOOL_CHOICE=auto
|
||||
# DIALECTIC_LEVELS__medium__MODEL_CONFIG__TRANSPORT=openai
|
||||
# DIALECTIC_LEVELS__medium__MODEL_CONFIG__MODEL=gpt-5.4-mini
|
||||
# DIALECTIC_LEVELS__medium__MAX_TOOL_ITERATIONS=2
|
||||
# DIALECTIC_LEVELS__high__MODEL_CONFIG__TRANSPORT=openai
|
||||
# DIALECTIC_LEVELS__high__MODEL_CONFIG__MODEL=gpt-5.4-mini
|
||||
# DIALECTIC_LEVELS__high__MAX_TOOL_ITERATIONS=4
|
||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__TRANSPORT=openai
|
||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__MODEL=gpt-5.4-mini
|
||||
# DIALECTIC_LEVELS__max__MAX_TOOL_ITERATIONS=10
|
||||
# Optional overrides:
|
||||
# DIALECTIC_LEVELS__minimal__MODEL_CONFIG__MODEL=your-model-here
|
||||
# DIALECTIC_LEVELS__low__MODEL_CONFIG__MODEL=your-model-here
|
||||
# DIALECTIC_LEVELS__medium__MODEL_CONFIG__MODEL=your-model-here
|
||||
# DIALECTIC_LEVELS__high__MODEL_CONFIG__MODEL=your-model-here
|
||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__MODEL=your-model-here
|
||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__THINKING_EFFORT=medium
|
||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__THINKING_BUDGET_TOKENS=1024
|
||||
# Optional backup per level (must set both or neither):
|
||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__FALLBACK__MODEL=gemini-2.5-pro
|
||||
# DIALECTIC_LEVELS__max__MODEL_CONFIG__FALLBACK__TRANSPORT=gemini
|
||||
|
||||
# =============================================================================
|
||||
# Summary
|
||||
# =============================================================================
|
||||
# SUMMARY_ENABLED=true
|
||||
# Defaults:
|
||||
# SUMMARY_MODEL_CONFIG__TRANSPORT=openai
|
||||
# SUMMARY_MODEL_CONFIG__MODEL=gpt-5.4-mini
|
||||
# Optional overrides:
|
||||
# SUMMARY_MODEL_CONFIG__MODEL=your-model-here
|
||||
# SUMMARY_MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
||||
# SUMMARY_MODEL_CONFIG__THINKING_EFFORT=minimal
|
||||
# SUMMARY_MODEL_CONFIG__THINKING_BUDGET_TOKENS=1024 # Gemini/Anthropic only
|
||||
# SUMMARY_MESSAGES_PER_SHORT_SUMMARY=20
|
||||
# SUMMARY_MESSAGES_PER_LONG_SUMMARY=60
|
||||
# SUMMARY_MAX_TOKENS_SHORT=1000
|
||||
# SUMMARY_MAX_TOKENS_LONG=4000
|
||||
# SUMMARY_MODEL_CONFIG__FALLBACK__MODEL=
|
||||
|
||||
# =============================================================================
|
||||
# Dream
|
||||
# =============================================================================
|
||||
# DREAM_ENABLED=true
|
||||
# Defaults:
|
||||
# DREAM_DEDUCTION_MODEL_CONFIG__TRANSPORT=openai
|
||||
# DREAM_DEDUCTION_MODEL_CONFIG__MODEL=gpt-5.4-mini
|
||||
# DREAM_INDUCTION_MODEL_CONFIG__TRANSPORT=openai
|
||||
# DREAM_INDUCTION_MODEL_CONFIG__MODEL=gpt-5.4-mini
|
||||
# Optional overrides:
|
||||
# DREAM_DEDUCTION_MODEL_CONFIG__MODEL=your-model-here
|
||||
# DREAM_DEDUCTION_MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
||||
# DREAM_INDUCTION_MODEL_CONFIG__MODEL=your-model-here
|
||||
# DREAM_INDUCTION_MODEL_CONFIG__OVERRIDES__BASE_URL=https://openrouter.ai/api/v1
|
||||
# DREAM_DOCUMENT_THRESHOLD=50
|
||||
# DREAM_IDLE_TIMEOUT_MINUTES=60
|
||||
# DREAM_MIN_HOURS_BETWEEN_DREAMS=8
|
||||
# DREAM_ENABLED_TYPES=["omni"]
|
||||
# DREAM_MAX_TOOL_ITERATIONS=20
|
||||
# DREAM_HISTORY_TOKEN_LIMIT=16384
|
||||
|
||||
# Surprisal sampling (advanced):
|
||||
# DREAM_SURPRISAL__ENABLED=false
|
||||
# DREAM_SURPRISAL__TREE_TYPE=kdtree
|
||||
# DREAM_SURPRISAL__TREE_K=5
|
||||
# DREAM_SURPRISAL__SAMPLING_STRATEGY=recent
|
||||
# DREAM_SURPRISAL__SAMPLE_SIZE=200
|
||||
# DREAM_SURPRISAL__TOP_PERCENT_SURPRISAL=0.10
|
||||
# DREAM_SURPRISAL__MIN_HIGH_SURPRISAL_FOR_REPLACE=10
|
||||
# DREAM_SURPRISAL__INCLUDE_LEVELS=["explicit","deductive"]
|
||||
|
||||
# =============================================================================
|
||||
# Webhook Settings
|
||||
# =============================================================================
|
||||
# WEBHOOK_SECRET=
|
||||
# WEBHOOK_MAX_WORKSPACE_LIMIT=10
|
||||
|
||||
# =============================================================================
|
||||
# Monitoring and Observability (Optional)
|
||||
# =============================================================================
|
||||
# Sentry error tracking
|
||||
# SENTRY_ENABLED=false
|
||||
# SENTRY_DSN=your-sentry-dsn-here
|
||||
# SENTRY_RELEASE=your-release-semver
|
||||
# SENTRY_ENVIRONMENT=development
|
||||
# SENTRY_TRACES_SAMPLE_RATE=0.1
|
||||
# SENTRY_PROFILES_SAMPLE_RATE=0.1
|
||||
|
||||
# =============================================================================
|
||||
# Prometheus Metrics Settings (Pull-based metrics)
|
||||
# =============================================================================
|
||||
# METRICS_ENABLED=false
|
||||
# METRICS_NAMESPACE=honcho # Inherits from NAMESPACE if not set
|
||||
|
||||
# =============================================================================
|
||||
# CloudEvents Telemetry Settings (Analytics events)
|
||||
# =============================================================================
|
||||
# TELEMETRY_ENABLED=false
|
||||
# TELEMETRY_ENDPOINT=https://telemetry.honcho.dev/v1/events
|
||||
# TELEMETRY_HEADERS={"Authorization": "Bearer your-token"} # JSON string for auth headers
|
||||
# TELEMETRY_BATCH_SIZE=100
|
||||
# TELEMETRY_FLUSH_INTERVAL_SECONDS=1.0
|
||||
# TELEMETRY_FLUSH_THRESHOLD=50
|
||||
# TELEMETRY_MAX_RETRIES=3
|
||||
# TELEMETRY_MAX_BUFFER_SIZE=10000
|
||||
# TELEMETRY_NAMESPACE=honcho # Inherits from NAMESPACE if not set
|
||||
|
||||
# =============================================================================
|
||||
# Cache
|
||||
# =============================================================================
|
||||
# CACHE_ENABLED=false
|
||||
# CACHE_URL="redis://localhost:6379/0?suppress=true"
|
||||
# CACHE_NAMESPACE="honcho" # Inherits from NAMESPACE if not set
|
||||
# CACHE_DEFAULT_TTL_SECONDS=300
|
||||
# CACHE_DEFAULT_LOCK_TTL_SECONDS=5
|
||||
|
||||
# =============================================================================
|
||||
# Vector Store Settings
|
||||
# =============================================================================
|
||||
# Vector store type: "pgvector", "turbopuffer", or "lancedb"
|
||||
VECTOR_STORE_TYPE=pgvector
|
||||
|
||||
# Migration flag: set to true when migration from pgvector is complete
|
||||
VECTOR_STORE_MIGRATED=false
|
||||
|
||||
# Global namespace prefix for all vector namespaces
|
||||
# Namespaces follow the pattern: {NAMESPACE}.{type}.{hash}
|
||||
# where hash is a base64url-encoded SHA-256 of the workspace/peer names
|
||||
# - Documents: {NAMESPACE}.doc.{hash(workspace, observer, observed)}
|
||||
# - Messages: {NAMESPACE}.msg.{hash(workspace)}
|
||||
# VECTOR_STORE_NAMESPACE=honcho # Inherits from NAMESPACE if not set
|
||||
|
||||
# Embedding dimensions are configured via EMBEDDING_VECTOR_DIMENSIONS (see top
|
||||
# of this file). VECTOR_STORE_DIMENSIONS is deprecated and ignored.
|
||||
|
||||
# Turbopuffer-specific settings (required if TYPE is "turbopuffer")
|
||||
# VECTOR_STORE_TURBOPUFFER_API_KEY=your-turbopuffer-api-key
|
||||
# VECTOR_STORE_TURBOPUFFER_REGION=gcp-us-east4
|
||||
|
||||
# LanceDB-specific settings (local embedded mode)
|
||||
# VECTOR_STORE_LANCEDB_PATH=./lancedb_data
|
||||
|
||||
# Reconciliation interval for background sync (default: 5 minutes)
|
||||
# VECTOR_STORE_RECONCILIATION_INTERVAL_SECONDS=300
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
name: "🐞 Bug Report"
|
||||
about: "Report an issue to help the project improve."
|
||||
title: "[Bug] "
|
||||
labels: "bug"
|
||||
assignees: ""
|
||||
|
||||
---
|
||||
|
||||
# **🐞 Bug Report**
|
||||
|
||||
## **Describe the bug**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
### **Is this a regression?**
|
||||
<!-- Did this behaviour used to work in the previous version? -->
|
||||
<!-- Yes, the last version in which this bug was not present was: ... -->
|
||||
|
||||
---
|
||||
|
||||
### **To Reproduce**
|
||||
|
||||
<!-- Steps to reproduce the error:
|
||||
(e.g.:)
|
||||
1. Use x argument / navigate to
|
||||
2. Fill this information
|
||||
3. Go to...
|
||||
4. See error -->
|
||||
|
||||
<!-- Write the steps here (add or remove as many steps as needed)-->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
---
|
||||
|
||||
### **Expected behaviour**
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
### **Media prove**
|
||||
<!-- If applicable, add screenshots or videos to help explain your problem. -->
|
||||
|
||||
---
|
||||
|
||||
### **Your environment**
|
||||
|
||||
<!-- use all the applicable bulleted list elements for this specific issue,
|
||||
and remove all the bulleted list elements that are not relevant for this issue. -->
|
||||
|
||||
* OS: <!--[e.g. Ubuntu 5.4.0-26-generic x86_64 / Windows 1904 ...]-->
|
||||
* Browser name and version:
|
||||
* Honcho Server Version: <!-- e.g. v0.0.8 -->
|
||||
* Honcho Client Version: <!-- e.g. Python v0.0.8 -->
|
||||
|
||||
---
|
||||
|
||||
### **Additional context**
|
||||
<!-- Add any other context or additional information about the problem here.-->
|
||||
|
||||
*
|
||||
|
||||
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||
|
||||
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||
|
||||
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
---
|
||||
name: "💉 Failing Test"
|
||||
about: "Report failing tests or CI jobs."
|
||||
title: "[Test] "
|
||||
labels: "Type: Test"
|
||||
assignees: ""
|
||||
|
||||
---
|
||||
|
||||
# **💉 Failing Test**
|
||||
|
||||
## **Which jobs/test(s) are failing**
|
||||
<!-- The CI jobs or tests that are failing -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
## **Reason for failure/description**
|
||||
<!-- Try to describe why the test is failing or what we are missing to make it pass. -->
|
||||
|
||||
---
|
||||
|
||||
### **Media prove**
|
||||
<!-- If applicable, add screenshots or videos to help explain your problem. -->
|
||||
|
||||
---
|
||||
|
||||
### **Additional context**
|
||||
<!-- Add any other context or additional information about the problem here. -->
|
||||
|
||||
*
|
||||
|
||||
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||
|
||||
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||
|
||||
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
---
|
||||
name: "📚 Documentation or README.md issue report"
|
||||
about: "Report an issue in the project's documentation or README.md file."
|
||||
title: ""
|
||||
labels: "documentation"
|
||||
assignees: ""
|
||||
|
||||
---
|
||||
# **📚 Documentation Issue Report**
|
||||
|
||||
## **Describe the bug**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
### **To Reproduce**
|
||||
|
||||
<!-- Steps to reproduce the error:
|
||||
(e.g.:)
|
||||
1. Use x argument / navigate to
|
||||
2. Fill this information
|
||||
3. Go to...
|
||||
4. See error -->
|
||||
|
||||
<!-- Write the steps here (add or remove as many steps as needed)-->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
---
|
||||
|
||||
### **Media prove**
|
||||
<!-- If applicable, add screenshots or videos to help explain your problem. -->
|
||||
|
||||
---
|
||||
|
||||
## **Describe the solution you'd like**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
### **Additional context**
|
||||
<!-- Add any other context or additional information about the problem here.-->
|
||||
|
||||
*
|
||||
|
||||
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||
|
||||
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||
|
||||
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
name: "🚀🆕 Feature Request"
|
||||
about: "Suggest an idea or possible new feature for this project."
|
||||
title: ""
|
||||
labels: 'feature'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# **🚀 Feature Request**
|
||||
|
||||
## **Is your feature request related to a problem? Please describe.**
|
||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
## **Describe the solution you'd like**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
## **Describe alternatives you've considered**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
### **Additional context**
|
||||
<!-- Add any other context or additional information about the problem here.-->
|
||||
|
||||
*
|
||||
|
||||
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||
|
||||
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||
|
||||
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
---
|
||||
name: "🚀➕ Enhancement Request"
|
||||
about: "Suggest an enhancement for this project. Improve an existing feature"
|
||||
title: ""
|
||||
labels: "Type: Enhancement"
|
||||
assignees: ""
|
||||
|
||||
---
|
||||
|
||||
# **🚀 Enhancement Request**
|
||||
|
||||
## **Is your enhancement request related to a problem? Please describe.**
|
||||
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
## **Describe the solution you'd like**
|
||||
<!-- A clear and concise description of what you want to happen. -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
## **Describe alternatives you've considered**
|
||||
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
### **Additional context**
|
||||
<!-- Add any other context or additional information about the problem here.-->
|
||||
|
||||
*
|
||||
|
||||
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||
|
||||
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||
|
||||
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
name: "⚠️ Security Report"
|
||||
about: "Report an issue to help the project improve."
|
||||
title: ""
|
||||
labels: "security"
|
||||
assignees: ""
|
||||
|
||||
---
|
||||
|
||||
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||
|
||||
READ CAREFULLY IF YOUR ISSUE REPORT CONTAINS SENSIBLE OR PRIVATE DATA:
|
||||
(data that might be leaked or subtracted from our servers due to this
|
||||
security issue).
|
||||
|
||||
If this security report (or the guide on how to "identify the security bug") includes
|
||||
certain personal information or involves personal identifiable data, or you believe
|
||||
that the data that you might leak by exposing the way on how to attack the project
|
||||
could be considered as a data leak or could violate the privacy of any kind of
|
||||
data or sensible data, please do not post it here and directly email the developer:
|
||||
(hello@plasticlabs.ai). You should post the issue with the least amount of
|
||||
sensible or private data as possible to help us manage the security issue, and
|
||||
with the extra data sent from your email to the developer (if any), we will deeply
|
||||
analyze and try to fix it as fast as possible.
|
||||
|
||||
If you are in doubt about the data that you might post here (screenshots or media
|
||||
also, count as data), please directly email us.
|
||||
|
||||
The data that must NOT be posted here:
|
||||
|
||||
* Legal and/or full names
|
||||
* Names or usernames combined with other identifiers like phone numbers or email addresses
|
||||
* Health or financial information (including insurance information, social security numbers, etc.)
|
||||
* Information about political or religious affiliations
|
||||
* Information about race, ethnicity, sexual orientation, gender, or other identifying information that could be used for discriminatory purposes
|
||||
|
||||
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||
|
||||
# **⚠️ Security Report**
|
||||
|
||||
## **Describe the security issue**
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
### **To Reproduce**
|
||||
|
||||
<!-- Steps to reproduce the error:
|
||||
(e.g.:)
|
||||
1. Use x argument / navigate to
|
||||
2. Fill this information
|
||||
3. Go to...
|
||||
4. See error -->
|
||||
|
||||
<!-- Write the steps here (add or remove as many steps as needed)-->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
---
|
||||
|
||||
### **Expected behaviour**
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
### **Media prove**
|
||||
<!-- If applicable, add screenshots or videos to help explain your problem. -->
|
||||
|
||||
---
|
||||
|
||||
### **Your environment**
|
||||
|
||||
<!-- use all the applicable bulleted list elements for this specific issue,
|
||||
and remove all the bulleted list elements that are not relevant for this issue. -->
|
||||
|
||||
* OS: <!--[e.g. Ubuntu 5.4.0-26-generic x86_64 / Windows 1904 ...]-->
|
||||
* Browser name and version:
|
||||
* Honcho Server Version: <!--[e.g. v0.0.1]-->
|
||||
* Honcho Client Version: <!--[e.g. Python v0.0.1]-->
|
||||
|
||||
---
|
||||
|
||||
### **Additional context**
|
||||
<!-- Add any other context or additional information about the problem here.-->
|
||||
|
||||
*
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
name: "❓ Question or Support Request"
|
||||
about: "Questions and requests for support."
|
||||
title: ""
|
||||
labels: "question"
|
||||
assignees: ""
|
||||
|
||||
---
|
||||
|
||||
# **❓ Question or Support Request**
|
||||
|
||||
## **Describe your question or ask for support.**
|
||||
<!-- A clear and concise description of what your doubt is. -->
|
||||
|
||||
*
|
||||
|
||||
<!--📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛
|
||||
|
||||
Before posting any questions or asking for support, first read the project's README.md file and
|
||||
(if there is any) the WIKI pages or any other additional documentation that might be listed
|
||||
in the project's README.md file.
|
||||
|
||||
To expedite issue processing, please search open and closed issues before submitting a new one.
|
||||
|
||||
📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛📛-->
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# **Release v[X.X.X]**
|
||||
|
||||
<!-- This PR fixes #NUMBER_OF_THE_ISSUE, and fixes #NUMBER_OF_THE_ISSUE -->
|
||||
|
||||
## **Description**
|
||||
|
||||
<!-- 📛📛
|
||||
Please include a summary of the change and/or which issue is fixed.
|
||||
List any dependencies required for this change, if there are any.
|
||||
📛📛 -->
|
||||
|
||||
*
|
||||
|
||||
---
|
||||
|
||||
### **Additional context**
|
||||
|
||||
<!-- Add any other context or additional information about the pull request.-->
|
||||
|
||||
*
|
||||
|
||||
<!-- 📛📛📛📛
|
||||
If it fixes any current issue please let us know this way:
|
||||
Uncomment the comment above "description", then add your number of issues after the "#".
|
||||
Example: # **This pull request fixes #NUMBER_OF_THE_ISSUE issue**
|
||||
If there are multiple issues to be closed with the merge of this pull request
|
||||
please do it like so: **This pull request fixes #NUMBER_OF_THE_ISSUE, fixes #NUMBER_OF_THE_ISSUE and fixes #NUMBER_OF_THE_ISSUE issue**.
|
||||
For more information on closing issues using keywords, please check https://docs.github.com/en/enterprise/2.16/user/github/managing-your-work-on-github/closing-issues-using-keywords#closing-multiple-issues
|
||||
📛📛📛📛 -->
|
||||
|
||||
## **Changelog**
|
||||
|
||||
<!-- 📛📛📛📛
|
||||
Log of changes introduced in this release in the style of https://keepachangelog.com/en/1.1.0/
|
||||
📛📛📛📛 -->
|
||||
|
||||
### **Added**
|
||||
|
||||
### **Changed**
|
||||
|
||||
### **Deprecated**
|
||||
|
||||
### **Removed**
|
||||
|
||||
### **Fixed**
|
||||
|
||||
### **Security**
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- v*
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
id-token: write
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3.3.0
|
||||
with:
|
||||
platforms: linux/amd64, linux/arm64
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5.5.1
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
tags: |
|
||||
type=ref,event=tag
|
||||
type=raw,value=latest,enable={{is_default_branch}}
|
||||
- name: Log in to GitHub Container Registry
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3.1.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Build and Push
|
||||
id: push
|
||||
uses: docker/build-push-action@v5.3.0
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v1.1.2
|
||||
with:
|
||||
subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
subject-digest: ${{ steps.push.outputs.digest }}
|
||||
push-to-registry: true
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/
|
||||
|
||||
name: Fly Deploy (Production Environment)
|
||||
permissions:
|
||||
contents: read
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version to deploy (without v prefix)"
|
||||
required: true
|
||||
type: string
|
||||
default: 'manual'
|
||||
|
||||
jobs:
|
||||
deploy-honcho-prod-image:
|
||||
name: Deploy Honcho Image (Production Environment)
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: deploy-prod-group
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: superfly/flyctl-actions/setup-flyctl@1.5
|
||||
- run: |
|
||||
# Determine the image label based on trigger type
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
IMAGE_LABEL="deployment-${{ github.event.inputs.version }}"
|
||||
else
|
||||
IMAGE_LABEL="deployment-${{ github.ref_name }}"
|
||||
fi
|
||||
flyctl deploy -a honcho-prod-image --remote-only --build-only --push --no-cache --image-label "$IMAGE_LABEL"
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_PROD_API_TOKEN }}
|
||||
|
||||
prompt-service:
|
||||
name: Push to Service (Production Environment)
|
||||
needs: deploy-honcho-prod-image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send POST request
|
||||
env:
|
||||
GITHUB_REF_NAME: ${{ github.ref_name }}
|
||||
run: |
|
||||
# Determine version and image label based on trigger type
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
TAG="${{ github.event.inputs.version }}"
|
||||
IMAGE_LABEL="honcho-prod-image:deployment-${{ github.event.inputs.version }}"
|
||||
else
|
||||
TAG=${GITHUB_REF_NAME#v}
|
||||
IMAGE_LABEL="honcho-prod-image:deployment-${GITHUB_REF_NAME}"
|
||||
fi
|
||||
|
||||
curl --fail -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer ${{ secrets.PROD_ENV_WEBHOOK_SECRET }}" \
|
||||
-d "{\"version\":\"$TAG\",\"image_label\":\"$IMAGE_LABEL\"}" \
|
||||
"${{ secrets.PROD_ENV_URL }}/webhooks/v1/add_honcho_version"
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
# See https://fly.io/docs/app-guides/continuous-deployment-with-github-actions/
|
||||
|
||||
name: Fly Deploy (Test Environment)
|
||||
permissions:
|
||||
contents: read
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version to deploy (without v prefix)"
|
||||
required: true
|
||||
type: string
|
||||
default: 'manual'
|
||||
|
||||
jobs:
|
||||
deploy-honcho-image:
|
||||
name: Deploy Honcho Image (Test Environment)
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: deploy-test-group
|
||||
cancel-in-progress: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: superfly/flyctl-actions/setup-flyctl@1.5
|
||||
- run: |
|
||||
# Determine the image label based on trigger type
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
IMAGE_LABEL="deployment-${{ github.event.inputs.version }}"
|
||||
else
|
||||
IMAGE_LABEL="deployment-${{ github.ref_name }}"
|
||||
fi
|
||||
flyctl deploy -a honcho-image --remote-only --build-only --push --no-cache --image-label "$IMAGE_LABEL"
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||
|
||||
prompt-service:
|
||||
name: Push to Service (Test Environment)
|
||||
runs-on: ubuntu-latest
|
||||
needs: deploy-honcho-image
|
||||
steps:
|
||||
- name: Send POST request
|
||||
env:
|
||||
GITHUB_REF_NAME: ${{ github.ref_name }}
|
||||
run: |
|
||||
# Determine version and image label based on trigger type
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
TAG="${{ github.event.inputs.version }}"
|
||||
IMAGE_LABEL="honcho-image:deployment-${{ github.event.inputs.version }}"
|
||||
else
|
||||
TAG=${GITHUB_REF_NAME#v}
|
||||
IMAGE_LABEL="honcho-image:deployment-${GITHUB_REF_NAME}"
|
||||
fi
|
||||
|
||||
curl --fail -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer ${{ secrets.TEST_ENV_WEBHOOK_SECRET }}" \
|
||||
-d "{\"version\":\"$TAG\",\"image_label\":\"$IMAGE_LABEL\"}" \
|
||||
"${{ secrets.TEST_ENV_URL }}/webhooks/v1/add_honcho_version"
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
name: Start Fly Runner
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
outputs:
|
||||
runner-ready:
|
||||
description: "Whether the runner is ready"
|
||||
value: ${{ jobs.start-runner.outputs.runner-ready }}
|
||||
machine-id:
|
||||
description: "The Fly machine ID that was started"
|
||||
value: ${{ jobs.start-runner.outputs.machine-id }}
|
||||
runner-labels:
|
||||
description: "Labels to target the self-hosted runner"
|
||||
value: ${{ jobs.start-runner.outputs.runner-labels }}
|
||||
runner-name:
|
||||
description: "Resolved GitHub runner name"
|
||||
value: ${{ jobs.start-runner.outputs.runner-name }}
|
||||
|
||||
env:
|
||||
FLY_RUNNER_APP: ivysaur
|
||||
FLY_RUNNER_REGION: iad
|
||||
FLY_RUNNER_IMAGE: registry.fly.io/ivysaur:latest
|
||||
|
||||
jobs:
|
||||
start-runner:
|
||||
name: Start Fly Runner
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
outputs:
|
||||
runner-ready: ${{ steps.wait-for-runner.outputs.ready }}
|
||||
machine-id: ${{ steps.machine-management.outputs.machine-id }}
|
||||
runner-labels: ${{ steps.generate-labels.outputs.labels }}
|
||||
runner-name: ${{ steps.wait-for-runner.outputs.runner-name }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Generate unique runner labels
|
||||
id: generate-labels
|
||||
run: |
|
||||
UNIQUE_LABELS='"self-hosted","${{ github.run_id }}"'
|
||||
echo "Generated unique labels: $UNIQUE_LABELS"
|
||||
echo "labels=$UNIQUE_LABELS" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Setup Fly CLI
|
||||
uses: superfly/flyctl-actions/setup-flyctl@master
|
||||
|
||||
- name: Get Fly app info
|
||||
id: get-app-info
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }}
|
||||
run: |
|
||||
echo "Getting app info for ${FLY_RUNNER_APP}..."
|
||||
flyctl status -a "${FLY_RUNNER_APP}"
|
||||
|
||||
- name: Set GH_TOKEN in Fly secrets
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }}
|
||||
run: |
|
||||
echo "Setting GH_TOKEN in Fly secrets..."
|
||||
flyctl secrets set GH_TOKEN="${{ secrets.GH_TOKEN_ACTIONS }}" -a "${FLY_RUNNER_APP}"
|
||||
|
||||
- name: Create Fly machine
|
||||
id: machine-management
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Always create a fresh ephemeral machine
|
||||
FULL_OUTPUT=$(flyctl machines run "${FLY_RUNNER_IMAGE}" \
|
||||
-a "${FLY_RUNNER_APP}" \
|
||||
--region "${FLY_RUNNER_REGION}" \
|
||||
--env RUN_ID=${{ github.run_id }} \
|
||||
--env TEST_TYPE="honcho-unified-runner" \
|
||||
--vm-size shared-cpu-8x \
|
||||
--vm-memory 8192 )
|
||||
|
||||
MACHINE_ID=$(echo "$FULL_OUTPUT" | grep "Machine ID:" | awk '{print $3}')
|
||||
echo "Created machine: $MACHINE_ID"
|
||||
echo "machine-id=$MACHINE_ID" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Wait for runner to be online
|
||||
id: wait-for-runner
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_ACTIONS }}
|
||||
MAX_WAIT: 420
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -z "${GITHUB_TOKEN}" ]; then
|
||||
echo "GH_TOKEN secret is required to poll the Actions runner API."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
EXPECTED_RUNNER_NAME="honcho-unified-runner-${{ github.run_id }}"
|
||||
echo "Waiting for runner named ${EXPECTED_RUNNER_NAME} to come online..."
|
||||
|
||||
WAITED=0
|
||||
RUNNER_NAME=""
|
||||
while [ $WAITED -lt $MAX_WAIT ]; do
|
||||
RESPONSE=$(curl -s \
|
||||
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/${{ github.repository }}/actions/runners")
|
||||
|
||||
if echo "$RESPONSE" | grep -q '"message"'; then
|
||||
echo "API Error: $(echo "$RESPONSE" | jq -r '.message')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find runner with exact name and is online and not busy
|
||||
RUNNER_LINE=$(echo "$RESPONSE" | jq -r --arg runner_name "$EXPECTED_RUNNER_NAME" '.runners[]? | select(.name == $runner_name) | select(.status == "online") | select(.busy == false) | "\(.name)|\(.id)"' | head -n 1)
|
||||
|
||||
if [ -n "$RUNNER_LINE" ]; then
|
||||
RUNNER_NAME=$(echo "$RUNNER_LINE" | cut -d'|' -f1)
|
||||
echo "✅ Found runner: ${RUNNER_NAME}"
|
||||
echo "ready=true" >> "$GITHUB_OUTPUT"
|
||||
echo "runner-name=${RUNNER_NAME}" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "⏳ Waiting for runner... (${WAITED}s elapsed)"
|
||||
sleep 15
|
||||
WAITED=$((WAITED + 15))
|
||||
done
|
||||
|
||||
echo "Runner failed to come online within ${MAX_WAIT} seconds"
|
||||
echo "ready=false" >> "$GITHUB_OUTPUT"
|
||||
exit 1
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
name: Static Analysis
|
||||
on: [push]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
basedpyright:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: "Set up Python"
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version-file: "pyproject.toml"
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v2
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
- name: Install the project
|
||||
run: uv sync --all-extras --dev
|
||||
- name: run basedpyright
|
||||
run: uv run basedpyright
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
name: Unified Tests (Fly Runner)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'tests/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
jobs:
|
||||
start-runner:
|
||||
name: Start Fly Runner
|
||||
uses: ./.github/workflows/start-fly-runner.yml
|
||||
secrets: inherit
|
||||
|
||||
unified-tests:
|
||||
name: Run Unified Tests
|
||||
runs-on: ${{ fromJSON(format('[{0}]', needs.start-runner.outputs.runner-labels)) }}
|
||||
needs: start-runner
|
||||
if: needs.start-runner.outputs.runner-ready == 'true'
|
||||
timeout-minutes: 90
|
||||
environment: unified-tests
|
||||
permissions:
|
||||
id-token: write # Required for OIDC authentication with AWS
|
||||
contents: read
|
||||
|
||||
env:
|
||||
PYTHONUNBUFFERED: "1"
|
||||
TEST_DISCORD_WEBHOOK_URL: ${{ secrets.TEST_DISCORD_WEBHOOK_URL }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
|
||||
- name: Configure AWS credentials
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::444554165670:role/GitHubActionsS3Role
|
||||
aws-region: us-east-1
|
||||
role-duration-seconds: 43200 # 12 hours
|
||||
|
||||
- name: Fetch secrets from AWS Secrets Manager
|
||||
uses: aws-actions/aws-secretsmanager-get-secrets@v2
|
||||
with:
|
||||
secret-ids: |
|
||||
,testing/unified/tests
|
||||
parse-json-secrets: true
|
||||
|
||||
- name: Verify Docker is available
|
||||
run: docker info
|
||||
|
||||
- name: Verify uv and Python
|
||||
run: |
|
||||
uv --version
|
||||
python3.12 --version
|
||||
which python3.12
|
||||
|
||||
- name: Install the project
|
||||
run: uv sync --all-extras
|
||||
|
||||
- name: Run unified tests
|
||||
run: uv run python -m tests.unified.run
|
||||
|
||||
cleanup-machine:
|
||||
name: Cleanup Fly Machine and Runner
|
||||
runs-on: ubuntu-latest
|
||||
needs: [start-runner, unified-tests]
|
||||
if: always() && needs.start-runner.outputs.machine-id != ''
|
||||
env:
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN_TESTING }}
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_ACTIONS }}
|
||||
FLY_RUNNER_APP: ivysaur
|
||||
steps:
|
||||
- name: Setup Fly CLI
|
||||
uses: superfly/flyctl-actions/setup-flyctl@1.5
|
||||
|
||||
- name: Cleanup fly machine
|
||||
run: |
|
||||
set -euo pipefail
|
||||
MACHINE_ID="${{ needs.start-runner.outputs.machine-id }}"
|
||||
if [ -z "$MACHINE_ID" ]; then
|
||||
echo "No machine ID provided, skipping Fly cleanup."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "🧹 Cleaning up machine: $MACHINE_ID"
|
||||
flyctl machines stop "$MACHINE_ID" -a "$FLY_RUNNER_APP" || echo "Machine may already be stopped"
|
||||
flyctl machines destroy "$MACHINE_ID" -a "$FLY_RUNNER_APP" --force || echo "Failed to destroy machine"
|
||||
|
||||
- name: Cleanup GitHub runner
|
||||
run: |
|
||||
set -euo pipefail
|
||||
RUNNER_NAME="${{ needs.start-runner.outputs.runner-name }}"
|
||||
FALLBACK_LABEL="${{ github.run_id }}"
|
||||
|
||||
echo "🗑️ Cleaning up GitHub runner (name: ${RUNNER_NAME:-unknown}, label: ${FALLBACK_LABEL})"
|
||||
|
||||
RUNNERS_RESPONSE=$(curl -s \
|
||||
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
"https://api.github.com/repos/${{ github.repository }}/actions/runners")
|
||||
|
||||
if echo "$RUNNERS_RESPONSE" | grep -q '"message"'; then
|
||||
echo "⚠️ Failed to fetch runners: $(echo "$RUNNERS_RESPONSE" | jq -r '.message')"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
RUNNER_ID=""
|
||||
if [ -n "$RUNNER_NAME" ]; then
|
||||
RUNNER_ID=$(echo "$RUNNERS_RESPONSE" | jq -r --arg name "$RUNNER_NAME" '.runners[]? | select(.name == $name) | .id')
|
||||
fi
|
||||
|
||||
if [ -z "$RUNNER_ID" ]; then
|
||||
RUNNER_ID=$(echo "$RUNNERS_RESPONSE" | jq -r --arg label "$FALLBACK_LABEL" '.runners[]? | select([.labels[].name] | index($label)) | .id' | head -n 1)
|
||||
fi
|
||||
|
||||
if [ -z "$RUNNER_ID" ] || [ "$RUNNER_ID" = "null" ]; then
|
||||
echo "⚠️ Runner not found, nothing to delete."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
DELETE_RESPONSE=$(curl -s -w "%{http_code}" \
|
||||
-X DELETE \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer $GITHUB_TOKEN" \
|
||||
"https://api.github.com/repos/${{ github.repository }}/actions/runners/$RUNNER_ID")
|
||||
|
||||
HTTP_CODE="${DELETE_RESPONSE: -3}"
|
||||
if [ "$HTTP_CODE" = "204" ]; then
|
||||
echo "✅ Successfully deleted runner."
|
||||
else
|
||||
echo "⚠️ Failed to delete runner. HTTP code: $HTTP_CODE"
|
||||
echo "Response: ${DELETE_RESPONSE%???}"
|
||||
fi
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
name: FastAPI Tests with PostgreSQL and uv
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- '**.py'
|
||||
- '**.ts'
|
||||
- '**.js'
|
||||
- '**.tsx'
|
||||
- '**.jsx'
|
||||
- 'pyproject.toml'
|
||||
- 'uv.lock'
|
||||
- 'sdks/typescript/package.json'
|
||||
- 'sdks/typescript/bun.lock'
|
||||
- '.github/workflows/unittest.yml'
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- '**.py'
|
||||
- '**.ts'
|
||||
- '**.js'
|
||||
- '**.tsx'
|
||||
- '**.jsx'
|
||||
- 'pyproject.toml'
|
||||
- 'uv.lock'
|
||||
- 'sdks/typescript/package.json'
|
||||
- 'sdks/typescript/bun.lock'
|
||||
- '.github/workflows/unittest.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
# Determine which tests to run based on changed files
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
python: ${{ steps.filter.outputs.python }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
python:
|
||||
- '**.py'
|
||||
- 'pyproject.toml'
|
||||
- 'uv.lock'
|
||||
- 'migrations/**'
|
||||
- 'sdks/typescript/**'
|
||||
- '.github/workflows/unittest.yml'
|
||||
|
||||
test-python:
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.python == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
database:
|
||||
image: pgvector/pgvector:pg15
|
||||
env:
|
||||
POSTGRES_DB: test_db
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_HOST_AUTH_METHOD: trust
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install uv
|
||||
uses: astral-sh/setup-uv@v2
|
||||
with:
|
||||
enable-cache: true
|
||||
cache-dependency-glob: "uv.lock"
|
||||
|
||||
- name: "Set up Python"
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version-file: "pyproject.toml"
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
|
||||
- name: Install TypeScript SDK dependencies
|
||||
run: bun install
|
||||
working-directory: sdks/typescript
|
||||
|
||||
- name: Install the project
|
||||
run: uv sync --all-extras --dev
|
||||
|
||||
- name: Run Tests
|
||||
run: uv run pytest -x
|
||||
env:
|
||||
DB_CONNECTION_URI: postgresql+psycopg://postgres:postgres@localhost:5432/test_db
|
||||
AUTH_USE_AUTH: false
|
||||
SENTRY_ENABLED: false
|
||||
LLM_OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
LLM_ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
LLM_OPENAI_COMPATIBLE_API_KEY: test-key
|
||||
LLM_OPENAI_COMPATIBLE_BASE_URL: http://localhost:8000
|
||||
DERIVER_PROVIDER: openai
|
||||
DERIVER_MODEL: test
|
||||
DIALECTIC_LEVELS__minimal__PROVIDER: openai
|
||||
DIALECTIC_LEVELS__minimal__MODEL: test
|
||||
DIALECTIC_LEVELS__minimal__THINKING_BUDGET_TOKENS: 0
|
||||
DIALECTIC_LEVELS__minimal__MAX_TOOL_ITERATIONS: 2
|
||||
DIALECTIC_LEVELS__low__PROVIDER: openai
|
||||
DIALECTIC_LEVELS__low__MODEL: test
|
||||
DIALECTIC_LEVELS__low__THINKING_BUDGET_TOKENS: 0
|
||||
DIALECTIC_LEVELS__low__MAX_TOOL_ITERATIONS: 5
|
||||
DIALECTIC_LEVELS__medium__PROVIDER: openai
|
||||
DIALECTIC_LEVELS__medium__MODEL: test
|
||||
DIALECTIC_LEVELS__medium__THINKING_BUDGET_TOKENS: 0
|
||||
DIALECTIC_LEVELS__medium__MAX_TOOL_ITERATIONS: 4
|
||||
DIALECTIC_LEVELS__high__PROVIDER: openai
|
||||
DIALECTIC_LEVELS__high__MODEL: test
|
||||
DIALECTIC_LEVELS__high__THINKING_BUDGET_TOKENS: 0
|
||||
DIALECTIC_LEVELS__high__MAX_TOOL_ITERATIONS: 4
|
||||
DIALECTIC_LEVELS__max__PROVIDER: openai
|
||||
DIALECTIC_LEVELS__max__MODEL: test
|
||||
DIALECTIC_LEVELS__max__THINKING_BUDGET_TOKENS: 0
|
||||
DIALECTIC_LEVELS__max__MAX_TOOL_ITERATIONS: 10
|
||||
DIALECTIC_QUERY_GENERATION_PROVIDER: openai
|
||||
DIALECTIC_QUERY_GENERATION_MODEL: test
|
||||
SUMMARY_PROVIDER: openai
|
||||
SUMMARY_MODEL: test
|
||||
|
||||
# Status check for branch protection rules
|
||||
# This job always runs and reports success only if all required jobs pass
|
||||
test-status:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [changes, test-python]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check test results
|
||||
run: |
|
||||
if [[ "${{ needs.changes.outputs.python }}" == "true" && "${{ needs.test-python.result }}" != "success" && "${{ needs.test-python.result }}" != "skipped" ]]; then
|
||||
echo "Python tests failed or were cancelled"
|
||||
exit 1
|
||||
fi
|
||||
echo "All required tests passed!"
|
||||
|
|
@ -0,0 +1,195 @@
|
|||
.worktrees/
|
||||
api/**/*.db
|
||||
api/data
|
||||
api/docker-compose.yml
|
||||
|
||||
*.db
|
||||
data
|
||||
redis-data
|
||||
docker-compose.yml
|
||||
compose.yml
|
||||
|
||||
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
*.sqlite
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
**/docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
.env.backup*
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
.DS_Store
|
||||
|
||||
supabase/
|
||||
|
||||
docs/node_modules
|
||||
|
||||
timing_logs.csv
|
||||
|
||||
config.json
|
||||
config.toml
|
||||
.aider*
|
||||
|
||||
CRUSH.md
|
||||
.crush/
|
||||
|
||||
metrics.jsonl
|
||||
AGENTS.md
|
||||
lancedb_data/
|
||||
grafana-data/
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"default": true,
|
||||
"MD013": false,
|
||||
"MD024": false,
|
||||
"MD025": false,
|
||||
"MD029": false,
|
||||
"MD040": false,
|
||||
"MD041": false,
|
||||
"line-length": false,
|
||||
"no-duplicate-heading": false,
|
||||
"single-h1": false,
|
||||
"ol-prefix": false,
|
||||
"fenced-code-language": false,
|
||||
"first-line-h1": false
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
# .pre-commit-config.yaml
|
||||
repos:
|
||||
# Basic file checks (run on all files)
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-json
|
||||
- id: check-toml
|
||||
- id: check-added-large-files
|
||||
args: ['--maxkb=1000']
|
||||
- id: check-merge-conflict
|
||||
- id: debug-statements
|
||||
files: \.(py|js|ts)$
|
||||
- id: mixed-line-ending
|
||||
args: ['--fix=lf']
|
||||
# Additional checks from suggestions
|
||||
- id: check-docstring-first
|
||||
files: \.py$
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-case-conflict
|
||||
|
||||
# Python code formatting and linting with ruff
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.8.4
|
||||
hooks:
|
||||
# Linter - only on Python directories
|
||||
- id: ruff
|
||||
args: [--fix]
|
||||
files: ^(src/|tests/|scripts/|migrations/|sdks/python/).*\.py$
|
||||
# Formatter - only on Python directories
|
||||
- id: ruff-format
|
||||
files: ^(src/|tests/|scripts/|migrations/|sdks/python/).*\.py$
|
||||
|
||||
# Security checks - only on main src code (not tests/scripts)
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
rev: 1.7.10
|
||||
hooks:
|
||||
- id: bandit
|
||||
args: ['-r']
|
||||
files: ^(src/|sdks/python/src/).*\.py$
|
||||
|
||||
# Local hooks using your uv environment
|
||||
- repo: local
|
||||
hooks:
|
||||
# TypeScript linting with biome
|
||||
- id: biome-check
|
||||
name: biome check and format
|
||||
entry: bash -c 'cd sdks/typescript && bun run lint:fix'
|
||||
language: system
|
||||
files: ^sdks/typescript/.*\.(js|ts|jsx|tsx|json|jsonc)$
|
||||
pass_filenames: false
|
||||
|
||||
# Type checking with basedpyright - only on main Python code
|
||||
- id: basedpyright
|
||||
name: basedpyright
|
||||
entry: uv run basedpyright
|
||||
language: system
|
||||
files: ^(src/|tests/|sdks/python/|scripts/).*\.py$
|
||||
require_serial: true
|
||||
pass_filenames: true
|
||||
|
||||
# Run main application tests
|
||||
- id: pytest-main
|
||||
name: pytest (main app)
|
||||
entry: uv run pytest -x tests/ --ignore=tests/alembic/
|
||||
language: system
|
||||
files: ^(src/|tests/).*\.py$
|
||||
stages: [pre-push]
|
||||
pass_filenames: false
|
||||
|
||||
# Run Alembic tests only when migrations change
|
||||
- id: pytest-alembic
|
||||
name: pytest (alembic migrations)
|
||||
entry: uv run python scripts/run_alembic_tests.py
|
||||
language: system
|
||||
files: ^(migrations/versions/.*\.py|tests/alembic/.*\.py)$
|
||||
stages: [pre-push]
|
||||
pass_filenames: true
|
||||
require_serial: true
|
||||
|
||||
# Ensure each alembic migration revision has a corresponding test file
|
||||
- id: ensure-alembic-coverage
|
||||
name: ensure alembic migration test coverage
|
||||
entry: uv run python scripts/ensure_alembic_tests.py
|
||||
language: system
|
||||
files: ^(migrations/versions/.*\.py|tests/alembic/revisions/.*\.py)$
|
||||
stages: [pre-push]
|
||||
pass_filenames: false
|
||||
|
||||
# Run Python SDK tests (if they exist)
|
||||
- id: pytest-python-sdk
|
||||
name: pytest (Python SDK)
|
||||
entry: bash -c 'if [ -d "sdks/python/tests" ]; then cd sdks/python && uv run pytest; fi'
|
||||
language: system
|
||||
files: ^sdks/python/.*\.py$
|
||||
stages: [pre-push]
|
||||
pass_filenames: false
|
||||
|
||||
# TypeScript build with bun (tests run via pytest)
|
||||
- id: typescript-check
|
||||
name: TypeScript build
|
||||
entry: bash -c 'if [ -f "sdks/typescript/package.json" ]; then cd sdks/typescript && bun run build; fi'
|
||||
language: system
|
||||
files: ^sdks/typescript/.*\.(js|ts|jsx|tsx|json)$
|
||||
stages: [pre-push]
|
||||
pass_filenames: false
|
||||
|
||||
# TypeScript type checking with bun
|
||||
- id: typescript-typecheck
|
||||
name: TypeScript type check
|
||||
entry: bash -c 'if [ -f "sdks/typescript/package.json" ]; then cd sdks/typescript && bun run typecheck; fi'
|
||||
language: system
|
||||
files: ^sdks/typescript/.*\.(ts|tsx)$
|
||||
pass_filenames: false
|
||||
|
||||
# Documentation linting
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.37.0
|
||||
hooks:
|
||||
- id: markdownlint
|
||||
args: ['--fix']
|
||||
files: \.(md|mdx)$
|
||||
|
||||
# Commit message linting
|
||||
- repo: https://github.com/commitizen-tools/commitizen
|
||||
rev: v3.13.0
|
||||
hooks:
|
||||
- id: commitizen
|
||||
stages: [commit-msg]
|
||||
|
|
@ -0,0 +1 @@
|
|||
3.11
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": ".."
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"python.analysis.typeCheckingMode": "basic",
|
||||
"files.exclude": {},
|
||||
"python.testing.pytestArgs": [
|
||||
"tests"
|
||||
],
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestEnabled": true
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "sync",
|
||||
"type": "shell",
|
||||
"command": "uv sync",
|
||||
"group": "none",
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "shared"
|
||||
},
|
||||
"runOptions": {
|
||||
"runOn": "folderOpen"
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,845 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [3.0.7] - 2026-05-21
|
||||
|
||||
### Added
|
||||
|
||||
- New `src/llm/` module as the single owner of provider runtime: clients, backends, history adapters, tool loop, request builder, credentials, and caching policy (#459)
|
||||
- `AttemptPlan` dataclass captures per-retry provider selection (client, model, reasoning_effort, thinking_budget_tokens, selected_config) and pins it across stream-final retries so streaming doesn't bounce back to primary after the tool loop has settled on fallback (#459)
|
||||
- Gemini JSON-schema sanitizer for `function_declarations` — strips keywords Gemini's validator rejects (`additionalProperties`, `allOf`, etc.) while preserving semantics for all other backends (#459)
|
||||
- Dreamer specialists derive `effective_max_tokens` from `model_config.max_output_tokens` with a per-specialist default fallback (#459)
|
||||
- New cloudevent `LLMCallCompletedEvent` (`llm.call.completed`) fires once per provider hit with full cost-attribution context: transport/provider_label, model, token counts with cache breakdown, finish_reason, outcome, `is_final_attempt`, retry/fallback state, duration, tool-call shape, streaming flag, and agent correlation (`run_id` + iteration). Includes a `CallPurpose` closed enum (`deriver.representation`, `dialectic.answer`, `dream.deduction|induction`, `summary.short|long`) (#637)
|
||||
- `RepresentationCompletedEvent` now carries `total_input_tokens` for full-trace cost attribution (#637)
|
||||
- Per-emitter `honcho_version` injection on all CloudEvents plus emitter health metrics (#637)
|
||||
- `TelemetrySettings.HIGH_VOLUME_SAMPLE_RATE` (default 1.0) — deterministic per-`run_id` sampler so an entire agent trace is kept or dropped together; aggregate envelopes bypass the sampler (#637)
|
||||
- Deriver custom instructions: per-workspace/peer guidance threaded into the deriver prompt with a `MAX_CUSTOM_INSTRUCTIONS_TOKENS` budget (default 2000); deriver `MAX_INPUT_TOKENS` raised 23000 → 25000 to make room (#609)
|
||||
- Configurable embedding dimensions: `EMBEDDING_MODEL_CONFIG__DIMENSIONS_MODE` (`auto`/`always`/`never`) controls whether the OpenAI `dimensions=` parameter is forwarded; `auto` (default) sends it when the operator explicitly set `EMBEDDING_VECTOR_DIMENSIONS` and the model is not on the known-rejecting allowlist (#678)
|
||||
- New `honcho-cli` package — Python CLI for inspecting and managing peers, sessions, and configuration against a Honcho deployment (#424)
|
||||
- `HONCHO_API_URL` env var support in the MCP Worker, enabling self-hosted Honcho deployments to point the Worker at their own instance instead of `https://api.honcho.dev` (#575)
|
||||
- API ID `max_length` increased from 100 to 512 across `WorkspaceCreate`, `PeerCreate`, and `SessionCreate` to align the API contract with the underlying DB schema (#684)
|
||||
- Regression tests covering fallback-config thinking-param reach, provider_params → extra_params boundary, OpenAI reasoning-model parameter routing, Gemini blocked finish_reason handling, and fail-fast `max_tool_iterations` validation (#459)
|
||||
|
||||
### Changed
|
||||
|
||||
- All LLM orchestration moved out of `src/utils/clients.py` into `src/llm/` with modules split by responsibility (api, executor, tool_loop, runtime, registry, conversation, request_builder, credentials, caching, backends, history_adapters) (#459)
|
||||
- Default `ModelConfig` factories (deriver, summary, dreamer specialists, dialectic levels) normalized to `openai/gpt-5.4-mini` with no extra parameters set by default; operators add transport/thinking overrides explicitly (#459)
|
||||
- OpenAI reasoning-model routing widened via `_uses_max_completion_tokens` heuristic covering `gpt-5.x` and `o1/o3/o4` — these models receive `max_completion_tokens` instead of `max_tokens` (#459)
|
||||
- Override client factories switched from unbounded `@cache` to `@lru_cache(maxsize=128)` for predictable memory growth on long-running processes (#459)
|
||||
- `get_backend` now delegates to `client_for_model_config`, so the live-test path and production path share one missing-API-key validation (#459)
|
||||
- Blocked Gemini responses (`SAFETY`, `RECITATION`, `PROHIBITED_CONTENT`, `BLOCKLIST`) raise `LLMError` in the streaming path too (previously only the non-streaming path), ensuring retry/fallback logic fires uniformly (#459)
|
||||
- Transport-change env overrides now strip transport-specific thinking params (thinking_budget_tokens vs. reasoning_effort) during config merge, including at the dialectic-level merge, so switching from Anthropic → OpenAI doesn't leave orphaned Anthropic-only params that the OpenAI backend would reject (#459)
|
||||
- `max_tool_iterations` out-of-range inputs now raise `ValidationException` instead of being silently clamped (#459)
|
||||
- Public API schemas (`WorkspaceCreate`, `PeerCreate`, `SessionCreate`) and SDK validation (`api_types.py`, `validation.ts`) accept IDs up to 512 chars (was 100) (#684)
|
||||
- Peer card prompts reframed as stable identity markers (replaces the prior "biographical/profile facts" language). Induction specialist is now opted out of peer card writes (`can_update_peer_card = False`) so only deduction touches the card (#686)
|
||||
- Vector store queries no longer fetch embedding vectors — only document metadata is returned, reducing payload size and DB load (pgvector, lancedb, turbopuffer) (#682)
|
||||
- Langfuse trace metadata now includes `namespace`, `model`, and `provider` so traces can be filtered by deployment slice (#565)
|
||||
- Deriver: model-aware tokenizer (replaces the previously hardcoded encoding) and explicit guard on empty message content (#647)
|
||||
- Dialectic level defaults now merge correctly with per-level overrides in `src/config` (DEV-1733) (#656)
|
||||
- Default dialectic tool choice switched from forced/required to `auto` (#630)
|
||||
- Vector sync given a substantial retry budget to tolerate transient embedding provider outages (#604)
|
||||
- `AgentToolConclusionsDeletedEvent` payload now carries `levels` for parity with the rest of the conclusion event surface (#612)
|
||||
- Turbopuffer vector store: `InternalServerError` caught and surfaced as a warning rather than a hard failure; unused `upsert_with_retry` and `VectorUpsertResult` removed; explicit silent and explicit-error paths for vector DB server errors (#561)
|
||||
- Troubleshooting docs updated to reflect nested-env-var form for per-component thinking-budget overrides (#459)
|
||||
- README refresh (#681)
|
||||
- CLAUDE.md refreshed against the current `src/` layout (#680)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fallback `ModelConfig` temperature and `thinking_budget_tokens` reach the backend on the final retry — previously the primary's values were pre-populated into caller kwargs early and clobbered fallback values via `effective_config_for_call(update=...)` (#459)
|
||||
- Stream-final retries pin to the `AttemptPlan` that succeeded rather than re-running provider selection through the outer `current_attempt` ContextVar (which could roll streaming back to primary after the tool loop had already switched to fallback) (#459)
|
||||
- OpenAI structured-output calls continue to use `chat.completions.parse()` with strict schema enforcement, while tool-calling paths use `chat.completions.create()` without `strict:True` for broader proxy compatibility (OpenRouter, vLLM, Ollama) (#459)
|
||||
- Gemini `cached_content` reuse keys now include `system_instruction` and `tool_config` so cache hits don't cross configurations that differ only in those fields (#459)
|
||||
- Removed strict parameter validation for thinking params on Anthropic and OpenAI transports — was rejecting valid per-transport configs (#686)
|
||||
- `reverse` query parameter is now honored on the v3 workspace list (`POST /v3/workspaces/list`), peer list (`POST /v3/workspaces/{workspace_id}/peers/list`), workspace-scoped session list (`POST /v3/workspaces/{workspace_id}/sessions/list`), and peer-scoped session list (`POST /v3/workspaces/{workspace_id}/peers/{peer_id}/sessions`). Honcho SDKs at 2.1.0+ were already sending `reverse=true` for these routes but the server silently ignored it. Ties on `created_at` now fall back to the internal nanoid `id` so ordering remains stable across pages (#685)
|
||||
- LLM client factories now receive `base_url` from `LLMSettings` for default providers — previously the override path honored `base_url` but the default path didn't, so operators pointing at OpenAI-compatible proxies via `LLM__OPENAI_BASE_URL` were ignored (#643, fixes #641)
|
||||
- Internal N+1 query in dialectic agent tool execution (DEV-1721) — collapsed per-iteration DB lookups into a single fetch (#652)
|
||||
- Dreamer threshold and time-guard semantics: `check_and_schedule_dream` count filter now includes only `documents.level == 'explicit'` (dreamer-created levels are output, not input, and were inflating the threshold and creating a feedback loop); `last_dream_at` write relocated from `enqueue_dream` into `process_dream` so duplicate enqueues or failed runs no longer reset the 8-hour time guard (#573)
|
||||
- Deriver: blank observations are filtered out before embedding (previously triggered noisy embedding calls and persisted empty rows); blank-observation filtering unified across tool paths (#615)
|
||||
- Surprisal module: filter for level observations changed from `{"level": levels}` to `{"level": {"in": levels}}` — `apply_filter()` requires operator syntax, so the prior call silently returned 0 results and made the entire Surprisal phase of the Dream cycle a no-op (#581, fixes #559)
|
||||
- Removed hardcoded `stop_sequences` override from Deriver `ModelConfig` (was clobbering operator-configured stop sequences) (#587)
|
||||
- Removed stale `stop_sequences` from tests (#607)
|
||||
- Embedding client: `embed()` now wraps single-string input in an array, restoring compatibility with OpenAI-compatible third-party providers that reject scalar input (#586)
|
||||
- Docker Compose: deriver service startup gated on the API service healthcheck (prevents races where the deriver starts before the API has run migrations) (#689)
|
||||
- Docker image: `HEALTHCHECK` directive removed from the shared base image — it probed an HTTP endpoint only the API serves, permanently marking deriver containers as unhealthy. Service-level health checks now belong in each service's own configuration (k8s readiness/liveness probes on the API Deployment only) (#530)
|
||||
- `tests/unified`: `--test-dir`/`--test-file` arguments now use an argparse mutually-exclusive group instead of manual validation (#650)
|
||||
- CrewAI example updated for the latest CrewAI protocol (#631)
|
||||
|
||||
### Removed
|
||||
|
||||
- `src/utils/clients.py` deleted; its responsibilities are split across `src/llm/registry.py`, `src/llm/credentials.py`, and the backend-specific modules (#459)
|
||||
- `HEALTHCHECK` directive removed from the shared Docker image (#530)
|
||||
|
||||
## [3.0.6] - 2026-04-10
|
||||
|
||||
### Changed
|
||||
|
||||
- Tightened transaction scopes across search, agent tools, queue manager, and webhook delivery to minimize DB connection hold time during external operations (#525)
|
||||
- Search operations refactored to two-phase pattern — external work (embeddings, LLM calls) completes before opening a transaction (#525)
|
||||
- Agent tool executor performs external operations before acquiring DB sessions (#525)
|
||||
- Queue manager transaction scope reduced to only the critical section (#525)
|
||||
- Webhook delivery no longer holds a DB session parameter (#525)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Session leakage in non-session-scoped dialectic chat calls (#526)
|
||||
|
||||
### Added
|
||||
|
||||
- Health check endpoint (`/health`) for container orchestration and load balancer probes (#510)
|
||||
|
||||
## [3.0.5] - 2026-04-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- explicit rollback on all transactions to force connection closed
|
||||
|
||||
## [3.0.4] - 2026-04-02
|
||||
|
||||
### Added
|
||||
|
||||
- JSONB metadata validation enforces 100 key limit and max depth of 5 (#419)
|
||||
|
||||
### Changed
|
||||
|
||||
- Schemas refactored from single `schemas.py` into `schemas/api.py`, `schemas/configuration.py`, and `schemas/internal.py` with backwards-compatible re-exports (#419)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Missing `deleted_at` filter on `RepresentationManager._query_documents_recent()` and `._query_documents_most_derived()` allowed soft-deleted documents to leak into the deriver's working representation (#456)
|
||||
- `CleanupStaleItemsCompletedEvent` emitted spuriously when no queue item was actually deleted (#454)
|
||||
- Empty JSON file uploads caused unhandled errors; now returns normalized error responses (#434)
|
||||
- Memory leak: `_observation_locks` switched to `WeakValueDictionary` to prevent unbounded growth (#419)
|
||||
- SQL injection in `dependencies.py`: parameterized `set_config` calls to prevent injection via request context (#419)
|
||||
- NUL byte crashes: string inputs (message content, queries, peer cards) now stripped at schema level (#419)
|
||||
- Filter recursion depth capped at 5 to prevent stack overflow (#419)
|
||||
- Dedup-skipped observations now correctly reflected in created counts (#477)
|
||||
- External vector store support for message search — routes queries through configured external vector store with oversampling and
|
||||
deduplication to handle chunked embeddings (#479)
|
||||
- Dialectic agent no longer holds a DB connection during LLM calls — embeddings are pre-computed before tool execution, DB sessions isolated in `extract_preferences`, `query_documents` no longer accepts a DB session parameter (#477)
|
||||
|
||||
## [3.0.3] - 2026-02-25
|
||||
|
||||
### Added
|
||||
|
||||
- Consolidated session context into a single DB session with 40/60 token budget allocation between summary and messages
|
||||
- Observation validation via `ObservationInput` Pydantic schema with partial-success support and batch embedding with per-observation fallback
|
||||
- Peer card hard cap of 40 facts with case-insensitive deduplication and whitespace normalization
|
||||
- Safe integer coercion (`_safe_int`) for all LLM tool inputs to handle non-integer values like `"Infinity"`
|
||||
- Embedding pre-computation and reuse across multiple search calls in dialectic and representation flows
|
||||
- Peer existence validation in dialectic chat endpoints — raises ResourceNotFoundException instead of silently failing
|
||||
- Logging filter to suppress noisy `GET /metrics` access logs
|
||||
- Oolong long-context aggregation benchmark (synth and real variants, 1K–4M token context windows)
|
||||
- MolecularBench fact quality evaluation (ambiguity, decontextuality, minimality scoring)
|
||||
- CoverageBench information recall evaluation (gold fact extraction, coverage matching, QA verification)
|
||||
- LoCoMo summary-as-context baseline evaluation
|
||||
- Webhook delivery tests, dependency lifecycle tests, queue cleanup tests, summarizer fallback tests
|
||||
- Parallel test execution via pytest-xdist with worker-specific databases
|
||||
- `test_reasoning_levels.py` script for LOCOM dataset testing across reasoning levels
|
||||
|
||||
### Changed
|
||||
|
||||
- Workspace deletion is now async — returns 202 Accepted, validates no active sessions (409 Conflict), cascade-deletes in background
|
||||
- Redis caching layer now stores plain-dict instead of ORM objects, with v2-prefixed keys, storage, resilient `safe_cache_set`/`safe_cache_delete` helpers, and deferred post-commit cache invalidation
|
||||
- All `get_or_create_*` CRUD operations now use savepoints (`db.begin_nested()`) instead of commit/rollback for race condition prevention
|
||||
- Reconciler vector sync uses direct ORM mutation instead of batch parameterized UPDATE statements
|
||||
- Summarizer enforces hard word limit in prompt and creates fallback text for empty summaries with `summary_tokens = 0`
|
||||
- Blocked Gemini responses (SAFETY, RECITATION, PROHIBITED_CONTENT, BLOCKLIST) now raise `LLMError` to trigger retry/backup-provider logic
|
||||
- Gemini client explicitly sets `max_output_tokens` from `max_tokens` parameter
|
||||
- All deriver and metrics collector logging replaced with structured `logging.getLogger(__name__)` calls
|
||||
- Dreamer specialist prompts updated to enforce durable-facts-only peer cards with max 40 entries and deduplication
|
||||
- `GetOrCreateResult` changed from `NamedTuple` to `dataclass` with `async post_commit()` method
|
||||
- FastAPI upgraded from 0.111.0 to 0.131.0; added pyarrow dependency
|
||||
- Queue status filtering to only show user-facing tasks (representation, summary, dream); excludes internal infrastructure tasks
|
||||
|
||||
### Fixed
|
||||
|
||||
- JWT timestamp bug — `JWTParams.t` was evaluated once at class definition time instead of per-instance
|
||||
- Session cache invalidation on deletion was missing
|
||||
- `get_peer_card()` now properly propagates `ResourceNotFoundException` instead of swallowing it
|
||||
- `set_peer_card()` ensures peer exists via `get_or_create_peers()` before updating
|
||||
- Backup provider failover with proper tool input type safety
|
||||
- Removed `setup_admin_jwt()` from server startup
|
||||
- Sentry coroutine detection switched from `asyncio.iscoroutinefunction` to `inspect.iscoroutinefunction`
|
||||
|
||||
### Removed
|
||||
|
||||
- `explicit.py` and `obex.py` benchmarks replaced by coverage.py and molecular.py
|
||||
- Claude Code review automation workflow (`.github/workflows/claude.yml`)
|
||||
- Coverage reporting from default pytest configuration
|
||||
|
||||
## [3.0.2] - 2026-01-27
|
||||
|
||||
### Added
|
||||
|
||||
- Documentation for reasoning_level and Claude Code plugin
|
||||
|
||||
### Changed
|
||||
|
||||
- Gave dreaming sub-agents better prompting around peer card creation, tweaked overall prompts
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added message-search fallback for memory search tool, necessary in fresh sessions
|
||||
- Made FLUSH_ENABLED a config value
|
||||
- Removed N+1 query in search_messages
|
||||
|
||||
## [3.0.1] - 2026-01-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Token counting in Explicit Agent Loop
|
||||
- Backwards compatibility of queue items
|
||||
|
||||
## [3.0.0] - 2026-01-19
|
||||
|
||||
### Added
|
||||
|
||||
- Agentic Dreamer for intelligent memory consolidation using LLM agents
|
||||
- Agentic Dialectic for query answering using LLM agents with tool use
|
||||
- Reasoning levels configuration for dialectic (`minimal`, `low`, `medium`, `high`, `max`)
|
||||
- Prometheus token tracking for deriver and dialectic operations
|
||||
- n8n integration
|
||||
- Cloud Events for auditable telemetry
|
||||
- External Vector Store support for turbopuffer and lancedb with reconciliation flow
|
||||
|
||||
### Changed
|
||||
|
||||
- API route renaming for consistency
|
||||
- Dreamer and dialectic now respect peer card configuration settings
|
||||
- Observations renamed to Conclusions across API and SDKs
|
||||
- Deriver to buffer representation tasks to normalize workloads
|
||||
- Local Representation tasks to create singular QueueItems
|
||||
- getContext endpoint to use `search_query` rather than force `last_user_message`
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dream scheduling bugs
|
||||
- Summary creation when start_message_id > end_message_id
|
||||
- Cashews upgrade to prevent NoScriptError
|
||||
- Memory leak in `accumulate_metric` call
|
||||
|
||||
### Removed
|
||||
|
||||
- Peer card configuration from message configuration; peer cards no longer created/updated in deriver process
|
||||
|
||||
## [2.5.1] - 2025-12-15
|
||||
|
||||
### Fixed
|
||||
|
||||
- Backwards compatibility for `message_ids` field in documents to handle legacy tuple format
|
||||
|
||||
## [2.5.0] - 2025-12-03
|
||||
|
||||
### Added
|
||||
|
||||
- Message level configurations
|
||||
- CRUD operations for observations
|
||||
- Comprehensive test cases for harness
|
||||
- Peer level get_context
|
||||
- Set Peer Card Method
|
||||
- Manual dreaming trigger endpoint
|
||||
|
||||
### Changed
|
||||
|
||||
- Configurations to support more flags for fine-grained control of the deriver, peer cards, summaries, etc.
|
||||
- Working Representations to support more fine-grained parameters
|
||||
|
||||
### Fixed
|
||||
|
||||
- File uploads to match `MessageCreate` structure
|
||||
- Cache invalidation strategy
|
||||
|
||||
## [2.4.3] - 2025-11-20
|
||||
|
||||
### Added
|
||||
|
||||
- Redis caching to improve DB IO
|
||||
- Backup LLM provider to avoid failures when a provider is down
|
||||
|
||||
### Changed
|
||||
|
||||
- QueueItems to use standardized columns
|
||||
- Improved Deduplication logic for Representation Tasks
|
||||
- More finegrained metrics for representation, summary, and peer card tasks
|
||||
- DB constraint to follow standard naming conventions
|
||||
|
||||
## [2.4.2] - 2025-11-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Langfuse tracing to have readable waterfalls
|
||||
- Alembic Migrations to match models.py
|
||||
- message_in_seq correctly included in webhook payload
|
||||
|
||||
### Changed
|
||||
|
||||
- Alembic to always use a session pooler
|
||||
- Statement timeout during alembic operations to 5 min
|
||||
|
||||
## [2.4.1] - 2025-10-24
|
||||
|
||||
### Added
|
||||
|
||||
- Alembic migration validation test suite
|
||||
|
||||
### Fixed
|
||||
|
||||
- Alembic migrations to batch changes
|
||||
- Batch message creation sequence number
|
||||
|
||||
### Changed
|
||||
|
||||
- Logging infrastructure to remove noisy messages
|
||||
- Sentry integration is centralized
|
||||
|
||||
## [2.4.0] - 2025-10-09
|
||||
|
||||
### Added
|
||||
|
||||
- Unified `Representation` class
|
||||
- vllm client support
|
||||
- Periodic queue cleanup logic
|
||||
- WIP Dreaming Feature
|
||||
- LongMemEval to Test Bench
|
||||
- Prometheus Client for better Metrics
|
||||
- Performance metrics instrumentation
|
||||
- Error reporting to deriver
|
||||
- Workspace Delete Method
|
||||
- Multi-db option in test harness
|
||||
|
||||
### Changed
|
||||
|
||||
- Working Representations are Queried on the fly rather than cached in metadata
|
||||
- EmbeddingStore to RepresentationFactory
|
||||
- Summary Response Model to use public_id of message for cutoff
|
||||
- Semantic across codebase to reference resources based on `observer` and `observed`
|
||||
- Prompts for Deriver & Dialectic to reference peer_id and add examples
|
||||
- `Get Context` route returns peer card and representation in addition to messages and summaries
|
||||
- Refactoring logger.info calls to logger.debug where applicable
|
||||
|
||||
### Fixed
|
||||
|
||||
- Gemini client to use async methods
|
||||
|
||||
## [2.3.3] — 2025-10-01
|
||||
|
||||
### Changed
|
||||
|
||||
- Deriver Rollup Queue processes interleaved messages for more context
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dialectic Streaming to follow SSE conventions
|
||||
- Sentry tracing in the deriver
|
||||
|
||||
## [2.3.2] — 2025-09-25
|
||||
|
||||
### Added
|
||||
|
||||
- Get peer cards endpoint (`GET /v2/peers/{peer_id}/card`) for retrieving targeted peer context information
|
||||
|
||||
### Changed
|
||||
|
||||
- Replaced Mirascope dependency with small client implementation for better control
|
||||
- Optimized deriver performance by using joins on messages table instead of storing token count in queue payload
|
||||
- Database scope optimization for various operations
|
||||
- Batch representation task processing for ~10x speed improvement in practice
|
||||
|
||||
### Fixed
|
||||
|
||||
- Separated clean and claim work units in queue manager to prevent race conditions
|
||||
- Skip locked ActiveQueueSession rows on delete operations
|
||||
- Langfuse SDK integration updates for compatibility
|
||||
- Added configurable maximum message size to prevent token overflow in deriver
|
||||
- Various minor bugfixes
|
||||
|
||||
## [2.3.1] - 2025-09-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added max message count to deriver in order to not overflow token limits
|
||||
|
||||
## [2.3.0] — 2025-08-14
|
||||
|
||||
### Added
|
||||
|
||||
- `getSummaries` endpoint to get all available summaries for a session directly
|
||||
- Peer Card feature to improve context for deriver and dialectic
|
||||
|
||||
### Changed
|
||||
|
||||
- Session Peer limit to be based on observers instead, renamed config value to
|
||||
`SESSION_OBSERVERS_LIMIT`
|
||||
- `Messages` can take a custom timestamp for the `created_at` field, defaulting
|
||||
to the current time
|
||||
- `get_context` endpoint returns detailed `Summary` object rather than just
|
||||
summary content
|
||||
- Working representations use a FIFO queue structure to maintain facts rather
|
||||
than a full rewrite
|
||||
- Optimized deriver enqueue by prefetching message sequence numbers (eliminates N+1 queries)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Deriver uses `get_context` internally to prevent context window limit errors
|
||||
- Embedding store will truncate context when querying documents to prevent embedding
|
||||
token limit errors
|
||||
- Queue manager to schedule work based on available works rather than total
|
||||
number of workers
|
||||
- Queue manager to use atomic db transactions rather than long lived transaction
|
||||
for the worker lifecycle
|
||||
- Timestamp formats unified to ISO 8601 across the codebase
|
||||
- Internal get_context method's cutoff value is exclusive now
|
||||
|
||||
## [2.2.0] — 2025-08-07
|
||||
|
||||
### Added
|
||||
|
||||
- Arbitrary filters now available on all search endpoints
|
||||
- Search combines full-text and semantic using reciprocal rank fusion
|
||||
- Webhook support (currently only supports queue_empty and test events, more to come)
|
||||
- Small test harness and custom test format for evaluating Honcho output quality
|
||||
- Added MCP server and documentation for it
|
||||
|
||||
### Changed
|
||||
|
||||
- Search has 10 results by default, max 100 results
|
||||
- Queue structure generalized to handle more event types
|
||||
- Summarizer now exhaustive by default and tuned for performance
|
||||
|
||||
### Fixed
|
||||
|
||||
- Resolve race condition for peers that leave a session while sending messages
|
||||
- Added explicit rollback to solve integrity error in queue
|
||||
- Re-introduced Sentry tracing to deriver
|
||||
- Better integrity logic in get_or_create API methods
|
||||
|
||||
## [2.1.2] — 2025-07-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- Summarizer module to ignore empty summaries and pass appropriate one to get_context
|
||||
- Structured Outputs calls with OpenAI provider to pass strict=True to Pydantic Schema
|
||||
|
||||
## [2.1.1] — 2025-07-23
|
||||
|
||||
### Added
|
||||
|
||||
- Test harness for custom Honcho evaluations
|
||||
- Better support for session and peer aware dialectic queries
|
||||
- Langfuse settings
|
||||
- Added recent history to dialectic prompt, dynamic based on new context window size setting
|
||||
|
||||
### Fixed
|
||||
|
||||
- Summary queue logic
|
||||
- Formatting of logs
|
||||
- Filtering by session
|
||||
- Peer targeting in queries
|
||||
|
||||
### Changed
|
||||
|
||||
- Made query expansion in dialectic off by default
|
||||
- Overhauled logging
|
||||
- Refactor summarization for performance and code clarity
|
||||
- Refactor queue payloads for clarity
|
||||
|
||||
## [2.1.0] — 2025-07-17
|
||||
|
||||
### Added
|
||||
|
||||
- File uploads
|
||||
- Brand new "ROTE" deriver system
|
||||
- Updated dialectic system
|
||||
- Local working representations
|
||||
- Better logging for deriver/dialectic
|
||||
- Endpoint for deriver queue status
|
||||
|
||||
### Fixed
|
||||
|
||||
- Document insertion
|
||||
- Session-scoped and peer-targeted dialectic queries work now
|
||||
|
||||
### Removed
|
||||
|
||||
- Peer-level messages
|
||||
|
||||
### Changed
|
||||
|
||||
- Dialectic chat endpoint takes a single query
|
||||
- Rearranged configuration values (LLM, Deriver, Dialectic, History->Summary)
|
||||
|
||||
## [2.0.5] - 2025-07-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- Groq API client to use the Async library
|
||||
|
||||
## [2.0.4] - 2025-07-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- Migration/provision scripts did not have correct database connection arguments, causing timeouts
|
||||
|
||||
## [2.0.3] - 2025-07-01
|
||||
|
||||
### Fixed
|
||||
|
||||
- Bug that causes runtime error when Sentry flags are enabled
|
||||
|
||||
## [2.0.2] - 2025-06-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Database initialization was misconfigured and led to provision_db script failing: switch to consistent working configuration with transaction pooler
|
||||
|
||||
## [2.0.1] - 2025-06-26
|
||||
|
||||
### Added
|
||||
|
||||
- Ergonomic SDKs for Python and TypeScript (uses Stainless underneath)
|
||||
- Deriver Queue Status endpoint
|
||||
- Complex arbitrary filters on workspace/session/peer/message
|
||||
- Message embedding table for full semantic search
|
||||
|
||||
### Changed
|
||||
|
||||
- Overhauled documentation
|
||||
- BasedPyright typing for entire project
|
||||
- Resource filtering expanded to include logical operators
|
||||
|
||||
### Fixed
|
||||
|
||||
- Various bugs
|
||||
- Use new config arrangement everywhere
|
||||
- Remove hardcoded responses
|
||||
|
||||
## [2.0.0] - 2025-06-24
|
||||
|
||||
### Added
|
||||
|
||||
- Ability to get a peer's working representation
|
||||
- Metadata to all data primitives (Workspaces, Peers, Sessions, Messages)
|
||||
- Internal metadata to store Honcho's state no longer exposed in API
|
||||
- Batch message operations and enhanced message querying with token and message count limits
|
||||
- Search and summary functionalities scoped by workspace, peer, and session
|
||||
- Session context retrieval with summaries and token allocation
|
||||
- HNSW Index for Documents Table
|
||||
- Centralized Configuration via Environment Variables or `config.toml` file
|
||||
|
||||
### Changed
|
||||
|
||||
- API route is now /v2/
|
||||
- New architecture centered around the concept of a "peer" replaces the former
|
||||
"app"/"user"/"session" paradigm
|
||||
- Workspaces replace "apps" as top-level namespace
|
||||
- Peers replace "users"
|
||||
- Sessions no longer nested beneath peers and no longer limited to a single
|
||||
user-assistant model. A session exists independently of any one peer and
|
||||
peers can be added to and removed from sessions.
|
||||
- Dialectic API is now part of the Peer, not the Session
|
||||
- Dialectic API now allows queries to be scoped to a session or "targeted"
|
||||
to a fellow peer
|
||||
- Database schema migrated to adopt workspace/peer/session naming and structure
|
||||
- Authentication and JWT scopes updated to workspace/peer/session hierarchy
|
||||
- Queue processing now works on 'work units' instead of sessions
|
||||
- Message token counting updated with tiktoken integration and fallback heuristic
|
||||
- Queue and message processing updated to handle sender/target and task types for multi-peer scenarios
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improved error handling and validation for batch message operations and metadata
|
||||
- Database Sessions to be more atomic to reduce idle in transaction time
|
||||
|
||||
### Removed
|
||||
|
||||
- Metamessages removed in favor of metadata
|
||||
- Collections and Documents no longer exposed in the API, solely internal
|
||||
- Obsolete tests for apps, users, collections, documents, and metamessages
|
||||
|
||||
## [1.1.0] - 2025-05-15
|
||||
|
||||
### Added
|
||||
|
||||
- Normalize resources to remove joins and increase query performance
|
||||
- Query tracing for debugging
|
||||
|
||||
### Changed
|
||||
|
||||
- `/list` endpoints to not require a request body
|
||||
- `metamessage_type` to `label` with backwards compatibility
|
||||
- Database Provisioning to rely on alembic
|
||||
- Database Session Manager to explicitly rollback transactions before closing
|
||||
the connection
|
||||
|
||||
### Fixed
|
||||
|
||||
- Alembic Migrations to include initial database migrations
|
||||
- Sentry Middleware to not report Honcho Exceptions
|
||||
|
||||
## [1.0.0] - 2025-04-10
|
||||
|
||||
### Added
|
||||
|
||||
- JWT based API authentication
|
||||
- Configurable logging
|
||||
- Consolidated LLM Inference via `ModelClient` class
|
||||
- Dynamic logging configurable via environment variables
|
||||
|
||||
### Changed
|
||||
|
||||
- Deriver & Dialectic API to use Hybrid Memory Architecture
|
||||
- Metamessages are not strictly tied to a message
|
||||
- Database provisioning is a separate script instead of happening on startup
|
||||
- Consolidated `session/chat` and `session/chat/stream` endpoints
|
||||
|
||||
## [0.0.16] - 2025-03-05
|
||||
|
||||
### Added
|
||||
|
||||
- Detailed custom exceptions for better error handling
|
||||
- CLAUDE.md for claude code
|
||||
|
||||
### Changed
|
||||
|
||||
- Deriver to use a new cognitive architecture that only updates on user messages
|
||||
and updates user representation to apply more confidence scores to its known
|
||||
facts
|
||||
- Dialectic API token cutoff from 150 tokens to 300
|
||||
- Dialectic API uses Claude 3.7 Sonnet
|
||||
- SQLAlchemy echo changed to false by default, can be enabled with SQL_DEBUG
|
||||
environment flag
|
||||
|
||||
### Fixed
|
||||
|
||||
- Self-hosting documentation and README to mention `uv` instead of `poetry`
|
||||
|
||||
## [0.0.15] - 2025-01-06
|
||||
|
||||
### Added
|
||||
|
||||
- Alembic for handling database migrations
|
||||
- Additional indexes for reading Messages and Metamessages
|
||||
- Langfuse for prompt tracing
|
||||
|
||||
### Changed
|
||||
|
||||
- API validation using Pydantic
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dialectic Streaming Endpoint properly sends text in `StreamingResponse`
|
||||
- Deriver Queue handles graceful shutdown
|
||||
|
||||
## [0.0.14] — 2024-11-14
|
||||
|
||||
### Changed
|
||||
|
||||
- Query Documents endpoint is a POST request for better DX
|
||||
- `String` columns are now `TEXT` columns to match postgres best practices
|
||||
- Docstrings to have better stainless generations
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dialectic API to use most recent user representation
|
||||
- Prepared Statements Transient Error with `psycopg`
|
||||
- Queue parallel worker scheduling
|
||||
|
||||
## [0.0.13] — 2024-11-07
|
||||
|
||||
### Added
|
||||
|
||||
- Ability to clone session for a user to achieve more [loom-like](https://github.com/socketteer/loom/) behavior
|
||||
|
||||
## [0.0.12] — 2024-10-21
|
||||
|
||||
### Added
|
||||
|
||||
- GitHub Actions Testing
|
||||
- Ability to disable derivations on a session using the `deriver_disabled` flag
|
||||
in a session's metadata
|
||||
- `/v1/` prefix to all routes
|
||||
- Environment variable to control deriver workers
|
||||
|
||||
### Changed
|
||||
|
||||
- public_ids to use [NanoID](https://github.com/ai/nanoid) and internal ID to
|
||||
use `BigInt`
|
||||
- Dialectic Endpoint can take a list of queries
|
||||
- Using `uv` for project management
|
||||
- User Representations stored in a metamessage rather than using reserved
|
||||
collection
|
||||
- Base model for Dialectic API and Deriver is now Claude 3.5 Sonnet
|
||||
- Paginated GET requests now POST requests for better developer UX
|
||||
|
||||
### Removed
|
||||
|
||||
- Mirascope Dependency
|
||||
- Slowapi Dependency
|
||||
- Opentelemetry Dependencies and Setup
|
||||
|
||||
## [0.0.11] — 2024-08-01
|
||||
|
||||
### Added
|
||||
|
||||
- `session_id` column to `QueueItem` Table
|
||||
- `ActiveQueueSession` Table to track, which sessions are being actively
|
||||
processed
|
||||
- Queue can process multiple sessions at once
|
||||
|
||||
### Changed
|
||||
|
||||
- Sessions do not require a `location_id`
|
||||
- Detailed printing using `rich`
|
||||
|
||||
## [0.0.10] — 2024-07-23
|
||||
|
||||
### Added
|
||||
|
||||
- Test cases for Storage API
|
||||
- Sentry tracing and profiling
|
||||
- Additional Error handling
|
||||
|
||||
### Changed
|
||||
|
||||
- Document API uses same embedding endpoint as deriver
|
||||
- CRUD operations use one less database call by removing extra refresh
|
||||
- Use database for timestampz rather than API
|
||||
- Pydantic schemas to use modern syntax
|
||||
|
||||
### Fixed
|
||||
|
||||
- Deriver queue resolution
|
||||
|
||||
## [0.0.9] — 2024-05-16
|
||||
|
||||
### Added
|
||||
|
||||
- Deriver to docker compose
|
||||
- Postgres based Queue for background jobs
|
||||
|
||||
### Changed
|
||||
|
||||
- Deriver to use a queue instead of supabase realtime
|
||||
- Using mirascope instead of langchain
|
||||
|
||||
### Removed
|
||||
|
||||
- Legacy SDKs in preference for stainless SDKs
|
||||
|
||||
## [0.0.8] — 2024-05-09
|
||||
|
||||
### Added
|
||||
|
||||
- Documentation to OpenAPI
|
||||
- Bearer token auth to OpenAPI routes
|
||||
- Get by ID routes for users and collections
|
||||
- [NodeJS](https://github.com/plastic-labs/honcho-node) SDK support
|
||||
|
||||
### Changed
|
||||
|
||||
- Authentication Middleware now implemented using built-in FastAPI Security
|
||||
module
|
||||
- Get by name routes for users and collections now include "name" in slug
|
||||
- Python SDK moved to separate [repository](https://github.com/plastic-labs/honcho-python)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Error reporting for methods with integrity errors due to unique key
|
||||
constraints
|
||||
|
||||
## [0.0.7] — 2024-04-01
|
||||
|
||||
### Added
|
||||
|
||||
- Authentication Middleware Interface
|
||||
|
||||
## [0.0.6] — 2024-03-21
|
||||
|
||||
### Added
|
||||
|
||||
- Full docker-compose for API and Database
|
||||
|
||||
### Fixed
|
||||
|
||||
- API Response schema removed unnecessary fields
|
||||
- OTEL logging to properly work with async database engine
|
||||
- `fly.toml` default settings for deriver set `auto_stop=false`
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactored API server into multiple route files
|
||||
|
||||
## [0.0.5] — 2024-03-14
|
||||
|
||||
### Added
|
||||
|
||||
- Metadata to all data primitives (Users, Sessions, Messages, etc.)
|
||||
- Ability to filter paginated GET requests by JSON filter based on metadata
|
||||
- Optional Sentry error monitoring
|
||||
- Optional Opentelemetry logging
|
||||
- Dialectic API to interact with honcho agent and get insights about users
|
||||
- Automatic Fact Derivation Script for automatically generating simple memory
|
||||
|
||||
### Changed
|
||||
|
||||
- API Server now uses async methods to make use of benefits of FastAPI
|
||||
|
||||
## [0.0.4] — 2024-02-22
|
||||
|
||||
### Added
|
||||
|
||||
- apps table with a relationship to the users table
|
||||
- users table with a relationship to the collections and sessions tables
|
||||
- Reverse Pagination support to get recent messages, sessions, etc. more easily
|
||||
- Linting Rules
|
||||
|
||||
### Changed
|
||||
|
||||
- Get sessions method returns all sessions including inactive
|
||||
- using timestampz instead of timestamp
|
||||
|
||||
## [0.0.3] — 2024-02-15
|
||||
|
||||
### Added
|
||||
|
||||
- Collections table to reference a collection of embedding documents
|
||||
- Documents table to hold vector embeddings for RAG workflows
|
||||
- Local scripts for running a postgres database with pgvector installed
|
||||
- OpenAI Dependency for embedding models
|
||||
- PGvector dependency for vector db support
|
||||
|
||||
### Changed
|
||||
|
||||
- session_data is now metadata
|
||||
- session_data is a JSON field used python `dict` for compatibility
|
||||
|
||||
## [0.0.2] — 2024-02-01
|
||||
|
||||
### Added
|
||||
|
||||
- Pagination for requests via `fastapi_pagination`
|
||||
- Metamessages
|
||||
- `get_message` routes
|
||||
- `created_at` field added to each Table
|
||||
- Message size limits
|
||||
|
||||
### Changed
|
||||
|
||||
- IDs are now UUIDs
|
||||
- default rate limit now 100 requests per minute
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed messages from session response model
|
||||
|
||||
## [0.0.1] — 2024-02-01
|
||||
|
||||
### Added
|
||||
|
||||
- Rate limiting of 10 requests for minute
|
||||
- Application level scoping
|
||||
|
|
@ -0,0 +1,299 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
# Honcho Overview
|
||||
|
||||
## What is Honcho?
|
||||
|
||||
Honcho is an infrastructure layer for building AI agents with memory and social cognition. Its primary purposes include:
|
||||
|
||||
- Imbuing agents with a sense of identity
|
||||
- Personalizing user experiences through understanding user psychology
|
||||
- Providing a Chat Endpoint (the Dialectic agent) that injects personal context just-in-time
|
||||
- Supporting development of LLM-powered applications that adapt to end users
|
||||
- Enabling multi-peer sessions where multiple participants (users or agents) can interact
|
||||
|
||||
Honcho leverages the inherent reasoning capabilities of LLMs to build coherent models of user psychology over time, enabling more personalized and effective AI interactions.
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Peer Paradigm
|
||||
|
||||
Honcho uses a peer-based model where both users and agents are represented as "peers". This unified approach enables:
|
||||
|
||||
- Multi-participant sessions with mixed human and AI agents
|
||||
- Configurable observation settings (which peers observe which others)
|
||||
- Flexible identity management for all participants
|
||||
|
||||
### Key Primitives
|
||||
|
||||
- **Workspace** (formerly App): The root organizational unit containing all resources
|
||||
- **Peer** (formerly User): Any participant in the system (human or AI)
|
||||
- **Session**: A conversation context that can involve multiple peers
|
||||
- **Message**: Data units that can represent communication between peers OR arbitrary data ingested by a peer to enhance its global representation
|
||||
- **Collections & Documents**: Internal vector storage for peer representations. Collections are keyed by `(observer, observed)` peer pairs. Collections/Documents are not directly exposed via API, but the observations stored within them are exposed as **Conclusions** (see `/v3/.../conclusions` endpoints).
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### API Structure
|
||||
|
||||
All API routes follow the pattern: `/v3/{resource}/{id}/{action}`. Most "list/search" endpoints are `POST` so they can accept rich filter bodies.
|
||||
|
||||
- **Workspaces**: Create, list, update, search
|
||||
- **Peers**: Create, list, update, chat (dialectic), messages, representation
|
||||
- **Sessions**: Create, list, update, delete, clone, manage peers, get context
|
||||
- **Messages**: Create (batch up to 100), upload (file), list, get, update
|
||||
- **Conclusions**: Create, list, query (semantic search), delete — the API-facing name for observations stored in `(observer, observed)` collections
|
||||
- **Keys**: Create scoped JWTs
|
||||
- **Webhooks**: Register endpoint, list, delete, test
|
||||
|
||||
### Key Features
|
||||
|
||||
#### Chat Endpoint (Dialectic agent) (`/peers/{peer_id}/chat`)
|
||||
|
||||
- Provides bespoke responses informed by the representation
|
||||
- Integrates long-term facts from vector storage
|
||||
- Supports streaming responses
|
||||
- Configurable LLM providers
|
||||
|
||||
#### Message Processing Pipeline
|
||||
|
||||
1. Messages created via API (batch or single)
|
||||
2. Enqueued for background processing:
|
||||
- `representation`: Update peer's context
|
||||
- `summary`: Create session summaries
|
||||
3. Session-based queue processing ensures order
|
||||
4. Results stored internally in vector DB
|
||||
|
||||
### Configuration
|
||||
|
||||
- Hierarchical config: config.toml + environment variables
|
||||
- Database settings with connection pooling
|
||||
- Multiple LLM provider support
|
||||
- Background worker (deriver) settings
|
||||
- Authentication can be toggled on/off
|
||||
|
||||
## Development Guide
|
||||
|
||||
### Commands
|
||||
|
||||
- Setup: `uv sync`
|
||||
- Run server: `uv run fastapi dev src/main.py`
|
||||
- Run tests: `uv run pytest tests/`
|
||||
- Run single test: `uv run pytest tests/path/to/test_file.py::test_function`
|
||||
- Linting: `uv run ruff check src/`
|
||||
- Typechecking: `uv run basedpyright`
|
||||
- Format code: `uv run ruff format src/`
|
||||
|
||||
### SDK Testing
|
||||
|
||||
#### TypeScript SDK
|
||||
|
||||
**🚨 DO NOT RUN `bun test` DIRECTLY. IT WILL NOT WORK. 🚨**
|
||||
|
||||
The TypeScript SDK tests require a running Honcho server with database and Redis. Running `bun test` alone will fail immediately because there's no server. The tests are orchestrated via pytest which handles all the infrastructure setup.
|
||||
|
||||
**The ONLY way to run TypeScript SDK tests:**
|
||||
|
||||
```bash
|
||||
# From the monorepo root (not from sdks/typescript/)
|
||||
uv run pytest tests/ -k typescript
|
||||
```
|
||||
|
||||
**To type-check the TypeScript SDK (this is fine to run directly):**
|
||||
|
||||
```bash
|
||||
cd sdks/typescript && bun run tsc --noEmit
|
||||
```
|
||||
|
||||
### Code Style
|
||||
|
||||
- Follow isort conventions with absolute imports preferred
|
||||
- Use explicit type hints with SQLAlchemy mapped_column annotations
|
||||
- snake_case for variables/functions; PascalCase for classes
|
||||
- Line length: 88 chars (Black compatible)
|
||||
- Explicit error handling with appropriate exception types
|
||||
- Docstrings: Use Google style docstrings
|
||||
- **Never hold a DB session during external calls** (LLM, embedding, HTTP). If a function needs both a DB session and an external call result, compute the external result first and pass it as a parameter. This avoids tying up DB connections during slow network I/O. Use `tracked_db` for short-lived, DB-only operations; pass a shared session when multiple DB-only calls can reuse one connection.
|
||||
|
||||
### Runtime Architecture
|
||||
|
||||
Honcho runs as two cooperating processes that share a Postgres database and Redis cache:
|
||||
|
||||
- **API server** (`uv run fastapi dev src/main.py`) — handles HTTP, enqueues background work, returns immediately. Hosts the **Dialectic** agent inline (synchronous tool loop during chat requests).
|
||||
- **Deriver worker** (`uv run python -m src.deriver`) — long-running queue consumer (uvloop). Runs the **Deriver**, **Summarizer**, and **Dreamer** off the queue. Can run multiple instances (`DERIVER_WORKERS`). Also hosts an in-process **Reconciler scheduler** (`src/reconciler/`) that periodically embeds messages with `sync_state='pending'` in `MessageEmbedding` and cleans up stale queue items — embedding generation is decoupled from message creation by design.
|
||||
|
||||
### Agent Architecture
|
||||
|
||||
Honcho uses several specialized LLM agents. They share tool definitions and the LLM client abstraction in `src/utils/agent_tools.py` + `src/llm/`.
|
||||
|
||||
> **Terminology:** what users see as **conclusions** (the public API surface and the term we use in documentation) is called **observations** in code symbols — `create_observations`, `delete_observations`, `get_observation_context`, etc. Doc prose below uses "conclusions"; references to actual code symbols stay as "observations."
|
||||
|
||||
#### 1. Deriver (`src/deriver/`)
|
||||
|
||||
**Role**: Memory formation through content ingestion.
|
||||
|
||||
The Deriver processes batches of incoming messages and extracts conclusions about peers. The current architecture is "minimal deriver" — a **single LLM call** per batch using structured output, not an agentic tool loop. This trades flexibility for cost and predictability.
|
||||
|
||||
- **Trigger**: Messages enqueued by `src/deriver/enqueue.py` on message create; consumed by `src/deriver/queue_manager.py` → `consumer.process_item()` → `deriver.process_representation_tasks_batch()`.
|
||||
- **Output**: Explicit conclusions (direct facts) and deductive conclusions (inferences) saved to `(observer, observed)` collections.
|
||||
- **Entry point**: `src/deriver/__main__.py` → `queue_manager.main()`.
|
||||
- **Prompts**: `src/deriver/prompts.py` (`minimal_deriver_prompt`).
|
||||
- **Custom instructions**: per-workspace/peer guidance can be threaded into the prompt via reasoning configuration; `DERIVER__MAX_CUSTOM_INSTRUCTIONS_TOKENS` caps the addition (default 2000) and `DERIVER__MAX_INPUT_TOKENS` defaults to 25000 to make room.
|
||||
|
||||
#### 2. Dialectic (`src/dialectic/`)
|
||||
|
||||
**Role**: Analysis and recall for answering queries.
|
||||
|
||||
The Dialectic answers questions about peers by strategically gathering context from memory. It is the only tool-using agent on the synchronous request path — it loops over `DIALECTIC_TOOLS` until it has enough context to answer. (The Dreamer specialists also use tools, but run off the queue.)
|
||||
|
||||
- **Trigger**: API call to `POST /v3/.../peers/{peer_id}/chat`.
|
||||
- **Tools** (see `DIALECTIC_TOOLS` in `src/utils/agent_tools.py`): `search_memory`, `search_messages`, `get_observation_context`, `grep_messages`, `get_messages_by_date_range`, `search_messages_temporal`, `get_reasoning_chain`. At the `minimal` reasoning level, a reduced set (`DIALECTIC_TOOLS_MINIMAL`) is used: just `search_memory` + `search_messages`.
|
||||
- **Reasoning levels**: 5 tiers — `minimal`, `low`, `medium`, `high`, `max` — each with its own model config (see `DialecticLevelSettings` in `src/config.py`).
|
||||
- **Output**: Natural language response grounded in gathered context. Supports SSE streaming.
|
||||
- **Entry point**: `src/dialectic/chat.py` → `agentic_chat()` / `agentic_chat_stream()` → `DialecticAgent` (in `src/dialectic/core.py`).
|
||||
|
||||
#### 3. Dreamer (`src/dreamer/`)
|
||||
|
||||
**Role**: Consolidation and self-improvement of memory.
|
||||
|
||||
The Dreamer is an orchestrated multi-specialist system that runs during scheduled "dreams" to consolidate conclusions and build reasoning trees.
|
||||
|
||||
- **Trigger**: Scheduled via `DreamScheduler` (`src/dreamer/dream_scheduler.py`) or explicit dream task on the queue.
|
||||
- **Strategy**: Surprisal-based prioritization (`src/dreamer/surprisal.py`) selects which conclusions to expand. The orchestrator (`orchestrator.run_dream`) runs two specialist phases:
|
||||
1. **DeductionSpecialist** (`specialists.py`) — produces deductive conclusions from explicit conclusions. Tools: `get_recent_observations`, `search_memory`, `search_messages`, `create_observations_deductive`, `delete_observations`, `update_peer_card`.
|
||||
2. **InductionSpecialist** — produces inductive conclusions from explicit + deductive conclusions. Tools: same discovery set + `create_observations_inductive`, `update_peer_card`.
|
||||
- **Reasoning trees** (`src/dreamer/trees/`, migration `f1a2b3c4d5e6_add_reasoning_tree_columns`): each conclusion links to its premises and downstream conclusions, enabling `get_reasoning_chain` traversal at recall time.
|
||||
- **Output**: Deductive/inductive conclusions, consolidated redundancies, updated peer cards.
|
||||
- **Entry point**: `src/dreamer/orchestrator.py` → `process_dream()` (the package-level export from `src/dreamer/__init__.py`), which wraps `run_dream()`.
|
||||
|
||||
#### 4. Summarizer (`src/utils/summarizer.py`)
|
||||
|
||||
**Role**: Two-tier session summarization (direct LLM call — no agentic tools).
|
||||
|
||||
- **Trigger**: Runs as part of the queue pipeline alongside representation tasks.
|
||||
- **Tiers**: short summary every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` messages (default 20); long summary every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60). Token caps configurable via `SUMMARY_MAX_TOKENS_SHORT` / `SUMMARY_MAX_TOKENS_LONG`.
|
||||
|
||||
#### Shared Agent Infrastructure
|
||||
|
||||
- **Tool definitions** (`src/utils/agent_tools.py`): unified `TOOLS` dict; per-agent lists (`DIALECTIC_TOOLS`, `DIALECTIC_TOOLS_MINIMAL`, `DREAMER_TOOLS`, `DEDUCTION_SPECIALIST_TOOLS`, `INDUCTION_SPECIALIST_TOOLS`).
|
||||
- **LLM subsystem** (`src/llm/`): provider-agnostic `honcho_llm_call()`. Backends in `src/llm/backends/` (`anthropic.py`, `gemini.py`, `openai.py`). Includes prompt caching (`caching.py`), structured output (`structured_output.py`), tool loop (`tool_loop.py`), history adapters for cross-provider message formats, and a model registry. Per-retry provider selection is pinned via an `AttemptPlan` so stream-final retries don't bounce back to primary after the tool loop has settled on fallback.
|
||||
- **Per-agent model config**: each agent has its own `MODEL_CONFIG` in `src/config.py` with fallback chains (see `ConfiguredModelSettings`, `FallbackModelSettings`).
|
||||
- **Telemetry**: cloudevents in `src/telemetry/events/` cover API routes, dialectic, dream, deletion, reconciliation, representation, and per-call LLM accounting (`llm.py` — `LLMCallCompletedEvent` fires once per provider hit with full cost-attribution context). High-volume events are sampled deterministically per `run_id` via `TelemetrySettings.HIGH_VOLUME_SAMPLE_RATE`.
|
||||
|
||||
### Project Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── main.py # FastAPI app: middleware, routers, lifespan, exception handlers
|
||||
├── models.py # SQLAlchemy ORM models (Workspace/Peer/Session/Message/
|
||||
│ # MessageEmbedding/Collection/Document/QueueItem/...)
|
||||
├── config.py # Pydantic-settings configuration (very large; see README)
|
||||
├── db.py # Engine + session/context management (request_context var)
|
||||
├── dependencies.py # FastAPI DI (tracked_db, etc.)
|
||||
├── exceptions.py # Custom exception types (HonchoException + subclasses)
|
||||
├── security.py # JWT authentication
|
||||
├── embedding_client.py # Embedding provider client (configurable dimensions
|
||||
│ # via EMBEDDING_MODEL_CONFIG__DIMENSIONS_MODE)
|
||||
├── schemas/ # Pydantic schemas
|
||||
│ ├── api.py # Public API request/response schemas
|
||||
│ ├── configuration.py # Per-resource configuration schemas
|
||||
│ └── internal.py # Internal-only schemas (queue payloads, etc.)
|
||||
├── crud/ # Per-resource DB operations
|
||||
│ ├── collection.py, deriver.py, document.py, message.py
|
||||
│ ├── peer.py, peer_card.py, representation.py (RepresentationManager)
|
||||
│ ├── session.py, webhook.py, workspace.py
|
||||
├── routers/ # FastAPI route handlers (all under /v3)
|
||||
│ ├── workspaces.py, peers.py (dialectic /chat lives here), sessions.py
|
||||
│ ├── messages.py, conclusions.py, keys.py, webhooks.py
|
||||
├── dialectic/ # Dialectic agent — runs inline per chat request
|
||||
│ ├── chat.py # agentic_chat() / agentic_chat_stream()
|
||||
│ ├── core.py # DialecticAgent (the tool-loop driver)
|
||||
│ └── prompts.py
|
||||
├── deriver/ # Background queue consumer (separate process)
|
||||
│ ├── __main__.py # `python -m src.deriver` entry point
|
||||
│ ├── queue_manager.py # QueueManager + main() loop
|
||||
│ ├── consumer.py # process_item dispatcher (representation / deletion / reconciler)
|
||||
│ ├── deriver.py # "minimal deriver" — single-LLM-call batch processor
|
||||
│ ├── enqueue.py # API → queue producer
|
||||
│ └── prompts.py
|
||||
├── dreamer/ # Memory consolidation (runs off the queue)
|
||||
│ ├── orchestrator.py # run_dream() / process_dream()
|
||||
│ ├── specialists.py # DeductionSpecialist + InductionSpecialist
|
||||
│ ├── dream_scheduler.py
|
||||
│ ├── surprisal.py # Surprisal-based conclusion prioritization
|
||||
│ └── trees/ # Reasoning-tree primitives
|
||||
├── reconciler/ # In-process scheduler hosted by the deriver worker
|
||||
│ ├── scheduler.py # ReconcilerScheduler (started from queue_manager.py)
|
||||
│ ├── sync_vectors.py # Embeds MessageEmbedding rows with sync_state='pending'
|
||||
│ └── queue_cleanup.py # Removes stale queue items
|
||||
├── llm/ # Provider-agnostic LLM client subsystem
|
||||
│ ├── api.py, backend.py, executor.py, runtime.py, registry.py
|
||||
│ ├── caching.py, structured_output.py, tool_loop.py, conversation.py
|
||||
│ ├── history_adapters.py, request_builder.py, credentials.py, types.py
|
||||
│ └── backends/ # anthropic.py, gemini.py, openai.py
|
||||
├── cache/ # Redis cache abstraction (cashews-backed)
|
||||
│ └── client.py
|
||||
├── vector_store/ # Optional external vector stores (pgvector is default,
|
||||
│ │ # implemented via MessageEmbedding/Document in models+crud)
|
||||
│ ├── lancedb.py
|
||||
│ └── turbopuffer.py
|
||||
├── telemetry/ # Observability
|
||||
│ ├── emitter.py # CloudEvents emitter
|
||||
│ ├── logging.py # Logging helpers + route-template extraction
|
||||
│ ├── metrics_collector.py, reasoning_traces.py, sentry.py
|
||||
│ ├── events/ # Event type definitions
|
||||
│ └── prometheus/ # Prometheus metric definitions
|
||||
├── utils/ # Cross-cutting utilities
|
||||
│ ├── agent_tools.py # Tool definitions + per-agent tool lists
|
||||
│ ├── summarizer.py # Two-tier session summarizer
|
||||
│ ├── representation.py # Representation formatting (distinct from crud/representation.py)
|
||||
│ ├── search.py, filter.py, formatting.py
|
||||
│ ├── tokens.py # tiktoken-based counting
|
||||
│ ├── work_unit.py, queue_payload.py
|
||||
│ ├── config_helpers.py, json_parser.py, files.py
|
||||
│ └── types.py
|
||||
└── webhooks/ # Webhook delivery
|
||||
├── events.py
|
||||
└── webhook_delivery.py
|
||||
```
|
||||
|
||||
- Tests in pytest with fixtures in tests/conftest.py; subdirs mirror src/ (`tests/deriver/`, `tests/dialectic/`, etc.) plus `tests/bench/` (perf benchmarks), `tests/integration/`, `tests/live_llm/` (gated by `--live-llm`), and `tests/unified/` (the unified runner).
|
||||
- Use environment variables via python-dotenv (.env). Config precedence: env > .env > config.toml > defaults.
|
||||
|
||||
### Database Design
|
||||
|
||||
- All tables use text IDs (nanoid format) as primary keys
|
||||
- Composite foreign keys for multi-tenant relationships
|
||||
- Feature flags on workspace, peer, and session levels
|
||||
- Token counting on messages for usage tracking
|
||||
- JSONB metadata fields for extensibility
|
||||
- HNSW indexes for vector similarity search
|
||||
|
||||
### Key Architectural Decisions
|
||||
|
||||
1. **Peer Paradigm**: humans and AI agents are unified as "Peers"; many-to-many with Sessions. Internal vector storage (Collections/Documents) is keyed by `(observer, observed)` peer pairs — the same mechanism powers self-representation (`observer == observed`) and cross-peer modeling.
|
||||
2. **Multi-Peer Sessions**: Sessions can have multiple participants with different observation settings.
|
||||
3. **API server / worker split**: API enqueues, deriver worker process consumes. Never block HTTP on LLM work. The Reconciler runs as an in-process scheduler inside the deriver, handling async embedding sync and queue cleanup.
|
||||
4. **"Minimal" deriver**: memory formation is a single structured-output LLM call per batch, not an agentic tool loop. Predictable cost, lower latency. The Dialectic is the one true tool-using agent.
|
||||
5. **Provider-agnostic LLM layer** (`src/llm/`): all model calls go through `honcho_llm_call()`. Backends (`anthropic`, `gemini`, `openai`) sit behind a registry; per-agent `MODEL_CONFIG` with fallback chains is resolved at call time.
|
||||
6. **Dialectic reasoning tiers**: 5 levels (`minimal` → `max`); each level has its own model config and tool set (`minimal` uses a reduced toolset).
|
||||
7. **Hybrid search**: Postgres FTS (GIN index on `to_tsvector('english', content)`) + vector similarity (HNSW on `MessageEmbedding.embedding`). `MessageEmbedding` is a separate table from `Message` with its own `sync_state` so embedding is decoupled from message creation.
|
||||
8. **Pluggable external vector stores**: defaults to pgvector inline; can swap to turbopuffer or lancedb (`VECTOR_STORE_*` config; `src/vector_store/`).
|
||||
9. **Composite-FK multi-tenancy**: `workspace_name` participates in nearly every composite FK. Cross-workspace data leakage is structurally impossible at the schema level.
|
||||
10. **Scoped Authentication**: JWTs can be scoped to workspace, peer, or session level.
|
||||
11. **Batch Operations**: Bulk message creation up to 100 messages per request.
|
||||
12. **Session History**: Two-tier summarization — short every `SUMMARY_MESSAGES_PER_SHORT_SUMMARY` (default 20), long every `SUMMARY_MESSAGES_PER_LONG_SUMMARY` (default 60).
|
||||
|
||||
### Error Handling
|
||||
|
||||
- Custom exceptions defined in src/exceptions.py
|
||||
- Use specific exception types (ResourceNotFoundException, ValidationException, etc.)
|
||||
- Proper logging with context instead of print statements
|
||||
- Global exception handlers defined in main.py
|
||||
- See docs/contributing/error-handling.mdx for details
|
||||
|
||||
### Notes
|
||||
|
||||
- Always use `uv run` or `uv` to prefix any commands related to python to ensure you use the virtual environment
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
# Contributing to Honcho
|
||||
|
||||
Thank you for your interest in contributing to Honcho! This guide outlines the process for contributing to the project and our development conventions.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Before you start contributing, please:
|
||||
|
||||
1. **Set up your development environment** - Follow the [Local Development guide](./README.md#local-development) in the README to get Honcho running locally.
|
||||
|
||||
2. **Join our community** - Feel free to join us in our [Discord](http://discord.gg/honcho) to discuss your changes, get help, or ask questions.
|
||||
|
||||
3. **Review existing issues** - Check the [issues tab](https://github.com/plastic-labs/honcho/issues) to see what's already being worked on or to find something to contribute to.
|
||||
|
||||
## Contribution Workflow
|
||||
|
||||
### 1. Fork and Clone
|
||||
|
||||
1. Fork the repository on GitHub
|
||||
2. Clone your fork locally:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/honcho.git
|
||||
cd honcho
|
||||
```
|
||||
|
||||
3. Add the upstream repository as a remote:
|
||||
|
||||
```bash
|
||||
git remote add upstream https://github.com/plastic-labs/honcho.git
|
||||
```
|
||||
|
||||
### 2. Create a Branch
|
||||
|
||||
Create a new branch for your feature or bug fix:
|
||||
|
||||
```bash
|
||||
git checkout -b feature/your-feature-name
|
||||
# or
|
||||
git checkout -b fix/your-bug-fix-name
|
||||
```
|
||||
|
||||
**Branch naming conventions:**
|
||||
|
||||
- `feature/description` - for new features
|
||||
- `fix/description` - for bug fixes
|
||||
- `docs/description` - for documentation updates
|
||||
- `refactor/description` - for code refactoring
|
||||
- `test/description` - for adding or updating tests
|
||||
|
||||
### 3. Make Your Changes
|
||||
|
||||
- Write clean, readable code that follows our coding standards (see below)
|
||||
- Add tests for new functionality
|
||||
- Update documentation as needed
|
||||
- Make sure your changes don't break existing functionality
|
||||
|
||||
### 4. Commit Your Changes
|
||||
|
||||
We follow conventional commit standards. Format your commit messages as:
|
||||
|
||||
```
|
||||
type(scope): description
|
||||
|
||||
[optional body]
|
||||
|
||||
[optional footer]
|
||||
```
|
||||
|
||||
**Types:**
|
||||
|
||||
- `feat`: A new feature
|
||||
- `fix`: A bug fix
|
||||
- `docs`: Documentation only changes
|
||||
- `style`: Changes that do not affect the meaning of the code
|
||||
- `refactor`: A code change that neither fixes a bug nor adds a feature
|
||||
- `test`: Adding missing tests or correcting existing tests
|
||||
- `chore`: Changes to the build process or auxiliary tools
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
git commit -m "feat(api): add new dialectic endpoint for user insights"
|
||||
git commit -m "fix(db): resolve connection pool timeout issue"
|
||||
git commit -m "docs(readme): update installation instructions"
|
||||
```
|
||||
|
||||
### 5. Submit a Pull Request
|
||||
|
||||
1. Push your branch to your fork:
|
||||
|
||||
```bash
|
||||
git push origin your-branch-name
|
||||
```
|
||||
|
||||
2. Create a pull request on GitHub from your branch to the `main` branch
|
||||
|
||||
3. Fill out the pull request template with:
|
||||
- A clear description of what changes you've made
|
||||
- The motivation for the changes
|
||||
- Any relevant issue numbers (use "Closes #123" to auto-close issues)
|
||||
- Screenshots or examples if applicable
|
||||
|
||||
## Pre-commit Hooks
|
||||
|
||||
Honcho uses pre-commit hooks to enforce code quality and consistency. They run linting, formatting, type checking, and security scans before each commit.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
uv add --dev pre-commit
|
||||
uv run pre-commit install \
|
||||
--hook-type pre-commit \
|
||||
--hook-type commit-msg \
|
||||
--hook-type pre-push
|
||||
```
|
||||
|
||||
### What the hooks do
|
||||
|
||||
- **Code Quality** — Python linting and formatting (ruff), TypeScript linting (biome)
|
||||
- **Type Checking** — Static analysis with basedpyright
|
||||
- **Security** — Vulnerability scanning with bandit
|
||||
- **Documentation** — Markdown linting and license header checks
|
||||
- **Testing** — Automated test runs for Python and TypeScript
|
||||
- **File Hygiene** — Trailing whitespace, line endings, file size checks
|
||||
- **Commit Standards** — Conventional commit message validation
|
||||
|
||||
### Manual execution
|
||||
|
||||
Run against all files without committing:
|
||||
|
||||
```bash
|
||||
uv run pre-commit run --all-files
|
||||
```
|
||||
|
||||
Run a specific hook:
|
||||
|
||||
```bash
|
||||
uv run pre-commit run ruff --all-files
|
||||
uv run pre-commit run basedpyright --all-files
|
||||
```
|
||||
|
||||
## Coding Standards
|
||||
|
||||
### Python Code Style
|
||||
|
||||
- Follow [PEP 8](https://www.python.org/dev/peps/pep-0008/) style guidelines
|
||||
- Use [ruff](https://docs.astral.sh/ruff/) for linting and code formatting
|
||||
- Use type hints where possible
|
||||
- Write docstrings for functions and classes using Google style docstrings
|
||||
|
||||
### Code Organization
|
||||
|
||||
- Keep functions focused and single-purpose
|
||||
- Use meaningful variable and function names
|
||||
- Add comments for complex logic
|
||||
- Follow existing patterns in the codebase
|
||||
|
||||
### Testing
|
||||
|
||||
- Write unit tests for new functionality
|
||||
- Ensure existing tests pass before submitting
|
||||
- Use descriptive test names that explain what is being tested
|
||||
- Mock external dependencies appropriately
|
||||
|
||||
### Documentation
|
||||
|
||||
- Update relevant documentation for new features
|
||||
- Include examples in docstrings where helpful
|
||||
- Keep README and other docs up to date with changes
|
||||
|
||||
## Review Process
|
||||
|
||||
1. **Automated checks** - Your PR will run through automated checks including tests and linting
|
||||
2. **Project maintainer review** - A project maintainer will review your code for:
|
||||
- Code quality and adherence to standards
|
||||
- Functionality and correctness
|
||||
- Test coverage
|
||||
- Documentation completeness
|
||||
3. **Discussion and iteration** - You may be asked to make changes or clarifications
|
||||
4. **Approval and merge** - Once approved, your PR will be merged into `main`
|
||||
|
||||
## Types of Contributions
|
||||
|
||||
We welcome various types of contributions:
|
||||
|
||||
- **Bug fixes** - Help us squash bugs and improve stability
|
||||
- **New features** - Add functionality that benefits the community
|
||||
- **Documentation** - Improve or expand our documentation
|
||||
- **Tests** - Increase test coverage and reliability
|
||||
- **Performance improvements** - Help make Honcho faster and more efficient
|
||||
- **Examples and tutorials** - Help other developers use Honcho
|
||||
|
||||
## Issue Reporting
|
||||
|
||||
When reporting bugs or requesting features:
|
||||
|
||||
1. Check if the issue already exists
|
||||
2. Use the appropriate issue template
|
||||
3. Provide clear reproduction steps for bugs
|
||||
4. Include relevant environment information
|
||||
5. Be specific about expected vs actual behavior
|
||||
|
||||
## Questions and Support
|
||||
|
||||
- **General questions** - Join our [Discord](http://discord.gg/honcho)
|
||||
- **Bug reports** - Use GitHub issues
|
||||
- **Feature requests** - Use GitHub issues with the feature request template
|
||||
- **Security issues** - Please email us privately rather than opening a public issue
|
||||
|
||||
## License
|
||||
|
||||
By contributing to Honcho, you agree that your contributions will be licensed under the same [AGPL-3.0 License](./LICENSE) that covers the project.
|
||||
|
||||
Thank you for helping make Honcho better! 🫡
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
# https://pythonspeed.com/articles/base-image-python-docker-images/
|
||||
# https://testdriven.io/blog/docker-best-practices/
|
||||
FROM python:3.13-slim-bookworm
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:0.9.24 /uv /bin/uv
|
||||
|
||||
# Set Working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Enable bytecode compilation
|
||||
ENV UV_COMPILE_BYTECODE=1
|
||||
|
||||
# Copy from the cache instead of linking since it's a mounted volume
|
||||
ENV UV_LINK_MODE=copy
|
||||
|
||||
# Python optimizations
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
# Install the project's dependencies using the lockfile and settings
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
--mount=type=bind,source=uv.lock,target=uv.lock \
|
||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||
uv sync --frozen --no-install-project --no-group dev
|
||||
|
||||
# Copy only requirements to cache them in docker layer
|
||||
COPY uv.lock pyproject.toml /app/
|
||||
|
||||
# Sync the project
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
uv sync --frozen --no-group dev
|
||||
|
||||
# Place executables in the environment at the front of the path
|
||||
ENV PATH="/app/.venv/bin:$PATH"
|
||||
ENV HOME=/app
|
||||
ENV UV_CACHE_DIR=/tmp/uv-cache
|
||||
|
||||
# Create non-root user and set ownership
|
||||
RUN addgroup --system app && adduser --system --group app && mkdir -p /tmp/uv-cache && chown -R app:app /app /tmp/uv-cache
|
||||
|
||||
COPY --chown=app:app src/ /app/src/
|
||||
COPY --chown=app:app migrations/ /app/migrations/
|
||||
COPY --chown=app:app scripts/ /app/scripts/
|
||||
COPY --chown=app:app docker/ /app/docker/
|
||||
COPY --chown=app:app alembic.ini /app/alembic.ini
|
||||
# Copy config files - this will copy config.toml if it exists, and config.toml.example
|
||||
COPY --chown=app:app config.toml* /app/
|
||||
|
||||
# Switch to non-root user
|
||||
USER app
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["fastapi", "run", "--host", "0.0.0.0", "src/main.py"]
|
||||
|
|
@ -0,0 +1,661 @@
|
|||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
|
@ -0,0 +1,667 @@
|
|||
<!-- markdownlint-disable MD033 -->
|
||||
<div align="center">
|
||||
<a href="https://app.honcho.dev" target="_blank">
|
||||
<img src="assets/honcho.svg" alt="Honcho" width="400">
|
||||
</a>
|
||||
</div>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
---
|
||||
|
||||

|
||||
[](https://pypi.org/project/honcho-ai/)
|
||||
[](https://npmjs.org/package/@honcho-ai/sdk)
|
||||
[](https://discord.gg/honcho)
|
||||
|
||||
**Honcho is memory infrastructure for building stateful agents that understand changing people, agents, groups, projects, and ideas over time.**
|
||||
|
||||
Store messages and events, let Honcho reason in the background, then query peer representations, session context, search results, or natural-language insights from any model or framework. Use it managed at [api.honcho.dev](https://api.honcho.dev) or self-host the FastAPI server yourself.
|
||||
|
||||
Using Honcho as your memory system will earn your agents higher retention, more trust, and help you build data moats to out-compete incumbents.
|
||||
|
||||
> Honcho has defined the Pareto Frontier of Agent Memory. Watch the [video](https://x.com/honchodotdev/status/2002090546521911703?s=20), check out our [evals page](https://honcho.dev/evals/), and read the [blog post](https://blog.plasticlabs.ai/research/Benchmarking-Honcho) for more detail.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Start Here](#start-here)
|
||||
- [Why Honcho](#why-honcho)
|
||||
- [The Honcho Loop](#the-honcho-loop)
|
||||
- [Quickstart](#quickstart)
|
||||
- [What Honcho Gives You](#what-honcho-gives-you)
|
||||
- [Integrations](#integrations)
|
||||
- [Core Concepts](#core-concepts)
|
||||
- [Benchmarks & Evals](#benchmarks--evals)
|
||||
- [Self-hosting](#self-hosting)
|
||||
- [Configuration](#configuration)
|
||||
- [Architecture](#architecture)
|
||||
- [SDKs](#sdks)
|
||||
- [Learn More](#learn-more)
|
||||
- [Contributing](#contributing)
|
||||
- [License](#license)
|
||||
|
||||
The Honcho project is split between several repositories, with this one hosting the core service logic — implemented as a FastAPI server. Client SDKs for Python and TypeScript live in the [`sdks/`](./sdks) directory.
|
||||
|
||||
## Start Here
|
||||
|
||||
| I want to... | Path | Get started |
|
||||
| -------------------------------------- | ---------------------------------------------------------- | ----------------------------- |
|
||||
| Give my coding agent persistent memory | Claude Code, OpenCode, OpenClaw, Hermes, or any MCP client | [Integrations](#integrations) |
|
||||
| Add memory to my product | Python or TypeScript SDK | [Quickstart](#quickstart) |
|
||||
| Self-host Honcho | Docker / local development | [Self-hosting](#self-hosting) |
|
||||
|
||||
## Why Honcho
|
||||
|
||||
| Capability | What it means |
|
||||
| ----------------------- | ------------------------------------------------------------------------------------ |
|
||||
| Reasoning-first memory | Extracts conclusions from conversations and events, not just matching chunks. |
|
||||
| Peer-centric model | Tracks users, agents, groups, projects, and ideas as entities that change over time. |
|
||||
| Multi-peer perspective | Models what one peer knows about another when configured. |
|
||||
| Managed or self-hosted | Use `api.honcho.dev` or run the FastAPI server yourself. |
|
||||
| Agent-tool integrations | MCP, Claude Code, OpenCode, OpenClaw, Hermes, Cursor-compatible clients. |
|
||||
|
||||
## The Honcho Loop
|
||||
|
||||
1. **Store** conversations, events, documents, or tool traces as messages on a session.
|
||||
2. **Reason** — Honcho processes the queue in the background and updates peer representations.
|
||||
3. **Query** — ask Honcho for context, search results, peer representations, or a natural-language answer.
|
||||
4. **Inject** — drop the result into any LLM call or agent framework.
|
||||
|
||||
Concretely: workspaces hold peers, peers participate in sessions, messages live on sessions, and Honcho builds a per-peer representation that you query through the [Chat Endpoint](https://honcho.dev/docs/v3/documentation/features/chat) or directly.
|
||||
|
||||
## Quickstart
|
||||
|
||||
Get an API key at [app.honcho.dev](https://app.honcho.dev) — when you sign up you'll be prompted to join an organization, which gets its own dedicated Honcho instance and $100 free credits. Or [self-host](#self-hosting) and run against `http://localhost:8000`.
|
||||
|
||||
### Python
|
||||
|
||||
```bash
|
||||
pip install honcho-ai
|
||||
# or: uv add honcho-ai
|
||||
# or: poetry add honcho-ai
|
||||
```
|
||||
|
||||
```python
|
||||
import os
|
||||
from honcho import Honcho
|
||||
|
||||
# Managed service uses api.honcho.dev by default. For self-hosted, pass
|
||||
# base_url="http://localhost:8000" or set HONCHO_URL.
|
||||
honcho = Honcho(
|
||||
workspace_id="my-app-testing",
|
||||
api_key=os.environ["HONCHO_API_KEY"],
|
||||
)
|
||||
|
||||
# 1. Store: peers and messages on a session
|
||||
alice = honcho.peer("alice")
|
||||
tutor = honcho.peer("tutor")
|
||||
session = honcho.session("session-1")
|
||||
session.add_messages([
|
||||
alice.message("Hey there — can you help me with my math homework?"),
|
||||
tutor.message("Absolutely. Send me your first problem!"),
|
||||
])
|
||||
|
||||
# 2. Reason: happens asynchronously in the background.
|
||||
|
||||
# 3. Query: ask Honcho what it knows, or pull prompt-ready context.
|
||||
answer = alice.chat("What learning styles does the user respond to best?")
|
||||
context = session.context(summary=True, tokens=10_000)
|
||||
|
||||
# 4. Inject: hand the context to your model of choice.
|
||||
from openai import OpenAI
|
||||
client = OpenAI()
|
||||
completion = client.chat.completions.create(
|
||||
model=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"),
|
||||
messages=context.to_openai(assistant=tutor),
|
||||
)
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
```bash
|
||||
npm install @honcho-ai/sdk
|
||||
# or: bun add @honcho-ai/sdk
|
||||
```
|
||||
|
||||
```typescript
|
||||
import { Honcho } from "@honcho-ai/sdk";
|
||||
import OpenAI from "openai";
|
||||
|
||||
const honcho = new Honcho({
|
||||
workspaceId: "my-app-testing",
|
||||
apiKey: process.env.HONCHO_API_KEY,
|
||||
});
|
||||
|
||||
const alice = await honcho.peer("alice");
|
||||
const tutor = await honcho.peer("tutor");
|
||||
const session = await honcho.session("session-1");
|
||||
await session.addMessages([
|
||||
alice.message("Hey there — can you help me with my math homework?"),
|
||||
tutor.message("Absolutely. Send me your first problem!"),
|
||||
]);
|
||||
|
||||
const answer = await alice.chat(
|
||||
"What learning styles does the user respond to best?",
|
||||
);
|
||||
const context = await session.context({ summary: true, tokens: 10_000 });
|
||||
|
||||
const openai = new OpenAI();
|
||||
const completion = await openai.chat.completions.create({
|
||||
model: process.env.OPENAI_MODEL ?? "gpt-4o-mini",
|
||||
messages: context.toOpenAI({ assistant: tutor }),
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** background reasoning is asynchronous. Newly-added messages may take a moment to be reflected in chat/representation responses; for low-latency reads, use the [`representation`](https://honcho.dev/docs/v3/documentation/features/representation) endpoint.
|
||||
|
||||
## What Honcho Gives You
|
||||
|
||||
| Need | API |
|
||||
| ---------------------------------- | --------------------------------------------------------------- |
|
||||
| Save interaction history | `session.add_messages(...)` |
|
||||
| Ask what Honcho knows about a peer | `peer.chat(...)` |
|
||||
| Get prompt-ready context | `session.context(...).to_openai(...)` / `.to_anthropic(...)` |
|
||||
| Hybrid search (BM25 + vector) | `peer.search(...)`, `session.search(...)`, `honcho.search(...)` |
|
||||
| Low-latency static representations | `peer.representation(...)`, `session.representation(...)` |
|
||||
| Import documents | `session.upload_file(...)` |
|
||||
| Inspect background processing | `honcho.queue_status(...)` |
|
||||
|
||||
See the full [SDK Reference](https://honcho.dev/docs/v3/documentation/reference/sdk) and [API Reference](https://honcho.dev/docs/v3/api-reference/introduction).
|
||||
|
||||
## Integrations
|
||||
|
||||
### Claude Code
|
||||
|
||||
Two ways, depending on how deep you want to go:
|
||||
|
||||
**Plugin (richer integration — recommended for Claude Code users):**
|
||||
|
||||
```text
|
||||
/plugin marketplace add plastic-labs/claude-honcho
|
||||
/plugin install honcho@honcho
|
||||
```
|
||||
|
||||
**Raw MCP (works in any MCP client — Cursor, Cline, Windsurf, etc.):**
|
||||
|
||||
```bash
|
||||
claude mcp add honcho \
|
||||
--transport http \
|
||||
--url "https://mcp.honcho.dev" \
|
||||
--header "Authorization: Bearer hch-your-key-here" \
|
||||
--header "X-Honcho-User-Name: YourName"
|
||||
```
|
||||
|
||||
Details: [Claude Code guide](https://honcho.dev/docs/v3/guides/integrations/claude-code) · [MCP guide](https://honcho.dev/docs/v3/guides/integrations/mcp).
|
||||
|
||||
### OpenCode
|
||||
|
||||
```bash
|
||||
opencode plugin "@honcho-ai/opencode-honcho" --global
|
||||
```
|
||||
|
||||
Details: [OpenCode guide](https://honcho.dev/docs/v3/guides/integrations/opencode).
|
||||
|
||||
### OpenClaw
|
||||
|
||||
```bash
|
||||
openclaw plugins install @honcho-ai/openclaw-honcho
|
||||
openclaw honcho setup
|
||||
openclaw gateway --force
|
||||
```
|
||||
|
||||
`openclaw honcho setup` prompts for your API key, writes the config, and optionally migrates legacy `MEMORY.md` / `USER.md` / `IDENTITY.md` files into Honcho (non-destructive — originals are never deleted). Details: [OpenClaw guide](https://honcho.dev/docs/v3/guides/integrations/openclaw).
|
||||
|
||||
### Hermes
|
||||
|
||||
```bash
|
||||
hermes memory setup # select "honcho", point at api.honcho.dev or your local server
|
||||
```
|
||||
|
||||
Details: [Hermes guide](https://honcho.dev/docs/v3/guides/integrations/hermes).
|
||||
|
||||
### Add Honcho to your own codebase (agent skill)
|
||||
|
||||
For wiring the Honcho SDK into an existing application, install the integration skill — it explores your codebase, asks about integration preferences, generates the SDK setup, and verifies it works:
|
||||
|
||||
```bash
|
||||
npx skills add plastic-labs/honcho
|
||||
```
|
||||
|
||||
Then invoke `/honcho-integration` in Claude Code (or `/honcho-dev:integrate` via the plugin marketplace). Details: [agentic development guide](https://honcho.dev/docs/v3/documentation/introduction/vibecoding).
|
||||
|
||||
### Other MCP clients
|
||||
|
||||
The same `claude mcp add` form (or its client-specific equivalent) works in any MCP-compatible client. See [MCP guide](https://honcho.dev/docs/v3/guides/integrations/mcp).
|
||||
|
||||
## Core Concepts
|
||||
|
||||
Honcho organises everything around **peers** — humans and AI agents alike are first-class entities. The peer model enables:
|
||||
|
||||
- Multi-participant sessions with mixed human and AI agents
|
||||
- Configurable observation settings (which peers observe which others)
|
||||
- Flexible identity management for all participants
|
||||
- Support for complex multi-agent interactions
|
||||
|
||||
Peers exchange messages within sessions; Honcho reasons over those messages to build a representation of each peer that you can query.
|
||||
|
||||
- **Workspace** (formerly App): top-level container; isolates data between use cases.
|
||||
- **Peer** (formerly User): any participant — human user or AI agent.
|
||||
- **Session**: a conversation context; many-to-many with peers.
|
||||
- **Message**: an atomic data unit (peer-to-peer communication or ingested document chunk).
|
||||
|
||||
What you query out of Honcho:
|
||||
|
||||
- **Conclusions** — what Honcho has extracted about a peer (deductive and inductive). Exposed via the [conclusions API](https://honcho.dev/docs/v3/api-reference/introduction).
|
||||
- **Representations** — static, low-latency snapshots of what Honcho knows about a peer (optionally session-scoped).
|
||||
- **Peer Cards** — compact identity summaries.
|
||||
- **Session context / summaries** — prompt-ready bundles for long-running conversations.
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary>Internal storage (Collections & Documents)</summary>
|
||||
|
||||
Internally, Honcho stores peer-related observations in **collections** of vector-embedded **documents**. Collections are keyed by `(observer, observed)` peer pairs — the same mechanism powers self-representation (`observer == observed`) and cross-peer modelling (peer X's understanding of peer Y). These primitives are not exposed directly; the Conclusions API is the public surface.
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
<!-- TODO(vineeth/marketing): write the "Honcho vs RAG / vector DB / memory-only" comparison.
|
||||
Audit recommendation referenced; copy intentionally deferred to avoid inventing
|
||||
positioning claims unsupported by primary sources. -->
|
||||
|
||||
## Benchmarks & Evals
|
||||
|
||||
Honcho's evals span LongMemEval, LoCoMo, and other long-conversation benchmarks. See the [evals page](https://honcho.dev/evals/), the [research blog post](https://blog.plasticlabs.ai/research/Benchmarking-Honcho), and the [Pareto-frontier announcement video](https://x.com/honchodotdev/status/2002090546521911703?s=20) for methodology and reproducible results.
|
||||
|
||||
## Self-hosting
|
||||
|
||||
Honcho is open source under AGPL-3.0. You can run the full server locally with Docker, then point the SDKs at `http://localhost:8000`.
|
||||
|
||||
### Quick start (Docker)
|
||||
|
||||
```bash
|
||||
git clone https://github.com/plastic-labs/honcho.git
|
||||
cd honcho
|
||||
cp docker-compose.yml.example docker-compose.yml
|
||||
cp .env.template .env # fill in LLM_GEMINI_API_KEY / LLM_ANTHROPIC_API_KEY / LLM_OPENAI_API_KEY
|
||||
docker compose up
|
||||
```
|
||||
|
||||
Then point the SDKs at it:
|
||||
|
||||
```python
|
||||
honcho = Honcho(workspace_id="my-app-testing", base_url="http://localhost:8000")
|
||||
# or: export HONCHO_URL=http://localhost:8000
|
||||
```
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary>Local development without Docker</summary>
|
||||
|
||||
Below is a guide on setting up a local environment for running the Honcho Server without Docker.
|
||||
|
||||
#### Prerequisites and Dependencies
|
||||
|
||||
Honcho is developed using [python](https://www.python.org/) and [uv](https://docs.astral.sh/uv/).
|
||||
|
||||
The minimum python version is `3.10`
|
||||
The minimum uv version is `0.5.0`
|
||||
|
||||
#### Setup
|
||||
|
||||
Once the dependencies are installed on the system run the following steps to get
|
||||
the local project setup.
|
||||
|
||||
1. **Clone the repository**
|
||||
|
||||
```bash
|
||||
git clone https://github.com/plastic-labs/honcho.git
|
||||
```
|
||||
|
||||
2. **Enter the repository and install the python dependencies**
|
||||
|
||||
We recommend using a virtual environment to isolate the dependencies for Honcho
|
||||
from other projects on the same system. `uv` will create a virtual environment
|
||||
when you sync your dependencies in the project.
|
||||
|
||||
```bash
|
||||
cd honcho
|
||||
uv sync
|
||||
```
|
||||
|
||||
This will create a virtual environment and install the dependencies for Honcho.
|
||||
The default virtual environment will be located at `honcho/.venv`. Activate the
|
||||
virtual environment via:
|
||||
|
||||
```bash
|
||||
source honcho/.venv/bin/activate
|
||||
```
|
||||
|
||||
3. **Set up a database**
|
||||
|
||||
Honcho utilizes [Postgres](https://www.postgresql.org/) for its database with
|
||||
pgvector. An easy way to get started with a postgres database is to create a project
|
||||
with [Supabase](https://supabase.com/)
|
||||
|
||||
Alternatively, a `docker-compose` template is available with a sample database configuration.
|
||||
To use Docker:
|
||||
|
||||
```bash
|
||||
cp docker-compose.yml.example docker-compose.yml
|
||||
docker compose up -d database
|
||||
```
|
||||
|
||||
4. **Edit the environment variables**
|
||||
|
||||
Honcho uses a `.env` file for managing runtime environment variables. A
|
||||
`.env.template` file is included for convenience. Several of the configurations
|
||||
are not required and are only necessary for additional logging, monitoring, and
|
||||
security.
|
||||
|
||||
Below are the required configurations:
|
||||
|
||||
```env
|
||||
DB_CONNECTION_URI= # Connection uri for a postgres database (with postgresql+psycopg prefix)
|
||||
|
||||
# LLM Provider API Keys
|
||||
LLM_GEMINI_API_KEY= # API Key for Google Gemini (used for deriver, summary, and dialectic minimal/low by default)
|
||||
LLM_ANTHROPIC_API_KEY= # API Key for Anthropic (used for dialectic medium/high/max and dream by default)
|
||||
LLM_OPENAI_API_KEY= # API Key for OpenAI (used for embeddings when EMBED_MESSAGES=true)
|
||||
```
|
||||
|
||||
> Note that the `DB_CONNECTION_URI` must have the prefix `postgresql+psycopg` to
|
||||
> function properly. This is a requirement brought by `sqlalchemy`
|
||||
|
||||
The template has the additional functionality disabled by default. To ensure
|
||||
that they are disabled you can verify the following environment variables are
|
||||
set to false:
|
||||
|
||||
```env
|
||||
AUTH_USE_AUTH=false
|
||||
SENTRY_ENABLED=false
|
||||
```
|
||||
|
||||
If you set `AUTH_USE_AUTH` to true you will need to generate a JWT secret. You can
|
||||
do this with the following command:
|
||||
|
||||
```bash
|
||||
python scripts/generate_jwt_secret.py
|
||||
```
|
||||
|
||||
This will generate a JWT secret and print it to the console. You can then set
|
||||
the `AUTH_JWT_SECRET` environment variable. This is required for `AUTH_USE_AUTH`:
|
||||
|
||||
```env
|
||||
AUTH_JWT_SECRET=<generated_secret>
|
||||
```
|
||||
|
||||
5. **Run database migrations**
|
||||
|
||||
With the database set up and environment variables configured, run the migrations
|
||||
to create the necessary tables:
|
||||
|
||||
```bash
|
||||
uv run alembic upgrade head
|
||||
```
|
||||
|
||||
This will create all tables for Honcho including workspaces, peers, sessions,
|
||||
messages, and the queue system.
|
||||
|
||||
6. **Launch Honcho**
|
||||
|
||||
With everything set up, you can now launch a local instance of Honcho. In addition to the database, two
|
||||
components need to be running:
|
||||
|
||||
**Start the API server:**
|
||||
|
||||
```bash
|
||||
uv run fastapi dev src/main.py
|
||||
```
|
||||
|
||||
This is a development server that will reload whenever code is changed.
|
||||
|
||||
**Start a background worker (deriver):**
|
||||
|
||||
In a separate terminal, run:
|
||||
|
||||
```bash
|
||||
uv run python -m src.deriver
|
||||
```
|
||||
|
||||
The deriver generates representations, summaries, peer cards, and manages dreaming tasks. You can increase the number of derivers to improve runtime efficiency.
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
Contributors: see [`CONTRIBUTING.md`](./CONTRIBUTING.md) for pre-commit setup. Deploying to Fly.io: see [Self-hosting docs → Deploying on Fly.io](https://honcho.dev/docs/v3/contributing/self-hosting#deploying-on-fly-io).
|
||||
|
||||
## Configuration
|
||||
|
||||
Honcho uses a flexible configuration system that supports both TOML files and environment variables. Configuration values are loaded in priority order: **environment variables > `.env` file > `config.toml` > defaults**.
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary>Full configuration reference</summary>
|
||||
|
||||
### Using config.toml
|
||||
|
||||
Copy the example configuration file to get started:
|
||||
|
||||
```bash
|
||||
cp config.toml.example config.toml
|
||||
```
|
||||
|
||||
Then modify the values as needed. The TOML file is organized into sections:
|
||||
|
||||
- `[app]` - Application-level settings (log level, session limits, embedding settings, namespace)
|
||||
- `[db]` - Database connection and pool settings
|
||||
- `[auth]` - Authentication configuration
|
||||
- `[cache]` - Redis cache configuration
|
||||
- `[llm]` - LLM provider API keys and general settings
|
||||
- `[deriver]` - Background worker settings and representation configuration
|
||||
- `[peer_card]` - Peer card generation settings
|
||||
- `[dialectic]` - Chat Endpoint configuration with per-level reasoning settings
|
||||
- `[summary]` - Session summarization settings
|
||||
- `[dream]` - Dream processing configuration (including specialist models and surprisal settings)
|
||||
- `[webhook]` - Webhook configuration
|
||||
- `[metrics]` - Prometheus pull-based metrics
|
||||
- `[telemetry]` - CloudEvents telemetry for analytics
|
||||
- `[vector_store]` - Vector store configuration (pgvector, turbopuffer, or lancedb)
|
||||
- `[sentry]` - Error tracking and monitoring settings
|
||||
|
||||
### Using Environment Variables
|
||||
|
||||
All configuration values can be overridden using environment variables. The environment variable names follow this pattern:
|
||||
|
||||
- `{SECTION}_{KEY}` for top-level section settings
|
||||
- Use `__` inside `{KEY}` for nested settings
|
||||
- Just `{KEY}` for app-level settings
|
||||
|
||||
Examples:
|
||||
|
||||
- `DB_CONNECTION_URI` - Database connection string
|
||||
- `AUTH_JWT_SECRET` - JWT secret key
|
||||
- `DERIVER_MODEL_CONFIG__TRANSPORT` - Transport for the background deriver
|
||||
- `SUMMARY_MODEL_CONFIG__MODEL` - Summary model override
|
||||
- `DIALECTIC_LEVELS__low__MODEL_CONFIG__MODEL` - Model for low reasoning level
|
||||
- `LOG_LEVEL` - Application log level
|
||||
- `METRICS_ENABLED` - Enable Prometheus metrics
|
||||
- `TELEMETRY_ENABLED` - Enable CloudEvents telemetry
|
||||
|
||||
### Example
|
||||
|
||||
If you have this in `config.toml`:
|
||||
|
||||
```toml
|
||||
[db]
|
||||
CONNECTION_URI = "postgresql+psycopg://localhost/honcho_dev"
|
||||
POOL_SIZE = 10
|
||||
```
|
||||
|
||||
You can override just the connection URI in production:
|
||||
|
||||
```bash
|
||||
export DB_CONNECTION_URI="postgresql+psycopg://prod-server/honcho_prod"
|
||||
```
|
||||
|
||||
The application will use the production connection URI while keeping the pool size from config.toml.
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
## Architecture
|
||||
|
||||
Honcho splits into two services: **Storage** (workspaces, peers, sessions, messages, internal collections) and **Insights** (reasoning, conclusions, representations, summaries, the chat endpoint). Storage is synchronous via the API; Insights is asynchronous via a background queue consumed by the deriver worker process.
|
||||
|
||||
**Key features:**
|
||||
|
||||
- **Rich Reasoning System** — multiple implementation methods that extract conclusions from interactions and build comprehensive representations of peers
|
||||
- **Chat Endpoint** — reasoning-informed responses that integrate conclusions with current context
|
||||
- **Background Processing** — asynchronous processing pipeline for expensive operations like representation updates and session summarization
|
||||
- **Multi-Provider Support** — configurable LLM providers for different use cases
|
||||
|
||||
<!-- markdownlint-disable MD033 MD001 -->
|
||||
<details>
|
||||
<summary>Storage primitives in detail</summary>
|
||||
|
||||
Honcho contains several different primitives used for storing application and
|
||||
peer data. This data is used for managing conversations, modeling peer
|
||||
identity, building RAG applications, and more.
|
||||
|
||||
The philosophy behind Honcho is to provide a platform that is peer-centric and
|
||||
easily scalable from a single user to a million.
|
||||
|
||||
Below is a mapping of the different primitives and their relationships.
|
||||
|
||||
```
|
||||
Workspaces
|
||||
├── Peers ←──────────────────┐
|
||||
│ ├── Sessions │
|
||||
│ └── (internal collections, keyed by observer/observed peer pair)
|
||||
│ │
|
||||
│ │
|
||||
└── Sessions ←───────────────┤ (many-to-many)
|
||||
├── Peers ───────────────┘
|
||||
└── Messages (session-level)
|
||||
```
|
||||
|
||||
**Relationship Details:**
|
||||
|
||||
- A **Workspace** contains multiple **Peers**.
|
||||
- **Peers** and **Sessions** have a many-to-many relationship (peers can participate in multiple sessions, sessions can have multiple peers).
|
||||
- **Messages** belong to a session and are labelled by their source peer.
|
||||
- **Internal collections** of vector-embedded **documents** are keyed by `(observer, observed)` peer pairs. They are not directly exposed via the API; the observations stored in them are exposed as **Conclusions**.
|
||||
|
||||
Users familiar with APIs such as the OpenAI Assistants API will be familiar with
|
||||
much of the mapping here.
|
||||
|
||||
#### Workspaces
|
||||
|
||||
This is the top level construct of Honcho. Developers can register different
|
||||
`Workspaces` for different assistants, agents, AI enabled features, etc. It is a way to
|
||||
isolate data between use cases and provide multi-tenant capabilities.
|
||||
|
||||
#### Peers
|
||||
|
||||
Within a `Workspace` everything revolves around a `Peer`. The `Peer` object
|
||||
represents any participant in the system — whether human users or AI agents.
|
||||
This unified model enables complex multi-participant interactions.
|
||||
|
||||
#### Sessions
|
||||
|
||||
The `Session` object represents a set of interactions between `Peers` within a
|
||||
`Workspace`. Other applications may refer to this as a thread or conversation.
|
||||
Sessions can involve multiple peers with configurable observation settings.
|
||||
|
||||
#### Messages
|
||||
|
||||
The `Message` represents an atomic data unit that exists at the session level:
|
||||
communication between peers within a session context. All messages are labelled
|
||||
by their source peer and can be processed asynchronously to update their
|
||||
representations. This flexible design allows for both conversational interactions
|
||||
and broader data ingestion for personality modelling.
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 MD001 -->
|
||||
|
||||
<!-- markdownlint-disable MD033 -->
|
||||
<details>
|
||||
<summary>Reasoning pipeline</summary>
|
||||
|
||||
The reasoning functionality of Honcho is built on top of the Storage service. As
|
||||
`Messages` and `Sessions` are created for `Peers`, Honcho will asynchronously
|
||||
reason about peer psychology to derive facts about them and store them
|
||||
in reserved internal collections.
|
||||
|
||||
A high level summary of the pipeline is as follows:
|
||||
|
||||
1. Messages are created via the API.
|
||||
2. Derivation tasks are enqueued for background processing, including:
|
||||
- `representation`: update representations of `Peers`.
|
||||
- `summary`: create summaries of `Sessions`.
|
||||
3. Session-based queue processing ensures proper ordering.
|
||||
4. Results are stored internally and surfaced via the Conclusions API, Representations, Peer Cards, and the Chat Endpoint.
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 -->
|
||||
|
||||
<!-- markdownlint-disable MD033 MD001 -->
|
||||
<details>
|
||||
<summary>Retrieving data and insights</summary>
|
||||
|
||||
Honcho exposes several different ways to retrieve data from the system to best
|
||||
serve the needs of any given application.
|
||||
|
||||
#### Get Context
|
||||
|
||||
In long-running conversations with an LLM, the context window can fill up
|
||||
quickly. To address this, Honcho provides a `context`
|
||||
endpoint that returns a combination of messages, conclusions, summaries from a
|
||||
session up to a provided token limit.
|
||||
|
||||
Use this to keep sessions going indefinitely. If you'd like to see this in action, try out [Honcho Chat](https://honcho.chat).
|
||||
|
||||
#### Search
|
||||
|
||||
There are several search endpoints that let developers query messages at the
|
||||
`Workspace`, `Session`, or `Peer` level using a hybrid search strategy.
|
||||
|
||||
Requests can include advanced filters to further refine
|
||||
the results.
|
||||
|
||||
#### Chat API
|
||||
|
||||
The flagship interface for using these insights is the [Chat Endpoint](https://honcho.dev/docs/v3/documentation/features/chat) (`POST /peers/{peer_id}/chat`). It takes natural-language requests to get data about a peer and returns reasoning-grounded responses. Examples:
|
||||
|
||||
- Asking Honcho for a generic or specific insight about the peer.
|
||||
- Asking Honcho to hydrate a prompt with data about the peer's behaviour.
|
||||
- Asking Honcho for a second opinion on how to respond.
|
||||
- Getting personalised responses that incorporate long-term facts and context.
|
||||
|
||||
#### Representations
|
||||
|
||||
For low-latency use cases, Honcho provides access to a `representation` endpoint that returns a static document with insights about a peer in the context of a particular session. Use this to quickly add context to a prompt without having to wait for an LLM response.
|
||||
|
||||
</details>
|
||||
<!-- markdownlint-enable MD033 MD001 -->
|
||||
|
||||
## SDKs
|
||||
|
||||
- **Python** — [`honcho-ai`](https://pypi.org/project/honcho-ai/) on PyPI · source in [`sdks/python/`](./sdks/python)
|
||||
- **TypeScript** — [`@honcho-ai/sdk`](https://www.npmjs.com/package/@honcho-ai/sdk) on npm · source in [`sdks/typescript/`](./sdks/typescript)
|
||||
|
||||
SDKs are versioned independently of the server. Current SDK versions track each other; the server badge above reflects the deployed server version.
|
||||
|
||||
See the [SDK Reference](https://honcho.dev/docs/v3/documentation/reference/sdk) for full API surface, the [API Reference](https://honcho.dev/docs/v3/api-reference/introduction) for the raw HTTP API, and per-SDK example folders for runnable demos.
|
||||
|
||||
## Learn More
|
||||
|
||||
- [Developer documentation](https://honcho.dev/docs/) — full API surface, guides, integrations.
|
||||
- [Plastic Labs blog](https://blog.plasticlabs.ai/) — design philosophy and history of the project.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions to Honcho! Please read our [Contributing Guide](./CONTRIBUTING.md) for details on our development process, coding conventions, and how to submit pull requests.
|
||||
|
||||
## License
|
||||
|
||||
Honcho is licensed under the AGPL-3.0 License. Learn more at the [License file](./LICENSE).
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
# A generic, single database configuration.
|
||||
|
||||
[alembic]
|
||||
# path to migration scripts
|
||||
# Use forward slashes (/) also on windows to provide an os agnostic path
|
||||
script_location = migrations
|
||||
|
||||
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
||||
# Uncomment the line below if you want the files to be prepended with date and time
|
||||
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
|
||||
# for all available tokens
|
||||
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
||||
|
||||
# sys.path path, will be prepended to sys.path if present.
|
||||
# defaults to the current working directory.
|
||||
prepend_sys_path = .
|
||||
|
||||
# timezone to use when rendering the date within the migration file
|
||||
# as well as the filename.
|
||||
# If specified, requires the python>=3.9 or backports.zoneinfo library.
|
||||
# Any required deps can installed by adding `alembic[tz]` to the pip requirements
|
||||
# string value is passed to ZoneInfo()
|
||||
# leave blank for localtime
|
||||
# timezone =
|
||||
|
||||
# max length of characters to apply to the "slug" field
|
||||
# truncate_slug_length = 40
|
||||
|
||||
# set to 'true' to run the environment during
|
||||
# the 'revision' command, regardless of autogenerate
|
||||
# revision_environment = false
|
||||
|
||||
# set to 'true' to allow .pyc and .pyo files without
|
||||
# a source .py file to be detected as revisions in the
|
||||
# versions/ directory
|
||||
# sourceless = false
|
||||
|
||||
# version location specification; This defaults
|
||||
# to migrations/versions. When using multiple version
|
||||
# directories, initial revisions must be specified with --version-path.
|
||||
# The path separator used here should be the separator specified by "version_path_separator" below.
|
||||
# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
|
||||
|
||||
# version path separator; As mentioned above, this is the character used to split
|
||||
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
|
||||
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
|
||||
# Valid values for version_path_separator are:
|
||||
#
|
||||
# version_path_separator = :
|
||||
# version_path_separator = ;
|
||||
# version_path_separator = space
|
||||
# version_path_separator = newline
|
||||
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
|
||||
|
||||
# set to 'true' to search source files recursively
|
||||
# in each "version_locations" directory
|
||||
# new in Alembic version 1.10
|
||||
# recursive_version_locations = false
|
||||
|
||||
# the output encoding used when revision files
|
||||
# are written from script.py.mako
|
||||
# output_encoding = utf-8
|
||||
|
||||
sqlalchemy.url = driver://user:pass@localhost/dbname
|
||||
|
||||
|
||||
[post_write_hooks]
|
||||
# post_write_hooks defines scripts or Python functions that are run
|
||||
# on newly generated revision scripts. See the documentation for further
|
||||
# detail and examples
|
||||
|
||||
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
||||
# hooks = black
|
||||
# black.type = console_scripts
|
||||
# black.entrypoint = black
|
||||
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
||||
|
||||
# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
|
||||
# hooks = ruff
|
||||
# ruff.type = exec
|
||||
# ruff.executable = %(here)s/.venv/bin/ruff
|
||||
# ruff.options = --fix REVISION_SCRIPT_FILENAME
|
||||
|
||||
# Logging configuration
|
||||
[loggers]
|
||||
keys = root,sqlalchemy,alembic
|
||||
|
||||
[handlers]
|
||||
keys = console
|
||||
|
||||
[formatters]
|
||||
keys = generic
|
||||
|
||||
[logger_root]
|
||||
level = WARNING
|
||||
handlers = console
|
||||
qualname =
|
||||
|
||||
[logger_sqlalchemy]
|
||||
level = WARNING
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
|
||||
[logger_alembic]
|
||||
level = INFO
|
||||
handlers =
|
||||
qualname = alembic
|
||||
|
||||
[handler_console]
|
||||
class = StreamHandler
|
||||
args = (sys.stderr,)
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
|
||||
[formatter_generic]
|
||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||
datefmt = %H:%M:%S
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 491.02 144">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #b5daff;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #191919;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<rect class="cls-1" x="0" y="0" width="491.02" height="144" rx="70.34" ry="70.34"/>
|
||||
<g>
|
||||
<path class="cls-2" d="M42.36,73.11h0s0,0,0,0Z"/>
|
||||
<g>
|
||||
<path class="cls-2" d="M111.94,38.5v24.08h-16.9v-24.08h-21.06c-11.88,0-22.94,6.36-28.94,16.61-2.66,1.61-4.45,2.74-4.49,2.76-5.17,2.82-7.76,5.51-9.03,7.64-.99.02-1.84.75-1.98,1.75-.16,1.11.62,2.14,1.73,2.3s2.14-.62,2.3-1.73c.08-.57-.09-1.13-.42-1.55,1.87-3.02,5.83-5.51,8.25-6.83.42-.23,23.87-14.43,25.63-14.08.29.06.72.22,1.06.91.93,1.93-7.61,7.63-9.35,8.67-1.67,1-3.13,2.06-4.41,3.2-2.69,2.4-4.49,5.09-5.36,8.05-.53,1.78-2.02,6.91-5.66,8.19-.26-.57-.78-1.02-1.44-1.16-1.1-.23-2.18.48-2.41,1.58-.22,1.04.4,2.06,1.4,2.37,2.51,16.26,16.43,28.31,33.09,28.32h0s21.07.01,21.07.01v-28.16h16.9v28.16h21.07V38.5h-21.07,0ZM55.16,59.86c-.08,1.04-.32,2.24-.91,3.02-.62.82-1.79,1.52-2.84,2.01.83-1.79,2.09-3.47,3.75-5.03h0ZM73.96,83.12h-8.26c-.88,0-1.6.71-1.6,1.6,0,.44.18.84.47,1.13.29.29.69.47,1.13.47h8.26v17.37c-15.77,0-28.94-11.42-31.31-26.81.24-.18.44-.4.59-.67,3.01-1.08,5.29-2.56,7.32-9.03,1.09-.38,3.81-1.46,5.13-3.21,1.32-1.75,1.34-4.5,1.29-5.64.83-.63,1.73-1.24,2.7-1.82,5.56-3.33,11.57-7.86,10.05-11.01-.5-1.04-1.31-1.7-2.33-1.9-1.93-.38-11.56,5.06-18.73,9.32,5.94-7.87,15.32-12.63,25.31-12.63v42.82h0Z"/>
|
||||
<ellipse class="cls-2" cx="63.3" cy="66.65" rx="3.45" ry="5.35"/>
|
||||
<path class="cls-2" d="M187.15,40.14c4.44,2.01,7.66,5.5,9.66,10.49,2.01,4.99,3.01,12.11,3.01,21.36s-1,16.38-3.01,21.36c-2.01,4.99-5.23,8.48-9.66,10.49-4.44,2.01-10.64,3.01-18.6,3.01s-14.07-1-18.5-3.01c-4.44-2.01-7.66-5.5-9.66-10.49-2.01-4.99-3.01-12.11-3.01-21.36s1-16.38,3.01-21.36c2.01-4.99,5.23-8.48,9.66-10.49,4.44-2.01,10.6-3.01,18.5-3.01s14.16,1,18.6,3.01h0ZM162.97,54.38c-1.26,1.07-2.15,2.95-2.67,5.63s-.78,6.69-.78,11.99.26,9.31.78,11.99c.52,2.69,1.41,4.56,2.67,5.63s3.12,1.6,5.58,1.6,4.32-.53,5.58-1.6,2.15-2.95,2.67-5.63c.52-2.69.78-6.69.78-11.99s-.26-9.31-.78-11.99c-.52-2.69-1.41-4.56-2.67-5.63s-3.13-1.6-5.58-1.6-4.32.53-5.58,1.6Z"/>
|
||||
<path class="cls-2" d="M265.77,98.22c0,2.4-.61,4.21-1.84,5.44s-3.04,1.84-5.44,1.84h-8.06c-2.01,0-3.54-.45-4.61-1.36s-2.25-2.49-3.54-4.76l-14.57-23.02c-1.62-2.91-3.11-6.73-4.47-11.46h-.68c.64,4.53.97,8.61.97,12.24v28.36h-19.42v-59.73c0-2.4.61-4.21,1.84-5.44s3.04-1.84,5.44-1.84h8.06c2.01,0,3.53.45,4.56,1.36,1.03.91,2.23,2.49,3.59,4.76l13.99,21.95c1.75,3.11,3.46,6.93,5.15,11.46h.68c-.58-4.92-.88-8.97-.88-12.14l-.1-27.39h19.33v59.73h0Z"/>
|
||||
<path class="cls-2" d="M312.49,37.91c3.04.39,6.34,1.07,9.91,2.04l-1.55,15.34c-1.49,0-2.62-.03-3.4-.1l-17.09-.1c-2.27,0-3.98.47-5.15,1.41-1.17.94-1.98,2.59-2.43,4.95-.45,2.37-.68,5.87-.68,10.54s.23,8.18.68,10.54c.45,2.37,1.26,4.01,2.43,4.95,1.17.94,2.88,1.41,5.15,1.41,5.31,0,9.53-.05,12.67-.15s6.23-.31,9.27-.63l1.55,15.34c-3.43,1.17-6.9,1.99-10.39,2.48s-7.87.73-13.11.73c-7.51,0-13.47-1.12-17.87-3.35-4.4-2.23-7.59-5.86-9.57-10.88-1.98-5.02-2.96-11.83-2.96-20.44s.99-15.43,2.96-20.44c1.97-5.02,5.16-8.64,9.57-10.88,4.4-2.23,10.36-3.35,17.87-3.35,5.05,0,9.09.19,12.14.58h0Z"/>
|
||||
<path class="cls-2" d="M387.85,38.49v67.01h-21.07v-28.17h-16.9v28.17h-21.07V38.49h21.07v24.08h16.9v-24.08s21.07,0,21.07,0Z"/>
|
||||
<path class="cls-2" d="M441.99,40.14c4.44,2.01,7.66,5.5,9.66,10.49,2.01,4.99,3.01,12.11,3.01,21.36s-1,16.38-3.01,21.36c-2.01,4.99-5.23,8.48-9.66,10.49-4.44,2.01-10.64,3.01-18.6,3.01s-14.07-1-18.5-3.01c-4.44-2.01-7.66-5.5-9.66-10.49-2.01-4.99-3.01-12.11-3.01-21.36s1-16.38,3.01-21.36,5.23-8.48,9.66-10.49c4.44-2.01,10.6-3.01,18.5-3.01s14.16,1,18.6,3.01h0ZM417.81,54.38c-1.26,1.07-2.15,2.95-2.67,5.63-.52,2.69-.78,6.69-.78,11.99s.26,9.31.78,11.99c.52,2.69,1.41,4.56,2.67,5.63s3.12,1.6,5.58,1.6,4.32-.53,5.58-1.6,2.15-2.95,2.67-5.63c.52-2.69.78-6.69.78-11.99s-.26-9.31-.78-11.99c-.52-2.69-1.41-4.56-2.67-5.63s-3.13-1.6-5.58-1.6-4.32.53-5.58,1.6Z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
|
|
@ -0,0 +1,262 @@
|
|||
# Honcho Configuration File
|
||||
# This file demonstrates all available configuration options.
|
||||
# Copy this to config.toml and modify as needed.
|
||||
# Environment variables will override these values.
|
||||
|
||||
# Application-level settings
|
||||
[app]
|
||||
LOG_LEVEL = "INFO"
|
||||
SESSION_OBSERVERS_LIMIT = 10
|
||||
GET_CONTEXT_MAX_TOKENS = 100000
|
||||
MAX_FILE_SIZE = 5242880 # 5MB
|
||||
MAX_MESSAGE_SIZE = 25000 # Characters
|
||||
EMBED_MESSAGES = true
|
||||
# LANGFUSE_HOST = "https://api.langfuse.com"
|
||||
# LANGFUSE_PUBLIC_KEY = "your-public-key-here"
|
||||
# COLLECT_METRICS_LOCAL = false
|
||||
# LOCAL_METRICS_FILE = "metrics.jsonl"
|
||||
# REASONING_TRACES_FILE = "traces.jsonl" # Path to JSONL file for reasoning traces
|
||||
NAMESPACE = "honcho"
|
||||
|
||||
# Database settings
|
||||
[db]
|
||||
CONNECTION_URI = "postgresql+psycopg://postgres:postgres@localhost:5432/postgres"
|
||||
SCHEMA = "public"
|
||||
POOL_CLASS = "default"
|
||||
POOL_PRE_PING = true
|
||||
POOL_SIZE = 10
|
||||
MAX_OVERFLOW = 20
|
||||
POOL_TIMEOUT = 30 # seconds
|
||||
POOL_RECYCLE = 300 # seconds
|
||||
POOL_USE_LIFO = true
|
||||
SQL_DEBUG = false
|
||||
TRACING = false
|
||||
|
||||
# Authentication settings
|
||||
[auth]
|
||||
USE_AUTH = false
|
||||
JWT_SECRET = "your-secret-key-here" # Must be set if USE_AUTH is true
|
||||
|
||||
# Sentry settings
|
||||
[sentry]
|
||||
ENABLED = false
|
||||
DSN = ""
|
||||
RELEASE = ""
|
||||
ENVIRONMENT = "development"
|
||||
TRACES_SAMPLE_RATE = 0.1
|
||||
PROFILES_SAMPLE_RATE = 0.1
|
||||
|
||||
# LLM settings
|
||||
[llm]
|
||||
DEFAULT_MAX_TOKENS = 2500
|
||||
MAX_TOOL_OUTPUT_CHARS = 10000 # Max chars for tool output (~2500 tokens)
|
||||
MAX_MESSAGE_CONTENT_CHARS = 2000 # Max chars per message in tool results
|
||||
|
||||
# API Keys for LLM providers (set the ones you need)
|
||||
# Supported transports: openai, anthropic, gemini
|
||||
# Base URLs are set per-module via model_config.overrides.base_url
|
||||
# Built-in text-generation defaults use openai / gpt-5.4-mini.
|
||||
# Embeddings default to openai / text-embedding-3-small.
|
||||
OPENAI_API_KEY = "your-api-key-here"
|
||||
# ANTHROPIC_API_KEY = "your-api-key"
|
||||
# GEMINI_API_KEY = "your-api-key"
|
||||
|
||||
# Embedding settings
|
||||
[embedding]
|
||||
VECTOR_DIMENSIONS = 1536
|
||||
MAX_INPUT_TOKENS = 8192
|
||||
MAX_TOKENS_PER_REQUEST = 300000
|
||||
|
||||
[embedding.model_config]
|
||||
transport = "openai"
|
||||
model = "text-embedding-3-small"
|
||||
|
||||
# Optional module-level endpoint overrides
|
||||
# [embedding.model_config.overrides]
|
||||
# base_url = "https://embedding-proxy.internal.example/v1"
|
||||
# api_key_env = "EMBEDDING_CUSTOM_API_KEY"
|
||||
|
||||
# Deriver settings
|
||||
[deriver]
|
||||
ENABLED = true
|
||||
WORKERS = 1
|
||||
POLLING_SLEEP_INTERVAL_SECONDS = 1.0
|
||||
STALE_SESSION_TIMEOUT_MINUTES = 5
|
||||
# QUEUE_ERROR_RETENTION_SECONDS = 2592000 # 30 days
|
||||
DEDUPLICATE = true
|
||||
LOG_OBSERVATIONS = false
|
||||
MAX_INPUT_TOKENS = 25000
|
||||
MAX_CUSTOM_INSTRUCTIONS_TOKENS = 2000
|
||||
WORKING_REPRESENTATION_MAX_OBSERVATIONS = 100
|
||||
REPRESENTATION_BATCH_MAX_TOKENS = 1024
|
||||
FLUSH_ENABLED = false # Bypass batch token threshold, process work immediately
|
||||
|
||||
[deriver.model_config]
|
||||
transport = "openai"
|
||||
model = "gpt-5.4-mini"
|
||||
# temperature = 0.0
|
||||
# thinking_effort = "minimal"
|
||||
# thinking_budget_tokens = 1024
|
||||
# max_output_tokens = 4096
|
||||
|
||||
# Optional module-level endpoint overrides
|
||||
# transport = "openai"
|
||||
# model = "my-local-model"
|
||||
# [deriver.model_config.overrides]
|
||||
# base_url = "https://llm.internal.example/v1"
|
||||
# api_key_env = "DERIVER_CUSTOM_API_KEY"
|
||||
|
||||
# Optional fallback model
|
||||
# [deriver.model_config.fallback]
|
||||
# transport = "anthropic"
|
||||
# model = "claude-haiku-4-5"
|
||||
# [deriver.model_config.fallback.overrides]
|
||||
# base_url = "https://llm-backup.internal.example/v1"
|
||||
# api_key_env = "DERIVER_CUSTOM_BACKUP_API_KEY"
|
||||
# [deriver.model_config.overrides.provider_params]
|
||||
# verbosity = "low"
|
||||
|
||||
# Peer card settings
|
||||
[peer_card]
|
||||
ENABLED = true
|
||||
|
||||
# Dialectic settings
|
||||
[dialectic]
|
||||
MAX_OUTPUT_TOKENS = 8192
|
||||
MAX_INPUT_TOKENS = 100000
|
||||
HISTORY_TOKEN_LIMIT = 8192
|
||||
SESSION_HISTORY_MAX_TOKENS = 4096
|
||||
|
||||
# Per-level settings for reasoning levels
|
||||
# MAX_OUTPUT_TOKENS is optional per level; if not set, uses global MAX_OUTPUT_TOKENS
|
||||
[dialectic.levels.minimal]
|
||||
MAX_TOOL_ITERATIONS = 1
|
||||
MAX_OUTPUT_TOKENS = 250
|
||||
TOOL_CHOICE = "auto"
|
||||
|
||||
[dialectic.levels.minimal.model_config]
|
||||
transport = "openai"
|
||||
model = "gpt-5.4-mini"
|
||||
|
||||
[dialectic.levels.low]
|
||||
MAX_TOOL_ITERATIONS = 5
|
||||
TOOL_CHOICE = "auto"
|
||||
|
||||
[dialectic.levels.low.model_config]
|
||||
transport = "openai"
|
||||
model = "gpt-5.4-mini"
|
||||
|
||||
[dialectic.levels.medium]
|
||||
MAX_TOOL_ITERATIONS = 2
|
||||
|
||||
[dialectic.levels.medium.model_config]
|
||||
transport = "openai"
|
||||
model = "gpt-5.4-mini"
|
||||
|
||||
[dialectic.levels.high]
|
||||
MAX_TOOL_ITERATIONS = 4
|
||||
|
||||
[dialectic.levels.high.model_config]
|
||||
transport = "openai"
|
||||
model = "gpt-5.4-mini"
|
||||
|
||||
[dialectic.levels.max]
|
||||
MAX_TOOL_ITERATIONS = 10
|
||||
|
||||
[dialectic.levels.max.model_config]
|
||||
transport = "openai"
|
||||
model = "gpt-5.4-mini"
|
||||
|
||||
# [dialectic.levels.max.model_config.fallback]
|
||||
# transport = "gemini"
|
||||
# model = "gemini-2.5-pro"
|
||||
|
||||
# Summary settings
|
||||
[summary]
|
||||
ENABLED = true
|
||||
MESSAGES_PER_SHORT_SUMMARY = 20
|
||||
MESSAGES_PER_LONG_SUMMARY = 60
|
||||
MAX_TOKENS_SHORT = 1000
|
||||
MAX_TOKENS_LONG = 4000
|
||||
|
||||
[summary.model_config]
|
||||
transport = "openai"
|
||||
model = "gpt-5.4-mini"
|
||||
# thinking_effort = "minimal"
|
||||
# thinking_budget_tokens = 1024
|
||||
|
||||
# [summary.model_config.fallback]
|
||||
# transport = "anthropic"
|
||||
# model = "claude-haiku-4-5"
|
||||
|
||||
# Dream settings
|
||||
[dream]
|
||||
ENABLED = true
|
||||
DOCUMENT_THRESHOLD = 50
|
||||
IDLE_TIMEOUT_MINUTES = 60
|
||||
MIN_HOURS_BETWEEN_DREAMS = 8
|
||||
ENABLED_TYPES = ["omni"]
|
||||
MAX_TOOL_ITERATIONS = 20
|
||||
HISTORY_TOKEN_LIMIT = 16384
|
||||
|
||||
[dream.deduction_model_config]
|
||||
transport = "openai"
|
||||
model = "gpt-5.4-mini"
|
||||
|
||||
[dream.induction_model_config]
|
||||
transport = "openai"
|
||||
model = "gpt-5.4-mini"
|
||||
|
||||
# Surprisal-based sampling subsystem
|
||||
[dream.surprisal]
|
||||
ENABLED = false
|
||||
TREE_TYPE = "kdtree" # Options: kdtree, balltree, rptree, covertree, lsh, graph, prototype
|
||||
TREE_K = 5 # k for kNN-based trees
|
||||
SAMPLING_STRATEGY = "recent" # Options: recent, random, all
|
||||
SAMPLE_SIZE = 200
|
||||
TOP_PERCENT_SURPRISAL = 0.10 # Top 10% of observations
|
||||
MIN_HIGH_SURPRISAL_FOR_REPLACE = 10
|
||||
INCLUDE_LEVELS = ["explicit", "deductive"]
|
||||
|
||||
# Webhook settings
|
||||
[webhook]
|
||||
SECRET = ""
|
||||
MAX_WORKSPACE_LIMIT = 10
|
||||
|
||||
# Prometheus metrics settings (pull-based metrics)
|
||||
[metrics]
|
||||
ENABLED = false
|
||||
# NAMESPACE = "honcho" # Inherits from app.NAMESPACE if not set
|
||||
|
||||
# CloudEvents telemetry settings (analytics events)
|
||||
[telemetry]
|
||||
ENABLED = false
|
||||
# ENDPOINT = "https://telemetry.honcho.dev/v1/events"
|
||||
# HEADERS = '{"Authorization": "Bearer your-token"}' # JSON string for auth headers
|
||||
BATCH_SIZE = 100
|
||||
FLUSH_INTERVAL_SECONDS = 1.0
|
||||
FLUSH_THRESHOLD = 50
|
||||
MAX_RETRIES = 3
|
||||
MAX_BUFFER_SIZE = 10000
|
||||
# NAMESPACE = "honcho" # Inherits from app.NAMESPACE if not set
|
||||
|
||||
# Cache settings
|
||||
[cache]
|
||||
ENABLED = false
|
||||
URL = "redis://localhost:6379/0?suppress=true"
|
||||
# NAMESPACE = "honcho" # Inherits from app.NAMESPACE if not set
|
||||
DEFAULT_TTL_SECONDS = 300
|
||||
DEFAULT_LOCK_TTL_SECONDS = 5
|
||||
|
||||
# Vector store settings
|
||||
[vector_store]
|
||||
# Vector store type: "pgvector", "turbopuffer", or "lancedb"
|
||||
TYPE = "pgvector"
|
||||
# Migration flag: set to true when migration from pgvector is complete
|
||||
MIGRATED = false
|
||||
NAMESPACE = "honcho"
|
||||
# DIMENSIONS is deprecated; embedding.vector_dimensions is authoritative.
|
||||
# TURBOPUFFER_API_KEY = "your-turbopuffer-api-key"
|
||||
# TURBOPUFFER_REGION = "us-east-1"
|
||||
LANCEDB_PATH = "./lancedb_data"
|
||||
RECONCILIATION_INTERVAL_SECONDS = 300
|
||||
|
|
@ -0,0 +1 @@
|
|||
CREATE EXTENSION IF NOT EXISTS vector;
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
# Honcho Docker Compose
|
||||
#
|
||||
# Usage:
|
||||
# cp docker-compose.yml.example docker-compose.yml
|
||||
# cp .env.template .env # edit with your provider config
|
||||
# docker compose up -d --build
|
||||
#
|
||||
# By default, ports are bound to 127.0.0.1 (localhost only).
|
||||
# For development, uncomment the source mounts and monitoring services below.
|
||||
|
||||
services:
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
entrypoint: ["sh", "docker/entrypoint.sh"]
|
||||
depends_on:
|
||||
database:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- "127.0.0.1:8000:8000"
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"/app/.venv/bin/python",
|
||||
"-c",
|
||||
"import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=2).read()",
|
||||
]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
# -- Development: mount source for live reload --
|
||||
# volumes:
|
||||
# - .:/app
|
||||
# - venv:/app/.venv
|
||||
environment:
|
||||
- DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
|
||||
- CACHE_URL=redis://redis:6379/0?suppress=true
|
||||
- CACHE_ENABLED=true
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
restart: unless-stopped
|
||||
|
||||
deriver:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
entrypoint: ["/app/.venv/bin/python", "-m", "src.deriver"]
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_healthy
|
||||
database:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
# -- Development: mount source for live reload --
|
||||
# volumes:
|
||||
# - .:/app
|
||||
# - venv:/app/.venv
|
||||
environment:
|
||||
- DB_CONNECTION_URI=postgresql+psycopg://postgres:postgres@database:5432/postgres
|
||||
- CACHE_URL=redis://redis:6379/0?suppress=true
|
||||
- CACHE_ENABLED=true
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
restart: unless-stopped
|
||||
|
||||
database:
|
||||
image: pgvector/pgvector:pg15
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:5432:5432"
|
||||
command: ["postgres", "-c", "max_connections=200"]
|
||||
environment:
|
||||
- POSTGRES_DB=postgres
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
# Allow passwordless connections from the host (port is bound to 127.0.0.1).
|
||||
# Lets the local test suite and ad-hoc tools connect without supplying a
|
||||
# password. Do NOT use this in production.
|
||||
- POSTGRES_HOST_AUTH_METHOD=trust
|
||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||
volumes:
|
||||
- ./database/init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
- pgdata:/var/lib/postgresql/data/
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:8.2
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "127.0.0.1:6379:6379"
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "redis-cli ping"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
# -- Development: monitoring stack (uncomment to enable) --
|
||||
# prometheus:
|
||||
# image: prom/prometheus:v3.2.1
|
||||
# ports:
|
||||
# - "127.0.0.1:9090:9090"
|
||||
# volumes:
|
||||
# - ./docker/prometheus.yml:/etc/prometheus/prometheus.yml:ro
|
||||
# - prometheus-data:/prometheus
|
||||
# depends_on:
|
||||
# api:
|
||||
# condition: service_started
|
||||
# grafana:
|
||||
# image: grafana/grafana:11.4.0
|
||||
# ports:
|
||||
# - "127.0.0.1:3000:3000"
|
||||
# environment:
|
||||
# - GF_SECURITY_ADMIN_USER=admin
|
||||
# - GF_SECURITY_ADMIN_PASSWORD=admin
|
||||
# - GF_AUTH_ANONYMOUS_ENABLED=true
|
||||
# - GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
|
||||
# volumes:
|
||||
# - ./docker/grafana-datasource.yml:/etc/grafana/provisioning/datasources/datasource.yml:ro
|
||||
# depends_on:
|
||||
# prometheus:
|
||||
# condition: service_started
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
redis-data:
|
||||
# -- Development: uncomment if using source mounts --
|
||||
# venv:
|
||||
# prometheus-data:
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "Running database migrations..."
|
||||
/app/.venv/bin/python scripts/provision_db.py
|
||||
|
||||
echo "Starting API server..."
|
||||
exec /app/.venv/bin/fastapi run --host 0.0.0.0 src/main.py
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
url: http://prometheus:9090
|
||||
isDefault: true
|
||||
editable: false
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
global:
|
||||
scrape_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: honcho-api
|
||||
static_configs:
|
||||
- targets: ["api:8000"]
|
||||
- job_name: honcho-deriver
|
||||
static_configs:
|
||||
- targets: ["deriver:9090"]
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# Honcho Docs
|
||||
|
||||
These docs are built using Next.js via mintlify.
|
||||
|
||||
## Setting Up Honcho's Docs Locally
|
||||
|
||||
1. Clone the repository:
|
||||
|
||||
```
|
||||
git clone git@github.com:plastic-labs/honcho.git
|
||||
```
|
||||
|
||||
2. Navigate into the `docs` folder:
|
||||
|
||||
```
|
||||
cd honcho/docs/
|
||||
```
|
||||
|
||||
The docs folder contains the markdown files that make up the documentation. The majority of the files are in the pages directory. Some notable files in this folder include:
|
||||
|
||||
3. Verify that you have Node.js and npm installed in your system. You can check by running:
|
||||
|
||||
```
|
||||
node --version
|
||||
npm --version
|
||||
```
|
||||
|
||||
4. If not installed, download Node.js and npm from the respective official websites.
|
||||
|
||||
5. Once you have Node.js and npm running, proceed to install `pnpm` - another package manager that helps to manage project dependencies:
|
||||
|
||||
```
|
||||
npm install -g pnpm
|
||||
```
|
||||
|
||||
6. Install the project dependencies using pnpm:
|
||||
|
||||
```
|
||||
pnpm i
|
||||
```
|
||||
|
||||
7. After the successful installation of the project dependencies, start the local server:
|
||||
|
||||
```
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
Now, you should be able to view the docs on your local environment by visiting `http://localhost:3000`. You can explore the different markdown files and make changes as you see fit.
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
title: "SDK and API Compatibility Guide"
|
||||
description: "Compatibility guide for Honcho's SDKs and API"
|
||||
icon: "shield-check"
|
||||
---
|
||||
|
||||
This guide helps you match the right SDK version to your Honcho API version. Newer SDK patch versions are always backward-compatible within the same major version — install the latest patch for your range.
|
||||
|
||||
## Current Versions
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="TypeScript SDK" icon="js">
|
||||
**Latest:** v2.1.2
|
||||
|
||||
```bash
|
||||
npm install @honcho-ai/sdk
|
||||
```
|
||||
</Card>
|
||||
<Card title="Python SDK" icon="python">
|
||||
**Latest:** v2.1.2
|
||||
|
||||
```bash
|
||||
pip install honcho-ai
|
||||
```
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
|
||||
## Version Compatibility Table
|
||||
|
||||
| Honcho API Version | TypeScript SDK | Python SDK |
|
||||
|-------------------|---------------|------------|
|
||||
| v3.0.7 (Current) | v2.1.2 | v2.1.2 |
|
||||
| v3.0.6 | v2.1.1 | v2.1.1 |
|
||||
| v3.0.5 | v2.1.0 | v2.1.0 |
|
||||
| v3.0.4 | v2.1.0 | v2.1.0 |
|
||||
| v3.0.3 | v2.1.0 | v2.1.0 |
|
||||
| v3.0.2 | v2.0.0+ | v2.0.0+ |
|
||||
| v3.0.1 | v2.0.0+ | v2.0.0+ |
|
||||
| v3.0.0 | v2.0.0+ | v2.0.0+ |
|
||||
| v2.5.1 | v1.6.0 | v1.6.0 |
|
||||
| v2.5.0 | v1.6.0 | v1.6.0 |
|
||||
| v2.4.3 | v1.5.0 | v1.5.0 |
|
||||
| v2.4.2 | v1.5.0 | v1.5.0 |
|
||||
| v2.4.1 | v1.5.0 | v1.5.0 |
|
||||
| v2.4.0 | v1.5.0 | v1.5.0 |
|
||||
| v2.3.3 | v1.4.1 | v1.4.1 |
|
||||
| v2.3.2 | v1.4.0 | v1.4.0 |
|
||||
| v2.3.1 | v1.4.0 | v1.4.0 |
|
||||
| v2.3.0 | v1.4.0 | v1.4.0 |
|
||||
| v2.2.0 | v1.3.0 | v1.3.0 |
|
||||
| v2.1.1 | v1.2.1 | v1.2.2 |
|
||||
| v2.1.0 | v1.2.1 | v1.2.2 |
|
||||
| v2.0.5 | v1.1.0 | v1.1.0 |
|
||||
| v2.0.4 | v1.1.0 | v1.1.0 |
|
||||
|
|
@ -0,0 +1,950 @@
|
|||
---
|
||||
title: "Changelog"
|
||||
icon: "clock-rotate-left"
|
||||
---
|
||||
|
||||
Welcome to the Honcho changelog! This section documents all notable changes to the Honcho API and SDKs.
|
||||
|
||||
|
||||
<Accordion title="How to Read This Changelog">
|
||||
Each release is documented with:
|
||||
|
||||
- **Added**: New features and capabilities
|
||||
- **Changed**: Modifications to existing functionality
|
||||
- **Deprecated**: Features that will be removed in future versions
|
||||
- **Removed**: Features that have been removed
|
||||
- **Fixed**: Bug fixes and corrections
|
||||
- **Security**: Security-related improvements
|
||||
|
||||
## Version Format
|
||||
|
||||
Honcho follows [Semantic Versioning](https://semver.org/):
|
||||
- **MAJOR** version for incompatible API changes
|
||||
- **MINOR** version for backwards-compatible functionality additions
|
||||
- **PATCH** version for backwards-compatible bug fixes
|
||||
</Accordion>
|
||||
|
||||
### Honcho API and SDK Changelogs
|
||||
<Tabs>
|
||||
<Tab title="Honcho API">
|
||||
<Update label="v3.0.7 (Current)">
|
||||
### Added
|
||||
|
||||
- New `src/llm/` package as the single owner of provider runtime: clients, backends, history adapters, tool loop, request builder, credentials, and caching policy (#459)
|
||||
- New cloudevent `LLMCallCompletedEvent` (`llm.call.completed`) fires once per provider hit with full cost-attribution context: transport/provider_label, model, token counts with cache breakdown, finish_reason, outcome, retry/fallback state, duration, tool-call shape, streaming flag, and agent correlation (`run_id` + iteration) (#637)
|
||||
- `RepresentationCompletedEvent` now carries `total_input_tokens` for full-trace cost attribution; per-emitter `honcho_version` injection; deterministic per-`run_id` high-volume sampler via `TelemetrySettings.HIGH_VOLUME_SAMPLE_RATE` (#637)
|
||||
- Deriver custom instructions: per-workspace/peer guidance threaded into the deriver prompt with a `MAX_CUSTOM_INSTRUCTIONS_TOKENS` budget (default 2000); deriver `MAX_INPUT_TOKENS` raised 23000 → 25000 (#609)
|
||||
- Configurable embedding dimensions: `EMBEDDING_MODEL_CONFIG__DIMENSIONS_MODE` (`auto`/`always`/`never`) controls whether OpenAI `dimensions=` is forwarded (#678)
|
||||
- New `honcho-cli` package — Python CLI for inspecting and managing peers, sessions, and configuration against a Honcho deployment (#424)
|
||||
- `HONCHO_API_URL` env var support in the MCP Worker for self-hosted deployments (#575)
|
||||
- API ID `max_length` increased from 100 to 512 across `WorkspaceCreate`, `PeerCreate`, and `SessionCreate` to align with the DB schema (#684)
|
||||
- `AttemptPlan` dataclass pins per-retry provider selection across stream-final retries so streaming doesn't bounce back to primary after the tool loop has settled on fallback (#459)
|
||||
- Gemini JSON-schema sanitizer for `function_declarations` — strips keywords Gemini's validator rejects while preserving semantics for other backends (#459)
|
||||
|
||||
### Changed
|
||||
|
||||
- All LLM orchestration moved out of `src/utils/clients.py` into `src/llm/` with modules split by responsibility (#459)
|
||||
- Default `ModelConfig` factories (deriver, summary, dreamer specialists, dialectic levels) normalized with no extra parameters set by default; operators add transport/thinking overrides explicitly (#459)
|
||||
- OpenAI reasoning-model routing widened to cover `gpt-5.x` and `o1/o3/o4` — these models receive `max_completion_tokens` instead of `max_tokens` (#459)
|
||||
- Peer card prompts reframed as stable identity markers; induction specialist now opts out of peer card writes so only deduction touches the card (#686)
|
||||
- Vector store queries no longer fetch embedding vectors — only document metadata is returned, reducing payload size and DB load (pgvector, lancedb, turbopuffer) (#682)
|
||||
- Langfuse trace metadata now includes `namespace`, `model`, and `provider` so traces can be filtered by deployment slice (#565)
|
||||
- Deriver: model-aware tokenizer (replaces the previously hardcoded encoding) and explicit guard on empty message content (#647)
|
||||
- Dialectic level defaults now merge correctly with per-level overrides (#656)
|
||||
- Default dialectic tool choice switched to `auto` (#630)
|
||||
- Vector sync given a substantial retry budget to tolerate transient embedding provider outages (#604)
|
||||
- `AgentToolConclusionsDeletedEvent` payload now carries `levels` (#612)
|
||||
- Turbopuffer: `InternalServerError` caught and surfaced as a warning rather than a hard failure; vector store sync errors downgraded to warnings (#561)
|
||||
|
||||
### Fixed
|
||||
|
||||
- `reverse` query parameter is now honored on the v3 workspace list, peer list, workspace-scoped session list, and peer-scoped session list. Honcho SDKs at 2.1.0+ were already sending `reverse=true` for these routes but the server silently ignored it. Ties on `created_at` now fall back to the internal nanoid `id` for stable ordering across pages (#685)
|
||||
- LLM client factories now receive `base_url` from `LLMSettings` for default providers — operators pointing at OpenAI-compatible proxies via `LLM__OPENAI_BASE_URL` were previously ignored on the default path (#643, fixes #641)
|
||||
- Internal N+1 query in dialectic agent tool execution — collapsed per-iteration DB lookups into a single fetch (#652)
|
||||
- Dreamer threshold and time-guard semantics: count filter now includes only `documents.level == 'explicit'` (was inflating threshold via dreamer-created levels and creating a feedback loop); `last_dream_at` write relocated from enqueue to process so duplicate enqueues or failed runs no longer reset the 8-hour time guard (#573)
|
||||
- Deriver: blank observations are filtered out before embedding (previously triggered noisy embedding calls and persisted empty rows) (#615)
|
||||
- Surprisal module: filter format corrected from `{"level": levels}` to `{"level": {"in": levels}}` — the prior call silently returned 0 results and made the entire Surprisal phase of the Dream cycle a no-op (#581, fixes #559)
|
||||
- Removed hardcoded `stop_sequences` override from Deriver `ModelConfig` (was clobbering operator-configured stop sequences) (#587)
|
||||
- Embedding client: `embed()` now wraps single-string input in an array, restoring compatibility with OpenAI-compatible third-party providers that reject scalar input (#586)
|
||||
- Docker Compose: deriver service startup gated on the API service healthcheck — prevents races where the deriver starts before the API has run migrations (#689)
|
||||
- Docker image: `HEALTHCHECK` directive removed from the shared base image; service-level health checks now belong in each service's own configuration (#530)
|
||||
- Removed strict parameter validation for thinking params on Anthropic and OpenAI transports — was rejecting valid per-transport configs (#686)
|
||||
- Stream-final retries pin to the `AttemptPlan` that succeeded rather than re-running provider selection through the outer `current_attempt` ContextVar (#459)
|
||||
- Gemini `cached_content` reuse keys now include `system_instruction` and `tool_config` so cache hits don't cross configurations (#459)
|
||||
- CrewAI example updated for the latest CrewAI protocol (#631)
|
||||
|
||||
### Removed
|
||||
|
||||
- `src/utils/clients.py` deleted; its responsibilities are split across `src/llm/registry.py`, `src/llm/credentials.py`, and the backend-specific modules (#459)
|
||||
- `HEALTHCHECK` directive from the shared Docker image (#530)
|
||||
</Update>
|
||||
|
||||
<Update label="v3.0.6">
|
||||
### Changed
|
||||
|
||||
- Tightened transaction scopes across search, agent tools, queue manager, and webhook delivery to minimize DB connection hold time during external operations (#525)
|
||||
- Search operations refactored to two-phase pattern — external work (embeddings, LLM calls) completes before opening a transaction (#525)
|
||||
- Agent tool executor performs external operations before acquiring DB sessions (#525)
|
||||
- Queue manager transaction scope reduced to only the critical section (#525)
|
||||
- Webhook delivery no longer holds a DB session parameter (#525)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Session leakage in non-session-scoped dialectic chat calls (#526)
|
||||
|
||||
### Added
|
||||
|
||||
- Health check endpoint (`/health`) for container orchestration and load balancer probes (#510)
|
||||
</Update>
|
||||
|
||||
<Update label="v3.0.5">
|
||||
### Fixed
|
||||
|
||||
- explicit rollback on all transactions to force connection closed
|
||||
</Update>
|
||||
|
||||
<Update label="v3.0.4">
|
||||
### Added
|
||||
|
||||
- JSONB metadata validation enforces 100 key limit and max depth of 5 (#419)
|
||||
|
||||
### Changed
|
||||
|
||||
- Schemas refactored from single `schemas.py` into `schemas/api.py`, `schemas/configuration.py`, and `schemas/internal.py` with backwards-compatible re-exports (#419)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Missing `deleted_at` filter on `RepresentationManager._query_documents_recent()` and `._query_documents_most_derived()` allowed soft-deleted documents to leak into the deriver's working representation (#456)
|
||||
- `CleanupStaleItemsCompletedEvent` emitted spuriously when no queue item was actually deleted (#454)
|
||||
- Empty JSON file uploads caused unhandled errors; now returns normalized error responses (#434)
|
||||
- Memory leak: `_observation_locks` switched to `WeakValueDictionary` to prevent unbounded growth (#419)
|
||||
- SQL injection in `dependencies.py`: parameterized `set_config` calls to prevent injection via request context (#419)
|
||||
- NUL byte crashes: string inputs (message content, queries, peer cards) now stripped at schema level (#419)
|
||||
- Filter recursion depth capped at 5 to prevent stack overflow (#419)
|
||||
- Dedup-skipped observations now correctly reflected in created counts (#477)
|
||||
- External vector store support for message search — routes queries through configured external vector store with oversampling and
|
||||
deduplication to handle chunked embeddings (#479)
|
||||
- Dialectic agent no longer holds a DB connection during LLM calls — embeddings are pre-computed before tool execution, DB sessions isolated in `extract_preferences`, `query_documents` no longer accepts a DB session parameter (#477)
|
||||
</Update>
|
||||
<Update label="v3.0.3">
|
||||
### Added
|
||||
|
||||
- Consolidated session context into a single DB session with 40/60 token budget allocation between summary and messages
|
||||
- Observation validation via `ObservationInput` Pydantic schema with partial-success support and batch embedding with per-observation fallback
|
||||
- Peer card hard cap of 40 facts with case-insensitive deduplication and whitespace normalization
|
||||
- Safe integer coercion (`_safe_int`) for all LLM tool inputs to handle non-integer values like `"Infinity"`
|
||||
- Embedding pre-computation and reuse across multiple search calls in dialectic and representation flows
|
||||
- Peer existence validation in dialectic chat endpoints — raises ResourceNotFoundException instead of silently failing
|
||||
- Logging filter to suppress noisy `GET /metrics` access logs
|
||||
- Oolong long-context aggregation benchmark (synth and real variants, 1K–4M token context windows)
|
||||
- MolecularBench fact quality evaluation (ambiguity, decontextuality, minimality scoring)
|
||||
- CoverageBench information recall evaluation (gold fact extraction, coverage matching, QA verification)
|
||||
- LoCoMo summary-as-context baseline evaluation
|
||||
- Webhook delivery tests, dependency lifecycle tests, queue cleanup tests, summarizer fallback tests
|
||||
- Parallel test execution via pytest-xdist with worker-specific databases
|
||||
- `test_reasoning_levels.py` script for LOCOM dataset testing across reasoning levels
|
||||
|
||||
### Changed
|
||||
|
||||
- Workspace deletion is now async — returns 202 Accepted, validates no active sessions (409 Conflict), cascade-deletes in background
|
||||
- Redis caching layer now stores plain-dict instead of ORM objects, with v2-prefixed keys, storage, resilient `safe_cache_set`/`safe_cache_delete` helpers, and deferred post-commit cache invalidation
|
||||
- All `get_or_create_*` CRUD operations now use savepoints (`db.begin_nested()`) instead of commit/rollback for race condition prevention
|
||||
- Reconciler vector sync uses direct ORM mutation instead of batch parameterized UPDATE statements
|
||||
- Summarizer enforces hard word limit in prompt and creates fallback text for empty summaries with `summary_tokens = 0`
|
||||
- Blocked Gemini responses (SAFETY, RECITATION, PROHIBITED_CONTENT, BLOCKLIST) now raise `LLMError` to trigger retry/backup-provider logic
|
||||
- Gemini client explicitly sets `max_output_tokens` from `max_tokens` parameter
|
||||
- All deriver and metrics collector logging replaced with structured `logging.getLogger(__name__)` calls
|
||||
- Dreamer specialist prompts updated to enforce durable-facts-only peer cards with max 40 entries and deduplication
|
||||
- `GetOrCreateResult` changed from `NamedTuple` to `dataclass` with `async post_commit()` method
|
||||
- FastAPI upgraded from 0.111.0 to 0.131.0; added pyarrow dependency
|
||||
- Queue status filtering to only show user-facing tasks (representation, summary, dream); excludes internal infrastructure tasks
|
||||
|
||||
### Fixed
|
||||
|
||||
- JWT timestamp bug — `JWTParams.t` was evaluated once at class definition time instead of per-instance
|
||||
- Session cache invalidation on deletion was missing
|
||||
- `get_peer_card()` now properly propagates `ResourceNotFoundException` instead of swallowing it
|
||||
- `set_peer_card()` ensures peer exists via `get_or_create_peers()` before updating
|
||||
- Backup provider failover with proper tool input type safety
|
||||
- Removed `setup_admin_jwt()` from server startup
|
||||
- Sentry coroutine detection switched from `asyncio.iscoroutinefunction` to `inspect.iscoroutinefunction`
|
||||
|
||||
### Removed
|
||||
|
||||
- `explicit.py` and `obex.py` benchmarks replaced by coverage.py and molecular.py
|
||||
- Claude Code review automation workflow (`.github/workflows/claude.yml`)
|
||||
- Coverage reporting from default pytest configuration
|
||||
</Update>
|
||||
|
||||
<Update label="v3.0.2">
|
||||
### Added
|
||||
|
||||
- Documentation for reasoning_level and Claude Code plugin
|
||||
|
||||
### Changed
|
||||
|
||||
- Gave dreaming sub-agents better prompting around peer card creation, tweaked overall prompts
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added message-search fallback for memory search tool, necessary in fresh sessions
|
||||
- Made FLUSH_ENABLED a config value
|
||||
- Removed N+1 query in search_messages
|
||||
</Update>
|
||||
|
||||
<Update label="v3.0.1">
|
||||
### Fixed
|
||||
|
||||
- Token counting in Explicit Agent Loop
|
||||
- Backwards compatibility of queue items
|
||||
</Update>
|
||||
|
||||
<Update label="v3.0.0">
|
||||
### Added
|
||||
|
||||
- Agentic Dreamer for intelligent memory consolidation using LLM agents
|
||||
- Agentic Dialectic for query answering using LLM agents with tool use
|
||||
- Reasoning levels configuration for dialectic (`minimal`, `low`, `medium`, `high`, `max`)
|
||||
- Prometheus token tracking for deriver and dialectic operations
|
||||
- n8n integration
|
||||
- Cloud Events for auditable telemetry
|
||||
- External Vector Store support for turbopuffer and lancedb with reconciliation flow
|
||||
|
||||
### Changed
|
||||
|
||||
- API route renaming for consistency
|
||||
- Dreamer and dialectic now respect peer card configuration settings
|
||||
- Observations renamed to Conclusions across API and SDKs
|
||||
- Deriver to buffer representation tasks to normalize workloads
|
||||
- Local Representation tasks to create singular QueueItems
|
||||
- getContext endpoint to use `search_query` rather than force `last_user_message`
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dream scheduling bugs
|
||||
- Summary creation when start_message_id > end_message_id
|
||||
- Cashews upgrade to prevent NoScriptError
|
||||
- Memory leak in `accumulate_metric` call
|
||||
|
||||
### Removed
|
||||
|
||||
- Peer card configuration from message configuration; peer cards no longer created/updated in deriver process
|
||||
</Update>
|
||||
|
||||
<Update label="v2.5.1">
|
||||
### Fixed
|
||||
|
||||
- Backwards compatibility for `message_ids` field in documents to handle legacy tuple format
|
||||
</Update>
|
||||
|
||||
<Update label="v2.5.0">
|
||||
### Added
|
||||
|
||||
- Message level configurations
|
||||
- CRUD operations for observations
|
||||
- Comprehensive test cases for harness
|
||||
- Peer level get_context
|
||||
- Set Peer Card Method
|
||||
- Manual dreaming trigger endpoint
|
||||
|
||||
### Changed
|
||||
|
||||
- Configurations to support more flags for fine-grained control of the deriver, peer cards, summaries, etc.
|
||||
- Working Representations to support more fine-grained parameters
|
||||
|
||||
### Fixed
|
||||
|
||||
- File uploads to match `MessageCreate` structure
|
||||
- Cache invalidation strategy
|
||||
</Update>
|
||||
|
||||
<Update label="v2.4.3">
|
||||
### Added
|
||||
|
||||
- Redis caching to improve DB IO
|
||||
- Backup LLM provider to avoid failures when a provider is down
|
||||
|
||||
### Changed
|
||||
|
||||
- QueueItems to use standardized columns
|
||||
- Improved Deduplication logic for Representation Tasks
|
||||
- More finegrained metrics for representation, summary, and peer card tasks
|
||||
- DB constraint to follow standard naming conventions
|
||||
</Update>
|
||||
|
||||
<Update label="v2.4.2">
|
||||
### Fixed
|
||||
|
||||
- Langfuse tracing to have readable waterfalls
|
||||
- Alembic Migrations to match models.py
|
||||
- message_in_seq correctly included in webhook payload
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
- Alembic to always use a session pooler
|
||||
- Statement timeout during alembic operations to 5 min
|
||||
</Update>
|
||||
|
||||
<Update label="v2.4.1">
|
||||
### Added
|
||||
|
||||
- Alembic migration validation test suite
|
||||
|
||||
### Fixed
|
||||
|
||||
- Alembic migrations to batch changes
|
||||
- Batch message creation sequence number
|
||||
|
||||
### Changed
|
||||
|
||||
- Logging infrastructure to remove noisy messages
|
||||
- Sentry integration is centralized
|
||||
</Update>
|
||||
|
||||
<Update label="v2.4.0">
|
||||
### Added
|
||||
|
||||
- Unified `Representation` class
|
||||
- vllm client support
|
||||
- Periodic queue cleanup logic
|
||||
- WIP Dreaming Feature
|
||||
- LongMemEval to Test Bench
|
||||
- Prometheus Client for better Metrics
|
||||
- Performance metrics instrumentation
|
||||
- Error reporting to deriver
|
||||
- Workspace Delete Method
|
||||
- Multi-db option in test harness
|
||||
|
||||
### Changed
|
||||
|
||||
- Working Representations are Queried on the fly rather than cached in metadata
|
||||
- EmbeddingStore to RepresentationFactory
|
||||
- Summary Response Model to use public_id of message for cutoff
|
||||
- Semantic across codebase to reference resources based on `observer` and `observed`
|
||||
- Prompts for Deriver & Dialectic to reference peer_id and add examples
|
||||
- `Get Context` route returns peer card and representation in addition to messages and summaries
|
||||
- Refactoring logger.info calls to logger.debug where applicable
|
||||
|
||||
### Fixed
|
||||
|
||||
- Gemini client to use async methods
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="v2.3.3">
|
||||
### Changed
|
||||
|
||||
- Deriver Rollup Queue processes interleaved messages for more context
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dialectic Streaming to follow SSE conventions
|
||||
- Sentry tracing in the deriver
|
||||
</Update>
|
||||
|
||||
<Update label="v2.3.2">
|
||||
### Added
|
||||
|
||||
- Get peer cards endpoint (`GET /v2/peers/{peer_id}/card`) for retrieving targeted peer context information
|
||||
|
||||
### Changed
|
||||
|
||||
- Replaced Mirascope dependency with small client implementation for better control
|
||||
- Optimized deriver performance by using joins on messages table instead of storing token count in queue payload
|
||||
- Database scope optimization for various operations
|
||||
- Batch representation task processing for ~10x speed improvement in practice
|
||||
|
||||
### Fixed
|
||||
|
||||
- Separated clean and claim work units in queue manager to prevent race conditions
|
||||
- Skip locked ActiveQueueSession rows on delete operations
|
||||
- Langfuse SDK integration updates for compatibility
|
||||
- Added configurable maximum message size to prevent token overflow in deriver
|
||||
- Various minor bugfixes
|
||||
</Update>
|
||||
<Update label="v2.3.1">
|
||||
### Fixed
|
||||
|
||||
- Added max message count to deriver in order to not overflow token limits
|
||||
</Update>
|
||||
<Update label="v2.3.0">
|
||||
### Added
|
||||
|
||||
- `getSummaries` endpoint to get all available summaries for a session directly
|
||||
- Peer Card feature to improve context for deriver and dialectic
|
||||
|
||||
### Changed
|
||||
|
||||
- Session Peer limit to be based on observers instead, renamed config value to
|
||||
`SESSION_OBSERVERS_LIMIT`
|
||||
- `Messages` can take a custom timestamp for the `created_at` field, defaulting
|
||||
to the current time
|
||||
- `get_context` endpoint returns detailed `Summary` object rather than just
|
||||
summary content
|
||||
- Working representations use a FIFO queue structure to maintain facts rather
|
||||
than a full rewrite
|
||||
- Optimized deriver enqueue by prefetching message sequence numbers (eliminates N+1 queries)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Deriver uses `get_context` internally to prevent context window limit errors
|
||||
- Embedding store will truncate context when querying documents to prevent embedding
|
||||
token limit errors
|
||||
- Queue manager to schedule work based on available works rather than total
|
||||
number of workers
|
||||
- Queue manager to use atomic db transactions rather than long lived transaction
|
||||
for the worker lifecycle
|
||||
- Timestamp formats unified to ISO 8601 across the codebase
|
||||
- Internal get_context method's cutoff value is exclusive now
|
||||
</Update>
|
||||
<Update label="v2.2.0">
|
||||
### Added
|
||||
|
||||
- Arbitrary filters now available on all search endpoints
|
||||
- Search combines full-text and semantic using reciprocal rank fusion
|
||||
- Webhook support (currently only supports queue_empty and test events, more to come)
|
||||
- Small test harness and custom test format for evaluating Honcho output quality
|
||||
- Added MCP server and documentation for it
|
||||
|
||||
### Changed
|
||||
|
||||
- Search has 10 results by default, max 100 results
|
||||
- Queue structure generalized to handle more event types
|
||||
- Summarizer now exhaustive by default and tuned for performance
|
||||
|
||||
### Fixed
|
||||
|
||||
- Resolve race condition for peers that leave a session while sending messages
|
||||
- Added explicit rollback to solve integrity error in queue
|
||||
- Re-introduced Sentry tracing to deriver
|
||||
- Better integrity logic in get_or_create API methods
|
||||
</Update>
|
||||
|
||||
|
||||
<Update label="v2.1.2">
|
||||
### Fixed
|
||||
|
||||
- Summarizer module to ignore empty summaries and pass appropriate one to get_context
|
||||
- Structured Outputs calls with OpenAI provider to pass strict=True to Pydantic Schema
|
||||
</Update>
|
||||
|
||||
|
||||
<Update label="v2.1.1">
|
||||
### Added
|
||||
|
||||
- Test harness for custom Honcho evaluations
|
||||
- Better support for session and peer aware dialectic queries
|
||||
- Langfuse settings
|
||||
- Added recent history to dialectic prompt, dynamic based on new context window size setting
|
||||
|
||||
### Fixed
|
||||
|
||||
- Summary queue logic
|
||||
- Formatting of logs
|
||||
- Filtering by session
|
||||
- Peer targeting in queries
|
||||
|
||||
### Changed
|
||||
|
||||
- Made query expansion in dialectic off by default
|
||||
- Overhauled logging
|
||||
- Refactor summarization for performance and code clarity
|
||||
- Refactor queue payloads for clarity
|
||||
</Update>
|
||||
|
||||
<Update label="v2.1.0">
|
||||
### Added
|
||||
|
||||
- File uploads
|
||||
- Brand new "ROTE" deriver system
|
||||
- Updated dialectic system
|
||||
- Local working representations
|
||||
- Better logging for deriver/dialectic
|
||||
- Deriver Queue Status no longer has redundant data
|
||||
|
||||
### Fixed
|
||||
|
||||
- Document insertion
|
||||
- Session-scoped and peer-targeted dialectic queries work now
|
||||
- Minor bugs
|
||||
|
||||
### Removed
|
||||
|
||||
- Peer-level messages
|
||||
|
||||
### Changed
|
||||
|
||||
- Dialectic chat endpoint takes a single query
|
||||
- Rearranged configuration values (LLM, Deriver, Dialectic, History->Summary)
|
||||
</Update>
|
||||
|
||||
<Update label="v2.0.5">
|
||||
### Fixed
|
||||
|
||||
- Groq API client to use the Async library
|
||||
</Update>
|
||||
|
||||
<Update label="v2.0.4">
|
||||
### Fixed
|
||||
|
||||
- Migration/provision scripts did not have correct database connection arguments, causing timeouts
|
||||
</Update>
|
||||
|
||||
<Update label="v2.0.3">
|
||||
### Fixed
|
||||
|
||||
- Bug that causes runtime error when Sentry flags are enabled
|
||||
</Update>
|
||||
|
||||
<Update label="v2.0.2">
|
||||
### Fixed
|
||||
|
||||
- Database initialization was misconfigured and led to provision_db script failing: switch to consistent working configuration with transaction pooler
|
||||
</Update>
|
||||
|
||||
<Update label="v2.0.1">
|
||||
### Added
|
||||
|
||||
- Ergonomic SDKs for Python and TypeScript (uses Stainless underneath)
|
||||
- Deriver Queue Status endpoint
|
||||
- Complex arbitrary filters on workspace/session/peer/message
|
||||
- Message embedding table for full semantic search
|
||||
|
||||
### Changed
|
||||
|
||||
- Overhauled documentation
|
||||
- BasedPyright typing for entire project
|
||||
- Resource filtering expanded to include logical operators
|
||||
|
||||
### Fixed
|
||||
|
||||
- Various bugs
|
||||
- Use new config arrangement everywhere
|
||||
- Remove hardcoded responses
|
||||
</Update>
|
||||
|
||||
<Update label="v2.0.0">
|
||||
### Added
|
||||
|
||||
- Ability to get a peer's working representation
|
||||
- Metadata to all data primitives (Workspaces, Peers, Sessions, Messages)
|
||||
- Internal metadata to store Honcho's state no longer exposed in API
|
||||
- Batch message operations and enhanced message querying with token and message count limits
|
||||
- Search and summary functionalities scoped by workspace, peer, and session
|
||||
- Session context retrieval with summaries and token allocatio
|
||||
- HNSW Index for Documents Table
|
||||
- Centralized Configuration via Environment Variables or config.toml file
|
||||
|
||||
### Changed
|
||||
|
||||
- New architecture centered around the concept of a "peer" replaces the former
|
||||
"app"/"user"/"session" paradigm
|
||||
- Workspaces replace "apps" as top-level namespace
|
||||
- Peers replace "users"
|
||||
- Sessions no longer nested beneath peers and no longer limited to a single
|
||||
user-assistant model. A session exists independently of any one peer and
|
||||
peers can be added to and removed from sessions.
|
||||
- Dialectic API is now part of the Peer, not the Session
|
||||
- Dialectic API now allows queries to be scoped to a session or "targeted"
|
||||
to a fellow peer
|
||||
- Database schema migrated to adopt workspace/peer/session naming and structure
|
||||
- Authentication and JWT scopes updated to workspace/peer/session hierarchy
|
||||
- Queue processing now works on 'work units' instead of sessions
|
||||
- Message token counting updated with tiktoken integration and fallback heuristic
|
||||
- Queue and message processing updated to handle sender/target and task types for multi-peer scenarios
|
||||
|
||||
### Fixed
|
||||
|
||||
- Improved error handling and validation for batch message operations and metadata
|
||||
- Database Sessions to be more atomic to reduce idle in transaction time
|
||||
|
||||
### Removed
|
||||
|
||||
- Metamessages removed in favor of metadata
|
||||
- Collections and Documents no longer exposed in the API, solely internal
|
||||
- Obsolete tests for apps, users, collections, documents, and metamessages
|
||||
---
|
||||
</Update>
|
||||
|
||||
<Update label="v1.1.0">
|
||||
### Added
|
||||
|
||||
- Normalize resources to remove joins and increase query performance
|
||||
- Query tracing for debugging
|
||||
|
||||
### Changed
|
||||
|
||||
- `/list` endpoints to not require a request body
|
||||
- `metamessage_type` to `label` with backwards compatibility
|
||||
- Database Provisioning to rely on alembic
|
||||
- Database Session Manager to explicitly rollback transactions before closing
|
||||
the connection
|
||||
|
||||
### Fixed
|
||||
|
||||
- Alembic Migrations to include initial database migrations
|
||||
- Sentry Middleware to not report Honcho Exceptions
|
||||
</Update>
|
||||
|
||||
<Update label="v1.0.0">
|
||||
### Added
|
||||
|
||||
- JWT based API authentication
|
||||
- Configurable logging
|
||||
- Consolidated LLM Inference via `ModelClient` class
|
||||
- Dynamic logging configurable via environment variables
|
||||
|
||||
### Changed
|
||||
|
||||
- Deriver & Dialectic API to use Hybrid Memory Architecture
|
||||
- Metamessages are not strictly tied to a message
|
||||
- Database provisioning is a separate script instead of happening on startup
|
||||
- Consolidated `session/chat` and `session/chat/stream` endpoints
|
||||
</Update>
|
||||
|
||||
## Previous Releases
|
||||
|
||||
For a complete history of all releases, see our [GitHub Releases](https://github.com/plastic-labs/honcho/tags) page.
|
||||
</Tab>
|
||||
|
||||
<Tab title="Python SDK">
|
||||
[Python SDK](https://pypi.org/project/honcho-ai/)
|
||||
<Update label="v2.1.2 (Current)">
|
||||
### Added
|
||||
|
||||
- `page`, `size`, and `reverse` pagination parameters on `Honcho.workspaces()` and `HonchoAio.workspaces()`, closing the gap from 2.1.0 which added these to other list methods but not to `workspaces()`. Honoring `reverse` on the workspace/peer/session list routes also requires a Honcho server with the matching API fix; older servers silently ignore the parameter.
|
||||
- `peers` parameter on `Honcho.session()` and `HonchoAio.session()` — attach peers to a session at creation time instead of needing a follow-up `session.add_peers()` call. Accepts the same shapes as `Session.add_peers` (peer ID string, `Peer` object, list of either, or tuples with `SessionPeerConfig`).
|
||||
|
||||
### Changed
|
||||
|
||||
- `WorkspaceCreateParams`, `PeerCreateParams`, and `SessionCreateParams` now accept IDs up to 512 characters (was 100), matching the server-side schema change in Honcho v3.0.7.
|
||||
</Update>
|
||||
<Update label="v2.1.1">
|
||||
### Fixed
|
||||
|
||||
- Broadened HTTP retry logic to cover `httpx.NetworkError` and `httpx.RemoteProtocolError` in addition to `httpx.TimeoutException` and `httpx.ConnectError`, improving resilience against transient network failures
|
||||
</Update>
|
||||
<Update label="v2.1.0">
|
||||
### Added
|
||||
|
||||
- `created_at` property on `Peer` and `Session` objects
|
||||
- `is_active` property on `Session` objects
|
||||
- `get_message(message_id)` method on `Session` (sync and async) to fetch a single message by ID
|
||||
- `page`, `size`, and `reverse` pagination parameters on all list methods
|
||||
|
||||
### Changed
|
||||
|
||||
- **Breaking**: `peer()` and `session()` now always make a get-or-create API call — no more lazy initialization
|
||||
- Response configuration models now tolerate unknown fields from newer servers for forward compatibility
|
||||
|
||||
### Fixed
|
||||
|
||||
- Sync and async `Session.get_metadata()`, `get_configuration()`, and `refresh()` now refresh cached `created_at` and `is_active` values along with metadata and configuration
|
||||
- `honcho.__version__` now derives from package metadata, with a source-checkout fallback, so it stays aligned with released package versions
|
||||
</Update>
|
||||
<Update label="v2.0.2">
|
||||
### Changed
|
||||
|
||||
- All input models now reject unknown fields via strict Pydantic validation (`extra="forbid"`). Previously, misspelled or extraneous fields were silently ignored. Now a `ValidationError` is raised with the unrecognized field name.
|
||||
</Update>
|
||||
<Update label="v2.0.1">
|
||||
### Added
|
||||
|
||||
- `set_peer_card` method
|
||||
|
||||
### Changed
|
||||
|
||||
- `card` is now `get_card` with `card` kept for backwards compatibility and marked as deprecated
|
||||
</Update>
|
||||
<Update label="v2.0.0">
|
||||
### Added
|
||||
|
||||
- `ConclusionScope` object for CRUD operations on conclusions (renamed from observations)
|
||||
- Representation configuration support
|
||||
|
||||
### Changed
|
||||
|
||||
- Observations renamed to Conclusions across the SDK
|
||||
- Major SDK refactoring and cleanup
|
||||
- Simplified method signatures throughout
|
||||
- Representation endpoints now return `string` instead of old Representation object
|
||||
|
||||
### Removed
|
||||
|
||||
- Standalone types module (now uses honcho-core types)
|
||||
- Representation object
|
||||
</Update>
|
||||
<Update label="v1.6.0">
|
||||
### Added
|
||||
|
||||
- metadata and configuration fields to Workspace, Peer, Session, and Message objects
|
||||
- Session Clone methods
|
||||
- Peer level get_context method
|
||||
- `ObservationScope` object to perform CRUD operations on observations
|
||||
- Representation object for WorkingRepresentations
|
||||
|
||||
### Changed
|
||||
|
||||
- methods that take IDs, can all optionally take an object of the same type
|
||||
</Update>
|
||||
<Update label="v1.5.0">
|
||||
### Added
|
||||
|
||||
- Delete workspace method
|
||||
|
||||
### Changed
|
||||
|
||||
- message_id of `Summary` model is a string nanoid
|
||||
- Get Context can return Peer Card & Peer Representation
|
||||
</Update>
|
||||
<Update label="v1.4.1">
|
||||
### Added
|
||||
|
||||
- Get Peer Card method
|
||||
- Update Message metadata method
|
||||
- Session level deriver status methods
|
||||
- Delete session message
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dialectic Stream returns Iterators
|
||||
- Type warnings
|
||||
|
||||
### Changed
|
||||
|
||||
- Pagination class to match core implementation
|
||||
</Update>
|
||||
<Update label="v1.4.0">
|
||||
### Added
|
||||
|
||||
- getSummaries API returning structured summaries
|
||||
- Webhook support
|
||||
|
||||
### Changed
|
||||
|
||||
- Messages can take an optional `created_at` value, defaulting to the current
|
||||
time (UTC ISO 8601)
|
||||
</Update>
|
||||
<Update label="v1.2.2">
|
||||
### Added
|
||||
|
||||
- Filter parameter to various endpoints
|
||||
|
||||
</Update>
|
||||
<Update label="v1.2.1">
|
||||
### Fixed
|
||||
|
||||
- Honcho util import paths
|
||||
|
||||
</Update>
|
||||
<Update label="v1.2.0">
|
||||
### Added
|
||||
|
||||
- Get/poll deriver queue status endpoints added to workspace
|
||||
- Added endpoint to upload files as messages
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed peer messages in accordance with Honcho 2.1.0
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated chat endpoint to use singular `query` in accordance with Honcho 2.1.0
|
||||
|
||||
</Update>
|
||||
<Update label="v1.1.0">
|
||||
### Fixed
|
||||
|
||||
- Properly handle AsyncClient
|
||||
</Update>
|
||||
</Tab>
|
||||
|
||||
<Tab title="TypeScript SDK">
|
||||
[TypeScript SDK](https://www.npmjs.com/package/@honcho-ai/sdk)
|
||||
<Update label="v2.1.2 (Current)">
|
||||
### Added
|
||||
|
||||
- `peers` option on `Honcho.session()` — attach peers to a session at creation time instead of needing a follow-up `session.addPeers()` call. Accepts the same `PeerAddition` shape as `session.addPeers()` (peer ID strings, `Peer` objects, arrays of either, or a record with per-peer `observe_me`/`observe_others` config).
|
||||
|
||||
### Changed
|
||||
|
||||
- ID validation in `validation.ts` now accepts workspace, peer, and session IDs up to 512 characters (was 100), matching the server-side schema change in Honcho v3.0.7.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `Honcho.workspaces()` now actually forwards the `reverse` option to the server. The 2.1.0 changelog listed `workspaces()` among the list methods that gained `reverse`, but `client.ts` was missing the field on the params type and request builder, so the option was silently dropped. Honoring `reverse` on the workspace/peer/session list routes also requires a Honcho server with the matching API fix; older servers silently ignore the parameter.
|
||||
</Update>
|
||||
<Update label="v2.1.1">
|
||||
### Fixed
|
||||
|
||||
- Broadened fetch error retry logic to catch all `TypeError` network failures (connection resets, DNS errors, etc.) instead of only those with `'fetch'` in the message, improving resilience across runtimes (Node, Bun, browsers)
|
||||
</Update>
|
||||
<Update label="v2.1.0">
|
||||
### Added
|
||||
|
||||
- `createdAt` property on `Peer` and `Session` wrapper objects
|
||||
- `isActive` property on `Session` wrapper objects
|
||||
- `getMessage(messageId)` method on `Session` to fetch a single message by ID
|
||||
- `Peer.representation()`, `Session.representation()`, and `Session.context()` now accept `Message` objects for `searchQuery`
|
||||
- `page`, `size`, and `reverse` pagination controls on all list methods
|
||||
|
||||
### Changed
|
||||
|
||||
- **Breaking**: `searchQuery` removed from top-level `context()` options — use `representationOptions.searchQuery` instead:
|
||||
```typescript
|
||||
// Before (v2.0.x)
|
||||
await session.context({ searchQuery: "..." });
|
||||
// After (v2.1.0)
|
||||
await session.context({ representationOptions: { searchQuery: "..." } });
|
||||
```
|
||||
- List methods (`peers()`, `sessions()`, `messages()`, `workspaces()`) support both the new options object and the legacy raw-filter form
|
||||
- Representation search options now accept strings and content-like objects, including `Message` instances, while rejecting whitespace-only or invalid runtime inputs
|
||||
- **Breaking**: `peer()` and `session()` now always make a get-or-create API call — no more lazy initialization. If you relied on constructing SDK objects without triggering a network request, note that every `peer()` and `session()` call now hits the API:
|
||||
```typescript
|
||||
// Before (v2.0.x) — no API call
|
||||
const session = honcho.session("my-session");
|
||||
// After (v2.1.0) — makes a get-or-create API call
|
||||
const session = await honcho.session("my-session");
|
||||
```
|
||||
- Response configuration models now tolerate unknown fields from newer servers for forward compatibility
|
||||
- Moved `@types/node` from `dependencies` to `devDependencies`
|
||||
|
||||
### Fixed
|
||||
|
||||
- `uploadFile()` now rejects unsupported top-level binary/object inputs and only validates inputs the serializer can actually upload
|
||||
- `uploadFile()` now serializes message configuration using API field names, matching `addMessages()`
|
||||
- Session fetch methods now refresh cached `createdAt` and `isActive` values alongside metadata and configuration
|
||||
</Update>
|
||||
<Update label="v2.0.2">
|
||||
### Changed
|
||||
|
||||
- Client constructor now rejects unknown options via `.strict()` Zod validation. Previously, misspelled options (e.g., `baseUrl` instead of `baseURL`) were silently ignored, causing the SDK to fall back to defaults. Now a `ZodError` is thrown with the unrecognized key name.
|
||||
- All input schemas now use `.strict()` validation to reject unknown fields.
|
||||
- `FileUploadSchema.configuration` now uses `MessageConfigurationSchema` instead of open record type.
|
||||
|
||||
### Fixed
|
||||
|
||||
- README example used `baseUrl` instead of `baseURL`.
|
||||
</Update>
|
||||
<Update label="v2.0.1">
|
||||
### Added
|
||||
|
||||
- `setPeerCard` method
|
||||
|
||||
### Changed
|
||||
|
||||
- `card` is now `getCard` with `card` kept for backwards compatibility and marked as deprecated
|
||||
</Update>
|
||||
<Update label="v2.0.0">
|
||||
### Added
|
||||
|
||||
- `ConclusionScope` object for CRUD operations on conclusions (renamed from observations)
|
||||
- Representation configuration support
|
||||
|
||||
### Changed
|
||||
|
||||
- Observations renamed to Conclusions across the SDK
|
||||
- Major SDK refactoring and cleanup
|
||||
- Simplified method signatures throughout
|
||||
- Representation endpoints now return `string` instead of old Representation object
|
||||
|
||||
### Fixed
|
||||
|
||||
- Pagination `this` binding issue
|
||||
|
||||
### Removed
|
||||
|
||||
- Representation object
|
||||
- Stainless "core" SDK -- this SDK is now standalone
|
||||
</Update>
|
||||
<Update label="v1.6.0">
|
||||
### Added
|
||||
|
||||
- metadata and configuration fields to Workspace, Peer, Session, and Message objects
|
||||
- Session Clone methods
|
||||
- Peer level get_context method
|
||||
- `ObservationScope` object to perform CRUD operations on observations
|
||||
- Representation object for WorkingRepresentations
|
||||
|
||||
### Changed
|
||||
|
||||
- methods that take IDs, can all optionally take an object of the same type
|
||||
</Update>
|
||||
<Update label="v1.5.0">
|
||||
### Added
|
||||
|
||||
- Delete workspace method
|
||||
|
||||
### Changed
|
||||
|
||||
- message_id of `Summary` model is a string nanoid
|
||||
- Get Context can return Peer Card & Peer Representation
|
||||
</Update>
|
||||
<Update label="v1.4.1">
|
||||
### Added
|
||||
|
||||
- Get Peer Card method
|
||||
- Update Message metadata method
|
||||
- Session level deriver status methods
|
||||
- Delete session message
|
||||
|
||||
### Fixed
|
||||
|
||||
- Dialectic Stream returns Iterators
|
||||
- Type warnings
|
||||
|
||||
### Changed
|
||||
|
||||
- Pagination class to match core implementation
|
||||
</Update>
|
||||
<Update label="v1.4.0">
|
||||
### Added
|
||||
|
||||
- getSummaries API returning structured summaries
|
||||
- Webhook support
|
||||
|
||||
### Changed
|
||||
|
||||
- Messages can take an optional `created_at` value, defaulting to the current
|
||||
time (UTC ISO 8601)
|
||||
</Update>
|
||||
<Update label="v1.2.1">
|
||||
### Added
|
||||
|
||||
- linting via Biome
|
||||
- Adding filter parameter to various endpoints
|
||||
|
||||
### Fixed
|
||||
|
||||
- Order of parameters in `getSessions` endpoint
|
||||
</Update>
|
||||
<Update label="v1.2.0">
|
||||
### Added
|
||||
|
||||
- Get/poll deriver queue status endpoints added to workspace
|
||||
- Added endpoint to upload files as messages
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed peer messages in accordance with Honcho 2.1.0
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated chat endpoint to use singular `query` in accordance with Honcho 2.1.0
|
||||
</Update>
|
||||
<Update label="v1.1.0">
|
||||
### Fixed
|
||||
|
||||
- Create default workspace on Honcho client instantiation
|
||||
- Simplified Honcho client import path
|
||||
</Update>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you encounter issues using the Honcho API or its SDKs:
|
||||
|
||||
1. Open an issue on [GitHub](https://github.com/plastic-labs/honcho/issues)
|
||||
2. Join our [Discord community](http://discord.gg/honcho) for support
|
||||
|
|
@ -0,0 +1,596 @@
|
|||
{
|
||||
"$schema": "https://mintlify.com/docs.json",
|
||||
"theme": "mint",
|
||||
"name": "Honcho",
|
||||
"redirects": [
|
||||
{
|
||||
"source": "/",
|
||||
"destination": "/v3/documentation/introduction/overview"
|
||||
},
|
||||
{
|
||||
"source": "/v3/guides/integrations/claudecode",
|
||||
"destination": "/v3/guides/integrations/claude-code"
|
||||
}
|
||||
],
|
||||
"colors": {
|
||||
"primary": "#66AAFF",
|
||||
"dark": "#151E27",
|
||||
"light": "#86BCF2"
|
||||
},
|
||||
"favicon": "/favicon.svg",
|
||||
"contextual": {
|
||||
"options": ["copy", "view", "chatgpt", "claude"]
|
||||
},
|
||||
"navigation": {
|
||||
"versions": [
|
||||
{
|
||||
"version": "v3.0.7",
|
||||
"api": {
|
||||
"openapi": ["v3/openapi.json"]
|
||||
},
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Introduction",
|
||||
"pages": [
|
||||
"v3/documentation/introduction/overview",
|
||||
"v3/documentation/introduction/quickstart",
|
||||
"v3/documentation/introduction/vibecoding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Core Concepts",
|
||||
"pages": [
|
||||
"v3/documentation/core-concepts/architecture",
|
||||
"v3/documentation/core-concepts/reasoning",
|
||||
"v3/documentation/core-concepts/representation",
|
||||
"v3/documentation/core-concepts/design-patterns"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Features",
|
||||
"pages": [
|
||||
"v3/documentation/features/storing-data",
|
||||
"v3/documentation/features/get-context",
|
||||
"v3/documentation/features/chat",
|
||||
{
|
||||
"group": "Advanced",
|
||||
"pages": [
|
||||
"v3/documentation/features/advanced/overview",
|
||||
"v3/documentation/features/advanced/reasoning-configuration",
|
||||
"v3/documentation/features/advanced/summarizer",
|
||||
"v3/documentation/features/advanced/peer-card",
|
||||
"v3/documentation/features/advanced/representation-scopes",
|
||||
"v3/documentation/features/advanced/dreaming",
|
||||
"v3/documentation/features/advanced/queue-status",
|
||||
"v3/documentation/features/advanced/search",
|
||||
"v3/documentation/features/advanced/using-filters",
|
||||
"v3/documentation/features/advanced/streaming-response",
|
||||
"v3/documentation/features/advanced/file-uploads"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Reference",
|
||||
"pages": [
|
||||
"v3/documentation/reference/platform",
|
||||
"v3/documentation/reference/sdk",
|
||||
"v3/documentation/reference/cli"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Guides",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Overview",
|
||||
"pages": ["v3/guides/overview"]
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
"v3/guides/integrations/claude-code",
|
||||
"v3/guides/integrations/opencode",
|
||||
"v3/guides/integrations/vercel-ai-sdk",
|
||||
"v3/guides/integrations/crewai",
|
||||
"v3/guides/integrations/langgraph",
|
||||
"v3/guides/integrations/mcp",
|
||||
"v3/guides/integrations/n8n",
|
||||
"v3/guides/integrations/openclaw",
|
||||
"v3/guides/integrations/hermes",
|
||||
"v3/guides/integrations/zo-computer",
|
||||
"v3/guides/integrations/paperclip",
|
||||
"v3/guides/integrations/sillytavern"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Tutorials",
|
||||
"pages": [
|
||||
"v3/guides/discord",
|
||||
"v3/guides/granola",
|
||||
"v3/guides/telegram",
|
||||
"v3/guides/integrations/reachy-mini",
|
||||
"v3/guides/gmail"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Community Integrations",
|
||||
"pages": [
|
||||
"v3/guides/community/agent0",
|
||||
"v3/guides/community/pi-honcho-memory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Migrations",
|
||||
"pages": ["v3/guides/migrations/mem0"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Open Source",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Self-Hosting",
|
||||
"pages": [
|
||||
"v3/contributing/self-hosting",
|
||||
"v3/contributing/configuration",
|
||||
"v3/contributing/changing-embeddings",
|
||||
"v3/contributing/troubleshooting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Contributing",
|
||||
"pages": [
|
||||
"v3/contributing/guidelines",
|
||||
"v3/contributing/license"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "API Reference",
|
||||
"groups": [
|
||||
{
|
||||
"group": "API Documentation",
|
||||
"pages": ["v3/api-reference/introduction"]
|
||||
},
|
||||
{
|
||||
"group": "workspaces",
|
||||
"pages": [
|
||||
"v3/api-reference/endpoint/workspaces/get-or-create-workspace",
|
||||
"v3/api-reference/endpoint/workspaces/get-all-workspaces",
|
||||
"v3/api-reference/endpoint/workspaces/update-workspace",
|
||||
"v3/api-reference/endpoint/workspaces/delete-workspace",
|
||||
"v3/api-reference/endpoint/workspaces/search-workspace",
|
||||
"v3/api-reference/endpoint/workspaces/get-queue-status",
|
||||
"v3/api-reference/endpoint/workspaces/schedule-dream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "peers",
|
||||
"pages": [
|
||||
"v3/api-reference/endpoint/peers/get-peers",
|
||||
"v3/api-reference/endpoint/peers/get-or-create-peer",
|
||||
"v3/api-reference/endpoint/peers/update-peer",
|
||||
"v3/api-reference/endpoint/peers/get-sessions-for-peer",
|
||||
"v3/api-reference/endpoint/peers/chat",
|
||||
"v3/api-reference/endpoint/peers/get-representation",
|
||||
"v3/api-reference/endpoint/peers/get-peer-card",
|
||||
"v3/api-reference/endpoint/peers/set-peer-card",
|
||||
"v3/api-reference/endpoint/peers/get-peer-context",
|
||||
"v3/api-reference/endpoint/peers/search-peer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "sessions",
|
||||
"pages": [
|
||||
"v3/api-reference/endpoint/sessions/get-or-create-session",
|
||||
"v3/api-reference/endpoint/sessions/get-sessions",
|
||||
"v3/api-reference/endpoint/sessions/update-session",
|
||||
"v3/api-reference/endpoint/sessions/delete-session",
|
||||
"v3/api-reference/endpoint/sessions/clone-session",
|
||||
"v3/api-reference/endpoint/sessions/get-session-peers",
|
||||
"v3/api-reference/endpoint/sessions/set-session-peers",
|
||||
"v3/api-reference/endpoint/sessions/add-peers-to-session",
|
||||
"v3/api-reference/endpoint/sessions/remove-peers-from-session",
|
||||
"v3/api-reference/endpoint/sessions/get-peer-config",
|
||||
"v3/api-reference/endpoint/sessions/set-peer-config",
|
||||
"v3/api-reference/endpoint/sessions/get-session-context",
|
||||
"v3/api-reference/endpoint/sessions/get-session-summaries",
|
||||
"v3/api-reference/endpoint/sessions/search-session"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "messages",
|
||||
"pages": [
|
||||
"v3/api-reference/endpoint/messages/create-messages-for-session",
|
||||
"v3/api-reference/endpoint/messages/get-messages",
|
||||
"v3/api-reference/endpoint/messages/get-message",
|
||||
"v3/api-reference/endpoint/messages/update-message",
|
||||
"v3/api-reference/endpoint/messages/create-messages-with-file"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "conclusions",
|
||||
"pages": [
|
||||
"v3/api-reference/endpoint/conclusions/create-conclusions",
|
||||
"v3/api-reference/endpoint/conclusions/list-conclusions",
|
||||
"v3/api-reference/endpoint/conclusions/query-conclusions",
|
||||
"v3/api-reference/endpoint/conclusions/delete-conclusion"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "webhooks",
|
||||
"pages": [
|
||||
"v3/api-reference/endpoint/webhooks/list-webhook-endpoints",
|
||||
"v3/api-reference/endpoint/webhooks/get-or-create-webhook-endpoint",
|
||||
"v3/api-reference/endpoint/webhooks/delete-webhook-endpoint",
|
||||
"v3/api-reference/endpoint/webhooks/test-emit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "miscellaneous",
|
||||
"pages": ["v3/api-reference/endpoint/keys/create-key"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Changelog",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Overview",
|
||||
"pages": [
|
||||
"changelog/introduction",
|
||||
"changelog/compatibility-guide"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "v2.5.1",
|
||||
"api": {
|
||||
"openapi": ["v2/openapi.json"]
|
||||
},
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Introduction",
|
||||
"pages": [
|
||||
"v2/documentation/introduction/overview",
|
||||
"v2/documentation/introduction/quickstart",
|
||||
"v2/documentation/introduction/vibecoding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Core Concepts",
|
||||
"pages": [
|
||||
"v2/documentation/core-concepts/architecture",
|
||||
"v2/documentation/core-concepts/features/storing-data",
|
||||
"v2/documentation/core-concepts/features/dialectic-endpoint",
|
||||
"v2/documentation/core-concepts/features/get-context",
|
||||
"v2/documentation/core-concepts/features/search",
|
||||
"v2/documentation/core-concepts/features/working-rep",
|
||||
"v2/documentation/core-concepts/features/streaming-response",
|
||||
"v2/documentation/core-concepts/features/using-filters",
|
||||
"v2/documentation/core-concepts/features/file-uploads",
|
||||
"v2/documentation/core-concepts/features/queue-status",
|
||||
"v2/documentation/core-concepts/features/local-vs-global",
|
||||
"v2/documentation/core-concepts/configuration",
|
||||
"v2/documentation/core-concepts/summarizer",
|
||||
"v2/documentation/core-concepts/glossary"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Reference",
|
||||
"pages": [
|
||||
"v2/documentation/reference/platform",
|
||||
"v2/documentation/reference/sdk"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Spellbooks",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": ["v2/guides/overview"]
|
||||
},
|
||||
{
|
||||
"group": "Migrations",
|
||||
"pages": ["v2/migrations/from-mem0"]
|
||||
},
|
||||
{
|
||||
"group": "Integrations",
|
||||
"pages": [
|
||||
"v2/integrations/crewai",
|
||||
"v2/integrations/langgraph",
|
||||
"v2/integrations/mcp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Application Interfaces",
|
||||
"pages": [
|
||||
"v2/guides/discord",
|
||||
"v2/guides/n8n",
|
||||
"v2/guides/telegram"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "API Reference",
|
||||
"groups": [
|
||||
{
|
||||
"group": "API Documentation",
|
||||
"pages": ["v2/api-reference/introduction"]
|
||||
},
|
||||
{
|
||||
"group": "workspaces",
|
||||
"pages": [
|
||||
"v2/api-reference/endpoint/workspaces/get-or-create-workspace",
|
||||
"v2/api-reference/endpoint/workspaces/get-all-workspaces",
|
||||
"v2/api-reference/endpoint/workspaces/update-workspace",
|
||||
"v2/api-reference/endpoint/workspaces/delete-workspace",
|
||||
"v2/api-reference/endpoint/workspaces/search-workspace",
|
||||
"v2/api-reference/endpoint/workspaces/get-deriver-status",
|
||||
"v2/api-reference/endpoint/workspaces/trigger-dream"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "peers",
|
||||
"pages": [
|
||||
"v2/api-reference/endpoint/peers/get-peers",
|
||||
"v2/api-reference/endpoint/peers/get-or-create-peer",
|
||||
"v2/api-reference/endpoint/peers/update-peer",
|
||||
"v2/api-reference/endpoint/peers/get-sessions-for-peer",
|
||||
"v2/api-reference/endpoint/peers/chat",
|
||||
"v2/api-reference/endpoint/peers/get-working-representation",
|
||||
"v2/api-reference/endpoint/peers/get-peer-card",
|
||||
"v2/api-reference/endpoint/peers/set-peer-card",
|
||||
"v2/api-reference/endpoint/peers/get-peer-context",
|
||||
"v2/api-reference/endpoint/peers/search-peer"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "sessions",
|
||||
"pages": [
|
||||
"v2/api-reference/endpoint/sessions/get-or-create-session",
|
||||
"v2/api-reference/endpoint/sessions/get-sessions",
|
||||
"v2/api-reference/endpoint/sessions/update-session",
|
||||
"v2/api-reference/endpoint/sessions/delete-session",
|
||||
"v2/api-reference/endpoint/sessions/clone-session",
|
||||
"v2/api-reference/endpoint/sessions/get-session-peers",
|
||||
"v2/api-reference/endpoint/sessions/set-session-peers",
|
||||
"v2/api-reference/endpoint/sessions/add-peers-to-session",
|
||||
"v2/api-reference/endpoint/sessions/remove-peers-from-session",
|
||||
"v2/api-reference/endpoint/sessions/get-peer-config",
|
||||
"v2/api-reference/endpoint/sessions/set-peer-config",
|
||||
"v2/api-reference/endpoint/sessions/get-session-context",
|
||||
"v2/api-reference/endpoint/sessions/get-session-summaries",
|
||||
"v2/api-reference/endpoint/sessions/search-session"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "messages",
|
||||
"pages": [
|
||||
"v2/api-reference/endpoint/messages/create-messages-for-session",
|
||||
"v2/api-reference/endpoint/messages/get-messages",
|
||||
"v2/api-reference/endpoint/messages/get-message",
|
||||
"v2/api-reference/endpoint/messages/update-message",
|
||||
"v2/api-reference/endpoint/messages/create-messages-with-file"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "observations",
|
||||
"pages": [
|
||||
"v2/api-reference/endpoint/observations/create-observations",
|
||||
"v2/api-reference/endpoint/observations/list-observations",
|
||||
"v2/api-reference/endpoint/observations/query-observations",
|
||||
"v2/api-reference/endpoint/observations/delete-observation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "webhooks",
|
||||
"pages": [
|
||||
"v2/api-reference/endpoint/webhooks/list-webhook-endpoints",
|
||||
"v2/api-reference/endpoint/webhooks/get-or-create-webhook-endpoint",
|
||||
"v2/api-reference/endpoint/webhooks/delete-webhook-endpoint",
|
||||
"v2/api-reference/endpoint/webhooks/test-emit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "miscellaneous",
|
||||
"pages": [
|
||||
"v2/api-reference/endpoint/keys/create-key",
|
||||
"v2/api-reference/endpoint/metrics"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Contributing",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Contributing",
|
||||
"pages": [
|
||||
"v2/contributing/guidelines",
|
||||
"v2/contributing/self-hosting",
|
||||
"v2/contributing/configuration",
|
||||
"v2/contributing/license"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"version": "v1.1.0",
|
||||
"api": {
|
||||
"openapi": ["openapi.json"]
|
||||
},
|
||||
"tabs": [
|
||||
{
|
||||
"tab": "Documentation",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Get Started",
|
||||
"pages": [
|
||||
"v1/getting-started/introduction",
|
||||
"v1/getting-started/quickstart",
|
||||
"v1/getting-started/architecture"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Contributing",
|
||||
"pages": [
|
||||
"v1/contributing/guidelines",
|
||||
"v1/contributing/self-hosting",
|
||||
"v1/contributing/deploying",
|
||||
"v1/contributing/license"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "Spellbooks and Tutorials",
|
||||
"groups": [
|
||||
{
|
||||
"group": "Getting Started",
|
||||
"pages": ["v1/guides/overview", "v1/guides/streaming-response"]
|
||||
},
|
||||
{
|
||||
"group": "Application Interfaces",
|
||||
"pages": ["v1/guides/discord", "v1/guides/honcho-mcp"]
|
||||
},
|
||||
{
|
||||
"group": "Personal Memory",
|
||||
"pages": ["v1/guides/dialectic-endpoint"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tab": "API Reference",
|
||||
"groups": [
|
||||
{
|
||||
"group": "API Documentation",
|
||||
"pages": ["v1/api-reference/introduction"]
|
||||
},
|
||||
{
|
||||
"group": "apps",
|
||||
"pages": [
|
||||
"v1/api-reference/endpoint/apps/get-app",
|
||||
"v1/api-reference/endpoint/apps/get-all-apps",
|
||||
"v1/api-reference/endpoint/apps/update-app",
|
||||
"v1/api-reference/endpoint/apps/get-app-by-name",
|
||||
"v1/api-reference/endpoint/apps/create-app",
|
||||
"v1/api-reference/endpoint/apps/get-or-create-app"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "sessions",
|
||||
"pages": [
|
||||
"v1/api-reference/endpoint/sessions/get-sessions",
|
||||
"v1/api-reference/endpoint/sessions/create-session",
|
||||
"v1/api-reference/endpoint/sessions/get-session",
|
||||
"v1/api-reference/endpoint/sessions/update-session",
|
||||
"v1/api-reference/endpoint/sessions/delete-session",
|
||||
"v1/api-reference/endpoint/sessions/chat",
|
||||
"v1/api-reference/endpoint/sessions/clone-session"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "users",
|
||||
"pages": [
|
||||
"v1/api-reference/endpoint/users/get-users",
|
||||
"v1/api-reference/endpoint/users/create-user",
|
||||
"v1/api-reference/endpoint/users/get-user-by-name",
|
||||
"v1/api-reference/endpoint/users/get-user",
|
||||
"v1/api-reference/endpoint/users/get-or-create-user",
|
||||
"v1/api-reference/endpoint/users/update-user"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "messages",
|
||||
"pages": [
|
||||
"v1/api-reference/endpoint/messages/get-messages",
|
||||
"v1/api-reference/endpoint/messages/create-message-for-session",
|
||||
"v1/api-reference/endpoint/messages/create-batch-messages-for-session",
|
||||
"v1/api-reference/endpoint/messages/get-message",
|
||||
"v1/api-reference/endpoint/messages/update-message"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "keys",
|
||||
"pages": ["v1/api-reference/endpoint/keys/create-key"]
|
||||
},
|
||||
{
|
||||
"group": "metamessages",
|
||||
"pages": [
|
||||
"v1/api-reference/endpoint/metamessages/create-metamessage",
|
||||
"v1/api-reference/endpoint/metamessages/get-metamessages",
|
||||
"v1/api-reference/endpoint/metamessages/get-metamessage",
|
||||
"v1/api-reference/endpoint/metamessages/update-metamessage"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "collections",
|
||||
"pages": [
|
||||
"v1/api-reference/endpoint/collections/get-collections",
|
||||
"v1/api-reference/endpoint/collections/create-collection",
|
||||
"v1/api-reference/endpoint/collections/get-collection-by-name",
|
||||
"v1/api-reference/endpoint/collections/get-collection",
|
||||
"v1/api-reference/endpoint/collections/update-collection",
|
||||
"v1/api-reference/endpoint/collections/delete-collection"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "documents",
|
||||
"pages": [
|
||||
"v1/api-reference/endpoint/documents/get-documents",
|
||||
"v1/api-reference/endpoint/documents/create-document",
|
||||
"v1/api-reference/endpoint/documents/get-document",
|
||||
"v1/api-reference/endpoint/documents/update-document",
|
||||
"v1/api-reference/endpoint/documents/delete-document",
|
||||
"v1/api-reference/endpoint/documents/query-documents"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"logo": {
|
||||
"light": "/logo/honcho-dark.svg",
|
||||
"dark": "/logo/honcho-light.svg"
|
||||
},
|
||||
"navbar": {
|
||||
"primary": {
|
||||
"type": "github",
|
||||
"href": "https://github.com/plastic-labs/honcho"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"twitter": "https://x.com/honchodotdev",
|
||||
"github": "https://github.com/plastic-labs/honcho",
|
||||
"discord": "https://discord.gg/honcho",
|
||||
"linkedin": "https://www.linkedin.com/company/plasticlabs",
|
||||
"youtube": "https://www.youtube.com/@plasticlabs"
|
||||
}
|
||||
},
|
||||
"integrations": {
|
||||
"posthog": {
|
||||
"apiKey": "phc_1yrzzcgywqXGcerkkI4g7C0YfyPMcAKNOOvGcjTCiUk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<svg width="650" height="650" viewBox="0 0 650 650" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="650" height="650" fill="#101447"/>
|
||||
<g filter="url(#filter0_dd_11_8)">
|
||||
<path d="M325.56 80C238.59 80 157.54 126.59 113.62 201.69C94.1801 213.51 81.0001 221.78 80.7301 221.92C42.8201 242.58 23.9001 262.29 14.6201 277.9C7.41011 278.03 1.1701 283.37 0.150101 290.73C-1.0099 298.88 4.6801 306.44 12.8401 307.58C21.0101 308.74 28.5501 303.05 29.7101 294.88C30.3101 290.68 29.0701 286.63 26.6301 283.55C40.3601 261.4 69.3201 243.18 87.0601 233.52C90.1101 231.86 261.89 127.81 274.74 130.38C276.89 130.81 280.03 131.97 282.5 137.08C289.3 151.18 226.78 192.95 214.03 200.59C201.82 207.9 191.06 215.71 181.75 224.01C162.02 241.59 148.85 261.32 142.45 282.97C138.6 296.02 127.63 333.6 101.02 342.94C99.1301 338.74 95.3301 335.46 90.4701 334.46C82.4001 332.77 74.4901 337.95 72.8201 346.02C71.2201 353.66 75.7801 361.14 83.1201 363.34C101.49 482.47 203.51 570.77 325.56 570.77C460.86 570.77 570.95 460.69 570.95 325.38C570.95 190.07 460.87 80 325.56 80ZM187.7 236.51C187.12 244.15 185.37 252.9 181.07 258.61C176.54 264.61 167.95 269.71 160.23 273.31C166.33 260.21 175.51 247.92 187.7 236.51ZM325.56 557.54C210.02 557.54 113.46 473.91 96.1601 361.12C97.9201 359.84 99.4201 358.2 100.48 356.25C122.53 348.38 139.19 337.49 154.08 290.08C162.07 287.28 182.01 279.35 191.64 266.56C201.32 253.69 201.46 233.61 201.05 225.28C207.1 220.67 213.7 216.22 220.83 211.95C261.59 187.55 305.55 154.35 294.41 131.32C290.74 123.73 284.83 118.92 277.34 117.43C263.23 114.63 192.72 154.44 140.15 185.73C183.7 128.08 252.41 93.24 325.56 93.24C453.57 93.24 557.71 197.39 557.71 325.39C557.71 453.39 453.57 557.55 325.56 557.55V557.54Z" fill="white"/>
|
||||
<path d="M247.34 325.38C261.296 325.38 272.61 307.843 272.61 286.21C272.61 264.577 261.296 247.04 247.34 247.04C233.384 247.04 222.07 264.577 222.07 286.21C222.07 307.843 233.384 325.38 247.34 325.38Z" fill="white"/>
|
||||
<path d="M403.78 325.38C417.736 325.38 429.05 307.843 429.05 286.21C429.05 264.577 417.736 247.04 403.78 247.04C389.824 247.04 378.51 264.577 378.51 286.21C378.51 307.843 389.824 325.38 403.78 325.38Z" fill="white"/>
|
||||
<path d="M386.18 406.91H264.94C258.48 406.91 253.25 412.14 253.25 418.6C253.25 425.06 258.48 430.29 264.94 430.29H386.18C392.64 430.29 397.87 425.06 397.87 418.6C397.87 412.14 392.64 406.91 386.18 406.91Z" fill="white"/>
|
||||
<path d="M93.9401 333.61L93.9701 333.58L93.9401 333.6V333.62V333.61Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_dd_11_8" x="0" y="80" width="594.95" height="514.77" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="20" dy="20"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 0.352941 0 0 0 0 0.494118 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_11_8"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset dx="10" dy="10"/>
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
<feComposite in2="hardAlpha" operator="out"/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0.0352941 0 0 0 0 0.996078 0 0 0 0 0.972549 0 0 0 1 0"/>
|
||||
<feBlend mode="normal" in2="effect1_dropShadow_11_8" result="effect2_dropShadow_11_8"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_11_8" result="shape"/>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 265 KiB |
|
After Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 175 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 129 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 130 KiB |
|
After Width: | Height: | Size: 194 KiB |
|
After Width: | Height: | Size: 126 KiB |
|
After Width: | Height: | Size: 152 KiB |
|
After Width: | Height: | Size: 100 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 665 KiB |
|
After Width: | Height: | Size: 151 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 157 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
|
@ -0,0 +1,161 @@
|
|||
<svg width="700" height="320" viewBox="0 0 700 320" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2862_30)">
|
||||
<rect width="700" height="320" rx="16" fill="url(#paint0_linear_2862_30)"/>
|
||||
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="white"/>
|
||||
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="url(#paint1_radial_2862_30)"/>
|
||||
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="black" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="url(#paint2_linear_2862_30)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M311.72 247.034C283.108 246.887 258.409 231.208 246.538 201.531C234.656 171.825 238.271 134.702 253.583 101.377C282.195 101.524 306.894 117.203 318.765 146.88C330.647 176.586 327.031 213.709 311.72 247.034Z" stroke="url(#paint3_linear_2862_30)" stroke-opacity="0.05" stroke-width="0.530516"/>
|
||||
<path d="M305.839 247.174C343.92 237.419 377.154 210.619 393.585 171.64C410.017 132.661 405.98 90.1988 386.347 56.1934C348.266 65.9477 315.032 92.7486 298.601 131.728C282.169 170.706 286.206 213.168 305.839 247.174Z" fill="white"/>
|
||||
<path d="M305.839 247.174C343.92 237.419 377.154 210.619 393.585 171.64C410.017 132.661 405.98 90.1988 386.347 56.1934C348.266 65.9477 315.032 92.7486 298.601 131.728C282.169 170.706 286.206 213.168 305.839 247.174Z" fill="url(#paint4_radial_2862_30)"/>
|
||||
<path d="M393.341 171.537C376.971 210.369 343.89 237.091 305.969 246.867C286.462 212.959 282.476 170.663 298.845 131.831C315.215 92.9978 348.295 66.2765 386.217 56.5004C405.724 90.4077 409.71 132.704 393.341 171.537Z" stroke="url(#paint5_linear_2862_30)" stroke-opacity="0.05" stroke-width="0.530516"/>
|
||||
<path d="M305.686 246.995C329.749 266.114 361.965 272.832 393.67 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.045 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="white"/>
|
||||
<path d="M305.686 246.995C329.749 266.114 361.965 272.832 393.67 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.045 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="url(#paint6_radial_2862_30)"/>
|
||||
<path d="M305.686 246.995C329.749 266.114 361.965 272.832 393.67 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.045 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="black" fill-opacity="0.2" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M305.686 246.995C329.749 266.114 361.965 272.832 393.67 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.045 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="url(#paint7_linear_2862_30)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M393.586 261.878C362.034 272.529 329.98 265.88 306.002 246.907C317.534 215.919 341.57 190.327 373.13 179.673C404.681 169.023 436.735 175.671 460.714 194.644C449.181 225.632 425.145 251.224 393.586 261.878Z" stroke="url(#paint8_linear_2862_30)" stroke-opacity="0.05" stroke-width="0.530516"/>
|
||||
<g opacity="0.8" filter="url(#filter0_f_2862_30)">
|
||||
<circle cx="660" cy="-60" r="160" fill="#18E244" fill-opacity="0.4"/>
|
||||
</g>
|
||||
<g opacity="0.8" filter="url(#filter1_f_2862_30)">
|
||||
<circle cx="20" cy="213" r="160" fill="#18CAE2" fill-opacity="0.33"/>
|
||||
</g>
|
||||
<g opacity="0.8" filter="url(#filter2_f_2862_30)">
|
||||
<circle cx="660" cy="480" r="160" fill="#18E2B2" fill-opacity="0.52"/>
|
||||
</g>
|
||||
<g opacity="0.8" filter="url(#filter3_f_2862_30)">
|
||||
<circle cx="20" cy="413" r="160" fill="#4018E2" fill-opacity="0.22"/>
|
||||
</g>
|
||||
<path opacity="0.2" d="M0 50H700" stroke="url(#paint9_radial_2862_30)" stroke-dasharray="4 4"/>
|
||||
<path opacity="0.1" d="M0 82H700" stroke="url(#paint10_radial_2862_30)" stroke-dasharray="4 4"/>
|
||||
<path opacity="0.2" d="M239 0L239 320" stroke="url(#paint11_radial_2862_30)" stroke-dasharray="4 4"/>
|
||||
<path opacity="0.1" d="M271 0L271 320" stroke="url(#paint12_radial_2862_30)" stroke-dasharray="4 4"/>
|
||||
<path opacity="0.2" d="M461 0L461 320" stroke="url(#paint13_radial_2862_30)" stroke-dasharray="4 4"/>
|
||||
<path opacity="0.1" d="M429 0L429 320" stroke="url(#paint14_radial_2862_30)" stroke-dasharray="4 4"/>
|
||||
<path opacity="0.2" d="M0 271H700" stroke="url(#paint15_radial_2862_30)" stroke-dasharray="4 4"/>
|
||||
<path opacity="0.1" d="M0 239H700" stroke="url(#paint16_radial_2862_30)" stroke-dasharray="4 4"/>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M0 160H700" stroke="url(#paint17_linear_2862_30)"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.2">
|
||||
<path d="M511 -1L189 321" stroke="url(#paint18_linear_2862_30)"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.2">
|
||||
<path d="M511 321L189 -1" stroke="url(#paint19_linear_2862_30)"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<circle cx="350" cy="160" r="111" stroke="white"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<circle cx="350" cy="160" r="79" stroke="white"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_2862_30" x="260" y="-460" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_30"/>
|
||||
</filter>
|
||||
<filter id="filter1_f_2862_30" x="-380" y="-187" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_30"/>
|
||||
</filter>
|
||||
<filter id="filter2_f_2862_30" x="260" y="80" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_30"/>
|
||||
</filter>
|
||||
<filter id="filter3_f_2862_30" x="-380" y="13" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_30"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_2862_30" x1="1.04308e-05" y1="320" x2="710.784" y2="26.0793" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#18E299" stop-opacity="0.09"/>
|
||||
<stop offset="0.729167" stop-color="#0D9373" stop-opacity="0.08"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint1_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(208.697 189.703) rotate(-10.029) scale(169.097 167.466)">
|
||||
<stop stop-color="#00B0BB"/>
|
||||
<stop offset="1" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint2_linear_2862_30" x1="306.587" y1="93.5598" x2="252.341" y2="224.228" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#18E299"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_2862_30" x1="311.84" y1="123.717" x2="253.579" y2="224.761" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint4_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(313.407 243.64) rotate(-75.7542) scale(203.632 223.902)">
|
||||
<stop stop-color="#00BBBB"/>
|
||||
<stop offset="0.712616" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint5_linear_2862_30" x1="308.586" y1="102.284" x2="383.487" y2="201.169" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint6_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(311.446 249.925) rotate(-20.3524) scale(174.776 163.096)">
|
||||
<stop stop-color="#00B0BB"/>
|
||||
<stop offset="1" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint7_linear_2862_30" x1="395.842" y1="169.781" x2="332.121" y2="263.82" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00B1BC"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_2862_30" x1="395.842" y1="169.781" x2="370.99" y2="271.799" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint9_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(350 50) scale(398.125 182)">
|
||||
<stop offset="0.348958" stop-color="#84FFD3"/>
|
||||
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint10_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(350 82) scale(398.125 182)">
|
||||
<stop offset="0.348958" stop-color="#84FFD3"/>
|
||||
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint11_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(239 160) rotate(90) scale(182 182)">
|
||||
<stop offset="0.348958" stop-color="#84FFD3"/>
|
||||
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint12_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(271 160) rotate(90) scale(182 182)">
|
||||
<stop offset="0.348958" stop-color="#84FFD3"/>
|
||||
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint13_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(461 160) rotate(90) scale(182 182)">
|
||||
<stop offset="0.348958" stop-color="#84FFD3"/>
|
||||
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint14_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(429 160) rotate(90) scale(182 182)">
|
||||
<stop offset="0.348958" stop-color="#84FFD3"/>
|
||||
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint15_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(350 271) scale(398.125 182)">
|
||||
<stop offset="0.348958" stop-color="#84FFD3"/>
|
||||
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="paint16_radial_2862_30" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(350 239) scale(398.125 182)">
|
||||
<stop offset="0.348958" stop-color="#84FFD3"/>
|
||||
<stop offset="0.880208" stop-color="#18E299" stop-opacity="0"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint17_linear_2862_30" x1="0" y1="160" x2="700" y2="160" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.1"/>
|
||||
<stop offset="0.5" stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint18_linear_2862_30" x1="511" y1="-1" x2="189" y2="321" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.1"/>
|
||||
<stop offset="0.5" stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint19_linear_2862_30" x1="511" y1="321" x2="189" y2="-0.999997" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white" stop-opacity="0.1"/>
|
||||
<stop offset="0.5" stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_2862_30">
|
||||
<rect width="700" height="320" rx="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -0,0 +1,155 @@
|
|||
<svg width="700" height="320" viewBox="0 0 700 320" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2862_278)">
|
||||
<rect width="700" height="320" rx="16" fill="url(#paint0_linear_2862_278)"/>
|
||||
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="white"/>
|
||||
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="url(#paint1_radial_2862_278)"/>
|
||||
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="black" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M311.889 247.3C283.097 247.215 258.226 231.466 246.292 201.629C234.357 171.793 238.02 134.523 253.414 101.112C282.206 101.197 307.077 116.945 319.011 146.782C330.946 176.619 327.283 213.888 311.889 247.3Z" fill="url(#paint2_linear_2862_278)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M311.72 247.034C283.108 246.887 258.409 231.208 246.538 201.531C234.656 171.825 238.271 134.702 253.583 101.377C282.195 101.524 306.894 117.203 318.765 146.88C330.647 176.586 327.031 213.709 311.72 247.034Z" stroke="url(#paint3_linear_2862_278)" stroke-opacity="0.05" stroke-width="0.530516"/>
|
||||
<path d="M305.839 247.174C343.92 237.419 377.154 210.619 393.585 171.64C410.017 132.661 405.98 90.1988 386.347 56.1934C348.266 65.9477 315.032 92.7486 298.601 131.728C282.169 170.706 286.206 213.168 305.839 247.174Z" fill="white"/>
|
||||
<path d="M305.839 247.174C343.92 237.419 377.154 210.619 393.585 171.64C410.017 132.661 405.98 90.1988 386.347 56.1934C348.266 65.9477 315.032 92.7486 298.601 131.728C282.169 170.706 286.206 213.168 305.839 247.174Z" fill="url(#paint4_radial_2862_278)"/>
|
||||
<path d="M393.341 171.537C376.971 210.369 343.89 237.091 305.969 246.867C286.462 212.959 282.476 170.663 298.845 131.831C315.215 92.9978 348.295 66.2765 386.217 56.5004C405.724 90.4077 409.71 132.704 393.341 171.537Z" stroke="url(#paint5_linear_2862_278)" stroke-opacity="0.05" stroke-width="0.530516"/>
|
||||
<path d="M305.686 246.995C329.75 266.114 361.965 272.832 393.671 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.046 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="white"/>
|
||||
<path d="M305.686 246.995C329.75 266.114 361.965 272.832 393.671 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.046 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="url(#paint6_radial_2862_278)"/>
|
||||
<path d="M305.686 246.995C329.75 266.114 361.965 272.832 393.671 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.046 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="black" fill-opacity="0.2" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M305.686 246.995C329.75 266.114 361.965 272.832 393.671 262.129C425.376 251.426 449.499 225.691 461.03 194.556C436.967 175.437 404.751 168.719 373.046 179.422C341.34 190.125 317.217 215.86 305.686 246.995Z" fill="url(#paint7_linear_2862_278)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M393.586 261.878C362.035 272.529 329.981 265.88 306.002 246.907C317.535 215.919 341.571 190.327 373.13 179.673C404.682 169.023 436.736 175.671 460.715 194.644C449.182 225.632 425.146 251.224 393.586 261.878Z" stroke="url(#paint8_linear_2862_278)" stroke-opacity="0.05" stroke-width="0.530516"/>
|
||||
<g opacity="0.8" filter="url(#filter0_f_2862_278)">
|
||||
<circle cx="660" cy="-60" r="160" fill="#18E299" fill-opacity="0.4"/>
|
||||
</g>
|
||||
<g opacity="0.8" filter="url(#filter1_f_2862_278)">
|
||||
<circle cx="20" cy="213" r="160" fill="#18E299" fill-opacity="0.33"/>
|
||||
</g>
|
||||
<g opacity="0.8" filter="url(#filter2_f_2862_278)">
|
||||
<circle cx="660" cy="480" r="160" fill="#18E299" fill-opacity="0.52"/>
|
||||
</g>
|
||||
<g opacity="0.8" filter="url(#filter3_f_2862_278)">
|
||||
<circle cx="20" cy="413" r="160" fill="#18E299" fill-opacity="0.22"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M0 50H700" stroke="black" stroke-dasharray="4 4"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M0 82H700" stroke="black" stroke-dasharray="4 4"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M239 0L239 320" stroke="black" stroke-dasharray="4 4"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M271 0L271 320" stroke="black" stroke-dasharray="4 4"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M461 0L461 320" stroke="black" stroke-dasharray="4 4"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M350 0L350 320" stroke="url(#paint9_linear_2862_278)"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M429 0L429 320" stroke="black" stroke-dasharray="4 4"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M0 271H700" stroke="black" stroke-dasharray="4 4"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M0 239H700" stroke="black" stroke-dasharray="4 4"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M0 160H700" stroke="url(#paint10_linear_2862_278)"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M511 -1L189 321" stroke="url(#paint11_linear_2862_278)"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.1">
|
||||
<path d="M511 321L189 -1" stroke="url(#paint12_linear_2862_278)"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.05">
|
||||
<circle cx="350" cy="160" r="111" stroke="black"/>
|
||||
</g>
|
||||
<g style="mix-blend-mode:overlay" opacity="0.05">
|
||||
<circle cx="350" cy="160" r="79" stroke="black"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_f_2862_278" x="260" y="-460" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_278"/>
|
||||
</filter>
|
||||
<filter id="filter1_f_2862_278" x="-380" y="-187" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_278"/>
|
||||
</filter>
|
||||
<filter id="filter2_f_2862_278" x="260" y="80" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_278"/>
|
||||
</filter>
|
||||
<filter id="filter3_f_2862_278" x="-380" y="13" width="800" height="800" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feGaussianBlur stdDeviation="120" result="effect1_foregroundBlur_2862_278"/>
|
||||
</filter>
|
||||
<linearGradient id="paint0_linear_2862_278" x1="1.04308e-05" y1="320" x2="710.784" y2="26.0793" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#18E299" stop-opacity="0.09"/>
|
||||
<stop offset="0.729167" stop-color="#0D9373" stop-opacity="0.08"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint1_radial_2862_278" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(208.697 189.703) rotate(-10.029) scale(169.097 167.466)">
|
||||
<stop stop-color="#00B0BB"/>
|
||||
<stop offset="1" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint2_linear_2862_278" x1="306.587" y1="93.5598" x2="252.341" y2="224.228" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#18E299"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_2862_278" x1="311.84" y1="123.717" x2="253.579" y2="224.761" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint4_radial_2862_278" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(313.407 243.64) rotate(-75.7542) scale(203.632 223.902)">
|
||||
<stop stop-color="#00BBBB"/>
|
||||
<stop offset="0.712616" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint5_linear_2862_278" x1="308.586" y1="102.284" x2="383.487" y2="201.169" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint6_radial_2862_278" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(311.447 249.925) rotate(-20.3524) scale(174.776 163.096)">
|
||||
<stop stop-color="#00B0BB"/>
|
||||
<stop offset="1" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint7_linear_2862_278" x1="395.843" y1="169.781" x2="332.121" y2="263.82" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00B1BC"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_2862_278" x1="395.843" y1="169.781" x2="370.991" y2="271.799" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_2862_278" x1="350" y1="0" x2="350" y2="320" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity="0"/>
|
||||
<stop offset="0.0001" stop-opacity="0.3"/>
|
||||
<stop offset="0.333333"/>
|
||||
<stop offset="0.666667"/>
|
||||
<stop offset="1" stop-opacity="0.3"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint10_linear_2862_278" x1="0" y1="160" x2="700" y2="160" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity="0.1"/>
|
||||
<stop offset="0.5"/>
|
||||
<stop offset="1" stop-opacity="0.1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint11_linear_2862_278" x1="511" y1="-1" x2="189" y2="321" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity="0.1"/>
|
||||
<stop offset="0.5"/>
|
||||
<stop offset="1" stop-opacity="0.1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint12_linear_2862_278" x1="511" y1="321" x2="189" y2="-0.999997" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-opacity="0.1"/>
|
||||
<stop offset="0.5"/>
|
||||
<stop offset="1" stop-opacity="0.1"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_2862_278">
|
||||
<rect width="700" height="320" rx="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 244 KiB |
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 414 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 418 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 323 KiB |
|
|
@ -0,0 +1,55 @@
|
|||
<svg width="160" height="24" viewBox="0 0 160 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61504C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5913 7.95343 21.1394Z" fill="white"/>
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61504C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5913 7.95343 21.1394Z" fill="url(#paint0_radial_115_109)"/>
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61504C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5913 7.95343 21.1394Z" fill="black" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61504C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5913 7.95343 21.1394Z" fill="url(#paint1_linear_115_109)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M7.9354 21.1112C4.89702 21.0957 2.27411 19.4306 1.01347 16.279C-0.248375 13.1244 0.135612 9.18218 1.76165 5.64327C4.80004 5.65882 7.42295 7.32385 8.68359 10.4755C9.94543 13.63 9.56144 17.5723 7.9354 21.1112Z" stroke="url(#paint2_linear_115_109)" stroke-opacity="0.05" stroke-width="0.056338"/>
|
||||
<path d="M7.31038 21.2574C11.3543 20.2215 14.8836 17.3754 16.6285 13.2361C18.3735 9.09671 17.9448 4.58749 15.8598 0.976291C11.8159 2.01214 8.2866 4.85826 6.54167 8.99762C4.79674 13.137 5.2254 17.6462 7.31038 21.2574Z" fill="white"/>
|
||||
<path d="M7.31038 21.2574C11.3543 20.2215 14.8836 17.3754 16.6285 13.2361C18.3735 9.09671 17.9448 4.58749 15.8598 0.976291C11.8159 2.01214 8.2866 4.85826 6.54167 8.99762C4.79674 13.137 5.2254 17.6462 7.31038 21.2574Z" fill="url(#paint3_radial_115_109)"/>
|
||||
<path d="M16.6025 13.2251C14.8642 17.349 11.3512 20.1866 7.32411 21.2248C5.25257 17.624 4.82926 13.1324 6.56764 9.00855C8.30603 4.88472 11.819 2.04706 15.8461 1.00889C17.9176 4.60967 18.3409 9.10131 16.6025 13.2251Z" stroke="url(#paint4_linear_115_109)" stroke-opacity="0.05" stroke-width="0.056338"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="white"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="url(#paint5_radial_115_109)"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="black" fill-opacity="0.2" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="url(#paint6_linear_115_109)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M16.5682 22.7874C13.2176 23.9184 9.81361 23.2124 7.2672 21.1975C8.49194 17.9068 11.0444 15.189 14.3959 14.0577C17.7465 12.9266 21.1504 13.6326 23.6968 15.6476C22.4721 18.9383 19.9196 21.656 16.5682 22.7874Z" stroke="url(#paint7_linear_115_109)" stroke-opacity="0.05" stroke-width="0.056338"/>
|
||||
<path d="M34.2124 19V5.4H39.4924L41.6924 12.2L42.3724 14.74L43.0524 12.2L45.2524 5.4H50.4124V19H46.3324L46.5924 9.98L45.5324 13.68L43.7924 19H40.8324L39.0524 13.6L38.0324 10.02L38.2924 19H34.2124ZM52.4155 7.3V4.6H56.2955V7.3H52.4155ZM52.4155 19V8.14H56.2955V19H52.4155ZM58.1038 19V8.14H61.9838V9.58C62.6638 8.34 63.7438 7.76 65.0038 7.76C66.9638 7.76 68.6238 8.98 68.6238 11.78V19H64.7438V12.56C64.7438 11.34 64.3038 10.86 63.4838 10.86C62.6038 10.86 61.9838 11.58 61.9838 12.88V19H58.1038ZM70.9327 15.22V11.06H69.7327V8.14H70.9327V5.62H74.8127V8.14H76.9327V11.06H74.8127V14.6C74.8127 15.5 75.0327 16.06 76.2127 16.06H76.9327V19C76.4927 19.2 75.6727 19.38 74.6527 19.38C72.1527 19.38 70.9327 17.88 70.9327 15.22Z" fill="url(#paint8_radial_115_109)"/>
|
||||
<path d="M87.232 10.519C87.232 13.687 94.1125 11.2285 94.1125 15.832C94.1125 17.9935 92.3635 19.198 89.971 19.198C87.562 19.198 85.912 18.0925 85.417 15.832H87.001C87.364 17.1685 88.3705 17.8945 89.9875 17.8945C91.6705 17.8945 92.5615 17.152 92.5615 16.03C92.5615 12.598 85.681 15.1555 85.681 10.618C85.681 9.001 87.034 7.582 89.509 7.582C91.6705 7.582 93.403 8.6215 93.8155 11.014H92.215C91.8685 9.529 90.9115 8.8855 89.476 8.8855C88.057 8.8855 87.232 9.529 87.232 10.519ZM96.2499 16.4755V11.3935H95.0289V10.2385H96.2499V8.2255H97.7019V10.2385H99.6324V11.3935H97.7019V16.4755C97.7019 17.5315 98.0154 18.0265 99.3024 18.0265H99.5994V19.066C99.4344 19.1485 99.0714 19.198 98.6589 19.198C97.0254 19.198 96.2499 18.3235 96.2499 16.4755ZM102.516 13.093H101.064C101.345 11.1625 102.615 10.024 104.76 10.024C107.103 10.024 108.242 11.3935 108.242 13.4395V16.888C108.242 17.8945 108.324 18.5215 108.555 19H107.021C106.856 18.6535 106.806 18.142 106.79 17.614C106.047 18.7195 104.859 19.198 103.803 19.198C101.988 19.198 100.767 18.3565 100.767 16.69C100.767 15.4855 101.427 14.611 102.714 14.182C103.902 13.786 105.107 13.687 106.79 13.6705V13.4725C106.79 12.0535 106.13 11.278 104.628 11.278C103.374 11.278 102.698 11.971 102.516 13.093ZM102.252 16.657C102.252 17.4655 102.929 17.944 103.952 17.944C105.569 17.944 106.79 16.6735 106.79 15.172V14.7595C103.061 14.7925 102.252 15.5845 102.252 16.657ZM110.787 19V10.2385H112.239V11.5915C112.833 10.519 113.774 10.024 114.83 10.024C115.176 10.024 115.49 10.1065 115.655 10.2385V11.542C115.407 11.4595 115.094 11.4265 114.747 11.4265C112.998 11.4265 112.239 12.5155 112.239 14.0995V19H110.787ZM117.305 16.4755V11.3935H116.084V10.2385H117.305V8.2255H118.757V10.2385H120.688V11.3935H118.757V16.4755C118.757 17.5315 119.071 18.0265 120.358 18.0265H120.655V19.066C120.49 19.1485 120.127 19.198 119.714 19.198C118.081 19.198 117.305 18.3235 117.305 16.4755ZM129.809 16.1455C129.33 18.1915 127.862 19.198 125.865 19.198C123.324 19.198 121.79 17.482 121.79 14.6275C121.79 11.6575 123.324 10.024 125.783 10.024C128.258 10.024 129.743 11.7235 129.743 14.512V14.875H123.275C123.357 16.8385 124.281 17.944 125.865 17.944C127.103 17.944 127.977 17.35 128.291 16.1455H129.809ZM125.783 11.278C124.38 11.278 123.539 12.1525 123.324 13.786H128.225C128.027 12.169 127.152 11.278 125.783 11.278ZM131.843 19V10.2385H133.295V11.5915C133.889 10.519 134.829 10.024 135.885 10.024C136.232 10.024 136.545 10.1065 136.71 10.2385V11.542C136.463 11.4595 136.149 11.4265 135.803 11.4265C134.054 11.4265 133.295 12.5155 133.295 14.0995V19H131.843ZM141.763 19V7.78H143.281V13.192L148.413 7.78H150.327L145.047 13.291L150.459 19H148.413L143.281 13.621V19H141.763ZM152.06 9.067V7.12H153.512V9.067H152.06ZM152.06 19V10.2385H153.512V19H152.06ZM156.178 16.4755V11.3935H154.957V10.2385H156.178V8.2255H157.63V10.2385H159.56V11.3935H157.63V16.4755C157.63 17.5315 157.943 18.0265 159.23 18.0265H159.527V19.066C159.362 19.1485 158.999 19.198 158.587 19.198C156.953 19.198 156.178 18.3235 156.178 16.4755Z" fill="white" fill-opacity="0.55"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_115_109" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-3.00503 15.023) rotate(-10.029) scale(17.9572 17.784)">
|
||||
<stop stop-color="#00B0BB"/>
|
||||
<stop offset="1" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint1_linear_115_109" x1="7.39036" y1="4.81308" x2="1.62975" y2="18.6894" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#18E299"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_115_109" x1="7.94816" y1="8.01562" x2="1.7612" y2="18.746" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint3_radial_115_109" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(8.11404 20.8822) rotate(-75.7542) scale(21.6246 23.7772)">
|
||||
<stop stop-color="#00BBBB"/>
|
||||
<stop offset="0.712616" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint4_linear_115_109" x1="7.60205" y1="5.8709" x2="15.5561" y2="16.3719" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint5_radial_115_109" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(7.84537 21.5181) rotate(-20.3525) scale(18.5603 17.32)">
|
||||
<stop stop-color="#00B0BB"/>
|
||||
<stop offset="1" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint6_linear_115_109" x1="16.8078" y1="13.0071" x2="10.0409" y2="22.9937" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00B1BC"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_115_109" x1="16.8078" y1="13.0071" x2="14.1687" y2="23.841" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint8_radial_115_109" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(47.2781 7) rotate(19.0047) scale(67.5582 85.7506)">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="white" stop-opacity="0.5"/>
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 995.73 163.32">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
mask: url(#mask);
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #1A1C1E;
|
||||
}
|
||||
|
||||
.cls-2, .cls-3 {
|
||||
stroke-width: 0px;
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: #fff;
|
||||
}
|
||||
</style>
|
||||
<mask id="mask" x="-10.78" y="-24.14" width="114.88" height="198.1" maskUnits="userSpaceOnUse">
|
||||
<rect class="cls-3" x="-10.78" y="-24.14" width="114.88" height="198.1"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<g class="cls-1">
|
||||
<path class="cls-2" d="M30.05,84.26s0,0-.01.01h0s0,0,.01-.01Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="cls-2" d="M193.01,3.19v56.41h-39.58V3.19h-48.82c-.17,0-.34,0-.51,0-27.82,0-53.74,14.9-67.78,38.91-6.22,3.78-10.42,6.42-10.51,6.46-12.12,6.61-18.17,12.91-21.14,17.9-2.31.04-4.3,1.75-4.63,4.11-.37,2.6,1.45,5.02,4.06,5.39,2.61.37,5.02-1.45,5.39-4.06.19-1.34-.21-2.64-.99-3.62,4.39-7.08,13.65-12.91,19.32-16,.98-.53,55.9-33.8,60.02-32.98.69.14,1.69.51,2.48,2.14,2.17,4.51-17.82,17.87-21.9,20.31-3.9,2.34-7.34,4.83-10.32,7.49-6.31,5.62-10.52,11.93-12.56,18.85-1.23,4.18-4.74,16.19-13.25,19.18-.6-1.34-1.82-2.39-3.37-2.71-2.58-.54-5.11,1.12-5.64,3.7-.51,2.44.94,4.83,3.28,5.54,5.87,38.08,38.48,66.31,77.5,66.33v.03h49.36v-65.96h39.58v65.96h49.36V3.19h-49.36ZM60.02,53.22c-.18,2.44-.75,5.24-2.12,7.07-1.45,1.92-4.2,3.55-6.66,4.7,1.95-4.19,4.89-8.12,8.79-11.77ZM104.07,107.71h-19.35c-2.06,0-3.74,1.67-3.74,3.74,0,1.03.42,1.97,1.1,2.64.68.68,1.61,1.09,2.64,1.09h19.35v40.69c-36.93-.01-67.79-26.74-73.33-62.79.57-.41,1.04-.94,1.39-1.57,7.05-2.52,12.38-6,17.14-21.16,2.56-.89,8.93-3.43,12.01-7.52,3.1-4.11,3.14-10.53,3.01-13.2,1.94-1.48,4.05-2.9,6.33-4.26,13.03-7.8,27.09-18.42,23.53-25.78-1.17-2.43-3.06-3.97-5.46-4.44-4.51-.89-27.07,11.84-43.88,21.84,13.92-18.43,35.88-29.57,59.27-29.58v100.3Z"/>
|
||||
<ellipse class="cls-2" cx="79.09" cy="69.12" rx="8.08" ry="12.53"/>
|
||||
<path class="cls-2" d="M369.18,7.05c10.39,4.7,17.93,12.89,22.63,24.57,4.7,11.68,7.05,28.36,7.05,50.04s-2.35,38.37-7.05,50.04c-4.7,11.68-12.25,19.87-22.63,24.57-10.39,4.7-24.91,7.05-43.56,7.05s-32.95-2.35-43.33-7.05c-10.39-4.7-17.93-12.89-22.63-24.57-4.7-11.68-7.05-28.36-7.05-50.04s2.35-38.36,7.05-50.04c4.7-11.68,12.24-19.86,22.63-24.57,10.39-4.7,24.83-7.05,43.33-7.05s33.17,2.35,43.56,7.05ZM312.54,40.38c-2.96,2.5-5.04,6.9-6.26,13.19-1.22,6.29-1.82,15.66-1.82,28.09s.6,21.8,1.82,28.09c1.21,6.29,3.3,10.69,6.26,13.19s7.31,3.75,13.08,3.75,10.12-1.25,13.08-3.75,5.04-6.9,6.26-13.19c1.21-6.29,1.82-15.66,1.82-28.09s-.61-21.8-1.82-28.09c-1.22-6.29-3.3-10.69-6.26-13.19s-7.32-3.75-13.08-3.75-10.12,1.25-13.08,3.75Z"/>
|
||||
<path class="cls-2" d="M553.31,143.08c0,5.61-1.44,9.86-4.32,12.74-2.88,2.88-7.13,4.32-12.74,4.32h-18.88c-4.7,0-8.3-1.06-10.8-3.18-2.5-2.12-5.27-5.84-8.3-11.15l-34.12-53.91c-3.79-6.82-7.28-15.77-10.46-26.84h-1.59c1.51,10.62,2.27,20.17,2.27,28.66v66.42h-45.49V20.24c0-5.61,1.44-9.86,4.32-12.74s7.13-4.32,12.74-4.32h18.88c4.7,0,8.26,1.06,10.69,3.18,2.42,2.13,5.23,5.84,8.42,11.15l32.76,51.41c4.09,7.28,8.11,16.23,12.06,26.84h1.59c-1.36-11.52-2.05-21-2.05-28.43l-.23-64.15h45.27v139.89Z"/>
|
||||
<path class="cls-2" d="M662.72,1.82c7.13.91,14.86,2.5,23.2,4.78l-3.64,35.94c-3.49,0-6.14-.07-7.96-.23l-40.03-.23c-5.31,0-9.33,1.1-12.06,3.3-2.73,2.2-4.63,6.07-5.69,11.6-1.06,5.54-1.59,13.76-1.59,24.68s.53,19.15,1.59,24.68c1.06,5.54,2.96,9.4,5.69,11.6,2.73,2.2,6.75,3.3,12.06,3.3,12.43,0,22.33-.11,29.68-.34,7.35-.23,14.59-.72,21.72-1.48l3.64,35.94c-8.04,2.73-16.15,4.66-24.34,5.8-8.19,1.14-18.43,1.71-30.71,1.71-17.59,0-31.54-2.62-41.85-7.85-10.31-5.23-17.78-13.72-22.41-25.48-4.63-11.75-6.94-27.71-6.94-47.88s2.31-36.13,6.94-47.88c4.62-11.75,12.09-20.24,22.41-25.48,10.31-5.23,24.26-7.85,41.85-7.85,11.83,0,21.3.45,28.43,1.36Z"/>
|
||||
<path class="cls-2" d="M839.24,3.18v156.95h-49.36v-65.97h-39.58v65.97h-49.36V3.18h49.36v56.41h39.58V3.18h49.36Z"/>
|
||||
<path class="cls-2" d="M966.05,7.05c10.39,4.7,17.93,12.89,22.63,24.57,4.7,11.68,7.05,28.36,7.05,50.04s-2.35,38.37-7.05,50.04c-4.7,11.68-12.25,19.87-22.63,24.57-10.39,4.7-24.91,7.05-43.56,7.05s-32.95-2.35-43.33-7.05c-10.39-4.7-17.93-12.89-22.63-24.57-4.7-11.68-7.05-28.36-7.05-50.04s2.35-38.36,7.05-50.04c4.7-11.68,12.24-19.86,22.63-24.57,10.39-4.7,24.83-7.05,43.33-7.05s33.17,2.35,43.56,7.05ZM909.41,40.38c-2.96,2.5-5.04,6.9-6.26,13.19-1.22,6.29-1.82,15.66-1.82,28.09s.6,21.8,1.82,28.09c1.21,6.29,3.3,10.69,6.26,13.19s7.31,3.75,13.08,3.75,10.12-1.25,13.08-3.75,5.04-6.9,6.26-13.19c1.21-6.29,1.82-15.66,1.82-28.09s-.61-21.8-1.82-28.09c-1.22-6.29-3.3-10.69-6.26-13.19s-7.32-3.75-13.08-3.75-10.12,1.25-13.08,3.75Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 995.73 163.32">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
mask: url(#mask);
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #B5D9FD;
|
||||
stroke-width: 0px;
|
||||
}
|
||||
</style>
|
||||
<mask id="mask" x="-10.78" y="-24.14" width="114.88" height="198.1" maskUnits="userSpaceOnUse">
|
||||
<rect class="cls-2" x="-10.78" y="-24.14" width="114.88" height="198.1"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<g class="cls-1">
|
||||
<path class="cls-2" d="M30.05,84.26s0,0-.01.01h0s0,0,.01-.01Z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="cls-2" d="M193.01,3.19v56.41h-39.58V3.19h-48.82c-.17,0-.34,0-.51,0-27.82,0-53.74,14.9-67.78,38.91-6.22,3.78-10.42,6.42-10.51,6.46-12.12,6.61-18.17,12.91-21.14,17.9-2.31.04-4.3,1.75-4.63,4.11-.37,2.6,1.45,5.02,4.06,5.39,2.61.37,5.02-1.45,5.39-4.06.19-1.34-.21-2.64-.99-3.62,4.39-7.08,13.65-12.91,19.32-16,.98-.53,55.9-33.8,60.02-32.98.69.14,1.69.51,2.48,2.14,2.17,4.51-17.82,17.87-21.9,20.31-3.9,2.34-7.34,4.83-10.32,7.49-6.31,5.62-10.52,11.93-12.56,18.85-1.23,4.18-4.74,16.19-13.25,19.18-.6-1.34-1.82-2.39-3.37-2.71-2.58-.54-5.11,1.12-5.64,3.7-.51,2.44.94,4.83,3.28,5.54,5.87,38.08,38.48,66.31,77.5,66.33v.03h49.36v-65.96h39.58v65.96h49.36V3.19h-49.36ZM60.02,53.22c-.18,2.44-.75,5.24-2.12,7.07-1.45,1.92-4.2,3.55-6.66,4.7,1.95-4.19,4.89-8.12,8.79-11.77ZM104.07,107.71h-19.35c-2.06,0-3.74,1.67-3.74,3.74,0,1.03.42,1.97,1.1,2.64.68.68,1.61,1.09,2.64,1.09h19.35v40.69c-36.93-.01-67.79-26.74-73.33-62.79.57-.41,1.04-.94,1.39-1.57,7.05-2.52,12.38-6,17.14-21.16,2.56-.89,8.93-3.43,12.01-7.52,3.1-4.11,3.14-10.53,3.01-13.2,1.94-1.48,4.05-2.9,6.33-4.26,13.03-7.8,27.09-18.42,23.53-25.78-1.17-2.43-3.06-3.97-5.46-4.44-4.51-.89-27.07,11.84-43.88,21.84,13.92-18.43,35.88-29.57,59.27-29.58v100.3Z"/>
|
||||
<ellipse class="cls-2" cx="79.09" cy="69.12" rx="8.08" ry="12.53"/>
|
||||
<path class="cls-2" d="M369.18,7.05c10.39,4.7,17.93,12.89,22.63,24.57,4.7,11.68,7.05,28.36,7.05,50.04s-2.35,38.37-7.05,50.04c-4.7,11.68-12.25,19.87-22.63,24.57-10.39,4.7-24.91,7.05-43.56,7.05s-32.95-2.35-43.33-7.05c-10.39-4.7-17.93-12.89-22.63-24.57-4.7-11.68-7.05-28.36-7.05-50.04s2.35-38.36,7.05-50.04c4.7-11.68,12.24-19.86,22.63-24.57,10.39-4.7,24.83-7.05,43.33-7.05s33.17,2.35,43.56,7.05ZM312.54,40.38c-2.96,2.5-5.04,6.9-6.26,13.19-1.22,6.29-1.82,15.66-1.82,28.09s.6,21.8,1.82,28.09c1.21,6.29,3.3,10.69,6.26,13.19s7.31,3.75,13.08,3.75,10.12-1.25,13.08-3.75,5.04-6.9,6.26-13.19c1.21-6.29,1.82-15.66,1.82-28.09s-.61-21.8-1.82-28.09c-1.22-6.29-3.3-10.69-6.26-13.19s-7.32-3.75-13.08-3.75-10.12,1.25-13.08,3.75Z"/>
|
||||
<path class="cls-2" d="M553.31,143.08c0,5.61-1.44,9.86-4.32,12.74-2.88,2.88-7.13,4.32-12.74,4.32h-18.88c-4.7,0-8.3-1.06-10.8-3.18-2.5-2.12-5.27-5.84-8.3-11.15l-34.12-53.91c-3.79-6.82-7.28-15.77-10.46-26.84h-1.59c1.51,10.62,2.27,20.17,2.27,28.66v66.42h-45.49V20.24c0-5.61,1.44-9.86,4.32-12.74s7.13-4.32,12.74-4.32h18.88c4.7,0,8.26,1.06,10.69,3.18,2.42,2.13,5.23,5.84,8.42,11.15l32.76,51.41c4.09,7.28,8.11,16.23,12.06,26.84h1.59c-1.36-11.52-2.05-21-2.05-28.43l-.23-64.15h45.27v139.89Z"/>
|
||||
<path class="cls-2" d="M662.72,1.82c7.13.91,14.86,2.5,23.2,4.78l-3.64,35.94c-3.49,0-6.14-.07-7.96-.23l-40.03-.23c-5.31,0-9.33,1.1-12.06,3.3-2.73,2.2-4.63,6.07-5.69,11.6-1.06,5.54-1.59,13.76-1.59,24.68s.53,19.15,1.59,24.68c1.06,5.54,2.96,9.4,5.69,11.6,2.73,2.2,6.75,3.3,12.06,3.3,12.43,0,22.33-.11,29.68-.34,7.35-.23,14.59-.72,21.72-1.48l3.64,35.94c-8.04,2.73-16.15,4.66-24.34,5.8-8.19,1.14-18.43,1.71-30.71,1.71-17.59,0-31.54-2.62-41.85-7.85-10.31-5.23-17.78-13.72-22.41-25.48-4.63-11.75-6.94-27.71-6.94-47.88s2.31-36.13,6.94-47.88c4.62-11.75,12.09-20.24,22.41-25.48,10.31-5.23,24.26-7.85,41.85-7.85,11.83,0,21.3.45,28.43,1.36Z"/>
|
||||
<path class="cls-2" d="M839.24,3.18v156.95h-49.36v-65.97h-39.58v65.97h-49.36V3.18h49.36v56.41h39.58V3.18h49.36Z"/>
|
||||
<path class="cls-2" d="M966.05,7.05c10.39,4.7,17.93,12.89,22.63,24.57,4.7,11.68,7.05,28.36,7.05,50.04s-2.35,38.37-7.05,50.04c-4.7,11.68-12.25,19.87-22.63,24.57-10.39,4.7-24.91,7.05-43.56,7.05s-32.95-2.35-43.33-7.05c-10.39-4.7-17.93-12.89-22.63-24.57-4.7-11.68-7.05-28.36-7.05-50.04s2.35-38.36,7.05-50.04c4.7-11.68,12.24-19.86,22.63-24.57,10.39-4.7,24.83-7.05,43.33-7.05s33.17,2.35,43.56,7.05ZM909.41,40.38c-2.96,2.5-5.04,6.9-6.26,13.19-1.22,6.29-1.82,15.66-1.82,28.09s.6,21.8,1.82,28.09c1.21,6.29,3.3,10.69,6.26,13.19s7.31,3.75,13.08,3.75,10.12-1.25,13.08-3.75,5.04-6.9,6.26-13.19c1.21-6.29,1.82-15.66,1.82-28.09s-.61-21.8-1.82-28.09c-1.22-6.29-3.3-10.69-6.26-13.19s-7.32-3.75-13.08-3.75-10.12,1.25-13.08,3.75Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
|
|
@ -0,0 +1,51 @@
|
|||
<svg width="160" height="24" viewBox="0 0 160 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61504C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5913 7.95343 21.1394Z" fill="white"/>
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61504C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5913 7.95343 21.1394Z" fill="url(#paint0_radial_115_86)"/>
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61504C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5913 7.95343 21.1394Z" fill="black" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M7.95343 21.1394C4.89586 21.1304 2.25471 19.458 0.987296 16.2895C-0.280118 13.121 0.108924 9.16314 1.74363 5.61504C4.8012 5.62409 7.44235 7.29648 8.70976 10.465C9.97718 13.6335 9.58814 17.5913 7.95343 21.1394Z" fill="url(#paint1_linear_115_86)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M7.9354 21.1112C4.89702 21.0957 2.27411 19.4306 1.01347 16.279C-0.248375 13.1244 0.135612 9.18218 1.76165 5.64327C4.80004 5.65882 7.42295 7.32385 8.68359 10.4755C9.94543 13.63 9.56144 17.5723 7.9354 21.1112Z" stroke="url(#paint2_linear_115_86)" stroke-opacity="0.05" stroke-width="0.056338"/>
|
||||
<path d="M7.31038 21.2574C11.3543 20.2215 14.8836 17.3754 16.6285 13.2361C18.3735 9.09671 17.9448 4.58749 15.8598 0.976291C11.8159 2.01214 8.2866 4.85826 6.54167 8.99762C4.79674 13.137 5.2254 17.6462 7.31038 21.2574Z" fill="white"/>
|
||||
<path d="M7.31038 21.2574C11.3543 20.2215 14.8836 17.3754 16.6285 13.2361C18.3735 9.09671 17.9448 4.58749 15.8598 0.976291C11.8159 2.01214 8.2866 4.85826 6.54167 8.99762C4.79674 13.137 5.2254 17.6462 7.31038 21.2574Z" fill="url(#paint3_radial_115_86)"/>
|
||||
<path d="M16.6025 13.2251C14.8642 17.349 11.3512 20.1866 7.32411 21.2248C5.25257 17.624 4.82926 13.1324 6.56764 9.00855C8.30603 4.88472 11.819 2.04706 15.8461 1.00889C17.9176 4.60967 18.3409 9.10131 16.6025 13.2251Z" stroke="url(#paint4_linear_115_86)" stroke-opacity="0.05" stroke-width="0.056338"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="white"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="url(#paint5_radial_115_86)"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="black" fill-opacity="0.2" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M7.23368 21.2069C9.78906 23.2373 13.2102 23.9506 16.5772 22.8141C19.9441 21.6775 22.5058 18.9445 23.7304 15.6382C21.175 13.6078 17.7538 12.8944 14.3869 14.031C11.0199 15.1676 8.45822 17.9006 7.23368 21.2069Z" fill="url(#paint6_linear_115_86)" fill-opacity="0.5" style="mix-blend-mode:hard-light"/>
|
||||
<path d="M16.5682 22.7874C13.2176 23.9184 9.81361 23.2124 7.2672 21.1975C8.49194 17.9068 11.0444 15.189 14.3959 14.0577C17.7465 12.9266 21.1504 13.6326 23.6968 15.6476C22.4721 18.9383 19.9196 21.656 16.5682 22.7874Z" stroke="url(#paint7_linear_115_86)" stroke-opacity="0.05" stroke-width="0.056338"/>
|
||||
<path d="M34.2124 19V5.4H39.4924L41.6924 12.2L42.3724 14.74L43.0524 12.2L45.2524 5.4H50.4124V19H46.3324L46.5924 9.98L45.5324 13.68L43.7924 19H40.8324L39.0524 13.6L38.0324 10.02L38.2924 19H34.2124ZM52.4155 7.3V4.6H56.2955V7.3H52.4155ZM52.4155 19V8.14H56.2955V19H52.4155ZM58.1038 19V8.14H61.9838V9.58C62.6638 8.34 63.7438 7.76 65.0038 7.76C66.9638 7.76 68.6238 8.98 68.6238 11.78V19H64.7438V12.56C64.7438 11.34 64.3038 10.86 63.4838 10.86C62.6038 10.86 61.9838 11.58 61.9838 12.88V19H58.1038ZM70.9327 15.22V11.06H69.7327V8.14H70.9327V5.62H74.8127V8.14H76.9327V11.06H74.8127V14.6C74.8127 15.5 75.0327 16.06 76.2127 16.06H76.9327V19C76.4927 19.2 75.6727 19.38 74.6527 19.38C72.1527 19.38 70.9327 17.88 70.9327 15.22Z" fill="#001E13"/>
|
||||
<path d="M87.232 10.519C87.232 13.687 94.1125 11.2285 94.1125 15.832C94.1125 17.9935 92.3635 19.198 89.971 19.198C87.562 19.198 85.912 18.0925 85.417 15.832H87.001C87.364 17.1685 88.3705 17.8945 89.9875 17.8945C91.6705 17.8945 92.5615 17.152 92.5615 16.03C92.5615 12.598 85.681 15.1555 85.681 10.618C85.681 9.001 87.034 7.582 89.509 7.582C91.6705 7.582 93.403 8.6215 93.8155 11.014H92.215C91.8685 9.529 90.9115 8.8855 89.476 8.8855C88.057 8.8855 87.232 9.529 87.232 10.519ZM96.2499 16.4755V11.3935H95.0289V10.2385H96.2499V8.2255H97.7019V10.2385H99.6324V11.3935H97.7019V16.4755C97.7019 17.5315 98.0154 18.0265 99.3024 18.0265H99.5994V19.066C99.4344 19.1485 99.0714 19.198 98.6589 19.198C97.0254 19.198 96.2499 18.3235 96.2499 16.4755ZM102.516 13.093H101.064C101.345 11.1625 102.615 10.024 104.76 10.024C107.103 10.024 108.242 11.3935 108.242 13.4395V16.888C108.242 17.8945 108.324 18.5215 108.555 19H107.021C106.856 18.6535 106.806 18.142 106.79 17.614C106.047 18.7195 104.859 19.198 103.803 19.198C101.988 19.198 100.767 18.3565 100.767 16.69C100.767 15.4855 101.427 14.611 102.714 14.182C103.902 13.786 105.107 13.687 106.79 13.6705V13.4725C106.79 12.0535 106.13 11.278 104.628 11.278C103.374 11.278 102.698 11.971 102.516 13.093ZM102.252 16.657C102.252 17.4655 102.929 17.944 103.952 17.944C105.569 17.944 106.79 16.6735 106.79 15.172V14.7595C103.061 14.7925 102.252 15.5845 102.252 16.657ZM110.787 19V10.2385H112.239V11.5915C112.833 10.519 113.774 10.024 114.83 10.024C115.176 10.024 115.49 10.1065 115.655 10.2385V11.542C115.407 11.4595 115.094 11.4265 114.747 11.4265C112.998 11.4265 112.239 12.5155 112.239 14.0995V19H110.787ZM117.305 16.4755V11.3935H116.084V10.2385H117.305V8.2255H118.757V10.2385H120.688V11.3935H118.757V16.4755C118.757 17.5315 119.071 18.0265 120.358 18.0265H120.655V19.066C120.49 19.1485 120.127 19.198 119.714 19.198C118.081 19.198 117.305 18.3235 117.305 16.4755ZM129.809 16.1455C129.33 18.1915 127.862 19.198 125.865 19.198C123.324 19.198 121.79 17.482 121.79 14.6275C121.79 11.6575 123.324 10.024 125.783 10.024C128.258 10.024 129.743 11.7235 129.743 14.512V14.875H123.275C123.357 16.8385 124.281 17.944 125.865 17.944C127.103 17.944 127.977 17.35 128.291 16.1455H129.809ZM125.783 11.278C124.38 11.278 123.539 12.1525 123.324 13.786H128.225C128.027 12.169 127.152 11.278 125.783 11.278ZM131.843 19V10.2385H133.295V11.5915C133.889 10.519 134.829 10.024 135.885 10.024C136.232 10.024 136.545 10.1065 136.71 10.2385V11.542C136.463 11.4595 136.149 11.4265 135.803 11.4265C134.054 11.4265 133.295 12.5155 133.295 14.0995V19H131.843ZM141.763 19V7.78H143.281V13.192L148.413 7.78H150.327L145.047 13.291L150.459 19H148.413L143.281 13.621V19H141.763ZM152.06 9.067V7.12H153.512V9.067H152.06ZM152.06 19V10.2385H153.512V19H152.06ZM156.178 16.4755V11.3935H154.957V10.2385H156.178V8.2255H157.63V10.2385H159.56V11.3935H157.63V16.4755C157.63 17.5315 157.943 18.0265 159.23 18.0265H159.527V19.066C159.362 19.1485 158.999 19.198 158.587 19.198C156.953 19.198 156.178 18.3235 156.178 16.4755Z" fill="#002719" fill-opacity="0.6"/>
|
||||
<defs>
|
||||
<radialGradient id="paint0_radial_115_86" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(-3.00503 15.023) rotate(-10.029) scale(17.9572 17.784)">
|
||||
<stop stop-color="#00B0BB"/>
|
||||
<stop offset="1" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint1_linear_115_86" x1="7.39036" y1="4.81308" x2="1.62975" y2="18.6894" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#18E299"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_115_86" x1="7.94816" y1="8.01562" x2="1.7612" y2="18.746" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint3_radial_115_86" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(8.11404 20.8822) rotate(-75.7542) scale(21.6246 23.7772)">
|
||||
<stop stop-color="#00BBBB"/>
|
||||
<stop offset="0.712616" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint4_linear_115_86" x1="7.60205" y1="5.8709" x2="15.5561" y2="16.3719" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="paint5_radial_115_86" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(7.84537 21.5181) rotate(-20.3525) scale(18.5603 17.32)">
|
||||
<stop stop-color="#00B0BB"/>
|
||||
<stop offset="1" stop-color="#00DB65"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="paint6_linear_115_86" x1="16.8078" y1="13.0071" x2="10.0409" y2="22.9937" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00B1BC"/>
|
||||
<stop offset="1"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_115_86" x1="16.8078" y1="13.0071" x2="14.1687" y2="23.841" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.0 KiB |
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"name": "honcho-docs",
|
||||
"version": "1.0.0",
|
||||
"description": "## Setting Up `honcho-docs` Locally",
|
||||
"main": ".pnp.js",
|
||||
"scripts": {
|
||||
"dev": "mint dev",
|
||||
"openapi": "npx @mintlify/scraping openapi-file v3/openapi.json -o v3/api-reference/endpoint",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mintlify/scraping": "^4.0.467"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mint": "^4.2.204"
|
||||
}
|
||||
}
|
||||