fix: use real stainless releases, update to match

This commit is contained in:
Benjamin McCormick 2026-01-12 17:27:54 -05:00
parent 5b7ae0d82c
commit 2c66813944
25 changed files with 227 additions and 243 deletions

View File

@ -8,7 +8,7 @@ authors = [
{ name = "Plastic Labs", email = "hello@plasticlabs.ai" },
]
dependencies = [
"honcho-core @ https://pkg.stainless.com/s/honcho-python/b5539818022ab4ad757250ea3234648f8dc5906e/honcho_core-1.8.0-py3-none-any.whl",
"honcho-core==1.9.0",
"httpx>=0.28.0, <1",
"pydantic>=2.0.0, <3",
"typing-extensions>=4.12.0; python_version < \"3.12\"",

View File

@ -8,7 +8,7 @@ from typing import Any, Literal
import httpx
from honcho_core import AsyncHoncho as AsyncHonchoCore
from honcho_core import Honcho as HonchoCore
from honcho_core.types.workspaces import QueueGetStatusResponse
from honcho_core.types.workspaces import QueueStatusResponse
from honcho_core.types.workspaces.peer import Peer as PeerCore
from honcho_core.types.workspaces.session import Session as SessionCore
from honcho_core.types.workspaces.sessions.message import Message
@ -474,7 +474,7 @@ class AsyncHoncho(BaseModel):
observer: str | PeerBase | None = None,
sender: str | PeerBase | None = None,
session: str | SessionBase | None = None,
) -> QueueGetStatusResponse:
) -> QueueStatusResponse:
"""
Get the queue processing status, optionally scoped to an observer, sender, and/or session.
@ -499,7 +499,7 @@ class AsyncHoncho(BaseModel):
else (session if isinstance(session, str) else session.id)
)
return await self._client.workspaces.queue.get_status(
return await self._client.workspaces.queue.status(
workspace_id=self.workspace_id,
observer_id=resolved_observer_id,
sender_id=resolved_sender_id,
@ -517,7 +517,7 @@ class AsyncHoncho(BaseModel):
gt=0,
description="Maximum time to poll in seconds. Defaults to 5 minutes (300 seconds).",
),
) -> QueueGetStatusResponse:
) -> QueueStatusResponse:
"""
Poll get_queue_status until pending_work_units and in_progress_work_units are both 0.
This allows you to guarantee that all messages have been processed by the queue for
@ -532,7 +532,7 @@ class AsyncHoncho(BaseModel):
timeout: Maximum time to poll in seconds. Defaults to 5 minutes (300 seconds).
Returns:
QueueGetStatusResponse when all work units are complete
QueueStatusResponse when all work units are complete
Raises:
TimeoutError: If timeout is exceeded before work units complete

View File

