feat(advisor-agent): Introduce advisor agent functionalities with compliance, KYC, and script templates
- Added new modules for advisor compliance, KYC sessions, and script templates, enhancing the advisor agent's capabilities. - Implemented a comprehensive API structure under the `/api/advisor-agent` prefix, ensuring clear organization and access to new features. - Established database models and repositories for compliance rules and KYC sessions, facilitating robust data management. - Integrated exception handling and response models to improve error management and user feedback. - Updated settings to include new configurations for compliance and KYC features, ensuring flexibility and adaptability. This update significantly expands the advisor agent's functionality, providing essential tools for compliance and customer interaction while maintaining a structured API design.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
reps = [
|
||||
("AdvisorAuthContext", "AdvisorAuthContext"),
|
||||
("ScriptTemplateRepository", "ScriptTemplateRepository"),
|
||||
(
|
||||
"from app.service.script_template_service import ScriptTemplateService",
|
||||
"from app.service.script_template_service import ScriptTemplateService",
|
||||
),
|
||||
]
|
||||
for p in list(root.glob("app/**/*.py")) + list(root.glob("scripts/**/*.py")):
|
||||
t = p.read_text(encoding="utf-8")
|
||||
o = t
|
||||
for a, b in reps:
|
||||
t = t.replace(a, b)
|
||||
if t != o:
|
||||
p.write_text(t, encoding="utf-8")
|
||||
print(p.relative_to(root))
|
||||
@@ -0,0 +1,73 @@
|
||||
"""One-off: rewrite imports for advisor-agent merge (run from repo root)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
REPLACEMENTS = [
|
||||
("from app.model.schemas import", "from app.model.advisor_schemas import"),
|
||||
("from app.model.entities import ScriptTemplate", "from app.model.entities_advisor import ScriptTemplate"),
|
||||
("from app.model.entities import ComplianceRule", "from app.model.entities_advisor import ComplianceRule"),
|
||||
("from app.model.entities import ComplianceCheckLog", "from app.model.entities_advisor import ComplianceCheckLog"),
|
||||
("from app.model.entities import CopyTrackLog", "from app.model.entities_advisor import CopyTrackLog"),
|
||||
("from app.model.entities import KycSession", "from app.model.entities_advisor import KycSession"),
|
||||
("from app.model.entities import MarketAlert", "from app.model.entities_advisor import MarketAlert"),
|
||||
("from app.model.entities import TemplateUseLog", "from app.model.entities_advisor import TemplateUseLog"),
|
||||
("from app.repository.template_repository import", "from app.repository.script_template_repository import"),
|
||||
("from app.service.template_vector_service import", "from app.service.script_template_vector_service import"),
|
||||
("from app.service.template_service import", "from app.service.script_template_service import"),
|
||||
("TemplateRepository", "ScriptTemplateRepository"),
|
||||
("TemplateVectorService", "ScriptTemplateVectorService"),
|
||||
("class TemplateService:", "class ScriptTemplateService:"),
|
||||
("TemplateService(", "ScriptTemplateService("),
|
||||
("from app.api.deps import get_auth_context, require_permission", "from app.api.advisor_auth_adapter import get_advisor_auth, require_advisor_permission"),
|
||||
("from app.api.deps import require_permission", "from app.api.advisor_auth_adapter import require_advisor_permission"),
|
||||
("from app.api.deps import get_auth_context", "from app.api.advisor_auth_adapter import get_advisor_auth"),
|
||||
("require_permission(", "require_advisor_permission("),
|
||||
("get_auth_context", "get_advisor_auth"),
|
||||
("AuthContext", "AdvisorAuthContext"),
|
||||
("from app.model.advisor_schemas import AdvisorAuthContext", "from app.api.advisor_auth_adapter import AdvisorAuthContext"),
|
||||
("success_response", "_advisor_ok"),
|
||||
("from app.utils.response import _advisor_ok", "from app.api.advisor_http import advisor_ok as _advisor_ok"),
|
||||
]
|
||||
|
||||
GLOBS = [
|
||||
"app/service/compliance_*.py",
|
||||
"app/service/copy_*.py",
|
||||
"app/service/kyc_*.py",
|
||||
"app/service/market_*.py",
|
||||
"app/service/script_template*.py",
|
||||
"app/service/input_guard_service.py",
|
||||
"app/service/ownership_service.py",
|
||||
"app/service/llm_client.py",
|
||||
"app/repository/compliance_*.py",
|
||||
"app/repository/copy_*.py",
|
||||
"app/repository/kyc_*.py",
|
||||
"app/repository/market_*.py",
|
||||
"app/repository/script_template*.py",
|
||||
"app/api/allocation.py",
|
||||
"app/api/copy.py",
|
||||
"app/api/dashboard.py",
|
||||
"app/api/guard.py",
|
||||
"app/api/kyc.py",
|
||||
"app/api/market.py",
|
||||
"app/api/templates.py",
|
||||
"scripts/seed/*.py",
|
||||
"scripts/sync/sync_template_vectors.py",
|
||||
]
|
||||
|
||||
def main() -> None:
|
||||
for pattern in GLOBS:
|
||||
for path in ROOT.glob(pattern):
|
||||
text = path.read_text(encoding="utf-8")
|
||||
orig = text
|
||||
for old, new in REPLACEMENTS:
|
||||
text = text.replace(old, new)
|
||||
if text != orig:
|
||||
path.write_text(text, encoding="utf-8")
|
||||
print("fixed", path.relative_to(ROOT))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,55 @@
|
||||
"""Rewrite advisor sprint tests for merger canonical paths."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2] / "tests"
|
||||
|
||||
SCHEMA_IMPORTS = (
|
||||
"ComplianceCheckRequest",
|
||||
"ComplianceRuleCreate",
|
||||
"ComplianceRuleUpdate",
|
||||
"ComplianceCheckResult",
|
||||
"CopyTrackRequest",
|
||||
"TemplateCreate",
|
||||
"KycSessionCreate",
|
||||
"KycChatRequest",
|
||||
"MarketAlertScanRequest",
|
||||
"GuardCheckRequest",
|
||||
)
|
||||
|
||||
PATH_MAP = [
|
||||
("/api/v1/compliance/check", "/api/advisor-agent/compliance/content-check"),
|
||||
("/api/v1/compliance/ping", "/api/advisor-agent/compliance/ping"),
|
||||
("/api/v1/compliance/rules", "/api/advisor-agent/compliance/rules"),
|
||||
("/api/v1/templates/", "/api/advisor-agent/script-templates/"),
|
||||
("/api/v1/templates", "/api/advisor-agent/script-templates"),
|
||||
("/api/v1/market-alerts", "/api/advisor-agent/market-alerts"),
|
||||
("/api/v1/market/", "/api/advisor-agent/market/"),
|
||||
("/api/v1/kyc/", "/api/advisor-agent/kyc/"),
|
||||
("/api/v1/kyc", "/api/advisor-agent/kyc"),
|
||||
("/api/v1/copy/", "/api/advisor-agent/copy/"),
|
||||
("/api/v1/guard/", "/api/advisor-agent/guard/"),
|
||||
("/api/v1/allocation/", "/api/advisor-agent/allocation/"),
|
||||
("/api/v1/dashboard/", "/api/advisor-agent/dashboard/"),
|
||||
("/api/v1/admin/audit-logs", "/api/advisor-agent/admin/audit-logs"),
|
||||
]
|
||||
|
||||
for path in ROOT.glob("test_sprint*.py"):
|
||||
text = path.read_text(encoding="utf-8")
|
||||
orig = text
|
||||
for old, new in PATH_MAP:
|
||||
text = text.replace(old, new)
|
||||
text = text.replace("from app.model.schemas import", "from app.model.advisor_schemas import")
|
||||
text = text.replace("from app.service.template_service import", "from app.service.script_template_service import")
|
||||
text = text.replace("TemplateService", "ScriptTemplateService")
|
||||
text = re.sub(
|
||||
r'client\.post\(\s*"/api/v1/auth/login",\s*json=\{"username": "advisor_test", "password": "advisor_test"\},?\s*\)',
|
||||
'client.post("/api/auth/login", json={"actor_id": "STAFF-10086", "token_type": "staff"})',
|
||||
text,
|
||||
)
|
||||
text = text.replace('"access_token"', '"access_token"') # login response shape differs
|
||||
if text != orig:
|
||||
path.write_text(text, encoding="utf-8")
|
||||
print("updated", path.name)
|
||||
@@ -0,0 +1,55 @@
|
||||
"""Second pass: auth + stale advisor imports in sprint tests."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2] / "tests"
|
||||
|
||||
LOGIN_BLOCK = re.compile(
|
||||
r"def login_token\(username: str, password: str\) -> str:\n"
|
||||
r" response = client\.post\(\n"
|
||||
r' "/api/v1/auth/login",\n'
|
||||
r' json=\{"username": username, "password": password\},\n'
|
||||
r" \)\n"
|
||||
r" assert response\.status_code == 200\n"
|
||||
r' return response\.json\(\)\["data"\]\["access_token"\]\n',
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
LOGIN_INLINE = re.compile(
|
||||
r'client\.post\(\s*\n?\s*"/api/v1/auth/login",\s*\n?\s*json=\{"username": "[^"]+", "password": "[^"]+"\},?\s*\)',
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
REPLACEMENTS = [
|
||||
("from app.config.database import AgentSessionLocal", "from app.advisor_db import AgentSessionLocal"),
|
||||
("from app.config.database import agent_engine", ""),
|
||||
("from app.model.entities import ComplianceRule", "from app.model.entities_advisor import ComplianceRule"),
|
||||
("app.config.database", "app.advisor_db"),
|
||||
]
|
||||
|
||||
for path in sorted(ROOT.glob("test_sprint*.py")) + [ROOT / "test_demo_kyc_advisor_mapping.py"]:
|
||||
if not path.exists():
|
||||
continue
|
||||
text = path.read_text(encoding="utf-8")
|
||||
orig = text
|
||||
for old, new in REPLACEMENTS:
|
||||
text = text.replace(old, new)
|
||||
text = LOGIN_BLOCK.sub(
|
||||
"from tests.advisor_test_utils import login_staff_token\n\n"
|
||||
"def login_token(username: str, password: str) -> str:\n"
|
||||
" from tests.advisor_test_utils import STAFF_ADVISOR, STAFF_COMPLIANCE\n"
|
||||
" actor = STAFF_COMPLIANCE if username == \"compliance_test\" else STAFF_ADVISOR\n"
|
||||
" return login_staff_token(client, actor_id=actor)\n",
|
||||
text,
|
||||
)
|
||||
|
||||
def _inline_login(match: re.Match[str]) -> str:
|
||||
return 'client.post("/api/auth/login", json={"actor_id": "STAFF-10086", "token_type": "staff"})'
|
||||
|
||||
text = LOGIN_INLINE.sub(_inline_login, text)
|
||||
text = re.sub(r"\n\n\n+", "\n\n", text)
|
||||
if text != orig:
|
||||
path.write_text(text, encoding="utf-8")
|
||||
print("updated", path.name)
|
||||
@@ -1,30 +1,60 @@
|
||||
# 口径字典 + 问数模板种子(本机 MySQL jinrong_agent)␍
|
||||
param(␍
|
||||
[ValidateSet("Auto", "Docker", "Local")]␍
|
||||
[string]$RestartBackend = "Local",␍
|
||||
[switch]$SkipRestart␍
|
||||
)␍
|
||||
$ErrorActionPreference = "Stop"␍
|
||||
$Root = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)␍
|
||||
$AgentDir = Join-Path $Root "scripts\agent"␍
|
||||
␍
|
||||
Write-Host "Seeding analyst metric dict..."␍
|
||||
Get-Content (Join-Path $AgentDir "seed-analyst-metric-dict.sql") -Raw | mysql -u root jinrong_agent␍
|
||||
␍
|
||||
Write-Host "Seeding analyst query templates..."␍
|
||||
Get-Content (Join-Path $AgentDir "seed-analyst-query-templates.sql") -Raw | mysql -u root jinrong_agent␍
|
||||
␍
|
||||
Write-Host "Seeding analyst few-shots (D-11 published)..."␍
|
||||
Get-Content (Join-Path $AgentDir "seed-analyst-few-shots.sql") -Raw | mysql -u root jinrong_agent␍
|
||||
␍
|
||||
Write-Host "Done."␍
|
||||
␍
|
||||
if (-not $SkipRestart) {␍
|
||||
$RestartScript = Join-Path $Root "scripts\dev\restart-dev.ps1"␍
|
||||
if (Test-Path $RestartScript) {␍
|
||||
Write-Host "Restarting backend after seed ($RestartBackend)..." -ForegroundColor Cyan␍
|
||||
& $RestartScript -Backend $RestartBackend -SkipFrontend␍
|
||||
} else {␍
|
||||
Write-Host "WARN: restart-dev.ps1 not found; skip backend restart." -ForegroundColor Yellow␍
|
||||
}␍
|
||||
}␍
|
||||
# 口径字典 + 问数模板种子(本机 MySQL jinrong_agent)
|
||||
|
||||
param(
|
||||
|
||||
[ValidateSet("Auto", "Docker", "Local")]
|
||||
|
||||
[string]$RestartBackend = "Local",
|
||||
|
||||
[switch]$SkipRestart
|
||||
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$Root = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
|
||||
|
||||
$AgentDir = Join-Path $Root "scripts\agent"
|
||||
|
||||
|
||||
|
||||
Write-Host "Seeding analyst metric dict..."
|
||||
|
||||
Get-Content (Join-Path $AgentDir "seed-analyst-metric-dict.sql") -Raw | mysql -u root jinrong_agent
|
||||
|
||||
|
||||
|
||||
Write-Host "Seeding analyst query templates..."
|
||||
|
||||
Get-Content (Join-Path $AgentDir "seed-analyst-query-templates.sql") -Raw | mysql -u root jinrong_agent
|
||||
|
||||
|
||||
|
||||
Write-Host "Seeding analyst few-shots (D-11 published)..."
|
||||
|
||||
Get-Content (Join-Path $AgentDir "seed-analyst-few-shots.sql") -Raw | mysql -u root jinrong_agent
|
||||
|
||||
|
||||
|
||||
Write-Host "Done."
|
||||
|
||||
|
||||
|
||||
if (-not $SkipRestart) {
|
||||
|
||||
$RestartScript = Join-Path $Root "scripts\dev\restart-dev.ps1"
|
||||
|
||||
if (Test-Path $RestartScript) {
|
||||
|
||||
Write-Host "Restarting backend after seed ($RestartBackend)..." -ForegroundColor Cyan
|
||||
|
||||
& $RestartScript -Backend $RestartBackend -SkipFrontend
|
||||
|
||||
} else {
|
||||
|
||||
Write-Host "WARN: restart-dev.ps1 not found; skip backend restart." -ForegroundColor Yellow
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user