mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-20 23:45:22 +08:00
fix(web): correct monitoring empty states and Space request headers
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user