feat: deliver agent foundation day 1

This commit is contained in:
caoxiaozhu
2026-05-11 03:51:24 +00:00
parent f738b6cdd4
commit b2beeaa136
54 changed files with 6747 additions and 1724 deletions

View File

@@ -1,21 +1,41 @@
from app.models.approval import ApprovalRecord
from app.models.employee_change_log import EmployeeChangeLog
from app.models.employee import Employee
from app.models.organization import OrganizationUnit
from app.models.reimbursement import ReimbursementRequest
from app.models.role import Role
from app.models.system_model_setting import SystemModelSetting
from app.models.system_setting import SystemSetting
from app.models.system_setting_secret import SystemSettingSecret
__all__ = [
"ApprovalRecord",
"Employee",
"EmployeeChangeLog",
"OrganizationUnit",
"ReimbursementRequest",
"Role",
"SystemModelSetting",
"SystemSetting",
"SystemSettingSecret",
]
from app.models.agent_asset import AgentAsset, AgentAssetReview, AgentAssetVersion
from app.models.agent_run import AgentRun, AgentToolCall, SemanticParseLog
from app.models.approval import ApprovalRecord
from app.models.audit_log import AuditLog
from app.models.employee_change_log import EmployeeChangeLog
from app.models.employee import Employee
from app.models.financial_record import (
AccountsPayableRecord,
AccountsReceivableRecord,
ExpenseClaim,
ExpenseClaimItem,
)
from app.models.organization import OrganizationUnit
from app.models.reimbursement import ReimbursementRequest
from app.models.role import Role
from app.models.system_model_setting import SystemModelSetting
from app.models.system_setting import SystemSetting
from app.models.system_setting_secret import SystemSettingSecret
__all__ = [
"AccountsPayableRecord",
"AccountsReceivableRecord",
"AgentAsset",
"AgentAssetReview",
"AgentAssetVersion",
"AgentRun",
"AgentToolCall",
"ApprovalRecord",
"AuditLog",
"Employee",
"EmployeeChangeLog",
"ExpenseClaim",
"ExpenseClaimItem",
"OrganizationUnit",
"ReimbursementRequest",
"Role",
"SemanticParseLog",
"SystemModelSetting",
"SystemSetting",
"SystemSettingSecret",
]

View File

