Use prompt history for online mining records
This commit is contained in:
@@ -89,9 +89,32 @@ def source_config(source: str) -> dict[str, Any]:
|
||||
return SOURCE_CONFIGS[source]
|
||||
|
||||
|
||||
def date_range_ms(date_str: str | None) -> tuple[int, int]:
|
||||
def parse_date(value: str) -> datetime:
|
||||
text = str(value).strip()
|
||||
for fmt in ("%Y%m%d", "%Y-%m-%d"):
|
||||
try:
|
||||
return datetime.strptime(text, fmt)
|
||||
except ValueError:
|
||||
continue
|
||||
raise OnlineMiningError("date must be YYYYMMDD or YYYY-MM-DD")
|
||||
|
||||
|
||||
def date_range_ms(date_str: str | None, *, date_from: str | None = None, date_to: str | None = None, lookback_days: int | None = None) -> tuple[int, int]:
|
||||
if date_from or date_to:
|
||||
if not date_from or not date_to:
|
||||
raise OnlineMiningError("date_from and date_to must be provided together")
|
||||
start_obj = parse_date(date_from)
|
||||
end_obj = parse_date(date_to) + timedelta(days=1)
|
||||
if end_obj <= start_obj:
|
||||
raise OnlineMiningError("date_to must be greater than or equal to date_from")
|
||||
return int(start_obj.timestamp() * 1000), int(end_obj.timestamp() * 1000) - 1
|
||||
if lookback_days is not None:
|
||||
if lookback_days <= 0 or lookback_days > 31:
|
||||
raise OnlineMiningError("lookback_days must be between 1 and 31")
|
||||
end = datetime.now()
|
||||
return int((end - timedelta(days=lookback_days)).timestamp() * 1000), int(end.timestamp() * 1000)
|
||||
if date_str:
|
||||
date_obj = datetime.strptime(str(date_str), "%Y%m%d")
|
||||
date_obj = parse_date(date_str)
|
||||
start_ms = int(date_obj.timestamp() * 1000)
|
||||
end_ms = int((date_obj + timedelta(days=1)).timestamp() * 1000) - 1
|
||||
else:
|
||||
@@ -266,10 +289,13 @@ def search_docs(
|
||||
date: str | None,
|
||||
size: int,
|
||||
query_filters: list[dict[str, Any]] | None = None,
|
||||
date_from: str | None = None,
|
||||
date_to: str | None = None,
|
||||
lookback_days: int | None = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
config = source_config(source)
|
||||
client = build_client(source)
|
||||
start_ms, end_ms = date_range_ms(date)
|
||||
start_ms, end_ms = date_range_ms(date, date_from=date_from, date_to=date_to, lookback_days=lookback_days)
|
||||
filters = [{"range": {str(config["time_field"]): {"gte": start_ms, "lte": end_ms}}}]
|
||||
filters.extend(deepcopy(query_filters or []))
|
||||
body = {
|
||||
@@ -281,7 +307,15 @@ def search_docs(
|
||||
return [deep_parse(hit.get("_source", {})) for hit in response.get("hits", {}).get("hits", [])]
|
||||
|
||||
|
||||
def fetch_one_by_request_id(source: str, request_id: str, date: str | None = None) -> dict[str, Any] | None:
|
||||
def fetch_one_by_request_id(
|
||||
source: str,
|
||||
request_id: str,
|
||||
date: str | None = None,
|
||||
*,
|
||||
date_from: str | None = None,
|
||||
date_to: str | None = None,
|
||||
lookback_days: int | None = None,
|
||||
) -> dict[str, Any] | None:
|
||||
config = source_config(source)
|
||||
id_field = str(config["id_field"])
|
||||
# 精确查优先用 .keyword;部分索引字段本身就是 keyword,所以再兜底原字段。
|
||||
@@ -292,56 +326,20 @@ def fetch_one_by_request_id(source: str, request_id: str, date: str | None = Non
|
||||
{"wildcard": {id_field: {"value": f"{request_id}*"}}},
|
||||
]
|
||||
for query_filter in attempts:
|
||||
docs = search_docs(source=source, date=date, size=1, query_filters=[query_filter])
|
||||
docs = search_docs(
|
||||
source=source,
|
||||
date=date,
|
||||
size=1,
|
||||
query_filters=[query_filter],
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
lookback_days=lookback_days,
|
||||
)
|
||||
if docs:
|
||||
return docs[0]
|
||||
return None
|
||||
|
||||
|
||||
def fetch_session_turns(
|
||||
*,
|
||||
session_id: str,
|
||||
before_timestamp: int,
|
||||
date: str | None = None,
|
||||
limit: int = 10,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""按主表 session_id 拉当前请求之前的多轮上下文。"""
|
||||
|
||||
if not session_id or not before_timestamp:
|
||||
return []
|
||||
docs: list[dict[str, Any]] = []
|
||||
# arch-flat-nlp-log-f-* 里 session_id 在部分索引没有 .keyword 子字段,
|
||||
# 所以必须先试 keyword,再兜底原字段。
|
||||
for session_field in ("session_id.keyword", "session_id"):
|
||||
filters = [
|
||||
{"term": {session_field: session_id}},
|
||||
{"range": {"timestamp": {"lt": before_timestamp}}},
|
||||
]
|
||||
docs = search_docs(source="main", date=date, size=max(limit * 3, limit), query_filters=filters)
|
||||
if docs:
|
||||
break
|
||||
cases = [extract_main_case(doc) for doc in docs]
|
||||
cases = [
|
||||
case
|
||||
for case in cases
|
||||
if str(case.get("session_id") or "") == session_id
|
||||
and isinstance(case.get("timestamp"), (int, str))
|
||||
and int(case.get("timestamp") or 0) < before_timestamp
|
||||
and str(case.get("query") or "").strip()
|
||||
]
|
||||
cases.sort(key=lambda item: int(item.get("timestamp") or 0))
|
||||
return cases[-limit:]
|
||||
|
||||
|
||||
def fetch_session_turns_for_case(case: dict[str, Any], *, date: str | None = None, limit: int = 10) -> list[dict[str, Any]]:
|
||||
session_id = str(case.get("session_id") or "").strip()
|
||||
try:
|
||||
timestamp = int(case.get("timestamp") or 0)
|
||||
except (TypeError, ValueError):
|
||||
timestamp = 0
|
||||
return fetch_session_turns(session_id=session_id, before_timestamp=timestamp, date=date, limit=limit)
|
||||
|
||||
|
||||
def fetch_by_terms(source: str, *, field: str, value: str, date: str | None = None, size: int = 10) -> list[dict[str, Any]]:
|
||||
"""小范围精确字段查询,供脚本探测 session/request 字段时使用。"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user