fix(web): correct monitoring empty states and Space request headers

This commit is contained in:
Hyu
2026-10-12 13:36:26 +08:00
parent 11ed8a2306
commit 417c552694
4 changed files with 47 additions and 9 deletions
@@ -24,12 +24,21 @@ def parse_iso_datetime(datetime_str: str | None) -> datetime.datetime | None:
return dt
def validate_monitoring_window(start_time: datetime.datetime | None, end_time: datetime.datetime | None) -> None:
def validate_monitoring_window(
start_time: datetime.datetime | None,
end_time: datetime.datetime | None,
*,
allow_point_window: bool = False,
) -> None:
"""Bound explicit dashboard windows to one year without silently truncating them."""
if start_time is None:
return
end = end_time or datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
if start_time >= end or end - start_time > datetime.timedelta(days=365):
if (
start_time > end
or (start_time == end and not allow_point_window)
or end - start_time > datetime.timedelta(days=365)
):
quart.abort(400, description='Monitoring time range must be positive and no longer than 365 days')
@@ -580,7 +589,8 @@ class MonitoringRouterGroup(group.RouterGroup):
"""Get detailed analysis for a specific session"""
start_time = parse_iso_datetime(quart.request.args.get('startTime'))
end_time = parse_iso_datetime(quart.request.args.get('endTime'))
validate_monitoring_window(start_time, end_time)
# Message pages can contain one timestamp; tool-call bounds are inclusive.
validate_monitoring_window(start_time, end_time, allow_point_window=True)
analysis = await self.ap.monitoring_service.get_session_analysis(
request_context,
session_id,
@@ -183,6 +183,31 @@ async def test_analysis_fails_closed_and_scopes_statistics(service):
assert result['session']['bot_id'] == 'b'
async def test_single_message_analysis_without_tool_calls(service):
from langbot.pkg.api.http.controller.groups.monitoring import validate_monitoring_window
from werkzeug.exceptions import BadRequest
await service.record_session_start(context('a'), session_id='person_42', **resource('a'))
await service.record_message(context('a'), session_id='person_42', message_content='hi', **resource('a'))
row = (await service.ap.persistence_mgr.execute_async(sa.select(models.MonitoringMessage.timestamp))).one()
timestamp = row.timestamp
validate_monitoring_window(timestamp, timestamp, allow_point_window=True)
result = await service.get_session_analysis(
context(), 'person_42', bot_id='a', start_time=timestamp, end_time=timestamp
)
assert result['found'] is True
assert result['message_stats']['total'] == 1
assert result['tool_calls'] == []
for start, end in [
(timestamp + dt.timedelta(seconds=1), timestamp),
(timestamp, timestamp + dt.timedelta(days=366)),
]:
with pytest.raises(BadRequest):
validate_monitoring_window(start, end, allow_point_window=True)
with pytest.raises(BadRequest):
validate_monitoring_window(timestamp, timestamp)
async def test_activity_requires_bot_and_upsert_counts_racing_first_queries(service):
for _ in range(2):
await service.record_session_start(context('a'), session_id='person_42', **resource('a'))
@@ -340,16 +340,12 @@ export default function PipelineMigration({
return (
<>
{(count > 0 || previewError || results.length > 0) && (
{(count > 0 || results.length > 0) && (
<Alert className="mb-4 shrink-0 border-amber-200 bg-amber-50 text-amber-900 dark:border-amber-900/50 dark:bg-amber-950/30 dark:text-amber-200">
<AlertTriangle aria-hidden="true" />
<AlertTitle>{t('pipelineMigration.title')}</AlertTitle>
<AlertDescription className="flex items-center justify-between gap-3 text-amber-800 dark:text-amber-200">
<span>
{previewError
? t('pipelineMigration.previewError')
: t('pipelineMigration.detected', { count })}
</span>
<span>{t('pipelineMigration.detected', { count })}</span>
<Button
variant="outline"
size="sm"
+7
View File
@@ -4,6 +4,7 @@ import axios, {
AxiosResponse,
AxiosError,
} from 'axios';
import i18n from 'i18next';
import {
clearActiveWorkspaceUuid,
getActiveWorkspaceUuid,
@@ -79,6 +80,12 @@ export abstract class BaseHttpClient {
// 请求拦截
this.instance.interceptors.request.use(
async (config) => {
// Account-only endpoints have no selected Workspace. Workspace routes
// resolve their persisted language and override this presentation hint.
// This is a LangBot backend hint, not part of the public Space API.
if (!this.disableToken) {
config.headers['X-LangBot-Language'] = i18n.language || 'en-US';
}
// 客户端添加认证头
if (typeof window !== 'undefined' && !this.disableToken) {
const session = this.getSessionSync();