fix(anthropic+feishu): model-gate max_tokens fallback; wire Feishu channel_prompt

Two independent fixes salvaged from #12811 (closing it; one of its three
bundled fixes — Discord free_response — is already on main).

Anthropic max_tokens (#12790): the chat-completions max_tokens fallback only
fired for OpenRouter/Nous URLs, so any other proxy serving a Claude model
(AWS Bedrock, NVIDIA, LiteLLM, vLLM, corporate gateways) shipped requests
with no max_tokens and inherited the proxy's low default (Bedrock: 4096),
exhausting on thinking + large tool calls. Changed the gate in
chat_completion_helpers.build_api_kwargs from URL-gated to model-gated:
fires whenever the model matches an _ANTHROPIC_OUTPUT_LIMITS key. This also
fixes a latent miss — the old 'claude' substring gate skipped MiniMax and
Qwen3 even on OpenRouter. Remains a last-resort fallback (build_kwargs only
applies it after ephemeral/user/profile max_tokens), so it never overrides
an explicit value, and only touches the chat-completions transport (native
Anthropic Messages API is a separate path).

Feishu channel_prompt (#12805): the Feishu adapter never resolved
channel_prompts config, unlike Discord/Slack, so per-channel role prompts
were silently ignored. Added _resolve_channel_prompt() (delegating to the
shared gateway.platforms.base.resolve_channel_prompt) and wired it into all
three MessageEvent construction sites — inbound message, reaction routing,
and card-action routing.

Tests: tests/gateway/test_feishu_channel_prompts.py (6 cases) covering exact
match, parent-thread fallback, no-match, missing-config safety, and event
propagation.
This commit is contained in:
teknium1 2026-06-30 16:51:04 -07:00 committed by Teknium
parent 20ca2d5759
commit 36bfe3a449
3 changed files with 121 additions and 6 deletions

View file

@ -2892,6 +2892,7 @@ class FeishuAdapter(BasePlatformAdapter):
source=source,
raw_message=data,
message_id=message_id,
channel_prompt=self._resolve_channel_prompt(chat_id),
timestamp=datetime.now(),
)
logger.info("[Feishu] Routing reaction %s:%s on bot message %s as synthetic event", action, emoji_type, message_id)
@ -2954,6 +2955,7 @@ class FeishuAdapter(BasePlatformAdapter):
source=source,
raw_message=data,
message_id=token or str(uuid.uuid4()),
channel_prompt=self._resolve_channel_prompt(chat_id),
timestamp=datetime.now(),
)
logger.info("[Feishu] Routing card action %r from %s in %s as synthetic command", action_tag, open_id, chat_id)
@ -3156,6 +3158,18 @@ class FeishuAdapter(BasePlatformAdapter):
# Inbound processing pipeline
# =========================================================================
def _resolve_channel_prompt(self, chat_id: str, parent_id: str | None = None) -> str | None:
"""Resolve a Feishu per-channel system prompt.
Mirrors the Discord/Slack behaviour so ``channel_prompts: {<chat_id>:
"<prompt>"}`` in ``PlatformConfig.extra`` is honoured for Feishu chats
instead of being silently ignored.
"""
from gateway.platforms.base import resolve_channel_prompt
_config = getattr(self, "config", None)
_extra = getattr(_config, "extra", None) or {}
return resolve_channel_prompt(_extra, chat_id, parent_id)
async def _process_inbound_message(
self,
*,
@ -3233,6 +3247,7 @@ class FeishuAdapter(BasePlatformAdapter):
media_types=media_types,
reply_to_message_id=reply_to_message_id,
reply_to_text=reply_to_text,
channel_prompt=self._resolve_channel_prompt(chat_id, thread_id or None),
timestamp=datetime.now(),
)
await self._dispatch_inbound_event(normalized)