Stabilize product data export flow

This commit is contained in:
wuyang6
2026-05-11 11:46:09 +08:00
parent 1752c81913
commit 0e2a00990f
19 changed files with 659 additions and 31 deletions
+61 -7
View File
@@ -1474,7 +1474,7 @@ def _data_agent_confirm_generation_plan_tool(arguments: dict[str, Any], context:
def _normalize_dataset_draft_tool(arguments: dict[str, Any], _context: ToolExecutionContext) -> str:
draft_text = _require_string(arguments, 'draft_text')
draft_text = _draft_text_from_arguments(arguments, _context)
batch_id = arguments.get('batch_id', 'aabbccdd')
if not isinstance(batch_id, str) or not batch_id.strip():
raise ToolExecutionError('batch_id must be a non-empty string')
@@ -1508,10 +1508,8 @@ def _normalize_dataset_draft_tool(arguments: dict[str, Any], _context: ToolExecu
def _validate_dataset_records_tool(arguments: dict[str, Any], _context: ToolExecutionContext) -> str:
if 'records' not in arguments:
raise ToolExecutionError('records is required')
try:
records = records_from_tool_argument(arguments['records'])
records = _records_from_arguments(arguments, _context)
payload = validate_dataset_records(records)
except (DataRecordError, json.JSONDecodeError) as exc:
raise ToolExecutionError(str(exc)) from exc
@@ -1519,8 +1517,6 @@ def _validate_dataset_records_tool(arguments: dict[str, Any], _context: ToolExec
def _export_dataset_records_tool(arguments: dict[str, Any], context: ToolExecutionContext) -> str:
if 'records' not in arguments:
raise ToolExecutionError('records is required')
output_format = arguments.get('output_format', 'jsonl')
if not isinstance(output_format, str):
raise ToolExecutionError('output_format must be a string')
@@ -1530,8 +1526,11 @@ def _export_dataset_records_tool(arguments: dict[str, Any], context: ToolExecuti
overwrite = arguments.get('overwrite', True)
if not isinstance(overwrite, bool):
raise ToolExecutionError('overwrite must be a boolean')
export_table = arguments.get('export_table', True)
if not isinstance(export_table, bool):
raise ToolExecutionError('export_table must be a boolean')
try:
records = records_from_tool_argument(arguments['records'])
records = _records_from_arguments(arguments, context)
canonical_filename = 'records.json' if output_format == 'json' else 'records.jsonl'
payload = export_dataset_records(
records,
@@ -1544,12 +1543,67 @@ def _export_dataset_records_tool(arguments: dict[str, Any], context: ToolExecuti
output_format=output_format,
require_validation_ok=require_validation_ok,
overwrite=overwrite,
export_table=export_table,
)
except (DataRecordError, json.JSONDecodeError) as exc:
raise ToolExecutionError(str(exc)) from exc
return json.dumps(payload, ensure_ascii=False, indent=2)
def _draft_text_from_arguments(arguments: dict[str, Any], context: ToolExecutionContext) -> str:
draft_text = arguments.get('draft_text')
draft_path = arguments.get('draft_path')
has_text = isinstance(draft_text, str) and bool(draft_text.strip())
has_path = isinstance(draft_path, str) and bool(draft_path.strip())
if has_text and has_path:
raise ToolExecutionError('provide only one of draft_text or draft_path')
if has_text:
return draft_text
if not has_path:
raise ToolExecutionError('draft_text or draft_path is required')
path = _resolve_path(draft_path, context, allow_missing=False, allow_outside_root=True)
if not path.is_file():
raise ToolExecutionError(f'draft_path is not a file: {path}')
return path.read_text(encoding='utf-8', errors='replace')
def _records_from_arguments(arguments: dict[str, Any], context: ToolExecutionContext) -> list[dict[str, Any]]:
has_records = 'records' in arguments and arguments.get('records') is not None
records_path = arguments.get('records_path')
has_path = isinstance(records_path, str) and bool(records_path.strip())
if has_records and has_path:
raise ToolExecutionError('provide only one of records or records_path')
if has_records:
return records_from_tool_argument(arguments['records'])
if not has_path:
raise ToolExecutionError('records or records_path is required')
path = _resolve_path(records_path, context, allow_missing=False, allow_outside_root=True)
if not path.is_file():
raise ToolExecutionError(f'records_path is not a file: {path}')
return _records_from_file(path)
def _records_from_file(path: Path) -> list[dict[str, Any]]:
text = path.read_text(encoding='utf-8', errors='replace').strip()
if not text:
raise DataRecordError('records_path is empty')
if path.suffix == '.jsonl':
records: list[dict[str, Any]] = []
for line_number, line in enumerate(text.splitlines(), start=1):
stripped = line.strip()
if not stripped:
continue
item = json.loads(stripped)
if not isinstance(item, dict):
raise DataRecordError(f'records_path line {line_number} must decode to an object')
records.append(item)
return records_from_tool_argument(records)
payload = json.loads(text)
if isinstance(payload, dict) and isinstance(payload.get('records'), list):
payload = payload['records']
return records_from_tool_argument(payload)
def _data_agent_output_path(
output_path: str,
context: ToolExecutionContext,