Skip to content

Commit d92147e

Browse files
[query] log
1 parent a49b6b2 commit d92147e

5 files changed

Lines changed: 318 additions & 2 deletions

File tree

‎dashscope/finetune/agentic_rl.py‎

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from dashscope.finetune.reinforcement.common.errors import (
4444
RegistrationError,
4545
ValidationError,
46+
InstanceQueryError,
4647
RuntimeErrorWithCode,
4748
ValueErrorWithCode,
4849
DatasetsError,
@@ -426,7 +427,22 @@ async def test_functions(
426427
functype: FunctionType,
427428
input_data: Dict[str, Any],
428429
api_key: str = None,
430+
pull_logs: bool = False,
431+
log_page_size: int = 100,
429432
):
433+
"""Test a deployed function instance with custom input data.
434+
435+
Args:
436+
instance_id: Target function instance ID.
437+
functype: Function type (ROLLOUT/REWARD/GROUP_REWARD).
438+
input_data: Test input payload.
439+
api_key: DashScope API key (uses DASHSCOPE_API_KEY env var
440+
if omitted).
441+
pull_logs: If True, pull all logs of the function instance
442+
(with pagination) after verification and print them
443+
between separator markers.
444+
log_page_size: Page size used when pulling logs.
445+
"""
430446
try:
431447
set_api_key(api_key)
432448

@@ -450,13 +466,146 @@ async def test_functions(
450466
},
451467
)
452468

