fix: Make deriver use get context internally (#184)
* fix: Make deriver use get context internally * chore: Code Rabbit nits
This commit is contained in:
parent
04b1f64d2e
commit
13645d2e05
|
|
@ -181,3 +181,6 @@ timing_logs.csv
|
|||
|
||||
config.toml
|
||||
.aider*
|
||||
|
||||
CRUSH.md
|
||||
.crush/
|
||||
|
|
|
|||
76
CLAUDE.md
76
CLAUDE.md
|
|
@ -1,3 +1,7 @@
|
|||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
# Honcho Overview
|
||||
|
||||
## What is Honcho?
|
||||
|
|
@ -100,28 +104,64 @@ All API routes follow the pattern: `/v1/{resource}/{id}/{action}`
|
|||
|
||||
```
|
||||
src/
|
||||
├── main.py # FastAPI app setup with middleware and exception handlers
|
||||
├── models.py # SQLAlchemy ORM models with proper type annotations
|
||||
├── schemas.py # Pydantic validation schemas for API
|
||||
├── crud.py # Database operations
|
||||
├── dependencies.py # Dependency injection (DB sessions)
|
||||
├── exceptions.py # Custom exception types
|
||||
├── security.py # JWT authentication
|
||||
├── agent.py # Dialectic API implementation
|
||||
├── routers/ # API endpoints
|
||||
├── main.py # FastAPI app setup with middleware and exception handlers
|
||||
├── models.py # SQLAlchemy ORM models with proper type annotations
|
||||
├── schemas.py # Pydantic validation schemas for API
|
||||
├── config.py # Configuration management
|
||||
├── db.py # Database connection and session management
|
||||
├── dependencies.py # Dependency injection (DB sessions)
|
||||
├── exceptions.py # Custom exception types
|
||||
├── security.py # JWT authentication
|
||||
├── embedding_client.py # Embedding service client
|
||||
├── crud/ # Database operations
|
||||
│ ├── __init__.py
|
||||
│ ├── collection.py # Collection CRUD operations
|
||||
│ ├── deriver.py # Deriver-related CRUD operations
|
||||
│ ├── document.py # Document CRUD operations
|
||||
│ ├── message.py # Message CRUD operations
|
||||
│ ├── peer.py # Peer CRUD operations
|
||||
│ ├── representation.py # Representation CRUD operations
|
||||
│ ├── session.py # Session CRUD operations
|
||||
│ ├── webhook.py # Webhook CRUD operations
|
||||
│ └── workspace.py # Workspace CRUD operations
|
||||
├── dialectic/ # Dialectic API implementation
|
||||
│ ├── __init__.py
|
||||
│ ├── chat.py # Chat functionality
|
||||
│ ├── prompts.py # Prompt templates
|
||||
│ └── utils.py # Dialectic utilities
|
||||
├── routers/ # API endpoints
|
||||
│ ├── workspaces.py
|
||||
│ ├── peers.py
|
||||
│ ├── sessions.py
|
||||
│ ├── messages.py
|
||||
│ └── keys.py
|
||||
├── deriver/ # Background processing system
|
||||
│ ├── consumer.py # Message processing logic
|
||||
│ ├── queue.py # Queue management
|
||||
│ └── tom/ # Theory of Mind implementations
|
||||
└── utils/ # Utilities
|
||||
├── history.py # Session history management
|
||||
├── cache.py # Caching utilities
|
||||
└── model_client.py # LLM client abstraction
|
||||
│ ├── keys.py
|
||||
│ └── webhooks.py # Webhook endpoints
|
||||
├── deriver/ # Background processing system
|
||||
│ ├── __init__.py
|
||||
│ ├── __main__.py # Deriver entry point
|
||||
│ ├── consumer.py # Message consumer
|
||||
│ ├── deriver.py # Main deriver logic
|
||||
│ ├── enqueue.py # Queue operations
|
||||
│ ├── prompts.py # Deriver prompts
|
||||
│ ├── queue_manager.py # Queue management
|
||||
│ ├── queue_payload.py # Queue payload schemas
|
||||
│ └── utils.py # Deriver utilities
|
||||
├── utils/ # Utilities
|
||||
│ ├── __init__.py
|
||||
│ ├── clients.py # LLM client abstraction
|
||||
│ ├── embedding_store.py # Vector storage management
|
||||
│ ├── files.py # File handling utilities
|
||||
│ ├── filter.py # Query filtering utilities
|
||||
│ ├── formatting.py # Message formatting utilities
|
||||
│ ├── logging.py # Logging configuration
|
||||
│ ├── search.py # Search functionality
|
||||
│ ├── shared_models.py # Shared data models
|
||||
│ ├── summarizer.py # Session summarization
|
||||
│ └── types.py # Type definitions
|
||||
└── webhooks/ # Webhook system
|
||||
├── events.py # Webhook event definitions
|
||||
├── webhook_delivery.py # Webhook delivery logic
|
||||
└── README.md # Webhook documentation
|
||||
```
|
||||
|
||||
- Tests in pytest with fixtures in tests/conftest.py
|
||||
|
|
|
|||
|
|
@ -194,6 +194,11 @@ class DeriverSettings(HonchoSettings):
|
|||
# Thinking budget tokens are only applied when using Anthropic as provider
|
||||
THINKING_BUDGET_TOKENS: Annotated[int, Field(default=1024, gt=0, le=5000)] = 1024
|
||||
|
||||
# Context token limit for get_context method
|
||||
CONTEXT_TOKEN_LIMIT: Annotated[int, Field(default=30_000, gt=1000, le=100_000)] = (
|
||||
30_000
|
||||
)
|
||||
|
||||
|
||||
class DialecticSettings(HonchoSettings):
|
||||
model_config = SettingsConfigDict(env_prefix="DIALECTIC_", extra="ignore") # pyright: ignore
|
||||
|
|
|
|||
|
|
@ -169,12 +169,14 @@ class Deriver:
|
|||
# created_at is now always a datetime object from Pydantic validation
|
||||
message_dt_obj = created_at
|
||||
|
||||
formatted_history = await summarizer.get_summarized_history(
|
||||
# Use get_session_context_formatted with configurable token limit
|
||||
formatted_history = await summarizer.get_session_context_formatted(
|
||||
db,
|
||||
workspace_name,
|
||||
session_name,
|
||||
token_limit=settings.DERIVER.CONTEXT_TOKEN_LIMIT,
|
||||
cutoff=message_id,
|
||||
summary_type=summarizer.SummaryType.SHORT,
|
||||
include_summary=True,
|
||||
)
|
||||
|
||||
# instantiate embedding store from collection
|
||||
|
|
|
|||
|
|
@ -388,56 +388,13 @@ async def get_session_context(
|
|||
"""
|
||||
token_limit = tokens or config.settings.GET_CONTEXT_MAX_TOKENS
|
||||
|
||||
summary_content = ""
|
||||
messages_tokens = token_limit
|
||||
messages_start_id = 0
|
||||
|
||||
if summary:
|
||||
summary_tokens_limit = token_limit * 0.4
|
||||
|
||||
latest_short_summary, latest_long_summary = await summarizer.get_both_summaries(
|
||||
db,
|
||||
workspace_name=workspace_id,
|
||||
session_name=session_id,
|
||||
)
|
||||
|
||||
long_len = latest_long_summary["token_count"] if latest_long_summary else 0
|
||||
short_len = latest_short_summary["token_count"] if latest_short_summary else 0
|
||||
|
||||
# The goal is to return the longest summary that fits within the token limit
|
||||
# Sometimes (rarely) the short summary can be longer than the long summary,
|
||||
# so we need to check for that and return the longer one.
|
||||
|
||||
if (
|
||||
latest_long_summary
|
||||
and long_len <= summary_tokens_limit
|
||||
and long_len > short_len
|
||||
):
|
||||
summary_content = latest_long_summary["content"]
|
||||
messages_tokens = token_limit - latest_long_summary["token_count"]
|
||||
messages_start_id = latest_long_summary["message_id"]
|
||||
elif (
|
||||
latest_short_summary and short_len <= summary_tokens_limit and short_len > 0
|
||||
):
|
||||
summary_content = latest_short_summary["content"]
|
||||
messages_tokens = token_limit - latest_short_summary["token_count"]
|
||||
messages_start_id = latest_short_summary["message_id"]
|
||||
else:
|
||||
logger.info(
|
||||
"No summary available for get_context call with token limit %s, returning empty string. long_summary_len: %s, short_summary_len: %s",
|
||||
token_limit,
|
||||
long_len,
|
||||
short_len,
|
||||
)
|
||||
summary_content = ""
|
||||
|
||||
# Get the recent messages after summary to return verbatim
|
||||
messages = await crud.get_messages_id_range(
|
||||
# Use the shared get_session_context function from summarizer
|
||||
summary_content, messages = await summarizer.get_session_context(
|
||||
db,
|
||||
workspace_name=workspace_id,
|
||||
session_name=session_id,
|
||||
start_id=messages_start_id,
|
||||
token_limit=messages_tokens,
|
||||
token_limit=token_limit,
|
||||
include_summary=summary,
|
||||
)
|
||||
|
||||
return schemas.SessionContext(
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ __all__ = [
|
|||
"get_summary",
|
||||
"get_both_summaries",
|
||||
"get_summarized_history",
|
||||
"get_session_context",
|
||||
"get_session_context_formatted",
|
||||
"SummaryType",
|
||||
"Summary",
|
||||
]
|
||||
|
|
@ -554,6 +556,126 @@ async def get_both_summaries(
|
|||
return summaries.get(SummaryType.SHORT.value), summaries.get(SummaryType.LONG.value)
|
||||
|
||||
|
||||
async def get_session_context(
|
||||
db: AsyncSession,
|
||||
workspace_name: str,
|
||||
session_name: str,
|
||||
token_limit: int,
|
||||
*,
|
||||
cutoff: int | None = None,
|
||||
include_summary: bool = True,
|
||||
) -> tuple[str, list[models.Message]]:
|
||||
"""
|
||||
Get session context similar to the API endpoint but for internal use.
|
||||
|
||||
Args:
|
||||
db: Database session
|
||||
workspace_name: The workspace name
|
||||
session_name: The session name
|
||||
token_limit: Maximum tokens for the context
|
||||
cutoff: Optional message ID to stop at (exclusive)
|
||||
include_summary: Whether to include summary if available
|
||||
|
||||
Returns:
|
||||
Tuple of (summary_content, messages) where summary_content is the summary text (or empty string)
|
||||
and messages is the list of message objects
|
||||
"""
|
||||
summary_content = ""
|
||||
messages_tokens = token_limit
|
||||
messages_start_id = 0
|
||||
|
||||
if include_summary:
|
||||
# Allocate 40% of tokens to summary, 60% to messages
|
||||
summary_tokens_limit = int(token_limit * 0.4)
|
||||
|
||||
latest_short_summary, latest_long_summary = await get_both_summaries(
|
||||
db, workspace_name, session_name
|
||||
)
|
||||
|
||||
long_len = latest_long_summary["token_count"] if latest_long_summary else 0
|
||||
short_len = latest_short_summary["token_count"] if latest_short_summary else 0
|
||||
|
||||
# Return the longest summary that fits within the token limit
|
||||
if (
|
||||
latest_long_summary
|
||||
and long_len <= summary_tokens_limit
|
||||
and long_len > short_len
|
||||
):
|
||||
summary_content = latest_long_summary["content"]
|
||||
messages_tokens = token_limit - latest_long_summary["token_count"]
|
||||
messages_start_id = latest_long_summary["message_id"]
|
||||
elif (
|
||||
latest_short_summary and short_len <= summary_tokens_limit and short_len > 0
|
||||
):
|
||||
summary_content = latest_short_summary["content"]
|
||||
messages_tokens = token_limit - latest_short_summary["token_count"]
|
||||
messages_start_id = latest_short_summary["message_id"]
|
||||
else:
|
||||
logger.warning(
|
||||
"No summary available for get_context call with token limit %s, returning empty string. long_summary_len: %s, short_summary_len: %s",
|
||||
token_limit,
|
||||
long_len,
|
||||
short_len,
|
||||
)
|
||||
|
||||
# Get recent messages after summary
|
||||
messages = await crud.get_messages_id_range(
|
||||
db,
|
||||
workspace_name,
|
||||
session_name,
|
||||
start_id=messages_start_id,
|
||||
end_id=cutoff,
|
||||
token_limit=messages_tokens,
|
||||
)
|
||||
|
||||
return summary_content, messages
|
||||
|
||||
|
||||
async def get_session_context_formatted(
|
||||
db: AsyncSession,
|
||||
workspace_name: str,
|
||||
session_name: str,
|
||||
token_limit: int,
|
||||
*,
|
||||
cutoff: int | None = None,
|
||||
include_summary: bool = True,
|
||||
) -> str:
|
||||
"""
|
||||
Get formatted session context as a string for internal use (e.g., deriver).
|
||||
|
||||
This is a convenience wrapper around get_session_context that formats
|
||||
the output as a string.
|
||||
"""
|
||||
summary_content, messages = await get_session_context(
|
||||
db,
|
||||
workspace_name,
|
||||
session_name,
|
||||
token_limit,
|
||||
cutoff=cutoff,
|
||||
include_summary=include_summary,
|
||||
)
|
||||
|
||||
# Format the messages
|
||||
messages_text = _format_messages(messages)
|
||||
|
||||
if summary_content and messages_text:
|
||||
return f"""<summary>
|
||||
{summary_content}
|
||||
</summary>
|
||||
|
||||
<recent_messages>
|
||||
{messages_text}
|
||||
</recent_messages>"""
|
||||
elif summary_content:
|
||||
return f"""<summary>
|
||||
{summary_content}
|
||||
</summary>"""
|
||||
elif messages_text:
|
||||
return messages_text
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def _format_messages(messages: list[models.Message]) -> str:
|
||||
"""
|
||||
Format a list of messages into a string by concatenating their content and
|
||||
|
|
|
|||
Loading…
Reference in New Issue