@ -7,11 +7,11 @@ from typing import Literal
from honcho_core import AsyncHoncho as AsyncHonchoCore
from honcho_core._types import omit
from honcho_core.types.workspaces import PeerCardResponse
from honcho_core.types.workspaces.peer_get_context_response import (
PeerGetContextResponse,
from honcho_core.types.workspaces.peer_context_response import (
PeerContextResponse,
)
from honcho_core.types.workspaces.peer_get_representation_response import (
PeerGetRepresentationResponse,
from honcho_core.types.workspaces.peer_representation_response import (
PeerRepresentationResponse,
)
from honcho_core.types.workspaces.session import Session as SessionCore
from honcho_core.types.workspaces.sessions import MessageCreateParam
@ -565,8 +565,8 @@ class AsyncPeer(PeerBase):
if target is None
else (target if isinstance(target, str) else target.id)
)
data: PeerGetRepresentationResponse = (
await self._client.workspaces.peers.get_representation(
data: PeerRepresentationResponse = (
await self._client.workspaces.peers.representation(
peer_id=self.id,
workspace_id=self.workspace_id,
session_id=session_id,
@ -594,7 +594,7 @@ class AsyncPeer(PeerBase):
search_max_distance: float | None = None,
include_most_frequent: bool | None = None,
max_conclusions: int | None = None,
) -> PeerGetContextResponse:
) -> PeerContextResponse:
"""
Get context for this peer, including representation and peer card.
@ -637,7 +637,7 @@ class AsyncPeer(PeerBase):
else (target if isinstance(target, str) else target.id)
)
return await self._client.workspaces.peers.get_context(
return await self._client.workspaces.peers.context(
peer_id=self.id,
workspace_id=self.workspace_id,
target=target_id,

View File

@ -9,9 +9,9 @@ from typing import TYPE_CHECKING, Any
from honcho_core import AsyncHoncho as AsyncHonchoCore
from honcho_core._types import omit
from honcho_core.types.workspaces import QueueGetStatusResponse
from honcho_core.types.workspaces.peer_get_representation_response import (
PeerGetRepresentationResponse,
from honcho_core.types.workspaces import QueueStatusResponse
from honcho_core.types.workspaces.peer_representation_response import (
PeerRepresentationResponse,
)
from honcho_core.types.workspaces.sessions import MessageCreateParam
from honcho_core.types.workspaces.sessions.message import Message
@ -306,16 +306,14 @@ class AsyncSession(SessionBase):
Get the configuration for a peer in this session.
"""
peer_id = peer if isinstance(peer, str) else peer.id
peer_get_config_response = (
await self._client.workspaces.sessions.peers.get_config(
peer_id=peer_id,
workspace_id=self.workspace_id,
session_id=self.id,
)
peer_config_response = await self._client.workspaces.sessions.peers.config(
peer_id=peer_id,
workspace_id=self.workspace_id,
session_id=self.id,
)
return SessionPeerConfig(
observe_others=peer_get_config_response.observe_others,
observe_me=peer_get_config_response.observe_me,
observe_others=peer_config_response.observe_others,
observe_me=peer_config_response.observe_me,
)
async def set_peer_config(
@ -654,7 +652,7 @@ class AsyncSession(SessionBase):
if isinstance(last_user_message, Message)
else last_user_message
)
context = await self._client.workspaces.sessions.get_context(
context = await self._client.workspaces.sessions.context(
session_id=self.id,
workspace_id=self.workspace_id,
tokens=tokens if tokens is not None else omit,
@ -916,8 +914,8 @@ class AsyncSession(SessionBase):
if target is None
else (target if isinstance(target, str) else target.id)
)
data: PeerGetRepresentationResponse = (
await self._client.workspaces.peers.get_representation(
data: PeerRepresentationResponse = (
await self._client.workspaces.peers.representation(
peer_id,
workspace_id=self.workspace_id,
session_id=self.id,
@ -942,7 +940,7 @@ class AsyncSession(SessionBase):
self,
observer: str | PeerBase | None = None,
sender: str | PeerBase | None = None,
) -> QueueGetStatusResponse:
) -> QueueStatusResponse:
"""
Get the queue processing status, optionally scoped to an observer, sender, and/or session.
@ -961,7 +959,7 @@ class AsyncSession(SessionBase):
else (sender if isinstance(sender, str) else sender.id)
)
return await self._client.workspaces.queue.get_status(
return await self._client.workspaces.queue.status(
workspace_id=self.workspace_id,
observer_id=resolved_observer_id,
sender_id=resolved_sender_id,
@ -978,7 +976,7 @@ class AsyncSession(SessionBase):
gt=0,
description="Maximum time to poll in seconds. Defaults to 5 minutes (300 seconds).",
),
) -> QueueGetStatusResponse:
) -> QueueStatusResponse:
"""
Poll get_queue_status until pending_work_units and in_progress_work_units are both 0.
This allows you to guarantee that all messages have been processed by the queue for
@ -992,7 +990,7 @@ class AsyncSession(SessionBase):
timeout: Maximum time to poll in seconds. Defaults to 5 minutes (300 seconds).
Returns:
QueueGetStatusResponse when all work units are complete
QueueStatusResponse when all work units are complete
Raises:
TimeoutError: If timeout is exceeded before work units complete

View File

@ -6,7 +6,7 @@ from typing import Any, Literal
import httpx
from honcho_core import Honcho as HonchoCore
from honcho_core.types.workspaces import QueueGetStatusResponse
from honcho_core.types.workspaces import QueueStatusResponse
from honcho_core.types.workspaces.peer import Peer as PeerCore
from honcho_core.types.workspaces.session import Session as SessionCore
from honcho_core.types.workspaces.sessions.message import Message
@ -449,7 +449,7 @@ class Honcho(BaseModel):
observer: str | PeerBase | None = None,
sender: str | PeerBase | None = None,
session: str | SessionBase | None = None,
) -> QueueGetStatusResponse:
) -> QueueStatusResponse:
"""
Get the queue processing status, optionally scoped to an observer, sender, and/or session.
@ -474,7 +474,7 @@ class Honcho(BaseModel):
else (session if isinstance(session, str) else session.id)
)
return self._client.workspaces.queue.get_status(
return self._client.workspaces.queue.status(
workspace_id=self.workspace_id,
observer_id=resolved_observer_id,
sender_id=resolved_sender_id,
@ -492,7 +492,7 @@ class Honcho(BaseModel):
gt=0,
description="Maximum time to poll in seconds. Defaults to 5 minutes (300 seconds).",
),
) -> QueueGetStatusResponse:
) -> QueueStatusResponse:
"""
Poll get_queue_status until pending_work_units and in_progress_work_units are both 0.
This allows you to guarantee that all messages have been processed by the queue for
@ -507,7 +507,7 @@ class Honcho(BaseModel):
timeout: Maximum time to poll in seconds. Defaults to 5 minutes (300 seconds).
Returns:
QueueGetStatusResponse when all work units are complete
QueueStatusResponse when all work units are complete
Raises:
TimeoutError: If timeout is exceeded before work units complete

View File

@ -19,6 +19,7 @@ __all__ = [
"ConclusionCreateResponse",
"ConclusionScope",
"ConclusionCreateParams",
"AsyncConclusionScope",
]
ConclusionCreateResponse: TypeAlias = list[Conclusion]
@ -202,7 +203,7 @@ class ConclusionScope:
],
)
def get_representation(
def representation(
self,
search_query: str | None = None,
search_top_k: int | None = None,
@ -228,7 +229,7 @@ class ConclusionScope:
"""
from honcho_core._types import omit
response = self._client.workspaces.peers.get_representation(
response = self._client.workspaces.peers.representation(
peer_id=self.observer,
workspace_id=self.workspace_id,
target=self.observed,
@ -450,7 +451,7 @@ class AsyncConclusionScope:
"""
from honcho_core._types import omit
response = await self._client.workspaces.peers.get_representation(
response = await self._client.workspaces.peers.representation(
peer_id=self.observer,
workspace_id=self.workspace_id,
target=self.observed,

View File

@ -7,11 +7,11 @@ from typing import Literal
from honcho_core import Honcho as HonchoCore
from honcho_core._types import omit
from honcho_core.types.workspaces import PeerCardResponse
from honcho_core.types.workspaces.peer_get_context_response import (
PeerGetContextResponse,
from honcho_core.types.workspaces.peer_context_response import (
PeerContextResponse,
)
from honcho_core.types.workspaces.peer_get_representation_response import (
PeerGetRepresentationResponse,
from honcho_core.types.workspaces.peer_representation_response import (
PeerRepresentationResponse,
)
from honcho_core.types.workspaces.session import Session as SessionCore
from honcho_core.types.workspaces.sessions import MessageCreateParam
@ -541,24 +541,20 @@ class Peer(PeerBase):
if target is None
else (target if isinstance(target, str) else target.id)
)
data: PeerGetRepresentationResponse = (
self._client.workspaces.peers.get_representation(
peer_id=self.id,
workspace_id=self.workspace_id,
session_id=session_id,
target=target_id,
search_query=search_query if search_query is not None else omit,
search_top_k=search_top_k if search_top_k is not None else omit,
search_max_distance=search_max_distance
if search_max_distance is not None
else omit,
include_most_frequent=include_most_frequent
if include_most_frequent is not None
else omit,
max_conclusions=max_conclusions
if max_conclusions is not None
else omit,
)
data: PeerRepresentationResponse = self._client.workspaces.peers.representation(
peer_id=self.id,
workspace_id=self.workspace_id,
session_id=session_id,
target=target_id,
search_query=search_query if search_query is not None else omit,
search_top_k=search_top_k if search_top_k is not None else omit,
search_max_distance=search_max_distance
if search_max_distance is not None
else omit,
include_most_frequent=include_most_frequent
if include_most_frequent is not None
else omit,
max_conclusions=max_conclusions if max_conclusions is not None else omit,
)
return data.representation
@ -570,7 +566,7 @@ class Peer(PeerBase):
search_max_distance: float | None = None,
include_most_frequent: bool | None = None,
max_conclusions: int | None = None,
) -> PeerGetContextResponse:
) -> PeerContextResponse:
"""
Get context for this peer, including representation and peer card.
@ -614,7 +610,7 @@ class Peer(PeerBase):
else (target if isinstance(target, str) else target.id)
)
return self._client.workspaces.peers.get_context(
return self._client.workspaces.peers.context(
peer_id=self.id,
workspace_id=self.workspace_id,
target=target_id,

View File

@ -8,9 +8,9 @@ from typing import TYPE_CHECKING, Any
from honcho_core import Honcho as HonchoCore
from honcho_core._types import omit
from honcho_core.types.workspaces import QueueGetStatusResponse
from honcho_core.types.workspaces.peer_get_representation_response import (
PeerGetRepresentationResponse,
from honcho_core.types.workspaces import QueueStatusResponse
from honcho_core.types.workspaces.peer_representation_response import (
PeerRepresentationResponse,
)
from honcho_core.types.workspaces.sessions import MessageCreateParam
from honcho_core.types.workspaces.sessions.message import Message
@ -281,14 +281,14 @@ class Session(SessionBase):
Get the configuration for a peer in this session.
"""
peer_id = peer if isinstance(peer, str) else peer.id
peer_get_config_response = self._client.workspaces.sessions.peers.get_config(
peer_config_response = self._client.workspaces.sessions.peers.config(
peer_id=peer_id,
workspace_id=self.workspace_id,
session_id=self.id,
)
return SessionPeerConfig(
observe_others=peer_get_config_response.observe_others,
observe_me=peer_get_config_response.observe_me,
observe_others=peer_config_response.observe_others,
observe_me=peer_config_response.observe_me,
)
def set_peer_config(self, peer: str | PeerBase, config: SessionPeerConfig) -> None:
@ -625,7 +625,7 @@ class Session(SessionBase):
if isinstance(last_user_message, Message)
else last_user_message
)
context = self._client.workspaces.sessions.get_context(
context = self._client.workspaces.sessions.context(
session_id=self.id,
workspace_id=self.workspace_id,
tokens=tokens if tokens is not None else omit,
@ -887,24 +887,20 @@ class Session(SessionBase):
if target is None
else (target if isinstance(target, str) else target.id)
)
data: PeerGetRepresentationResponse = (
self._client.workspaces.peers.get_representation(
peer_id,
workspace_id=self.workspace_id,
session_id=self.id,
target=target_id,
search_query=search_query if search_query is not None else omit,
search_top_k=search_top_k if search_top_k is not None else omit,
search_max_distance=search_max_distance
if search_max_distance is not None
else omit,
include_most_frequent=include_most_frequent
if include_most_frequent is not None
else omit,
max_conclusions=max_conclusions
if max_conclusions is not None
else omit,
)
data: PeerRepresentationResponse = self._client.workspaces.peers.representation(
peer_id,
workspace_id=self.workspace_id,
session_id=self.id,
target=target_id,
search_query=search_query if search_query is not None else omit,
search_top_k=search_top_k if search_top_k is not None else omit,
search_max_distance=search_max_distance
if search_max_distance is not None
else omit,
include_most_frequent=include_most_frequent
if include_most_frequent is not None
else omit,
max_conclusions=max_conclusions if max_conclusions is not None else omit,
)
return data.representation
@ -913,7 +909,7 @@ class Session(SessionBase):
self,
observer: str | PeerBase | None = None,
sender: str | PeerBase | None = None,
) -> QueueGetStatusResponse:
) -> QueueStatusResponse:
"""
Get the queue processing status, optionally scoped to an observer, sender, and/or session.
@ -932,7 +928,7 @@ class Session(SessionBase):
else (sender if isinstance(sender, str) else sender.id)
)
return self._client.workspaces.queue.get_status(
return self._client.workspaces.queue.status(
workspace_id=self.workspace_id,
observer_id=resolved_observer_id,
sender_id=resolved_sender_id,
@ -949,7 +945,7 @@ class Session(SessionBase):
gt=0,
description="Maximum time to poll in seconds. Defaults to 5 minutes (300 seconds).",
),
) -> QueueGetStatusResponse:
) -> QueueStatusResponse:
"""
Poll get_queue_status until pending_work_units and in_progress_work_units are both 0.
This allows you to guarantee that all messages have been processed by the queue for
@ -963,7 +959,7 @@ class Session(SessionBase):
timeout: Maximum time to poll in seconds. Defaults to 5 minutes (300 seconds).
Returns:
QueueGetStatusResponse when all work units are complete
QueueStatusResponse when all work units are complete
Raises:
TimeoutError: If timeout is exceeded before work units complete

View File

@ -17,7 +17,7 @@ jest.mock('@honcho-ai/core', () => {
getOrCreate: jest.fn(),
},
queue: {
getStatus: jest.fn(),
status: jest.fn(),
},
getOrCreate: jest.fn().mockResolvedValue({ id: 'test-workspace', metadata: {} }),
update: jest.fn(),
@ -419,7 +419,7 @@ describe('Honcho Client', () => {
pending_work_units: 2,
sessions: { 'session1': { status: 'active' } },
};
mockClient.workspaces.queue.getStatus.mockResolvedValue(mockStatus);
mockClient.workspaces.queue.status.mockResolvedValue(mockStatus);
const status = await honcho.getQueueStatus();
@ -430,7 +430,7 @@ describe('Honcho Client', () => {
pendingWorkUnits: 2,
sessions: { 'session1': { status: 'active' } },
});
expect(mockClient.workspaces.queue.getStatus).toHaveBeenCalledWith(
expect(mockClient.workspaces.queue.status).toHaveBeenCalledWith(
'test-workspace',
{}
);
@ -443,7 +443,7 @@ describe('Honcho Client', () => {
in_progress_work_units: 1,
pending_work_units: 1,
};
mockClient.workspaces.queue.getStatus.mockResolvedValue(mockStatus);
mockClient.workspaces.queue.status.mockResolvedValue(mockStatus);
const status = await honcho.getQueueStatus({
observer: 'observer1',
@ -458,7 +458,7 @@ describe('Honcho Client', () => {
pendingWorkUnits: 1,
sessions: undefined,
});
expect(mockClient.workspaces.queue.getStatus).toHaveBeenCalledWith(
expect(mockClient.workspaces.queue.status).toHaveBeenCalledWith(
'test-workspace',
{
observer_id: 'observer1',
@ -477,7 +477,7 @@ describe('Honcho Client', () => {
in_progress_work_units: 0,
pending_work_units: 0,
};
mockClient.workspaces.queue.getStatus.mockResolvedValue(mockStatusComplete);
mockClient.workspaces.queue.status.mockResolvedValue(mockStatusComplete);
const status = await honcho.pollQueueStatus();
@ -497,7 +497,7 @@ describe('Honcho Client', () => {
in_progress_work_units: 2,
pending_work_units: 1,
};
mockClient.workspaces.queue.getStatus.mockResolvedValue(mockStatusPending);
mockClient.workspaces.queue.status.mockResolvedValue(mockStatusPending);
await expect(honcho.pollQueueStatus({ timeoutMs: 0 })).rejects.toThrow();
});

View File

@ -25,7 +25,7 @@ describe('Honcho SDK Integration Tests', () => {
getOrCreate: jest.fn(),
update: jest.fn(),
search: jest.fn(),
getRepresentation: jest.fn(),
representation: jest.fn(),
},
sessions: {
list: jest.fn(),
@ -38,7 +38,7 @@ describe('Honcho SDK Integration Tests', () => {
messages: { create: jest.fn(), list: jest.fn() },
getOrCreate: jest.fn(),
update: jest.fn(),
getContext: jest.fn(),
context: jest.fn(),
search: jest.fn(),
},
getOrCreate: jest.fn(),
@ -90,7 +90,7 @@ describe('Honcho SDK Integration Tests', () => {
mockWorkspacesApi.workspaces.sessions.messages.create.mockResolvedValue(
{}
)
mockWorkspacesApi.workspaces.sessions.getContext.mockResolvedValue(
mockWorkspacesApi.workspaces.sessions.context.mockResolvedValue(
mockContextData
)
mockWorkspacesApi.workspaces.peers.chat.mockResolvedValue({
@ -284,7 +284,7 @@ describe('Honcho SDK Integration Tests', () => {
mockWorkspacesApi.workspaces.peers.chat.mockRejectedValue(
new Error('Chat API failed')
)
mockWorkspacesApi.workspaces.sessions.getContext.mockRejectedValue(
mockWorkspacesApi.workspaces.sessions.context.mockRejectedValue(
new Error('Context API failed')
)
@ -372,7 +372,7 @@ describe('Honcho SDK Integration Tests', () => {
const mockRepresentation =
'Alice likes coffee\nAlice works as a developer\nAlice is a coffee-drinking developer'
mockWorkspacesApi.workspaces.peers.getRepresentation.mockResolvedValue({
mockWorkspacesApi.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentation,
})
@ -384,7 +384,7 @@ describe('Honcho SDK Integration Tests', () => {
const globalRep = await session.getRepresentation('alice')
expect(globalRep).toBe(mockRepresentation)
expect(
mockWorkspacesApi.workspaces.peers.getRepresentation
mockWorkspacesApi.workspaces.peers.representation
).toHaveBeenCalledWith('integration-test-workspace', 'alice', {
session_id: 'working-rep-session',
target: undefined,
@ -399,7 +399,7 @@ describe('Honcho SDK Integration Tests', () => {
const targetRep = await session.getRepresentation(alice, bob)
expect(targetRep).toBe(mockRepresentation)
expect(
mockWorkspacesApi.workspaces.peers.getRepresentation
mockWorkspacesApi.workspaces.peers.representation
).toHaveBeenCalledWith('integration-test-workspace', 'alice', {
session_id: 'working-rep-session',
target: 'bob',
@ -423,7 +423,7 @@ describe('Honcho SDK Integration Tests', () => {
total: 0,
hasNextPage: false,
})
mockWorkspacesApi.workspaces.sessions.getContext.mockResolvedValue({
mockWorkspacesApi.workspaces.sessions.context.mockResolvedValue({
messages: [],
})
@ -461,7 +461,7 @@ describe('Honcho SDK Integration Tests', () => {
expect(typeof message.metadata).toBe('object')
// Mock successful operations
mockWorkspacesApi.workspaces.sessions.getContext.mockResolvedValue({
mockWorkspacesApi.workspaces.sessions.context.mockResolvedValue({
messages: [{ id: 'msg1', content: 'Hello', peer_id: 'typed-peer' }],
summary: {
content: 'Test summary',

View File

@ -554,12 +554,12 @@ describe('Peer', () => {
describe('getRepresentation', () => {
beforeEach(() => {
mockClient.workspaces.peers.getRepresentation = jest.fn();
mockClient.workspaces.peers.representation = jest.fn();
});
it('should get working representation with no parameters', async () => {
const mockRepresentation = 'Observation 1\nObservation 2\nConclusion 1';
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentation,
});
@ -567,7 +567,7 @@ describe('Peer', () => {
expect(result).toBe(mockRepresentation);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'test-peer', {
session_id: undefined,
target: undefined,
@ -581,7 +581,7 @@ describe('Peer', () => {
it('should get working representation with session as string', async () => {
const mockRepresentation = 'Session-scoped observation';
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentation,
});
@ -589,7 +589,7 @@ describe('Peer', () => {
expect(result).toBe(mockRepresentation);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'test-peer', {
session_id: 'session-123',
target: undefined,
@ -604,7 +604,7 @@ describe('Peer', () => {
it('should get working representation with session as Session object', async () => {
const session = new Session('session-123', 'test-workspace', mockClient);
const mockRepresentation = 'Session object observation';
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentation,
});
@ -612,7 +612,7 @@ describe('Peer', () => {
expect(result).toBe(mockRepresentation);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'test-peer', {
session_id: 'session-123',
target: undefined,
@ -626,7 +626,7 @@ describe('Peer', () => {
it('should get working representation with target as string', async () => {
const mockRepresentation = "Observer's view of target";
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentation,
});
@ -634,7 +634,7 @@ describe('Peer', () => {
expect(result).toBe(mockRepresentation);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'test-peer', {
session_id: undefined,
target: 'target-peer',
@ -649,7 +649,7 @@ describe('Peer', () => {
it('should get working representation with target as Peer object', async () => {
const targetPeer = new Peer('target-peer', 'test-workspace', mockClient);
const mockRepresentation = "Observer's view of target peer object";
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentation,
});
@ -657,7 +657,7 @@ describe('Peer', () => {
expect(result).toBe(mockRepresentation);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'test-peer', {
session_id: undefined,
target: 'target-peer',
@ -671,7 +671,7 @@ describe('Peer', () => {
it('should get working representation with search query', async () => {
const mockRepresentation = 'Query-curated observation';
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentation,
});
@ -683,7 +683,7 @@ describe('Peer', () => {
expect(result).toBe(mockRepresentation);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'test-peer', {
session_id: undefined,
target: undefined,
@ -697,7 +697,7 @@ describe('Peer', () => {
it('should get working representation with custom size', async () => {
const mockRepresentation = 'Limited observations';
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentation,
});
@ -705,7 +705,7 @@ describe('Peer', () => {
expect(result).toBe(mockRepresentation);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'test-peer', {
session_id: undefined,
target: undefined,
@ -721,7 +721,7 @@ describe('Peer', () => {
const session = new Session('session-123', 'test-workspace', mockClient);
const targetPeer = new Peer('target-peer', 'test-workspace', mockClient);
const mockRepresentation = 'Fully parameterized observation\nConclusion with all params';
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentation,
});
@ -733,7 +733,7 @@ describe('Peer', () => {
expect(result).toBe(mockRepresentation);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'test-peer', {
session_id: 'session-123',
target: 'target-peer',
@ -747,7 +747,7 @@ describe('Peer', () => {
it('should get working representation with string session and string target', async () => {
const mockRepresentation = 'String params observation';
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentation,
});
@ -759,7 +759,7 @@ describe('Peer', () => {
expect(result).toBe(mockRepresentation);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'test-peer', {
session_id: 'session-456',
target: 'target-peer-123',
@ -773,7 +773,7 @@ describe('Peer', () => {
it('should handle boundary size values', async () => {
const mockRepresentationString = 'Boundary test representation';
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentationString,
});
@ -781,7 +781,7 @@ describe('Peer', () => {
const result1 = await peer.getRepresentation(undefined, undefined, { maxConclusions: 1 });
expect(result1).toBe(mockRepresentationString);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenLastCalledWith('test-workspace', 'test-peer', {
session_id: undefined,
target: undefined,
@ -796,7 +796,7 @@ describe('Peer', () => {
const result2 = await peer.getRepresentation(undefined, undefined, { maxConclusions: 100 });
expect(result2).toBe(mockRepresentationString);
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenLastCalledWith('test-workspace', 'test-peer', {
session_id: undefined,
target: undefined,
@ -809,7 +809,7 @@ describe('Peer', () => {
});
it('should handle API errors', async () => {
mockClient.workspaces.peers.getRepresentation.mockRejectedValue(
mockClient.workspaces.peers.representation.mockRejectedValue(
new Error('Working representation fetch failed')
);

View File

@ -14,7 +14,7 @@ jest.mock('@honcho-ai/core', () => {
set: jest.fn(),
remove: jest.fn(),
list: jest.fn(),
getConfig: jest.fn(),
config: jest.fn(),
setConfig: jest.fn(),
},
messages: {
@ -26,14 +26,14 @@ jest.mock('@honcho-ai/core', () => {
update: jest.fn(),
delete: jest.fn(),
clone: jest.fn(),
getContext: jest.fn(),
context: jest.fn(),
search: jest.fn(),
},
peers: {
getRepresentation: jest.fn(),
representation: jest.fn(),
},
queue: {
getStatus: jest.fn(),
status: jest.fn(),
},
getOrCreate: jest.fn(),
update: jest.fn(),
@ -333,7 +333,7 @@ describe('Session', () => {
describe('getPeerConfig', () => {
it('should return peer configuration', async () => {
const mockConfig = { observe_me: true, observe_others: false }
mockClient.workspaces.sessions.peers.getConfig.mockResolvedValue(
mockClient.workspaces.sessions.peers.config.mockResolvedValue(
mockConfig
)
@ -341,14 +341,14 @@ describe('Session', () => {
expect(config).toEqual(mockConfig)
expect(
mockClient.workspaces.sessions.peers.getConfig
mockClient.workspaces.sessions.peers.config
).toHaveBeenCalledWith('test-workspace', 'test-session', 'peer1')
})
it('should handle Peer object input', async () => {
const peer = new Peer('peer1', 'test-workspace', mockClient)
const mockConfig = { observe_me: false, observe_others: true }
mockClient.workspaces.sessions.peers.getConfig.mockResolvedValue(
mockClient.workspaces.sessions.peers.config.mockResolvedValue(
mockConfig
)
@ -356,7 +356,7 @@ describe('Session', () => {
expect(config).toEqual(mockConfig)
expect(
mockClient.workspaces.sessions.peers.getConfig
mockClient.workspaces.sessions.peers.config
).toHaveBeenCalledWith('test-workspace', 'test-session', 'peer1')
})
})
@ -775,7 +775,7 @@ describe('Session', () => {
token_count: 100,
},
}
mockClient.workspaces.sessions.getContext.mockResolvedValue(mockContext)
mockClient.workspaces.sessions.context.mockResolvedValue(mockContext)
const context = await session.getContext()
@ -783,7 +783,7 @@ describe('Session', () => {
expect(context.sessionId).toBe('test-session')
expect(context.messages).toEqual(mockContext.messages)
expect(context.summary?.content).toBe('Conversation summary')
expect(mockClient.workspaces.sessions.getContext).toHaveBeenCalledWith(
expect(mockClient.workspaces.sessions.context).toHaveBeenCalledWith(
'test-workspace',
'test-session',
{ tokens: undefined, summary: undefined }
@ -801,12 +801,12 @@ describe('Session', () => {
token_count: 50,
},
}
mockClient.workspaces.sessions.getContext.mockResolvedValue(mockContext)
mockClient.workspaces.sessions.context.mockResolvedValue(mockContext)
const context = await session.getContext({ summary: true, tokens: 1000 })
expect(context).toBeInstanceOf(SessionContext)
expect(mockClient.workspaces.sessions.getContext).toHaveBeenCalledWith(
expect(mockClient.workspaces.sessions.context).toHaveBeenCalledWith(
'test-workspace',
'test-session',
{ tokens: 1000, summary: true }
@ -817,7 +817,7 @@ describe('Session', () => {
const mockContext = {
messages: [{ id: 'msg1', content: 'Hello', peer_id: 'peer1' }],
}
mockClient.workspaces.sessions.getContext.mockResolvedValue(mockContext)
mockClient.workspaces.sessions.context.mockResolvedValue(mockContext)
const context = await session.getContext()
@ -825,7 +825,7 @@ describe('Session', () => {
})
it('should handle API errors', async () => {
mockClient.workspaces.sessions.getContext.mockRejectedValue(
mockClient.workspaces.sessions.context.mockRejectedValue(
new Error('Failed to get context')
)
@ -907,7 +907,7 @@ describe('Session', () => {
describe('getRepresentation', () => {
it('should get working representation with peer string', async () => {
const mockRepresentationString = 'Some knowledge about the peer'
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentationString,
})
@ -916,7 +916,7 @@ describe('Session', () => {
expect(typeof result).toBe('string')
expect(result).toBe(mockRepresentationString)
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'peer1', {
session_id: 'test-session',
target: undefined,
@ -931,7 +931,7 @@ describe('Session', () => {
it('should get working representation with Peer object', async () => {
const peer = new Peer('peer1', 'test-workspace', mockClient)
const mockRepresentationString = 'Some knowledge'
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentationString,
})
@ -940,7 +940,7 @@ describe('Session', () => {
expect(typeof result).toBe('string')
expect(result).toBe(mockRepresentationString)
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'peer1', {
session_id: 'test-session',
target: undefined,
@ -954,7 +954,7 @@ describe('Session', () => {
it('should get working representation with target peer string', async () => {
const mockRepresentationString = 'What peer1 knows about target'
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentationString,
})
@ -963,7 +963,7 @@ describe('Session', () => {
expect(typeof result).toBe('string')
expect(result).toBe(mockRepresentationString)
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'peer1', {
session_id: 'test-session',
target: 'target-peer',
@ -979,7 +979,7 @@ describe('Session', () => {
const peer = new Peer('peer1', 'test-workspace', mockClient)
const target = new Peer('target-peer', 'test-workspace', mockClient)
const mockRepresentationString = 'What peer1 knows about target'
mockClient.workspaces.peers.getRepresentation.mockResolvedValue({
mockClient.workspaces.peers.representation.mockResolvedValue({
representation: mockRepresentationString,
})
@ -988,7 +988,7 @@ describe('Session', () => {
expect(typeof result).toBe('string')
expect(result).toBe(mockRepresentationString)
expect(
mockClient.workspaces.peers.getRepresentation
mockClient.workspaces.peers.representation
).toHaveBeenCalledWith('test-workspace', 'peer1', {
session_id: 'test-session',
target: 'target-peer',
@ -1001,7 +1001,7 @@ describe('Session', () => {
})
it('should handle API errors', async () => {
mockClient.workspaces.peers.getRepresentation.mockRejectedValue(
mockClient.workspaces.peers.representation.mockRejectedValue(
new Error('Failed to get working representation')
)
@ -1106,7 +1106,7 @@ describe('Session', () => {
pending_work_units: 2,
sessions: { session1: { status: 'active' } },
}
mockClient.workspaces.queue.getStatus.mockResolvedValue(mockStatus)
mockClient.workspaces.queue.status.mockResolvedValue(mockStatus)
const status = await session.getQueueStatus()
@ -1117,7 +1117,7 @@ describe('Session', () => {
pendingWorkUnits: 2,
sessions: { session1: { status: 'active' } },
})
expect(mockClient.workspaces.queue.getStatus).toHaveBeenCalledWith(
expect(mockClient.workspaces.queue.status).toHaveBeenCalledWith(
'test-workspace',
{ session_id: 'test-session' }
)
@ -1130,7 +1130,7 @@ describe('Session', () => {
in_progress_work_units: 1,
pending_work_units: 1,
}
mockClient.workspaces.queue.getStatus.mockResolvedValue(mockStatus)
mockClient.workspaces.queue.status.mockResolvedValue(mockStatus)
const status = await session.getQueueStatus({
observer: 'observer1',
@ -1144,7 +1144,7 @@ describe('Session', () => {
pendingWorkUnits: 1,
sessions: undefined,
})
expect(mockClient.workspaces.queue.getStatus).toHaveBeenCalledWith(
expect(mockClient.workspaces.queue.status).toHaveBeenCalledWith(
'test-workspace',
{
observer_id: 'observer1',
@ -1162,7 +1162,7 @@ describe('Session', () => {
in_progress_work_units: 0,
pending_work_units: 0,
}
mockClient.workspaces.queue.getStatus.mockResolvedValue(
mockClient.workspaces.queue.status.mockResolvedValue(
mockStatusComplete
)
@ -1176,7 +1176,7 @@ describe('Session', () => {
sessions: undefined,
})
expect(mockClient.workspaces.queue.getStatus).toHaveBeenCalledWith(
expect(mockClient.workspaces.queue.status).toHaveBeenCalledWith(
'test-workspace',
{ session_id: 'test-session' }
)
@ -1189,7 +1189,7 @@ describe('Session', () => {
in_progress_work_units: 2,
pending_work_units: 1,
}
mockClient.workspaces.queue.getStatus.mockResolvedValue(mockStatusPending)
mockClient.workspaces.queue.status.mockResolvedValue(mockStatusPending)
await expect(
session.pollQueueStatus({ timeoutMs: 100 })

View File

@ -4,7 +4,7 @@
"": {
"name": "@honcho-ai/sdk",
"dependencies": {
"@honcho-ai/core": "https://pkg.stainless.com/s/honcho-node/dc4ac0ffd48642608da2f99f306f77f7ee5a9d1d/dist.tar.gz",
"@honcho-ai/core": "2.0.0",
"@types/node": "^24.0.1",
"zod": "4.0.0",
},
@ -106,7 +106,7 @@
"@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.8", "", { "os": "win32", "cpu": "x64" }, "sha512-RguzimPoZWtBapfKhKjcWXBVI91tiSprqdBYu7tWhgN8pKRZhw24rFeNZTNf6UiBfjCYCi9eFQs/JzJZIhuK4w=="],
"@honcho-ai/core": ["@honcho-ai/core@https://pkg.stainless.com/s/honcho-node/dc4ac0ffd48642608da2f99f306f77f7ee5a9d1d/dist.tar.gz", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }],
"@honcho-ai/core": ["@honcho-ai/core@2.0.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-2VVtVuyRtFzHvEca74PBrio5ee97P3rl2q68WkGT++8TL+a3SmFPTxjezxCvGgOForvUVZJu4I/7MkmpqwE+Fg=="],
"@istanbuljs/load-nyc-config": ["@istanbuljs/load-nyc-config@1.1.0", "", { "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" } }, "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ=="],

View File

@ -20,7 +20,7 @@
"test:coverage": "jest --coverage"
},
"dependencies": {
"@honcho-ai/core": "https://pkg.stainless.com/s/honcho-node/dc4ac0ffd48642608da2f99f306f77f7ee5a9d1d/dist.tar.gz",
"@honcho-ai/core": "2.0.0",
"@types/node": "^24.0.1",
"zod": "4.0.0"
},

View File

@ -1,8 +1,8 @@
import HonchoCore from '@honcho-ai/core'
import type { DefaultQuery } from '@honcho-ai/core/core'
import type {
QueueGetStatusParams,
QueueGetStatusResponse,
QueueStatusParams,
QueueStatusResponse,
} from '@honcho-ai/core/resources/workspaces/queue'
import type { Message } from '@honcho-ai/core/resources/workspaces/sessions/messages'
import { Page } from './pagination'
@ -504,7 +504,7 @@ export class Honcho {
completedWorkUnits: number
inProgressWorkUnits: number
pendingWorkUnits: number
sessions?: Record<string, QueueGetStatusResponse.Sessions>
sessions?: Record<string, QueueStatusResponse.Sessions>
}> {
const resolvedObserverId = options?.observer
? typeof options.observer === 'string'
@ -522,12 +522,12 @@ export class Honcho {
: options.session.id
: undefined
const queryParams: QueueGetStatusParams = {}
const queryParams: QueueStatusParams = {}
if (resolvedObserverId) queryParams.observer_id = resolvedObserverId
if (resolvedSenderId) queryParams.sender_id = resolvedSenderId
if (resolvedSessionId) queryParams.session_id = resolvedSessionId
const status = await this._client.workspaces.queue.getStatus(
const status = await this._client.workspaces.queue.status(
this.workspaceId,
queryParams
)
@ -570,7 +570,7 @@ export class Honcho {
completedWorkUnits: number
inProgressWorkUnits: number
pendingWorkUnits: number
sessions?: Record<string, QueueGetStatusResponse.Sessions>
sessions?: Record<string, QueueStatusResponse.Sessions>
}> {
const timeoutMs = options?.timeoutMs ?? 300000 // Default to 5 minutes
const startTime = Date.now()

View File

@ -300,7 +300,7 @@ export class ConclusionScope {
* @returns Promise resolving to a string of the representation
*/
async getRepresentation(options?: RepresentationOptions): Promise<string> {
const response = await this._client.workspaces.peers.getRepresentation(
const response = await this._client.workspaces.peers.representation(
this.workspaceId,
this.observer,
{

View File

@ -535,7 +535,7 @@ export class Peer {
: getRepresentationParams.target.id
: undefined
const response = await this._client.workspaces.peers.getRepresentation(
const response = await this._client.workspaces.peers.representation(
this.workspaceId,
this.id,
{
@ -590,7 +590,7 @@ export class Peer {
: target.id
: undefined
const response = await this._client.workspaces.peers.getContext(
const response = await this._client.workspaces.peers.context(
this.workspaceId,
this.id,
{

View File

@ -1,7 +1,7 @@
import type HonchoCore from '@honcho-ai/core'
import type {
QueueGetStatusParams,
QueueGetStatusResponse,
QueueStatusParams,
QueueStatusResponse,
} from '@honcho-ai/core/resources/workspaces/queue'
import type { Message } from '@honcho-ai/core/resources/workspaces/sessions/messages'
import type { Uploadable } from '@honcho-ai/core/uploads'
@ -337,7 +337,7 @@ export class Session {
*/
async getPeerConfig(peer: string | Peer): Promise<SessionPeerConfig> {
const peerId = typeof peer === 'string' ? peer : peer.id
return await this._client.workspaces.sessions.peers.getConfig(
return await this._client.workspaces.sessions.peers.config(
this.workspaceId,
this.id,
peerId
@ -700,7 +700,7 @@ export class Session {
? contextParams.lastUserMessage
: contextParams.lastUserMessage?.id
const context = await this._client.workspaces.sessions.getContext(
const context = await this._client.workspaces.sessions.context(
this.workspaceId,
this.id,
{
@ -820,7 +820,7 @@ export class Session {
completedWorkUnits: number
inProgressWorkUnits: number
pendingWorkUnits: number
sessions?: Record<string, QueueGetStatusResponse.Sessions>
sessions?: Record<string, QueueStatusResponse.Sessions>
}> {
const resolvedObserverId = options?.observer
? typeof options.observer === 'string'
@ -833,13 +833,13 @@ export class Session {
: options.sender.id
: undefined
const queryParams: QueueGetStatusParams = {
const queryParams: QueueStatusParams = {
session_id: this.id, // Always use this session's ID
}
if (resolvedObserverId) queryParams.observer_id = resolvedObserverId
if (resolvedSenderId) queryParams.sender_id = resolvedSenderId
const status = await this._client.workspaces.queue.getStatus(
const status = await this._client.workspaces.queue.status(
this.workspaceId,
queryParams
)
@ -880,7 +880,7 @@ export class Session {
completedWorkUnits: number
inProgressWorkUnits: number
pendingWorkUnits: number
sessions?: Record<string, QueueGetStatusResponse.Sessions>
sessions?: Record<string, QueueStatusResponse.Sessions>
}> {
const timeoutMs = options?.timeoutMs ?? 300000 // Default to 5 minutes
const startTime = Date.now()
@ -1057,7 +1057,7 @@ export class Session {
: getRepresentationParams.target.id
: undefined
const response = await this._client.workspaces.peers.getRepresentation(
const response = await this._client.workspaces.peers.representation(
this.workspaceId,
peerId,
{

View File

@ -252,7 +252,7 @@ class DeriverSettings(BackupLLMSettingsMixin, HonchoSettings):
# Whether to deduplicate documents when creating them
DEDUPLICATE: bool = True
MAX_OUTPUT_TOKENS: Annotated[int, Field(default=10_000, gt=0, le=100_000)] = 4096
MAX_OUTPUT_TOKENS: Annotated[int, Field(default=4096, gt=0, le=100_000)] = 4096
THINKING_BUDGET_TOKENS: Annotated[int, Field(default=1024, gt=0, le=5000)] = 1024
LOG_OBSERVATIONS: bool = False

View File

@ -86,7 +86,7 @@ def _extract_pattern_snippet(
if len(content) <= max_chars:
return content
match = re.search(pattern, content, re.IGNORECASE)
match = re.search(re.escape(pattern), content, re.IGNORECASE)
if not match:
# No match, return beginning
return content[:max_chars] + "..."
@ -1086,7 +1086,7 @@ async def _handle_get_observation_context(
async def _handle_search_messages(ctx: ToolContext, tool_input: dict[str, Any]) -> str:
"""Handle search_messages tool."""
query = tool_input["query"]
limit = min(tool_input.get("limit", 10), 10) # Cap at 10
limit = min(tool_input.get("limit", 10), 20) # Cap at 20
snippets = await crud.search_messages(
ctx.db,
workspace_name=ctx.workspace_name,
@ -1106,7 +1106,7 @@ async def _handle_grep_messages(ctx: ToolContext, tool_input: dict[str, Any]) ->
text = tool_input.get("text", "")
if not text:
return "ERROR: 'text' parameter is required"
limit = min(tool_input.get("limit", 10), 15) # Cap at 15
limit = min(tool_input.get("limit", 10), 30) # Cap at 30
context_window = min(tool_input.get("context_window", 2), 2) # Cap context
snippets = await crud.grep_messages(

View File

@ -47,23 +47,28 @@ def normalize_configuration_dict(raw: dict[str, Any]) -> dict[str, Any]:
normalized: dict[str, Any] = dict(raw)
reasoning_raw = normalized.get("reasoning")
reasoning: dict[str, Any] = (
cast(dict[str, Any], reasoning_raw) if isinstance(reasoning_raw, dict) else {}
)
reasoning_present = "reasoning" in normalized
reasoning: dict[str, Any]
if isinstance(reasoning_raw, dict):
reasoning = dict(cast(dict[str, Any], reasoning_raw))
else:
reasoning = {}
reasoning_enabled_explicit = reasoning.get("enabled") is not None
if not reasoning_enabled_explicit:
deriver_raw = normalized.get("deriver")
deriver = (
cast(dict[str, Any], deriver_raw) if isinstance(deriver_raw, dict) else {}
)
deriver: dict[str, Any]
if isinstance(deriver_raw, dict):
deriver = dict(cast(dict[str, Any], deriver_raw))
else:
deriver = {}
if deriver.get("enabled") is not None:
reasoning["enabled"] = bool(deriver["enabled"])
if not reasoning_enabled_explicit and normalized.get("skip_deriver") is True:
reasoning["enabled"] = False
if reasoning:
if reasoning_present or reasoning:
normalized["reasoning"] = reasoning
normalized.pop("deriver", None)

View File

@ -2,7 +2,7 @@ from unittest.mock import patch
import pytest
from fastapi.testclient import TestClient
from honcho_core.types.workspaces import QueueGetStatusResponse
from honcho_core.types.workspaces import QueueStatusResponse
from honcho_core.types.workspaces.sessions.message import Message
from sdks.python.src.honcho.async_client.client import AsyncHoncho
@ -201,7 +201,7 @@ async def test_get_deriver_status(client_fixture: tuple[Honcho | AsyncHoncho, st
assert isinstance(honcho_client, AsyncHoncho)
# Test with no parameters - this should work in the SDK even though API requires at least one
status = await honcho_client.get_queue_status()
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
assert hasattr(status, "total_work_units")
assert hasattr(status, "completed_work_units")
assert hasattr(status, "in_progress_work_units")
@ -211,28 +211,28 @@ async def test_get_deriver_status(client_fixture: tuple[Honcho | AsyncHoncho, st
peer = await honcho_client.peer(id="test-peer-deriver-status")
await peer.get_metadata() # Create the peer
status = await honcho_client.get_queue_status(observer=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
# Test with session_id only
session = await honcho_client.session(id="test-session-deriver-status")
await session.get_metadata() # Create the session
status = await honcho_client.get_queue_status(session=session.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
# Test with both peer and session
status = await honcho_client.get_queue_status(
observer=peer.id, session=session.id
)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
# Test with sender
status = await honcho_client.get_queue_status(observer=peer.id, sender=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
else:
assert isinstance(honcho_client, Honcho)
# Test with no parameters
status = honcho_client.get_queue_status()
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
assert hasattr(status, "total_work_units")
assert hasattr(status, "completed_work_units")
assert hasattr(status, "in_progress_work_units")
@ -242,21 +242,21 @@ async def test_get_deriver_status(client_fixture: tuple[Honcho | AsyncHoncho, st
peer = honcho_client.peer(id="test-peer-queue-status")
peer.get_metadata() # Create the peer
status = honcho_client.get_queue_status(observer=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
# Test with session_id only
session = honcho_client.session(id="test-session-queue-status")
session.get_metadata() # Create the session
status = honcho_client.get_queue_status(session=session.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
# Test with both peer and session
status = honcho_client.get_queue_status(observer=peer.id, session=session.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
# Test with sender
status = honcho_client.get_queue_status(observer=peer.id, sender=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
@pytest.mark.asyncio
@ -268,7 +268,7 @@ async def test_poll_queue_status(client_fixture: tuple[Honcho | AsyncHoncho, str
# Mock the get_queue_status method to return a "completed" status
# to avoid infinite polling in tests
completed_status = QueueGetStatusResponse(
completed_status = QueueStatusResponse(
total_work_units=0,
completed_work_units=0,
in_progress_work_units=0,
@ -281,7 +281,7 @@ async def test_poll_queue_status(client_fixture: tuple[Honcho | AsyncHoncho, str
honcho_client, "get_queue_status", return_value=completed_status
):
status = await honcho_client.poll_queue_status()
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
assert status.pending_work_units == 0
assert status.in_progress_work_units == 0
@ -293,14 +293,14 @@ async def test_poll_queue_status(client_fixture: tuple[Honcho | AsyncHoncho, str
status = await honcho_client.poll_queue_status(
observer=peer.id, sender=peer.id
)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
else:
assert isinstance(honcho_client, Honcho)
with patch.object(
honcho_client, "get_queue_status", return_value=completed_status
):
status = honcho_client.poll_queue_status()
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
assert status.pending_work_units == 0
assert status.in_progress_work_units == 0
@ -310,7 +310,7 @@ async def test_poll_queue_status(client_fixture: tuple[Honcho | AsyncHoncho, str
honcho_client, "get_queue_status", return_value=completed_status
):
status = honcho_client.poll_queue_status(observer=peer.id, sender=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
@pytest.mark.asyncio

View File

@ -1,7 +1,7 @@
from unittest.mock import AsyncMock, patch
import pytest
from honcho_core.types.workspaces import QueueGetStatusResponse
from honcho_core.types.workspaces import QueueStatusResponse
from sdks.python.src.honcho.async_client.client import AsyncHoncho
from sdks.python.src.honcho.async_client.peer import AsyncPeer
@ -465,7 +465,7 @@ async def test_session_get_queue_status(
assert isinstance(session, AsyncSession)
status = await session.get_queue_status()
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
assert hasattr(status, "total_work_units")
assert hasattr(status, "completed_work_units")
assert hasattr(status, "in_progress_work_units")
@ -476,15 +476,15 @@ async def test_session_get_queue_status(
peer = await honcho_client.peer(id="test-peer-session-deriver")
await peer.get_metadata() # Create the peer
status = await session.get_queue_status(observer=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
# Test with sender only
status = await session.get_queue_status(sender=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
# Test with both observer and sender
status = await session.get_queue_status(observer=peer.id, sender=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
else:
assert isinstance(honcho_client, Honcho)
session = honcho_client.session(id="test-session-deriver-status")
@ -492,7 +492,7 @@ async def test_session_get_queue_status(
# Test with no parameters
status = session.get_queue_status()
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
assert hasattr(status, "total_work_units")
assert hasattr(status, "completed_work_units")
assert hasattr(status, "in_progress_work_units")
@ -503,15 +503,15 @@ async def test_session_get_queue_status(
peer = honcho_client.peer(id="test-peer-session-deriver")
peer.get_metadata() # Create the peer
status = session.get_queue_status(observer=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
# Test with sender only
status = session.get_queue_status(sender=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
# Test with both observer and sender
status = session.get_queue_status(observer=peer.id, sender=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
@pytest.mark.asyncio
@ -525,7 +525,7 @@ async def test_session_poll_queue_status(
# Mock the get_queue_status method to return a "completed" status
# to avoid infinite polling in tests
completed_status = QueueGetStatusResponse(
completed_status = QueueStatusResponse(
total_work_units=0,
completed_work_units=0,
in_progress_work_units=0,
@ -543,7 +543,7 @@ async def test_session_poll_queue_status(
new=AsyncMock(return_value=completed_status),
):
status = await session.poll_queue_status()
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
assert status.pending_work_units == 0
assert status.in_progress_work_units == 0
@ -555,7 +555,7 @@ async def test_session_poll_queue_status(
new=AsyncMock(return_value=completed_status),
):
status = await session.poll_queue_status(observer=peer.id, sender=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
else:
assert isinstance(honcho_client, Honcho)
session = honcho_client.session(id="test-session-poll-queue")
@ -565,7 +565,7 @@ async def test_session_poll_queue_status(
session.__class__, "get_queue_status", return_value=completed_status
):
status = session.poll_queue_status()
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
assert status.pending_work_units == 0
assert status.in_progress_work_units == 0
@ -575,7 +575,7 @@ async def test_session_poll_queue_status(
session.__class__, "get_queue_status", return_value=completed_status
):
status = session.poll_queue_status(observer=peer.id, sender=peer.id)
assert isinstance(status, QueueGetStatusResponse)
assert isinstance(status, QueueStatusResponse)
@pytest.mark.asyncio

View File

@ -13,7 +13,7 @@ import httpx
from anthropic import AsyncAnthropic
from honcho.async_client.session import AsyncSession
from honcho.session_context import SessionContext
from honcho_core.types.workspaces import QueueGetStatusResponse
from honcho_core.types.workspaces import QueueStatusResponse
from pydantic import ValidationError
# Adjust path to allow imports from tests.bench
@ -292,7 +292,7 @@ class UnifiedTestExecutor:
await asyncio.sleep(1)
start = time.time()
while time.time() - start < timeout:
status: QueueGetStatusResponse = await self.client.get_queue_status()
status: QueueStatusResponse = await self.client.get_queue_status()
# status structure from schema: DeriverStatus with pending_work_units, in_progress_work_units
if status.pending_work_units == 0 and status.in_progress_work_units == 0:
return

22
uv.lock
View File

@ -859,7 +859,7 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "honcho-core", url = "https://pkg.stainless.com/s/honcho-python/b5539818022ab4ad757250ea3234648f8dc5906e/honcho_core-1.8.0-py3-none-any.whl" },
{ name = "honcho-core", specifier = "==1.9.0" },
{ name = "httpx", specifier = ">=0.28.0,<1" },
{ name = "pydantic", specifier = ">=2.0.0,<3" },
{ name = "typing-extensions", marker = "python_full_version < '3.12'", specifier = ">=4.12.0" },
@ -870,8 +870,8 @@ dev = [{ name = "ruff", specifier = ">=0.11.13" }]
[[package]]
name = "honcho-core"
version = "1.8.0"
source = { url = "https://pkg.stainless.com/s/honcho-python/b5539818022ab4ad757250ea3234648f8dc5906e/honcho_core-1.8.0-py3-none-any.whl" }
version = "1.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "distro" },
@ -880,23 +880,11 @@ dependencies = [
{ name = "sniffio" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e0/be/4496fa3deb447e958d06490e6a18ae173c8ddb427a86a60a2bcaa9bba649/honcho_core-1.9.0.tar.gz", hash = "sha256:baaa61be3826e9fd3489037a5606c6d87e6ff6ac0c2d17139ed2153691f9bae8", size = 144184, upload-time = "2026-01-12T22:11:27.159Z" }
wheels = [
{ url = "https://pkg.stainless.com/s/honcho-python/b5539818022ab4ad757250ea3234648f8dc5906e/honcho_core-1.8.0-py3-none-any.whl", hash = "sha256:2684c2dbd4d6479c381028038500b9972bbf3c66cf3f37c404f8cbe4cecfba01" },
{ url = "https://files.pythonhosted.org/packages/d1/64/99a96c61e15e6aaa696ea67a752bdcda0472712de2d5f5a0f827f0de22d9/honcho_core-1.9.0-py3-none-any.whl", hash = "sha256:b80f1215b5f9f5e134421b7243865eea6620e66fbe39629e3d59cc688031cf90", size = 138655, upload-time = "2026-01-12T22:11:26.204Z" },
]
[package.metadata]
requires-dist = [
{ name = "aiohttp", marker = "extra == 'aiohttp'" },
{ name = "anyio", specifier = ">=3.5.0,<5" },
{ name = "distro", specifier = ">=1.7.0,<2" },
{ name = "httpx", specifier = ">=0.23.0,<1" },
{ name = "httpx-aiohttp", marker = "extra == 'aiohttp'", specifier = ">=0.1.9" },
{ name = "pydantic", specifier = ">=1.9.0,<3" },
{ name = "sniffio" },
{ name = "typing-extensions", specifier = ">=4.10,<5" },
]
provides-extras = ["aiohttp"]
[[package]]
name = "httpcore"
version = "1.0.9"