Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions agent/model_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -1454,10 +1454,11 @@ def get_model_context_length(
d. GMI /models endpoint
e. Ollama native /api/show probe (any base_url, provider-agnostic)
f. models.dev registry lookup (with :cloud/-cloud suffix fallback)
6. OpenRouter live API metadata (Kimi-family 32k guard)
7. Hardcoded defaults (broad family patterns, longest-key-first)
8. Local server query (last resort)
9. Default fallback (256K)"""
6. Exact curated defaults
7. OpenRouter live API metadata (Kimi-family 32k guard)
8. Fuzzy hardcoded defaults (broad family patterns, longest-key-first)
9. Local server query (last resort)
10. Default fallback (256K)"""
# 0. Explicit config override — user knows best
if config_context_length is not None and isinstance(config_context_length, int) and config_context_length > 0:
return config_context_length
Expand Down Expand Up @@ -1672,15 +1673,21 @@ def get_model_context_length(
if ctx:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This model-context-resolution change is unrelated to the stated Hindsight recall work. Please exclude it from the Hindsight salvage so its behavior can be reviewed independently.

return ctx

# 6. OpenRouter live API metadata — provider-unaware fallback.
# 6. Exact curated defaults. Provider-aware branches above still win, but
# an exact local entry is more specific than provider-unaware OpenRouter
# metadata for bare model IDs like "hy3-preview".
exact_default = DEFAULT_CONTEXT_LENGTHS.get(model.lower())
if exact_default:
return exact_default

# 7. OpenRouter live API metadata — provider-unaware fallback.
# Only consulted when the provider is unknown (no effective_provider),
# because OpenRouter data is community-maintained and can be incorrect
# for models that belong to known providers with curated defaults.
if not effective_provider:
metadata = fetch_model_metadata()
if model in metadata:
or_ctx = metadata[model].get("context_length", DEFAULT_FALLBACK_CONTEXT)
# Guard against stale OpenRouter metadata for Kimi-family models.
if or_ctx == 32768 and _model_name_suggests_kimi(model):
logger.info(
"Rejecting OpenRouter metadata context=%s for %r "
Expand All @@ -1690,9 +1697,7 @@ def get_model_context_length(
else:
return or_ctx

# 7. (reserved)

# 8. Hardcoded defaults (fuzzy match — longest key first for specificity)
# 8. Fuzzy hardcoded defaults (longest key first for specificity)
# Only check `default_model in model` (is the key a substring of the input).
# The reverse (`model in default_model`) causes shorter names like
# "claude-sonnet-4" to incorrectly match "claude-sonnet-4-6" and return 1M.
Expand Down
19 changes: 12 additions & 7 deletions gateway/platforms/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,11 @@ def _thread_metadata_for_source(source, reply_to_message_id: str | None = None)
if thread_id is None:
return None
metadata = {"thread_id": thread_id}
if _platform_name(getattr(source, "platform", None)) == "telegram" and getattr(source, "chat_type", None) == "dm":
if (
_platform_name(getattr(source, "platform", None)) == "telegram"
and getattr(source, "chat_type", None) == "dm"
and str(thread_id).isdigit()
):
metadata["telegram_dm_topic_reply_fallback"] = True
tid = str(thread_id)
if tid and tid not in {"", "1"}:
Expand All @@ -79,11 +83,12 @@ def _thread_metadata_for_source(source, reply_to_message_id: str | None = None)
def _reply_anchor_for_event(event) -> str | None:
"""Return reply_to id for platforms that need reply semantics.

Telegram forum/supergroup topics should be routed by topic metadata, not by
replying to the triggering message. Hermes-created Telegram private-chat
topic lanes prefer replying to the triggering user message so the answer
stays attached to the active lane; synthetic/resumed sends fall back to
``direct_messages_topic_id`` metadata when no message id is available.
Telegram forum/supergroup topics are routed by topic metadata, but keeping
the triggering message as the reply anchor preserves threaded reply
behavior in adapters that support both. Hermes-created Telegram
private-chat topic lanes prefer replying to the triggering user message;
synthetic/resumed sends fall back to ``direct_messages_topic_id`` metadata
when no message id is available.
"""
source = getattr(event, "source", None)
platform = _platform_name(getattr(source, "platform", None))
Expand All @@ -93,7 +98,7 @@ def _reply_anchor_for_event(event) -> str | None:
# topic seed/anchor can render the bot response outside the active lane.
return getattr(event, "message_id", None) or getattr(event, "reply_to_message_id", None)
if platform == "telegram" and thread_id:
return None
return getattr(event, "message_id", None) or getattr(event, "reply_to_message_id", None)
if platform == "feishu" and thread_id and getattr(event, "reply_to_message_id", None):
return getattr(event, "reply_to_message_id", None)
return getattr(event, "message_id", None)
Expand Down
7 changes: 6 additions & 1 deletion gateway/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,7 @@ def _reload_runtime_env_preserving_config_authority() -> None:
MessageEvent,
MessageType,
_reply_anchor_for_event,
_thread_metadata_for_source,
merge_pending_message_event,
)
from gateway.restart import (
Expand Down Expand Up @@ -11500,7 +11501,10 @@ async def _deliver_media_from_response(
local_files, _ = adapter.extract_local_files(cleaned)
local_files = BasePlatformAdapter.filter_local_delivery_paths(local_files)

_thread_meta = self._thread_metadata_for_source(event.source, self._reply_anchor_for_event(event))
if hasattr(self, "_thread_metadata_for_source") and hasattr(self, "_reply_anchor_for_event"):
_thread_meta = self._thread_metadata_for_source(event.source, self._reply_anchor_for_event(event))
else:
_thread_meta = _thread_metadata_for_source(event.source, _reply_anchor_for_event(event))

_VIDEO_EXTS = {'.mp4', '.mov', '.avi', '.mkv', '.webm', '.3gp'}
_IMAGE_EXTS = {'.jpg', '.jpeg', '.png', '.webp', '.gif'}
Expand Down Expand Up @@ -13767,6 +13771,7 @@ def _thread_metadata_for_source(
if (
getattr(source, "platform", None) == Platform.TELEGRAM
and getattr(source, "chat_type", None) == "dm"
and str(thread_id).isdigit()
):
metadata["telegram_dm_topic_reply_fallback"] = True
# Telegram DM topic lanes need direct_messages_topic_id in metadata
Expand Down
2 changes: 1 addition & 1 deletion plugins/memory/hindsight/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ Available in `hybrid` and `tools` memory modes:
| Tool | Description |
|------|-------------|
| `hindsight_retain` | Store information with auto entity extraction; supports optional per-call `tags` |
| `hindsight_recall` | Multi-strategy search (semantic + entity graph) |
| `hindsight_recall` | Retrieve memories. Defaults to semantic/entity-graph recall; supports optional `method` (`recall`, `list`, `entity`) plus per-call `budget`, `max_tokens`, `types`, `tags`, `tags_match`, and exact-match `metadata` filters. `tag_groups` is passed through for `recall`/`entity`; `method="list"` uses the public list API and supports `limit`/`offset`; `method="entity"` supports `max_entity_tokens`. |
| `hindsight_reflect` | Cross-memory synthesis (LLM-powered) |

## Environment Variables
Expand Down
Loading
Loading