honcho/sdks/python
adavyas c73f6a0b7a
feat: Add workspace-level chat (#931)
* Add workspace-level chat (DEV-1326)

POST /v3/workspaces/{workspace_id}/chat: agentic dialectic over the whole
workspace instead of a single (observer, observed) pair. Salvaged from
plastic-labs/honcho#373 and re-grown on today's DialecticAgent:

- WorkspaceDialecticAgent subclasses DialecticAgent via four new seams
  (_get_tools, _create_tool_executor, _prefetch_intro, _trace_name) instead
  of a base-class extraction; observer/observed use empty-string sentinels.
- Routing-accelerated prefetch: workspace stats + top-5 active peers with
  their self peer-cards (pure DB, ~7ms measured) so routing-obvious queries
  resolve without a discovery tool round.
- Observation search stays pair-scoped (matches per-pair vector namespaces;
  avoids workspace-flat top-k dilution): search_memory/get_peer_card take
  observer/observed as tool arguments, with pair attribution in results.
- workspace_chat / workspace_chat_stream orchestrators, WorkspaceChatOptions
  schema (scope param seam left for the #897 scopes facade), SSE streaming,
  structured output via response_format.
- crud: get_workspace_stats, get_active_peers; format_documents_with_attribution.
- SDKs: Python Honcho.chat/chat_stream + HonchoAio mirrors; TypeScript
  honcho.chat/chatStream.
- 46 tests (route, orchestrator preflight, tool handlers, executor routing,
  attribution formatting) + unified test cases + docs.

Co-Authored-By: doria <93405247+dr-frmr@users.noreply.github.com>
Co-Authored-By: Benjamin McCormick <docterformer@protonmail.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: type SSE stream wrapper as AsyncIterator (basedpyright)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: silence unused db_session fixture warnings (basedpyright failOnWarnings)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs: drop docs changes from this PR (defer to follow-up)

Restores docs/v3/documentation/features/chat.mdx to main's version. This
also puts back the peer-chat Structured Outputs section (#896) that the
workspace-chat commit removed as a rebase artifact.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix: workspace message tools deny-all under rebased session scoping

The #882 rebase changed the unscoped-observer contract from falsy to
'observer is None': resolve_session_scope looked up the workspace
executor's observer='' sentinel as a real peer with no session
memberships and denied every workspace-flat message read (search, grep,
date-range, temporal, observation context) whenever no session was
pinned — the primary workspace-chat shape. Normalize the sentinel to
None at the five read-handler crud boundaries and add regression tests
that run the tools unpinned (verified to fail without the fix).

Also from review:
- wrap the workspace prefetch in the same degrade-to-None protection
  the base agent has (an overview query error no longer 500s the
  request or kills the SSE stream after headers)
- thread session_allowlist through create_workspace_tool_executor so
  the agent-level allowlist seam is honored end to end when scopes
  (#897) wire it up; allowlisted grep is covered by a test
- deterministic name tie-break in get_active_peers ordering

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* review: SDK response_format parity, shared query sanitizer, annotations

- TS SDK: WorkspaceChatParams gains response_format; _workspaceChat/
  _workspaceChatStream consume the shared interface instead of inline
  duplicates; chat/chatStream expose responseFormat.
- Consolidate the three identical sanitize_query validators into one
  NulStripped annotation.
- workspace_chat_stream: return annotation + full docstring.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* review: fold active peers into workspace stats; trace + query bounds

- Merge get_active_peers into get_workspace_stats (one discovery round
  instead of two); minimal loadout keeps a discovery tool via the merged
  stats tool. Fixed top-10 by recent activity; deeper discovery routes
  through search_messages.
- get_active_peers CRUD now aggregates over a trailing 90-day window so
  the chat-path prefetch never scans a workspace's full message history.
- Workspace agent inherits the "dialectic_chat" trace name; scope stays
  distinguished by agent_type/track_name (workspace name was already in
  telemetry context).
- Prefetch failure logs carry workspace + traceback; prompt no longer
  contrasts against a peer-level agent the model has no concept of;
  drop ticket identifiers from comments.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: add `scope` to workspace chat and exclude scope peers from stats

Workspace chat is peer-unanchored, so `scope` is always a session-union
allowlist (single name or list), fail-closed when empty. Stats and
active-peer prefetch drop scope-kind peers and honor the same allowlist.

* test: teach the unified runner `workspace_chat` and parse every case

QueryAction now accepts target=workspace_chat (SDK path, including
scope). A pytest over tests/unified/test_cases/*.json keeps the four
existing workspace-chat cases — and a new scoped one — from rotting
against the schema again.

* docs: tighten workspace-chat scope docs and judge prompt

Scoped workspace_chat uses the SDK, not raw HTTP. The scope fixture's
judge now requires the in-scope tea fact, not merely the absence of the
leak. format_sse_stream matches the peer-chat one-liner.

* fix(dialectic): restore the empty-memory fallback for workspace chat

`search_memory` auto-searches messages when a pair has no observations,
but the gate only admitted `agent_type == "dialectic"`. The workspace
executor passes `workspace_dialectic`, so workspace chat got a bare
"No observations found" and answered that it knew nothing rather than
falling through to message search.

Also fixes the two unified cases that never ran: `deriver` is not a
field on `WorkspaceConfiguration`, so both aborted at load with
`extra_forbidden`. `workspace_chat_scope` additionally enables reasoning,
since it asserts scope isolation and has no reason to depend on the
fallback path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(tests/unified): fail CI when unified tests fail

`runner.run()` tallied failures into `failed_count` and printed them, but
returned nothing, and both entrypoints ignored the result. The workflow
invokes `python -m tests.unified.run` bare, so the job has gone green on
failing and unrunnable cases since it was wired up in #291.

Return the count and exit non-zero on it. `INVALID SCHEMA` already counts
toward the tally, so a malformed case now fails the job instead of being
skipped silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(unified): assert scope peers stay out of workspace chat answers

Scope peers are real peer rows, so a regression in the `scope_peer_clause`
exclusion would surface `scope.therapy` through workspace stats or the
routing prefetch. Nothing asserted against that.

Adds the check to the existing scoped query and a new unscoped one, since
the two exercise different `get_active_peers` branches. Verified by
removing the exclusion, which fails the unscoped query.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: Remove dead code references

---------

Co-authored-by: doria <93405247+dr-frmr@users.noreply.github.com>
Co-authored-by: Benjamin McCormick <docterformer@protonmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Aakash Kattelu <aakash@plasticlabs.ai>
Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2026-08-24 15:54:23 -04:00
..
examples feat: honcho 3.0, sdks 2.0, excise stainless, update v3 docs, changelogs (#331) 2026-01-22 15:16:28 -05:00
src/honcho feat: Add workspace-level chat (#931) 2026-08-24 15:54:23 -04:00
.gitignore feat: add new ergo sdks to monorepo (#142) 2025-06-26 17:07:23 -04:00
CHANGELOG.md chore(docs): Update changelogs (#1007) 2026-08-10 14:56:07 -04:00
README.md Kass/readme refresh (#681) 2026-05-14 13:15:37 -04:00
pyproject.toml chore(docs): Update changelogs (#1007) 2026-08-10 14:56:07 -04:00

README.md

Honcho Python SDK

The official Python library for the Honcho conversational memory platform. Honcho provides tools for managing peers, sessions, and conversation context across multi-party interactions, enabling advanced conversational AI applications with persistent memory and theory-of-mind capabilities.

Installation

pip install honcho-ai

Quick Start

from honcho import Honcho

# Initialize client
client = Honcho(api_key="your-api-key")

# Create peers (participants in conversations)
alice = client.peer("alice")
bob = client.peer("bob")

# Create a session for group conversations
session = client.session("conversation-1")

# Add messages to the session
session.add_messages([
    alice.message("Hello, Bob!"),
    bob.message("Hi Alice, how are you?")
])

# Query conversation context
response = alice.chat("What did Bob say to the user?")
print(response)

Core Concepts

Peers

Peers represent participants in conversations.

# Create peers
assistant = client.peer("assistant")
user = client.peer("user-123")

# Chat with global context
response = user.chat("What did I talk about yesterday?")

# Chat with perspective of another peer
response = user.chat("Does the assistant know my preferences?", target=assistant)

Sessions

Sessions group related conversations and messages:

# Create a session
session = client.session("project-discussion")

# Add peers to session
session.add_peers([alice, bob])

# Add messages
session.add_messages([
    alice.message("Let's discuss the project timeline"),
    bob.message("I think we need two more weeks")
])

# Get conversation context
context = session.context()

Messages and Context

Retrieve and use conversation history:

# Get messages from a session
messages = session.messages()

# Convert to OpenAI format for further prompting
openai_messages = context.to_openai(assistant="assistant")

# Convert to Anthropic format for further prompting
anthropic_messages = context.to_anthropic(assistant="assistant")

Async Support

The SDK provides async access via the .aio accessor on any instance:

from honcho import Honcho

async def main():
    client = Honcho(api_key="your-api-key")

    # Async peer and session creation
    peer = await client.aio.peer("user-123")
    session = await client.aio.session("conversation-1")

    # Async chat
    response = await peer.aio.chat("What does this user prefer?")

    # Async iteration
    async for p in client.aio.peers():
        print(p.id)

Metadata Management

# Set peer metadata
user.set_metadata({"location": "San Francisco", "preferences": {"theme": "dark"}})

# Session metadata
session.set_metadata({"topic": "project-planning", "priority": "high"})

Multi-Perspective Queries

# Alice's view of what Bob knows
response = alice.chat("Does Bob remember our discussion about the budget?", target=bob)

# Session-specific perspective
response = alice.chat("What does Bob think about this project?",
                     target=bob,
                     session=session)

Configuration

Environment Variables

export HONCHO_API_KEY="your-api-key"
export HONCHO_BASE_URL="https://api.honcho.dev"  # Optional
export HONCHO_WORKSPACE_ID="your-workspace"  # Optional

Client Options

client = Honcho(
    api_key="your-api-key",
    environment="production",  # or "local"
    workspace_id="custom-workspace",
    base_url="https://api.honcho.dev"
)

License

Apache 2.0 - see LICENSE for details.

Support