sophhub 0.4.21 → 0.4.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents/parent-toddler/.config.json +7 -1
- package/agents/parent-toddler/HEARTBEAT.md +4 -4
- package/agents/parent-toddler/TOOLS.md +9 -0
- package/agents/parent-toddler/scripts/compact_sessions_over_threshold.py +368 -0
- package/package.json +1 -1
- package/skills/agent-install/src/SKILL.md +2 -0
- package/skills/agent-install/src/scripts/common.py +20 -0
- package/skills/agent-install/src/scripts/update_openclaw.py +5 -0
- package/skills/image-classify/skill.json +12 -5
- package/skills/image-classify/src/SKILL.md +1 -1
- package/skills/image-classify/src/references/config.json +1 -1
- package/skills/insurance-customer-policy/skill.json +12 -0
- package/skills/insurance-customer-policy/src/SKILL.md +121 -0
- package/skills/insurance-customer-policy/src/pyproject.toml +9 -0
- package/skills/insurance-customer-policy/src/scripts/cli.py +16 -0
- package/skills/insurance-customer-policy/src/scripts/cloud_insurance_full_test.py +785 -0
- package/skills/insurance-customer-policy/src/scripts/dashboard_all.py +205 -0
- package/skills/insurance-customer-policy/src/scripts/insurance_customer_cli/__init__.py +0 -0
- package/skills/insurance-customer-policy/src/scripts/insurance_customer_cli/__main__.py +4 -0
- package/skills/insurance-customer-policy/src/scripts/insurance_customer_cli/cli.py +816 -0
- package/skills/insurance-customer-policy/src/scripts/insurance_customer_cli/db.py +181 -0
- package/skills/insurance-customer-policy/src/scripts/insurance_customer_cli/insurance_paths.py +184 -0
- package/skills/insurance-customer-policy/src/scripts/run_e2e_smoke.py +164 -0
- package/skills/insurance-customer-policy/src/scripts/test_cloud_zero_config.py +217 -0
- package/skills/insurance-customer-policy/src/scripts/test_dashboard_all.py +113 -0
- package/skills/insurance-customer-policy/src/src/insurance_customer_cli/__init__.py +0 -0
- package/skills/insurance-customer-policy/src/src/insurance_customer_cli/__main__.py +4 -0
- package/skills/insurance-customer-policy/src/src/insurance_customer_cli/cli.py +816 -0
- package/skills/insurance-customer-policy/src/src/insurance_customer_cli/db.py +181 -0
- package/skills/insurance-customer-policy/src/src/insurance_customer_cli/insurance_paths.py +184 -0
- package/skills/insurance-product-analysis/skill.json +12 -0
- package/skills/insurance-product-analysis/src/SKILL.md +99 -0
- package/skills/insurance-product-analysis/src/pyproject.toml +9 -0
- package/skills/insurance-product-analysis/src/scripts/cli.py +16 -0
- package/skills/insurance-product-analysis/src/scripts/insurance_product_cli/__init__.py +0 -0
- package/skills/insurance-product-analysis/src/scripts/insurance_product_cli/__main__.py +4 -0
- package/skills/insurance-product-analysis/src/scripts/insurance_product_cli/cli.py +545 -0
- package/skills/insurance-product-analysis/src/scripts/insurance_product_cli/db.py +180 -0
- package/skills/insurance-product-analysis/src/scripts/insurance_product_cli/orchestrator.py +163 -0
- package/skills/insurance-product-analysis/src/scripts/run_e2e_smoke.py +202 -0
- package/skills/insurance-product-analysis/src/src/insurance_product_cli/__init__.py +0 -0
- package/skills/insurance-product-analysis/src/src/insurance_product_cli/__main__.py +4 -0
- package/skills/insurance-product-analysis/src/src/insurance_product_cli/cli.py +545 -0
- package/skills/insurance-product-analysis/src/src/insurance_product_cli/db.py +180 -0
- package/skills/insurance-product-analysis/src/src/insurance_product_cli/orchestrator.py +163 -0
- package/skills/insurance-sales-pipeline/skill.json +12 -0
- package/skills/insurance-sales-pipeline/src/SKILL.md +102 -0
- package/skills/insurance-sales-pipeline/src/pyproject.toml +9 -0
- package/skills/insurance-sales-pipeline/src/scripts/cli.py +16 -0
- package/skills/insurance-sales-pipeline/src/scripts/insurance_pipeline_cli/__init__.py +0 -0
- package/skills/insurance-sales-pipeline/src/scripts/insurance_pipeline_cli/__main__.py +4 -0
- package/skills/insurance-sales-pipeline/src/scripts/insurance_pipeline_cli/cli.py +496 -0
- package/skills/insurance-sales-pipeline/src/scripts/insurance_pipeline_cli/db.py +180 -0
- package/skills/insurance-sales-pipeline/src/scripts/insurance_pipeline_cli/orchestrator.py +36 -0
- package/skills/insurance-sales-pipeline/src/scripts/run_e2e_smoke.py +208 -0
- package/skills/insurance-sales-pipeline/src/src/insurance_pipeline_cli/__init__.py +0 -0
- package/skills/insurance-sales-pipeline/src/src/insurance_pipeline_cli/__main__.py +4 -0
- package/skills/insurance-sales-pipeline/src/src/insurance_pipeline_cli/cli.py +496 -0
- package/skills/insurance-sales-pipeline/src/src/insurance_pipeline_cli/db.py +180 -0
- package/skills/insurance-sales-pipeline/src/src/insurance_pipeline_cli/orchestrator.py +36 -0
- package/skills/insurance-schedule-renewal/skill.json +12 -0
- package/skills/insurance-schedule-renewal/src/SKILL.md +94 -0
- package/skills/insurance-schedule-renewal/src/pyproject.toml +9 -0
- package/skills/insurance-schedule-renewal/src/scripts/cli.py +16 -0
- package/skills/insurance-schedule-renewal/src/scripts/insurance_schedule_cli/__init__.py +0 -0
- package/skills/insurance-schedule-renewal/src/scripts/insurance_schedule_cli/__main__.py +4 -0
- package/skills/insurance-schedule-renewal/src/scripts/insurance_schedule_cli/cli.py +429 -0
- package/skills/insurance-schedule-renewal/src/scripts/insurance_schedule_cli/db.py +180 -0
- package/skills/insurance-schedule-renewal/src/scripts/insurance_schedule_cli/orchestrator.py +94 -0
- package/skills/insurance-schedule-renewal/src/scripts/run_e2e_smoke.py +218 -0
- package/skills/insurance-schedule-renewal/src/src/insurance_schedule_cli/__init__.py +0 -0
- package/skills/insurance-schedule-renewal/src/src/insurance_schedule_cli/__main__.py +4 -0
- package/skills/insurance-schedule-renewal/src/src/insurance_schedule_cli/cli.py +429 -0
- package/skills/insurance-schedule-renewal/src/src/insurance_schedule_cli/db.py +180 -0
- package/skills/insurance-schedule-renewal/src/src/insurance_schedule_cli/orchestrator.py +94 -0
- package/skills/insurance-shared-data/skill.json +20 -0
- package/skills/insurance-shared-data/src/SKILL.md +33 -0
- package/skills/insurance-shared-data/src/scripts/cloud_insurance_super_test.py +246 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Shared-db read helpers for insurance-sales-pipeline."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from insurance_pipeline_cli import db
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def customer_exists(customer_id: str) -> bool:
|
|
8
|
+
conn = db.connect()
|
|
9
|
+
try:
|
|
10
|
+
row = conn.execute(
|
|
11
|
+
"SELECT 1 FROM customers WHERE id=? AND status='active'",
|
|
12
|
+
(customer_id,),
|
|
13
|
+
).fetchone()
|
|
14
|
+
return bool(row)
|
|
15
|
+
finally:
|
|
16
|
+
conn.close()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def customer_query(customer_id: str) -> dict:
|
|
20
|
+
conn = db.connect()
|
|
21
|
+
try:
|
|
22
|
+
rows = list(
|
|
23
|
+
conn.execute("SELECT * FROM customers WHERE id=? AND status='active'", (customer_id,))
|
|
24
|
+
)
|
|
25
|
+
return {"ok": True, "customers": [dict(r) for r in rows], "count": len(rows)}
|
|
26
|
+
finally:
|
|
27
|
+
conn.close()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def product_query(name: str) -> dict:
|
|
31
|
+
conn = db.connect()
|
|
32
|
+
try:
|
|
33
|
+
rows = list(conn.execute("SELECT * FROM products WHERE name=? AND status='active'", (name,)))
|
|
34
|
+
return {"ok": True, "products": [dict(r) for r in rows], "count": len(rows)}
|
|
35
|
+
finally:
|
|
36
|
+
conn.close()
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""E2E smoke for insurance-sales-pipeline on shared insurance DB."""
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import sqlite3
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
import tempfile
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
HERE = Path(__file__).resolve().parent
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _discover_skills_root() -> Path:
|
|
18
|
+
env = os.environ.get("INSURANCE_SKILLS_ROOT") or os.environ.get("SKILLS_ROOT")
|
|
19
|
+
if env:
|
|
20
|
+
p = Path(env).expanduser().resolve()
|
|
21
|
+
if p.is_dir():
|
|
22
|
+
return p
|
|
23
|
+
candidates = [HERE.parents[2], HERE.parents[1], Path("/app/skills"), Path.cwd() / "skills"]
|
|
24
|
+
for c in candidates:
|
|
25
|
+
try:
|
|
26
|
+
if (c / "insurance-customer-policy").is_dir():
|
|
27
|
+
return c.resolve()
|
|
28
|
+
except OSError:
|
|
29
|
+
continue
|
|
30
|
+
raise SystemExit("[FAIL] cannot discover skills root for smoke test")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _resolve_customer_pkg(skills_root: Path) -> Path:
|
|
34
|
+
candidates = [
|
|
35
|
+
skills_root / "insurance-customer-policy" / "scripts",
|
|
36
|
+
skills_root / "insurance-customer-policy" / "src" / "scripts",
|
|
37
|
+
skills_root / "insurance-customer-policy" / "src" / "src",
|
|
38
|
+
]
|
|
39
|
+
for p in candidates:
|
|
40
|
+
try:
|
|
41
|
+
if (p / "insurance_customer_cli" / "__init__.py").is_file():
|
|
42
|
+
return p
|
|
43
|
+
except OSError:
|
|
44
|
+
continue
|
|
45
|
+
raise SystemExit("[FAIL] cannot locate insurance_customer_cli package for smoke test")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
SKILLS_ROOT = _discover_skills_root()
|
|
49
|
+
CUSTOMER_PKG = _resolve_customer_pkg(SKILLS_ROOT)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _resolve_cli(skill_folder: str) -> Path:
|
|
53
|
+
candidates = [
|
|
54
|
+
SKILLS_ROOT / skill_folder / "scripts" / "cli.py",
|
|
55
|
+
SKILLS_ROOT / skill_folder / "src" / "scripts" / "cli.py",
|
|
56
|
+
]
|
|
57
|
+
for p in candidates:
|
|
58
|
+
if p.is_file():
|
|
59
|
+
return p
|
|
60
|
+
raise SystemExit(f"[FAIL] cannot locate cli.py for {skill_folder}")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
PIPELINE_CLI = _resolve_cli("insurance-sales-pipeline")
|
|
64
|
+
CUSTOMER_CLI = _resolve_cli("insurance-customer-policy")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _run_json(cmd: list[str], env: dict[str, str], label: str, timeout: int = 120) -> dict:
|
|
68
|
+
r = subprocess.run(cmd, env=env, capture_output=True, text=True, encoding="utf-8", timeout=timeout)
|
|
69
|
+
if r.returncode not in (0, 1):
|
|
70
|
+
raise SystemExit(f"{label} rc={r.returncode}\nCMD: {' '.join(cmd)}\nSTDOUT:\n{r.stdout}\nSTDERR:\n{r.stderr}")
|
|
71
|
+
out = (r.stdout or "").strip()
|
|
72
|
+
if not out:
|
|
73
|
+
raise SystemExit(f"{label} empty stdout\nCMD: {' '.join(cmd)}\nrc={r.returncode}\nSTDERR:\n{r.stderr}")
|
|
74
|
+
try:
|
|
75
|
+
return json.loads(out)
|
|
76
|
+
except json.JSONDecodeError as e:
|
|
77
|
+
raise SystemExit(f"{label} bad json: {e}\nCMD: {' '.join(cmd)}\nSTDOUT:\n{out}\nSTDERR:\n{r.stderr}")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def run_pipeline(*args: str) -> dict:
|
|
81
|
+
cmd = [sys.executable, str(PIPELINE_CLI), *args]
|
|
82
|
+
env = {**os.environ, "PYTHONIOENCODING": "utf-8"}
|
|
83
|
+
return _run_json(cmd, env, "pipeline", timeout=120)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def run_customer(*args: str) -> dict:
|
|
87
|
+
cmd = [sys.executable, str(CUSTOMER_CLI), *args]
|
|
88
|
+
env = {**os.environ, "PYTHONIOENCODING": "utf-8"}
|
|
89
|
+
return _run_json(cmd, env, "customer", timeout=60)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def assert_ok(d: dict, *args: str) -> dict:
|
|
93
|
+
if not d.get("ok"):
|
|
94
|
+
raise SystemExit(f"expected ok=True got {d} args={args}")
|
|
95
|
+
return d
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def assert_err(d: dict, code: str) -> None:
|
|
99
|
+
if d.get("ok") or d.get("error") != code:
|
|
100
|
+
raise SystemExit(f"expected error={code} got {d}")
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def main() -> int:
|
|
104
|
+
tmp = tempfile.mkdtemp(prefix="ins_pipe_smoke_")
|
|
105
|
+
shared = str(Path(tmp) / "insurance.sqlite3")
|
|
106
|
+
os.environ["INSURANCE_DB_PATH"] = shared
|
|
107
|
+
os.environ["INSURANCE_PIPELINE_DB_PATH"] = shared
|
|
108
|
+
os.environ["INSURANCE_CUSTOMER_DB_PATH"] = shared
|
|
109
|
+
month = datetime.now().strftime("%Y-%m")
|
|
110
|
+
|
|
111
|
+
print("[setup] customers")
|
|
112
|
+
assert_ok(run_customer("customer-add", "--name", "张先生", "--phone", "13800138000",
|
|
113
|
+
"--age", "35", "--income", "300000"))
|
|
114
|
+
assert_ok(run_customer("customer-add", "--name", "李女士", "--phone", "13900139000", "--age", "40"))
|
|
115
|
+
assert_ok(run_customer("customer-add", "--name", "王先生", "--phone", "13700137000", "--age", "50"))
|
|
116
|
+
|
|
117
|
+
print("[1] deal-add: missing customer")
|
|
118
|
+
assert_err(run_pipeline("deal-add", "--customer-id", "C999"), "customer_not_found")
|
|
119
|
+
|
|
120
|
+
print("[2] deal-add x3 (different stages will be assigned later)")
|
|
121
|
+
d = assert_ok(run_pipeline("deal-add", "--customer-id", "C001",
|
|
122
|
+
"--product-type", "重疾险",
|
|
123
|
+
"--stage", "在谈", "--estimated-premium", "8000"))
|
|
124
|
+
assert d["id"] == "D001" and d["stage"] == "在谈"
|
|
125
|
+
assert_ok(run_pipeline("deal-add", "--customer-id", "C002",
|
|
126
|
+
"--product-type", "医疗险",
|
|
127
|
+
"--stage", "在谈", "--estimated-premium", "5000"))
|
|
128
|
+
assert_ok(run_pipeline("deal-add", "--customer-id", "C003",
|
|
129
|
+
"--product-type", "重疾险",
|
|
130
|
+
"--stage", "在谈", "--estimated-premium", "12000"))
|
|
131
|
+
|
|
132
|
+
print("[3] deal-stage: bad stage rejected")
|
|
133
|
+
assert_err(run_pipeline("deal-stage", "--deal-id", "D001", "--to", "瞎写阶段"), "bad_stage")
|
|
134
|
+
|
|
135
|
+
print("[4] deal-stage progression D001: 在谈 -> 方案中 -> 议价 -> 已成交")
|
|
136
|
+
assert_ok(run_pipeline("deal-stage", "--deal-id", "D001", "--to", "方案中", "--note", "已出方案"))
|
|
137
|
+
assert_ok(run_pipeline("deal-stage", "--deal-id", "D001", "--to", "议价"))
|
|
138
|
+
assert_ok(run_pipeline("deal-stage", "--deal-id", "D001", "--to", "已成交",
|
|
139
|
+
"--actual-premium", "8500"))
|
|
140
|
+
|
|
141
|
+
print("[5] deal-stage D002 to 方案中")
|
|
142
|
+
assert_ok(run_pipeline("deal-stage", "--deal-id", "D002", "--to", "方案中"))
|
|
143
|
+
|
|
144
|
+
print("[6] deal-query --status active (should be D002 in 方案中, D003 in 在谈)")
|
|
145
|
+
d = assert_ok(run_pipeline("deal-query", "--status", "active"))
|
|
146
|
+
stages = sorted([r["stage"] for r in d["deals"]])
|
|
147
|
+
assert stages == ["在谈", "方案中"], stages
|
|
148
|
+
|
|
149
|
+
print("[7] deal-forecast")
|
|
150
|
+
d = assert_ok(run_pipeline("deal-forecast", "--month", month))
|
|
151
|
+
# D003 在谈 12000*0.2=2400; D002 方案中 5000*0.5=2500
|
|
152
|
+
assert d["total_forecast"] == 2400 + 2500, d
|
|
153
|
+
assert d["closed"] == 8500, d
|
|
154
|
+
assert "重疾险" in d["by_type"]
|
|
155
|
+
assert d["by_type"]["重疾险"] == 2400.0
|
|
156
|
+
|
|
157
|
+
print("[8] target-track: set + read")
|
|
158
|
+
assert_ok(run_pipeline("target-track", "--month", month, "--target", "30000"))
|
|
159
|
+
d = assert_ok(run_pipeline("target-track", "--month", month))
|
|
160
|
+
assert d["target"] == 30000
|
|
161
|
+
assert d["closed"] == 8500
|
|
162
|
+
assert "目标跟踪" in d["markdown"]
|
|
163
|
+
assert d["forecast_breakdown"]["在谈"] == 2400.0
|
|
164
|
+
assert d["forecast_breakdown"]["方案中"] == 2500.0
|
|
165
|
+
|
|
166
|
+
print("[9] deal-lost: bad reason rejected")
|
|
167
|
+
assert_err(run_pipeline("deal-lost", "--deal-id", "D003", "--reason", "瞎写"), "bad_reason")
|
|
168
|
+
|
|
169
|
+
print("[10] deal-lost D003 with reason=价格")
|
|
170
|
+
assert_ok(run_pipeline("deal-lost", "--deal-id", "D003",
|
|
171
|
+
"--reason", "价格", "--note", "客户选了B公司"))
|
|
172
|
+
d = assert_ok(run_pipeline("deal-query", "--deal-id", "D003"))
|
|
173
|
+
assert d["deals"][0]["stage"] == "已流失"
|
|
174
|
+
assert d["deals"][0]["lost_reason"] == "价格"
|
|
175
|
+
|
|
176
|
+
print("[11] deal-lost --stats")
|
|
177
|
+
d = assert_ok(run_pipeline("deal-lost", "--stats", "--month", month))
|
|
178
|
+
assert d["total_count"] == 1
|
|
179
|
+
assert d["by_reason"][0]["reason"] == "价格"
|
|
180
|
+
|
|
181
|
+
print("[12] deal-update")
|
|
182
|
+
assert_ok(run_pipeline("deal-update", "--deal-id", "D002",
|
|
183
|
+
"--set", '{"estimated_premium": 6000, "note": "客户加保"}'))
|
|
184
|
+
d = assert_ok(run_pipeline("deal-query", "--deal-id", "D002"))
|
|
185
|
+
assert d["deals"][0]["estimated_premium"] == 6000
|
|
186
|
+
|
|
187
|
+
print("[13] deal-stage_log整链")
|
|
188
|
+
conn = sqlite3.connect(os.environ["INSURANCE_DB_PATH"])
|
|
189
|
+
rows = list(conn.execute("SELECT to_stage FROM deal_stage_log WHERE deal_id='D001' ORDER BY id"))
|
|
190
|
+
conn.close()
|
|
191
|
+
assert [r[0] for r in rows] == ["在谈", "方案中", "议价", "已成交"], [r[0] for r in rows]
|
|
192
|
+
|
|
193
|
+
print("[14] dashboard")
|
|
194
|
+
d = assert_ok(run_pipeline("dashboard", "--json"))
|
|
195
|
+
assert d["this_month_closed"] == 8500
|
|
196
|
+
assert d["this_month_target"] == 30000
|
|
197
|
+
assert d["active_deals"] >= 1
|
|
198
|
+
|
|
199
|
+
print("[15] deal-delete")
|
|
200
|
+
assert_err(run_pipeline("deal-delete", "--deal-id", "D002"), "need_confirm")
|
|
201
|
+
assert_ok(run_pipeline("deal-delete", "--deal-id", "D002", "--yes"))
|
|
202
|
+
|
|
203
|
+
print("ALL OK")
|
|
204
|
+
return 0
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
if __name__ == "__main__":
|
|
208
|
+
raise SystemExit(main())
|
|
File without changes
|