@@ -0,0 +1,79 @@
from __future__ import annotations
import uuid
from datetime import datetime
from typing import Any
from sqlalchemy import DateTime, ForeignKey, String, Text, UniqueConstraint, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.types import JSON
from app.db.base_class import Base
class AgentAsset(Base):
__tablename__ = "agent_assets"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
asset_type: Mapped[str] = mapped_column(String(20), index=True)
code: Mapped[str] = mapped_column(String(100), unique=True, index=True)
name: Mapped[str] = mapped_column(String(200))
description: Mapped[str] = mapped_column(Text(), default="")
domain: Mapped[str] = mapped_column(String(50), index=True)
scenario_json: Mapped[list[Any]] = mapped_column(JSON, default=list)
owner: Mapped[str] = mapped_column(String(100))
reviewer: Mapped[str | None] = mapped_column(String(100), nullable=True)
status: Mapped[str] = mapped_column(String(20), index=True, default="draft")
current_version: Mapped[str | None] = mapped_column(String(30), nullable=True)
config_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
versions = relationship(
"AgentAssetVersion",
back_populates="asset",
cascade="all, delete-orphan",
order_by="desc(AgentAssetVersion.created_at)",
)
reviews = relationship(
"AgentAssetReview",
back_populates="asset",
cascade="all, delete-orphan",
order_by="desc(AgentAssetReview.created_at)",
)
scheduled_runs = relationship("AgentRun", back_populates="task_asset")
class AgentAssetVersion(Base):
__tablename__ = "agent_asset_versions"
__table_args__ = (
UniqueConstraint("asset_id", "version", name="uq_agent_asset_versions_asset_version"),
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
asset_id: Mapped[str] = mapped_column(ForeignKey("agent_assets.id"), index=True)
version: Mapped[str] = mapped_column(String(30))
content: Mapped[str] = mapped_column(Text())
content_type: Mapped[str] = mapped_column(String(20))
change_note: Mapped[str | None] = mapped_column(Text(), nullable=True)
created_by: Mapped[str] = mapped_column(String(100))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
asset = relationship("AgentAsset", back_populates="versions")
class AgentAssetReview(Base):
__tablename__ = "agent_asset_reviews"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
asset_id: Mapped[str] = mapped_column(ForeignKey("agent_assets.id"), index=True)
version: Mapped[str] = mapped_column(String(30))
reviewer: Mapped[str] = mapped_column(String(100))
review_status: Mapped[str] = mapped_column(String(20), index=True)
review_note: Mapped[str | None] = mapped_column(Text(), nullable=True)
reviewed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
asset = relationship("AgentAsset", back_populates="reviews")

View File

@@ -0,0 +1,86 @@
from __future__ import annotations
import uuid
from datetime import datetime
from typing import Any
from sqlalchemy import DateTime, Float, ForeignKey, Integer, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.types import JSON
from app.db.base_class import Base
class AgentRun(Base):
__tablename__ = "agent_runs"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
run_id: Mapped[str] = mapped_column(String(50), unique=True, index=True)
agent: Mapped[str] = mapped_column(String(30), index=True)
source: Mapped[str] = mapped_column(String(30))
user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
task_id: Mapped[str | None] = mapped_column(
ForeignKey("agent_assets.id"), nullable=True, index=True
)
ontology_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
route_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
permission_level: Mapped[str] = mapped_column(String(30), default="read")
status: Mapped[str] = mapped_column(String(20), index=True)
result_summary: Mapped[str | None] = mapped_column(Text(), nullable=True)
error_message: Mapped[str | None] = mapped_column(Text(), nullable=True)
started_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), index=True
)
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
task_asset = relationship("AgentAsset", back_populates="scheduled_runs")
tool_calls = relationship(
"AgentToolCall",
back_populates="run",
cascade="all, delete-orphan",
order_by="asc(AgentToolCall.created_at)",
)
semantic_parse_logs = relationship(
"SemanticParseLog",
back_populates="run",
cascade="all, delete-orphan",
order_by="asc(SemanticParseLog.created_at)",
)
class AgentToolCall(Base):
__tablename__ = "agent_tool_calls"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
run_id: Mapped[str] = mapped_column(ForeignKey("agent_runs.run_id"), index=True)
tool_type: Mapped[str] = mapped_column(String(30))
tool_name: Mapped[str] = mapped_column(String(100))
request_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
response_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
status: Mapped[str] = mapped_column(String(20))
duration_ms: Mapped[int] = mapped_column(Integer, default=0)
error_message: Mapped[str | None] = mapped_column(Text(), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
run = relationship("AgentRun", back_populates="tool_calls")
class SemanticParseLog(Base):
__tablename__ = "semantic_parse_logs"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
run_id: Mapped[str] = mapped_column(ForeignKey("agent_runs.run_id"), index=True)
user_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
raw_query: Mapped[str] = mapped_column(Text())
scenario: Mapped[str] = mapped_column(String(50), index=True)
intent: Mapped[str] = mapped_column(String(50), index=True)
entities_json: Mapped[list[Any]] = mapped_column(JSON, default=list)
time_range_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
metrics_json: Mapped[list[Any]] = mapped_column(JSON, default=list)
constraints_json: Mapped[list[Any]] = mapped_column(JSON, default=list)
risk_flags_json: Mapped[list[Any]] = mapped_column(JSON, default=list)
permission_json: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
confidence: Mapped[float] = mapped_column(Float, default=0.0)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
run = relationship("AgentRun", back_populates="semantic_parse_logs")

View File

@@ -0,0 +1,25 @@
from __future__ import annotations
import uuid
from datetime import datetime
from typing import Any
from sqlalchemy import DateTime, String, func
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import JSON
from app.db.base_class import Base
class AuditLog(Base):
__tablename__ = "audit_logs"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
actor: Mapped[str] = mapped_column(String(100))
action: Mapped[str] = mapped_column(String(100), index=True)
resource_type: Mapped[str] = mapped_column(String(50), index=True)
resource_id: Mapped[str] = mapped_column(String(100), index=True)
before_json: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
after_json: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
request_id: Mapped[str] = mapped_column(String(64), index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())

View File

@@ -0,0 +1,118 @@
from __future__ import annotations
import uuid
from datetime import date, datetime
from decimal import Decimal
from typing import Any
from sqlalchemy import Date, DateTime, ForeignKey, Integer, Numeric, String, Text, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.types import JSON
from app.db.base_class import Base
class ExpenseClaim(Base):
__tablename__ = "expense_claims"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
claim_no: Mapped[str] = mapped_column(String(50), unique=True, index=True)
employee_id: Mapped[str | None] = mapped_column(
ForeignKey("employees.id"), nullable=True, index=True
)
employee_name: Mapped[str] = mapped_column(String(100), index=True)
department_id: Mapped[str | None] = mapped_column(
ForeignKey("organization_units.id"), nullable=True, index=True
)
department_name: Mapped[str] = mapped_column(String(100), index=True)
project_code: Mapped[str | None] = mapped_column(String(50), nullable=True)
expense_type: Mapped[str] = mapped_column(String(50), index=True)
reason: Mapped[str] = mapped_column(Text())
location: Mapped[str] = mapped_column(String(100))
amount: Mapped[Decimal] = mapped_column(Numeric(12, 2))
currency: Mapped[str] = mapped_column(String(10), default="CNY")
invoice_count: Mapped[int] = mapped_column(Integer, default=0)
occurred_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
submitted_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True, index=True
)
status: Mapped[str] = mapped_column(String(30), index=True)
approval_stage: Mapped[str | None] = mapped_column(String(50), nullable=True)
risk_flags_json: Mapped[list[Any]] = mapped_column(JSON, default=list)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
items = relationship(
"ExpenseClaimItem",
back_populates="claim",
cascade="all, delete-orphan",
order_by="asc(ExpenseClaimItem.item_date)",
)
class ExpenseClaimItem(Base):
__tablename__ = "expense_claim_items"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
claim_id: Mapped[str] = mapped_column(ForeignKey("expense_claims.id"), index=True)
item_date: Mapped[date] = mapped_column(Date(), index=True)
item_type: Mapped[str] = mapped_column(String(50))
item_reason: Mapped[str] = mapped_column(Text())
item_location: Mapped[str] = mapped_column(String(100))
item_amount: Mapped[Decimal] = mapped_column(Numeric(12, 2))
invoice_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
claim = relationship("ExpenseClaim", back_populates="items")
class AccountsReceivableRecord(Base):
__tablename__ = "accounts_receivable"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
receivable_no: Mapped[str] = mapped_column(String(50), unique=True, index=True)
customer_id: Mapped[str] = mapped_column(String(64), index=True)
customer_name: Mapped[str] = mapped_column(String(120), index=True)
contract_no: Mapped[str | None] = mapped_column(String(100), nullable=True)
invoice_no: Mapped[str | None] = mapped_column(String(100), nullable=True)
amount_receivable: Mapped[Decimal] = mapped_column(Numeric(12, 2))
amount_received: Mapped[Decimal] = mapped_column(Numeric(12, 2))
amount_outstanding: Mapped[Decimal] = mapped_column(Numeric(12, 2))
currency: Mapped[str] = mapped_column(String(10), default="CNY")
posting_date: Mapped[date] = mapped_column(Date(), index=True)
due_date: Mapped[date] = mapped_column(Date(), index=True)
aging_days: Mapped[int] = mapped_column(Integer, default=0)
status: Mapped[str] = mapped_column(String(30), index=True)
risk_flags_json: Mapped[list[Any]] = mapped_column(JSON, default=list)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)
class AccountsPayableRecord(Base):
__tablename__ = "accounts_payable"
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
payable_no: Mapped[str] = mapped_column(String(50), unique=True, index=True)
vendor_id: Mapped[str] = mapped_column(String(64), index=True)
vendor_name: Mapped[str] = mapped_column(String(120), index=True)
invoice_no: Mapped[str | None] = mapped_column(String(100), nullable=True)
amount_payable: Mapped[Decimal] = mapped_column(Numeric(12, 2))
amount_paid: Mapped[Decimal] = mapped_column(Numeric(12, 2))
amount_outstanding: Mapped[Decimal] = mapped_column(Numeric(12, 2))
currency: Mapped[str] = mapped_column(String(10), default="CNY")
posting_date: Mapped[date] = mapped_column(Date(), index=True)
due_date: Mapped[date] = mapped_column(Date(), index=True)
aging_days: Mapped[int] = mapped_column(Integer, default=0)
status: Mapped[str] = mapped_column(String(30), index=True)
risk_flags_json: Mapped[list[Any]] = mapped_column(JSON, default=list)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
)