453-
return await AgenticRLFunctionComponent.verify_function(
469+
result = await AgenticRLFunctionComponent.verify_function(
454470
value,
455471
instance_id,
456472
)
457473

474+
if pull_logs:
475+
await cls._pull_function_instance_logs(
476+
function_instance_id=instance_id,
477+
page_size=log_page_size,
478+
)
479+
480+
return result
481+
458482
except Exception as e:
459483
raise ValidationError(
460484
"Function test failed",
461485
error_code=3008,
462486
) from e
487+
488+
@classmethod
489+
async def query_function_instance_logs(
490+
cls,
491+
function_instance_id: str,
492+
page_number: int = 1,
493+
page_size: int = 100,
494+
start_time: Optional[int] = None,
495+
end_time: Optional[int] = None,
496+
keywords: Optional[List[str]] = None,
497+
api_key: str = None,
498+
) -> Dict[str, Any]:
499+
"""Query one page of logs for a function (faas) instance.
500+
501+
Args:
502+
function_instance_id: Target function instance ID.
503+
page_number: Page number, starting from 1.
504+
page_size: Number of log entries per page.
505+
start_time: Optional start time filter (in seconds).
506+
end_time: Optional end time filter (in seconds).
507+
keywords: Optional keyword filters for log messages.
508+
api_key: DashScope API key (uses DASHSCOPE_API_KEY env var
509+
if omitted).
510+
511+
Returns:
512+
Raw response dict of the log query API.
513+
"""
514+
try:
515+
set_api_key(api_key)
516+
517+
fc_component = AgenticRLFunctionComponent
518+
return await fc_component.query_function_instance_logs(
519+
function_instance_id=function_instance_id,
520+
page_number=page_number,
521+
page_size=page_size,
522+
start_time=start_time,
523+
end_time=end_time,
524+
keywords=keywords,
525+
)
526+
527+
except Exception as e:
528+
if hasattr(e, "error_code"):
529+
raise
530+
raise InstanceQueryError(
531+
"Function instance log query failed",
532+
error_code=3009,
533+
) from e
534+
535+
@classmethod
536+
async def query_all_function_instance_logs(
537+
cls,
538+
function_instance_id: str,
539+
page_size: int = 100,
540+
start_time: Optional[int] = None,
541+
end_time: Optional[int] = None,
542+
keywords: Optional[List[str]] = None,
543+
api_key: str = None,
544+
) -> List[str]:
545+
"""Fetch all logs of a function (faas) instance.
546+
547+
Pages are fetched one by one via the paginated log query until
548+
all entries are collected.
549+
550+
Args:
551+
function_instance_id: Target function instance ID.
552+
page_size: Number of log entries per page.
553+
start_time: Optional start time filter (in seconds).
554+
end_time: Optional end time filter (in seconds).
555+
keywords: Optional keyword filters for log messages.
556+
api_key: DashScope API key (uses DASHSCOPE_API_KEY env var
557+
if omitted).
558+
559+
Returns:
560+
Aggregated list of log messages across all pages.
561+
"""
562+
try:
563+
set_api_key(api_key)
564+
565+
fc_component = AgenticRLFunctionComponent
566+
return await fc_component.query_all_function_instance_logs(
567+
function_instance_id=function_instance_id,
568+
page_size=page_size,
569+
start_time=start_time,
570+
end_time=end_time,
571+
keywords=keywords,
572+
)
573+
574+
except Exception as e:
575+
if hasattr(e, "error_code"):
576+
raise
577+
raise InstanceQueryError(
578+
"Function instance log query failed",
579+
error_code=3010,
580+
) from e
581+
582+
@classmethod
583+
async def _pull_function_instance_logs(
584+
cls,
585+
function_instance_id: str,
586+
page_size: int = 100,
587+
) -> None:
588+
"""Pull all logs of a function instance and print them between
589+
separator markers (best effort, never raises)."""
590+
logger.info(
591+
"************start query log "
592+
f"(function_instance_id={function_instance_id})*******",
593+
)
594+
try:
595+
logs = await cls.query_all_function_instance_logs(
596+
function_instance_id=function_instance_id,
597+
page_size=page_size,
598+
)
599+
for entry in logs:
600+
logger.info(f"[function instance log] {entry}")
601+
logger.info(
602+
f"Pulled {len(logs)} log entries for function instance "
603+
f"{function_instance_id}",
604+
)
605+
except Exception as e:
606+
logger.warning(
607+
f"Failed to pull logs for function instance "
608+
f"{function_instance_id}: {e}",
609+
)
610+
finally:
611+
logger.info("************end query log *******")

‎dashscope/finetune/reinforcement/__init__.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
FC_PYPI_LIB,
2222
FC_PYPI_REPO,
2323
FC_QUERY_API,
24+
FC_QUERY_LOG_API,
2425
FC_REGISTER_REWARD_API,
2526
FC_REGISTER_ROLLOUT_API,
2627
FC_REGISTER_GROUP_REWARD_API,
@@ -159,6 +160,7 @@ def __getattr__(name):
159160
"FC_PYPI_LIB",
160161
"FC_PYPI_REPO",
161162
"FC_QUERY_API",
163+
"FC_QUERY_LOG_API",
162164
"FC_REGISTER_REWARD_API",
163165
"FC_REGISTER_ROLLOUT_API",
164166
"FC_REGISTER_GROUP_REWARD_API",

‎dashscope/finetune/reinforcement/common/constants.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ def get_int_env(env_var: str, default: int) -> int:
8383
)
8484
FC_LOAD_API = os.environ.get("FC_LOAD_API", f"{FC_BASE_ENDPOINT}/online/faas")
8585
FC_QUERY_API = os.environ.get("FC_QUERY_API", f"{FC_BASE_ENDPOINT}/query/faas")
86+
FC_QUERY_LOG_API = os.environ.get(
87+
"FC_QUERY_LOG_API",
88+
f"{FC_BASE_ENDPOINT}/query/faas/log",
89+
)
8690
FC_LAYER_CREATE_API = os.environ.get( # POST
8791
"FC_LAYER_CREATE_API",
8892
f"{FC_BASE_ENDPOINT}/create/faas/layer",

‎dashscope/finetune/reinforcement/common/model.py‎

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
FC_API_KEY,
2828
FC_LOAD_API,
2929
FC_QUERY_API,
30+
FC_QUERY_LOG_API,
3031
FC_REGISTER_REWARD_API,
3132
FC_REGISTER_ROLLOUT_API,
3233
FC_REGISTER_GROUP_REWARD_API,
@@ -1056,6 +1057,165 @@ async def verify_function(
10561057
error_code=2047,
10571058
) from e
10581059

1060+
@staticmethod
1061+
def _extract_log_page(
1062+
result: Dict[str, Any],
1063+
) -> Tuple[List[str], Optional[int]]:
1064+
"""Extract log messages and total count from a log query response.
1065+
1066+
Expected response format::
1067+
1068+
{
1069+
"success": true,
1070+
"code": null,
1071+
"message": null,
1072+
"data": {
1073+
"pageNumber": 1,
1074+
"pageSize": 50,
1075+
"totalCount": 128,
1076+
"logEntries": [
1077+
{"instanceId": "...", "message": "...",
1078+
"timestamp": 1699051200}
1079+
]
1080+
}
1081+
}
1082+
1083+
Only the "message" field of each log entry is kept; other fields
1084+
are ignored.
1085+
"""
1086+
data = result.get("data", {})
1087+
if not isinstance(data, Dict):
1088+
return [], None
1089+
1090+
entries = data.get("logEntries") or []
1091+
logs = [
1092+
entry.get("message", "") if isinstance(entry, Dict) else str(entry)
1093+
for entry in entries
1094+
]
1095+
1096+
total = data.get("totalCount")
1097+
if not isinstance(total, int):
1098+
total = None
1099+
1100+
return logs, total
1101+
1102+
@classmethod
1103+
async def query_function_instance_logs(
1104+
cls,
1105+
function_instance_id: str,
1106+
page_number: int = 1,
1107+
page_size: int = 100,
1108+
start_time: Optional[int] = None,
1109+
end_time: Optional[int] = None,
1110+
keywords: Optional[List[str]] = None,
1111+
) -> Dict[str, Any]:
1112+
"""Query logs of a function (faas) instance (single page).
1113+
1114+
Args:
1115+
function_instance_id: Target function instance ID.
1116+
page_number: Page number, starting from 1.
1117+
page_size: Number of log entries per page.
1118+
start_time: Optional start time filter (in seconds).
1119+
end_time: Optional end time filter (in seconds).
1120+
keywords: Optional keyword filters for log messages.
1121+
1122+
Returns:
1123+
Raw response dict of the log query API.
1124+
"""
1125+
if not function_instance_id:
1126+
raise InputError(
1127+
"No function instance ID available for log query",
1128+
error_code=2071,
1129+
)
1130+
1131+
url = f"{FC_QUERY_LOG_API}/{function_instance_id}"
1132+
request_data: Dict[str, Any] = {
1133+
"pageNumber": page_number,
1134+
"pageSize": page_size,
1135+
}
1136+
if start_time is not None:
1137+
request_data["startTime"] = start_time
1138+
if end_time is not None:
1139+
request_data["endTime"] = end_time
1140+
if keywords:
1141+
request_data["keywords"] = keywords
1142+
1143+
result = await client_fc(FC_API_KEY, url, request_data)
1144+
status = result.get("status", {})
1145+
if isinstance(status, Dict) and status.get("code", 200) != 200:
1146+
raise InstanceQueryError(
1147+
f"Log query failed: {result}",
1148+
error_code=2072,
1149+
instance_id=function_instance_id,
1150+
)
1151+
if result.get("success") is False:
1152+
raise InstanceQueryError(
1153+
f"Log query failed: {result.get('message') or result}",
1154+
error_code=2073,
1155+
instance_id=function_instance_id,
1156+
)
1157+
1158+
logger.debug(
1159+
f"Log query completed | FunctionInstanceID: "
1160+
f"{function_instance_id} | "
1161+
f"Page: {page_number} | Size: {page_size}",
1162+
)
1163+
return result
1164+
1165+
@classmethod
1166+
async def query_all_function_instance_logs(
1167+
cls,
1168+
function_instance_id: str,
1169+
page_size: int = 100,
1170+
start_time: Optional[int] = None,
1171+
end_time: Optional[int] = None,
1172+
keywords: Optional[List[str]] = None,
1173+
max_pages: int = 100,
1174+
) -> List[str]:
1175+
"""Fetch all logs of a function (faas) instance with pagination.
1176+
1177+
Iterates pages starting from page 1 until all entries are
1178+
collected (based on the reported total count, an empty page, or
1179+
a short page).
1180+
1181+
Args:
1182+
function_instance_id: Target function instance ID.
1183+
page_size: Number of log entries per page.
1184+
start_time: Optional start time filter (in seconds).
1185+
end_time: Optional end time filter (in seconds).
1186+
keywords: Optional keyword filters for log messages.
1187+
max_pages: Safety limit on the number of pages to fetch.
1188+
1189+
Returns:
1190+
Aggregated list of log messages across all pages.
1191+
"""
1192+
all_logs: List[str] = []
1193+
page_number = 1
1194+
while page_number <= max_pages:
1195+
result = await cls.query_function_instance_logs(
1196+
function_instance_id=function_instance_id,
1197+
page_number=page_number,
1198+
page_size=page_size,
1199+
start_time=start_time,
1200+
end_time=end_time,
1201+
keywords=keywords,
1202+
)
1203+
logs, total = cls._extract_log_page(result)
1204+
all_logs.extend(logs)
1205+
1206+
if total is not None and len(all_logs) >= total:
1207+
break
1208+
if len(logs) < page_size:
1209+
break
1210+
page_number += 1
1211+
1212+
logger.debug(
1213+
f"All logs fetched | FunctionInstanceID: "
1214+
f"{function_instance_id} | "
1215+
f"Pages: {page_number} | Entries: {len(all_logs)}",
1216+
)
1217+
return all_logs
1218+
10591219

10601220
class RolloutFunctionComponent(AgenticRLFunctionComponent):
10611221
"""Rollout function component with type fixed as ROLLOUT."""

‎dashscope/finetune/reinforcement/common/utils.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,7 @@ async def to_bailian_data(files: List[FileSpec]) -> List[str]:
679679
timeout=BAILIAN_FILE_TIMEOUT,
680680
retry_times=1,
681681
)
682+
logger.info(f"File upload result: {result}")
682683

683684
# Handle errors
684685
if result.get("status", {}).get("code", 200) != 200:
@@ -690,7 +691,7 @@ async def to_bailian_data(files: List[FileSpec]) -> List[str]:
690691
data = result.get("data", {})
691692
if "failed_uploads" in data and data["failed_uploads"]:
692693
failed_files = ", ".join(
693-
[f["name"] for f in data["failed_uploads"]],
694+
[f"{f.get('name')}: {f}" for f in data["failed_uploads"]],
694695
)
695696
raise OutputError(
696697
f"Partial upload failed: {failed_files}",

0 commit comments

Comments
 (0)