fix: change api contract to return ResourceNotFoundException (#375)

This commit is contained in:
Vineeth Voruganti 2026-02-09 13:04:37 -05:00 committed by GitHub
parent a78dfc24af
commit 7cc39d2b56
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 74 additions and 30 deletions

View File

@ -389,6 +389,9 @@ describe('Peer', () => {
describe('POST /peers/:id/card', () => {
test('card returns string array or null', async () => {
const peer = await client.peer('card-peer')
const session = await client.session('card-session')
await session.addPeers([peer.id])
await session.addMessages([peer.message('hello')])
const card = await peer.card()
@ -399,6 +402,9 @@ describe('Peer', () => {
test('card with target peer', async () => {
const observer = await client.peer('card-observer')
const observed = await client.peer('card-observed')
const session = await client.session('card-target-session')
await session.addPeers([observer.id, observed.id])
await session.addMessages([observer.message('hello'), observed.message('hi')])
const card = await observer.card(observed)
@ -408,6 +414,10 @@ describe('Peer', () => {
test('card with target ID string', async () => {
const peer = await client.peer('card-string-peer')
const target = await client.peer('some-target-id')
const session = await client.session('card-string-session')
await session.addPeers([peer.id, target.id])
await session.addMessages([peer.message('hello'), target.message('hi')])
const card = await peer.card('some-target-id')
@ -479,6 +489,9 @@ describe('Peer', () => {
describe('POST /peers/:id/context', () => {
test('context returns representation and card', async () => {
const peer = await client.peer('context-peer')
const session = await client.session('context-session')
await session.addPeers([peer.id])
await session.addMessages([peer.message('hello')])
const context = await peer.context()
@ -492,6 +505,9 @@ describe('Peer', () => {
test('context with target peer', async () => {
const observer = await client.peer('context-observer')
const observed = await client.peer('context-observed')
const session = await client.session('context-target-session')
await session.addPeers([observer.id, observed.id])
await session.addMessages([observer.message('hello'), observed.message('hi')])
const context = await observer.context({ target: observed })
@ -501,6 +517,9 @@ describe('Peer', () => {
test('context with options', async () => {
const peer = await client.peer('context-options-peer')
const session = await client.session('context-options-session')
await session.addPeers([peer.id])
await session.addMessages([peer.message('hello')])
const context = await peer.context({
searchQuery: 'interests',

View File

@ -3,12 +3,12 @@ from __future__ import annotations
import logging
from typing import cast
from sqlalchemy import select, update
from sqlalchemy import update
from sqlalchemy.ext.asyncio import AsyncSession
from src import models, schemas
from src.cache.client import cache
from src.crud.peer import get_or_create_peers, peer_cache_key
from src.crud.peer import get_or_create_peers, get_peer, peer_cache_key
logger = logging.getLogger(__name__)
@ -32,15 +32,12 @@ async def get_peer_card(
observer: Peer name of the observer
Returns:
The peer's card text if present, otherwise None (also None if peer not found).
The peer's card text if present, otherwise None.
Raises:
ResourceNotFoundException: If the peer does not exist.
"""
peer = await db.scalar(
select(models.Peer)
.where(models.Peer.workspace_name == workspace_name)
.where(models.Peer.name == observer)
)
if peer is None:
return None
peer = await get_peer(db, workspace_name, schemas.PeerCreate(name=observer))
return cast(
list[str] | None,
peer.internal_metadata.get(

View File

@ -8,7 +8,7 @@ using the DialecticAgent.
import logging
from collections.abc import AsyncIterator
from src import crud
from src import crud, schemas
from src.config import ReasoningLevel
from src.dependencies import tracked_db
from src.dialectic.core import DialecticAgent
@ -40,6 +40,11 @@ async def agentic_chat(
The synthesized answer string
"""
async with tracked_db("dialectic.agentic_chat") as db:
# Validate that the peers exist before proceeding
await crud.get_peer(db, workspace_name, schemas.PeerCreate(name=observer))
if observer != observed:
await crud.get_peer(db, workspace_name, schemas.PeerCreate(name=observed))
# Resolve configuration to check if peer cards should be used
session = None
if session_name:
@ -101,6 +106,11 @@ async def agentic_chat_stream(
Chunks of the response text as they are generated
"""
async with tracked_db("dialectic.agentic_chat_stream") as db:
# Validate that the peers exist before proceeding
await crud.get_peer(db, workspace_name, schemas.PeerCreate(name=observer))
if observer != observed:
await crud.get_peer(db, workspace_name, schemas.PeerCreate(name=observed))
# Resolve configuration to check if peer cards should be used
session = None
if session_name:

View File

@ -20,7 +20,7 @@ from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from src import crud
from src import crud, schemas
from src.config import settings
from src.schemas import ResolvedConfiguration
from src.telemetry import prometheus_metrics
@ -142,6 +142,11 @@ class BaseSpecialist(ABC):
task_name = f"dreamer_{self.name}_{run_id}"
start_time = time.perf_counter()
# Validate that the peers exist before proceeding
await crud.get_peer(db, workspace_name, schemas.PeerCreate(name=observer))
if observer != observed:
await crud.get_peer(db, workspace_name, schemas.PeerCreate(name=observed))
# Determine if peer card tools should be included
peer_card_enabled = configuration is None or configuration.peer_card.create

View File

@ -3,6 +3,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from src import models
from src.crud.peer_card import construct_peer_card_label, get_peer_card, set_peer_card
from src.exceptions import ResourceNotFoundException
@pytest.mark.asyncio
@ -50,33 +51,33 @@ async def test_peer_card_get_set_roundtrip(
@pytest.mark.asyncio
async def test_get_peer_card_missing_peer_returns_none(
async def test_get_peer_card_missing_peer_raises(
db_session: AsyncSession, sample_data: tuple[models.Workspace, models.Peer]
):
"""Getting a peer card for a non-existent peer should return None while creating the peer."""
"""Getting a peer card for a non-existent peer should raise ResourceNotFoundException."""
workspace, _existing_peer = sample_data
result = await get_peer_card(
db_session,
workspace.name,
observer="missing-peer",
observed="missing-peer",
)
assert result is None
with pytest.raises(ResourceNotFoundException):
await get_peer_card(
db_session,
workspace.name,
observer="missing-peer",
observed="missing-peer",
)
@pytest.mark.asyncio
async def test_get_peer_card_missing_workspace_returns_none(
async def test_get_peer_card_missing_workspace_raises(
db_session: AsyncSession, sample_data: tuple[models.Workspace, models.Peer]
):
"""Getting a peer card for a non-existent workspace should return None."""
"""Getting a peer card for a non-existent workspace should raise ResourceNotFoundException."""
_workspace, peer = sample_data
result = await get_peer_card(
db_session,
"missing-workspace",
observer=peer.name,
observed=peer.name,
)
assert result is None
with pytest.raises(ResourceNotFoundException):
await get_peer_card(
db_session,
"missing-workspace",
observer=peer.name,
observed=peer.name,
)
@pytest.mark.asyncio

View File

@ -113,6 +113,10 @@ def test_peer_card_operations(honcho_test_client: Honcho):
peer = honcho_test_client.peer(id="card-test-peer")
target = honcho_test_client.peer(id="card-test-target")
# Create the peers on the server by adding a message
session = honcho_test_client.session(id="card-test-session")
session.add_messages([peer.message("hello"), target.message("hi")])
# Initially card should be None
card = peer.get_card()
assert card is None

View File

@ -159,12 +159,20 @@ async def test_peer_card_with_empty_target(client_fixture: tuple[Honcho, str]):
if client_type == "async":
peer = await honcho_client.aio.peer(id="test-card-validation-peer")
session = await honcho_client.aio.session(id="test-card-validation-session")
# Create the peer on the server by adding a message
await session.aio.add_messages([peer.message("hello")])
# Empty target is treated as no target (same as None)
result = await peer.aio.get_card(target="")
assert result is None or isinstance(result, list)
else:
peer = honcho_client.peer(id="test-card-validation-peer")
session = honcho_client.session(id="test-card-validation-session")
# Create the peer on the server by adding a message
session.add_messages([peer.message("hello")])
# Empty target is treated as no target (same as None)
result = peer.get_card(target="")