401 lines
8.1 KiB
Python
401 lines
8.1 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import hashlib
|
||
|
|
import json
|
||
|
|
from datetime import UTC, date, datetime
|
||
|
|
from decimal import Decimal
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
from sqlalchemy import inspect, select, text
|
||
|
|
|
||
|
|
from app.core.agent_enums import (
|
||
|
|
AgentAssetContentType,
|
||
|
|
AgentAssetDomain,
|
||
|
|
AgentAssetStatus,
|
||
|
|
AgentAssetType,
|
||
|
|
AgentName,
|
||
|
|
AgentPermissionLevel,
|
||
|
|
AgentReviewStatus,
|
||
|
|
AgentRunSource,
|
||
|
|
AgentRunStatus,
|
||
|
|
AgentToolType,
|
||
|
|
)
|
||
|
|
from app.models.agent_asset import AgentAsset, AgentAssetReview, AgentAssetVersion
|
||
|
|
from app.models.agent_run import AgentRun, AgentToolCall, SemanticParseLog
|
||
|
|
from app.models.audit_log import AuditLog
|
||
|
|
from app.models.financial_record import (
|
||
|
|
AccountsPayableRecord,
|
||
|
|
AccountsReceivableRecord,
|
||
|
|
ExpenseClaim,
|
||
|
|
ExpenseClaimItem,
|
||
|
|
)
|
||
|
|
from app.services.agent_asset_rule_library import AgentAssetRuleLibraryManager
|
||
|
|
from app.services.agent_asset_spreadsheet import (
|
||
|
|
AgentAssetSpreadsheetManager,
|
||
|
|
COMPANY_COMMUNICATION_EXPENSE_RULE_CODE,
|
||
|
|
COMPANY_COMMUNICATION_EXPENSE_RULE_FILENAME,
|
||
|
|
COMPANY_TRAVEL_EXPENSE_RULE_CODE,
|
||
|
|
COMPANY_TRAVEL_EXPENSE_RULE_FILENAME,
|
||
|
|
FINANCE_RULES_LIBRARY,
|
||
|
|
RISK_RULES_LIBRARY,
|
||
|
|
)
|
||
|
|
from app.services.expense_rule_runtime import (
|
||
|
|
build_scene_submission_standard_markdown,
|
||
|
|
build_travel_risk_control_standard_markdown,
|
||
|
|
)
|
||
|
|
from app.services.agent_foundation_constants import (
|
||
|
|
ATTACHMENT_RULE_ASSET_CODE,
|
||
|
|
ATTACHMENT_RULE_RUNTIME_CONFIG,
|
||
|
|
COMPANY_COMMUNICATION_RULE_SCENARIO_JSON,
|
||
|
|
COMPANY_COMMUNICATION_RULE_VERSION,
|
||
|
|
COMPANY_TRAVEL_RULE_SCENARIO_JSON,
|
||
|
|
COMPANY_TRAVEL_RULE_VERSION,
|
||
|
|
DEMO_EXPENSE_CLAIM_SIGNATURES,
|
||
|
|
DEMO_PAYABLE_SIGNATURES,
|
||
|
|
DEMO_RECEIVABLE_SIGNATURES,
|
||
|
|
LEGACY_RULE_CODES,
|
||
|
|
PLATFORM_DESTINATION_LOCATION_RULE_FILENAME,
|
||
|
|
)
|
||
|
|
from app.core.logging import get_logger
|
||
|
|
|
||
|
|
logger = get_logger("app.services.agent_foundation")
|
||
|
|
|
||
|
|
class AgentFoundationSpreadsheetMixin:
|
||
|
|
def _ensure_company_travel_rule_spreadsheet_seed(
|
||
|
|
|
||
|
|
self,
|
||
|
|
|
||
|
|
asset: AgentAsset,
|
||
|
|
|
||
|
|
*,
|
||
|
|
|
||
|
|
version: str,
|
||
|
|
|
||
|
|
actor_name: str,
|
||
|
|
|
||
|
|
):
|
||
|
|
|
||
|
|
manager = AgentAssetSpreadsheetManager()
|
||
|
|
|
||
|
|
manager.ensure_rule_library_dirs()
|
||
|
|
|
||
|
|
live_document = manager.store_rule_library_spreadsheet(
|
||
|
|
|
||
|
|
library=FINANCE_RULES_LIBRARY,
|
||
|
|
|
||
|
|
file_name=COMPANY_TRAVEL_EXPENSE_RULE_FILENAME,
|
||
|
|
|
||
|
|
content=self._read_or_build_company_travel_rule_file(manager),
|
||
|
|
|
||
|
|
actor_name=actor_name,
|
||
|
|
|
||
|
|
source="rule-library",
|
||
|
|
|
||
|
|
)
|
||
|
|
|
||
|
|
existing_document = (
|
||
|
|
|
||
|
|
asset.config_json.get("rule_document")
|
||
|
|
|
||
|
|
if isinstance(asset.config_json, dict)
|
||
|
|
|
||
|
|
else None
|
||
|
|
|
||
|
|
)
|
||
|
|
|
||
|
|
storage_key = (
|
||
|
|
|
||
|
|
str(existing_document.get("storage_key") or "").strip()
|
||
|
|
|
||
|
|
if isinstance(existing_document, dict)
|
||
|
|
|
||
|
|
else ""
|
||
|
|
|
||
|
|
)
|
||
|
|
|
||
|
|
if storage_key:
|
||
|
|
|
||
|
|
try:
|
||
|
|
|
||
|
|
existing_path = manager.resolve_storage_path(storage_key)
|
||
|
|
|
||
|
|
except FileNotFoundError:
|
||
|
|
|
||
|
|
existing_path = None
|
||
|
|
|
||
|
|
if existing_path is not None and existing_path.exists():
|
||
|
|
|
||
|
|
asset.config_json = {
|
||
|
|
|
||
|
|
**(asset.config_json or {}),
|
||
|
|
|
||
|
|
"detail_mode": "spreadsheet",
|
||
|
|
|
||
|
|
"tag": "财务规则",
|
||
|
|
|
||
|
|
"rule_library": FINANCE_RULES_LIBRARY,
|
||
|
|
|
||
|
|
"rule_document": {
|
||
|
|
|
||
|
|
**AgentAssetSpreadsheetManager.build_rule_document_config(
|
||
|
|
|
||
|
|
live_document,
|
||
|
|
|
||
|
|
asset_version=version,
|
||
|
|
|
||
|
|
),
|
||
|
|
|
||
|
|
"storage_key": live_document.storage_key,
|
||
|
|
|
||
|
|
},
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return live_document
|
||
|
|
|
||
|
|
asset.config_json = {
|
||
|
|
|
||
|
|
**(asset.config_json or {}),
|
||
|
|
|
||
|
|
"detail_mode": "spreadsheet",
|
||
|
|
|
||
|
|
"tag": "财务规则",
|
||
|
|
|
||
|
|
"rule_library": FINANCE_RULES_LIBRARY,
|
||
|
|
|
||
|
|
"rule_document": {
|
||
|
|
|
||
|
|
**AgentAssetSpreadsheetManager.build_rule_document_config(
|
||
|
|
|
||
|
|
live_document,
|
||
|
|
|
||
|
|
asset_version=version,
|
||
|
|
|
||
|
|
),
|
||
|
|
|
||
|
|
"storage_key": live_document.storage_key,
|
||
|
|
|
||
|
|
},
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return live_document
|
||
|
|
|
||
|
|
def _ensure_company_communication_rule_spreadsheet_seed(
|
||
|
|
|
||
|
|
self,
|
||
|
|
|
||
|
|
asset: AgentAsset,
|
||
|
|
|
||
|
|
*,
|
||
|
|
|
||
|
|
version: str,
|
||
|
|
|
||
|
|
actor_name: str,
|
||
|
|
|
||
|
|
):
|
||
|
|
|
||
|
|
return self._ensure_finance_rule_spreadsheet_seed(
|
||
|
|
|
||
|
|
asset,
|
||
|
|
|
||
|
|
version=version,
|
||
|
|
|
||
|
|
actor_name=actor_name,
|
||
|
|
|
||
|
|
file_name=COMPANY_COMMUNICATION_EXPENSE_RULE_FILENAME,
|
||
|
|
|
||
|
|
fallback_sheet_name="通信费报销规则",
|
||
|
|
|
||
|
|
)
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
|
||
|
|
def _read_or_build_company_travel_rule_file(
|
||
|
|
|
||
|
|
manager: AgentAssetSpreadsheetManager,
|
||
|
|
|
||
|
|
) -> bytes:
|
||
|
|
|
||
|
|
live_key = (
|
||
|
|
|
||
|
|
Path("rules")
|
||
|
|
|
||
|
|
/ FINANCE_RULES_LIBRARY
|
||
|
|
|
||
|
|
/ COMPANY_TRAVEL_EXPENSE_RULE_FILENAME
|
||
|
|
|
||
|
|
).as_posix()
|
||
|
|
|
||
|
|
live_path = manager.resolve_storage_path(live_key)
|
||
|
|
|
||
|
|
if live_path.exists():
|
||
|
|
|
||
|
|
return live_path.read_bytes()
|
||
|
|
|
||
|
|
return AgentAssetSpreadsheetManager.build_blank_rule_workbook("差旅费报销规则")
|
||
|
|
|
||
|
|
def _ensure_finance_rule_spreadsheet_seed(
|
||
|
|
|
||
|
|
self,
|
||
|
|
|
||
|
|
asset: AgentAsset,
|
||
|
|
|
||
|
|
*,
|
||
|
|
|
||
|
|
version: str,
|
||
|
|
|
||
|
|
actor_name: str,
|
||
|
|
|
||
|
|
file_name: str,
|
||
|
|
|
||
|
|
fallback_sheet_name: str,
|
||
|
|
|
||
|
|
):
|
||
|
|
|
||
|
|
manager = AgentAssetSpreadsheetManager()
|
||
|
|
|
||
|
|
manager.ensure_rule_library_dirs()
|
||
|
|
|
||
|
|
live_document = manager.store_rule_library_spreadsheet(
|
||
|
|
|
||
|
|
library=FINANCE_RULES_LIBRARY,
|
||
|
|
|
||
|
|
file_name=file_name,
|
||
|
|
|
||
|
|
content=self._read_or_build_finance_rule_file(
|
||
|
|
|
||
|
|
manager,
|
||
|
|
|
||
|
|
file_name=file_name,
|
||
|
|
|
||
|
|
fallback_sheet_name=fallback_sheet_name,
|
||
|
|
|
||
|
|
),
|
||
|
|
|
||
|
|
actor_name=actor_name,
|
||
|
|
|
||
|
|
source="rule-library",
|
||
|
|
|
||
|
|
)
|
||
|
|
|
||
|
|
existing_document = (
|
||
|
|
|
||
|
|
asset.config_json.get("rule_document")
|
||
|
|
|
||
|
|
if isinstance(asset.config_json, dict)
|
||
|
|
|
||
|
|
else None
|
||
|
|
|
||
|
|
)
|
||
|
|
|
||
|
|
storage_key = (
|
||
|
|
|
||
|
|
str(existing_document.get("storage_key") or "").strip()
|
||
|
|
|
||
|
|
if isinstance(existing_document, dict)
|
||
|
|
|
||
|
|
else ""
|
||
|
|
|
||
|
|
)
|
||
|
|
|
||
|
|
if storage_key:
|
||
|
|
|
||
|
|
try:
|
||
|
|
|
||
|
|
existing_path = manager.resolve_storage_path(storage_key)
|
||
|
|
|
||
|
|
except FileNotFoundError:
|
||
|
|
|
||
|
|
existing_path = None
|
||
|
|
|
||
|
|
if existing_path is not None and existing_path.exists():
|
||
|
|
|
||
|
|
asset.config_json = {
|
||
|
|
|
||
|
|
**(asset.config_json or {}),
|
||
|
|
|
||
|
|
"detail_mode": "spreadsheet",
|
||
|
|
|
||
|
|
"tag": "财务规则",
|
||
|
|
|
||
|
|
"rule_library": FINANCE_RULES_LIBRARY,
|
||
|
|
|
||
|
|
"rule_document": {
|
||
|
|
|
||
|
|
**AgentAssetSpreadsheetManager.build_rule_document_config(
|
||
|
|
|
||
|
|
live_document,
|
||
|
|
|
||
|
|
asset_version=version,
|
||
|
|
|
||
|
|
),
|
||
|
|
|
||
|
|
"storage_key": live_document.storage_key,
|
||
|
|
|
||
|
|
},
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return live_document
|
||
|
|
|
||
|
|
asset.config_json = {
|
||
|
|
|
||
|
|
**(asset.config_json or {}),
|
||
|
|
|
||
|
|
"detail_mode": "spreadsheet",
|
||
|
|
|
||
|
|
"tag": "财务规则",
|
||
|
|
|
||
|
|
"rule_library": FINANCE_RULES_LIBRARY,
|
||
|
|
|
||
|
|
"rule_document": {
|
||
|
|
|
||
|
|
**AgentAssetSpreadsheetManager.build_rule_document_config(
|
||
|
|
|
||
|
|
live_document,
|
||
|
|
|
||
|
|
asset_version=version,
|
||
|
|
|
||
|
|
),
|
||
|
|
|
||
|
|
"storage_key": live_document.storage_key,
|
||
|
|
|
||
|
|
},
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return live_document
|
||
|
|
|
||
|
|
@staticmethod
|
||
|
|
|
||
|
|
def _read_or_build_finance_rule_file(
|
||
|
|
|
||
|
|
manager: AgentAssetSpreadsheetManager,
|
||
|
|
|
||
|
|
*,
|
||
|
|
|
||
|
|
file_name: str,
|
||
|
|
|
||
|
|
fallback_sheet_name: str,
|
||
|
|
|
||
|
|
) -> bytes:
|
||
|
|
|
||
|
|
live_key = (
|
||
|
|
|
||
|
|
Path("rules")
|
||
|
|
|
||
|
|
/ FINANCE_RULES_LIBRARY
|
||
|
|
|
||
|
|
/ file_name
|
||
|
|
|
||
|
|
).as_posix()
|
||
|
|
|
||
|
|
live_path = manager.resolve_storage_path(live_key)
|
||
|
|
|
||
|
|
if live_path.exists():
|
||
|
|
|
||
|
|
return live_path.read_bytes()
|
||
|
|
|
||
|
|
return AgentAssetSpreadsheetManager.build_blank_rule_workbook(fallback_sheet_name)
|