@shulex/cxclawctl 0.1.2 → 0.1.4
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/README.md +25 -22
- package/capabilities.lock.json +535 -131
- package/dist/cli.js +2203 -127
- package/dist/cli.js.map +1 -1
- package/package.json +20 -6
- package/requirements-cli.txt +0 -2
- package/skills/__lib__/__init__.py +0 -0
- package/skills/__lib__/skill-runtime/bootstrap.py +0 -134
- package/skills/__lib__/solvea-api/__init__.py +0 -0
- package/skills/__lib__/solvea-api/client.py +0 -1944
- package/skills/__lib__/solvea-api/dashboard.py +0 -100
- package/skills/__lib__/solvea-api/solvea_api.py +0 -1341
- package/skills/agent-analytics/SKILL.md +0 -74
- package/skills/agent-analytics/references/chat-report-fields.md +0 -36
- package/skills/agent-analytics/references/report-fields.md +0 -130
- package/skills/agent-analytics/references/transcript.md +0 -110
- package/skills/agent-builder/SKILL.md +0 -42
- package/skills/agent-builder/interfaces/fallback-chatflow.md +0 -47
- package/skills/agent-builder/references/chatflow/graph-spec.md +0 -259
- package/skills/agent-builder/references/chatflow/node-selection.md +0 -89
- package/skills/agent-builder/references/chatflow/nodes/agent.md +0 -350
- package/skills/agent-builder/references/chatflow/nodes/business-track.md +0 -43
- package/skills/agent-builder/references/chatflow/nodes/code.md +0 -157
- package/skills/agent-builder/references/chatflow/nodes/collect-order.md +0 -47
- package/skills/agent-builder/references/chatflow/nodes/finish.md +0 -34
- package/skills/agent-builder/references/chatflow/nodes/human-review.md +0 -60
- package/skills/agent-builder/references/chatflow/nodes/if-else.md +0 -100
- package/skills/agent-builder/references/chatflow/nodes/intent.md +0 -79
- package/skills/agent-builder/references/chatflow/nodes/knowledge-reply.md +0 -43
- package/skills/agent-builder/references/chatflow/nodes/llm.md +0 -117
- package/skills/agent-builder/references/chatflow/nodes/note.md +0 -38
- package/skills/agent-builder/references/chatflow/nodes/question.md +0 -97
- package/skills/agent-builder/references/chatflow/nodes/rag-reply.md +0 -68
- package/skills/agent-builder/references/chatflow/nodes/reply.md +0 -97
- package/skills/agent-builder/references/chatflow/nodes/start.md +0 -35
- package/skills/agent-builder/references/chatflow/nodes/sub-graph.md +0 -48
- package/skills/agent-builder/references/chatflow/nodes/tool.md +0 -138
- package/skills/agent-builder/references/chatflow/nodes/transfer-to-human.md +0 -63
- package/skills/agent-builder/references/chatflow/nodes/variable-assignment.md +0 -52
- package/skills/agent-builder/references/chatflow/nodes/variable-extract.md +0 -50
- package/skills/agent-builder/references/chatflow/nodes/variable-process.md +0 -70
- package/skills/agent-builder/references/chatflow/overview.md +0 -53
- package/skills/agent-builder/references/chatflow/visual-translation.md +0 -64
- package/skills/agent-builder/references/chatflow/workflow.md +0 -134
- package/skills/agent-builder/references/multi-agent/create-update.md +0 -185
- package/skills/agent-builder/references/multi-agent/graph-spec.md +0 -246
- package/skills/agent-builder/references/multi-agent/graph-structure.md +0 -12
- package/skills/agent-builder/references/multi-agent/migrate-to-chatflow.md +0 -13
- package/skills/agent-builder/references/multi-agent/migration/large.md +0 -554
- package/skills/agent-builder/references/multi-agent/migration/principles.md +0 -446
- package/skills/agent-builder/references/multi-agent/migration/small.md +0 -225
- package/skills/agent-builder/references/multi-agent/orchestration.md +0 -11
- package/skills/agent-builder/references/multi-agent/overview.md +0 -48
- package/skills/agent-builder/references/multi-agent/update.md +0 -128
- package/skills/agent-builder/references/multi-agent/workflow.md +0 -87
- package/skills/agent-builder/references/single-agent/overview.md +0 -48
- package/skills/agent-builder/references/single-agent/prompt.md +0 -16
- package/skills/agent-builder/references/single-agent/tools.md +0 -36
- package/skills/agent-builder/references/single-agent/voice.md +0 -46
- package/skills/agent-builder/references/tools/http-tool.md +0 -315
- package/skills/agent-builder/references/tools/mock-tool.md +0 -279
- package/skills/agent-builder/references/tools/overview.md +0 -46
- package/skills/agent-builder/references/tools/tool-config.md +0 -43
- package/skills/agent-builder/references/tools/x-token.md +0 -34
- package/skills/agent-builder/scripts/chatflow/chatflow_builder.py +0 -3458
- package/skills/agent-builder/scripts/chatflow/validate_graph.py +0 -1697
- package/skills/agent-builder/scripts/copy_marketplace_agent.py +0 -61
- package/skills/agent-builder/scripts/multi-agent/legacy_graph_analyzer.py +0 -5974
- package/skills/agent-builder/scripts/multi-agent/replay_compare.py +0 -679
- package/skills/agent-builder/scripts/patch_agent_tools.py +0 -135
- package/skills/agent-builder/scripts/tools/validate_tool_config.py +0 -384
- package/skills/agent-tester/SKILL.md +0 -35
- package/skills/agent-tester/batch-test/overview.md +0 -86
- package/skills/agent-tester/scripts/batch_test_sheet.py +0 -213
- package/skills/agent-tester/single-test/badcase-retest.md +0 -85
- package/skills/agent-tester/single-test/direct-test.md +0 -24
- package/skills/agent-tester/single-test/overview.md +0 -142
- package/skills/delivery/SKILL.md +0 -112
- package/skills/delivery/interfaces/classify.md +0 -38
- package/skills/delivery/interfaces/create-agents.md +0 -89
- package/skills/delivery/interfaces/delivery-test.md +0 -67
- package/skills/delivery/references/flow.md +0 -40
- package/skills/delivery/references/validation.md +0 -15
- package/skills/delivery/scripts/batch_test.py +0 -704
- package/skills/delivery/scripts/classify.py +0 -22
- package/skills/delivery/scripts/create_agents.py +0 -695
- package/skills/delivery/scripts/gen_delivery_report.py +0 -335
- package/skills/delivery/scripts/gen_excel.py +0 -234
- package/skills/delivery/scripts/gen_report.py +0 -208
- package/skills/delivery/scripts/plan.py +0 -163
- package/skills/delivery/scripts/render.py +0 -713
- package/skills/delivery/scripts/render_summary.py +0 -38
- package/skills/delivery/scripts/sample_history_cases.py +0 -109
- package/skills/kb-builder/SKILL.md +0 -80
- package/skills/kb-builder/interfaces/qa-upload.md +0 -55
- package/skills/kb-builder/interfaces/web-kb.md +0 -84
- package/skills/kb-builder/references/directory.md +0 -373
- package/skills/kb-builder/references/export.md +0 -282
- package/skills/kb-builder/references/isolation-keywords.md +0 -113
- package/skills/kb-builder/references/knowledge.md +0 -153
- package/skills/kb-builder/references/product.md +0 -52
- package/skills/kb-builder/references/qa-from-help-center.md +0 -58
- package/skills/kb-builder/references/qa-from-pdf.md +0 -94
- package/skills/kb-builder/references/qa-from-tickets.md +0 -168
- package/skills/kb-builder/references/qa-from-web.md +0 -98
- package/skills/kb-builder/references/qa-quality.md +0 -73
- package/skills/kb-builder/references/reply-template.md +0 -237
- package/skills/kb-builder/references/tag-isolation-migration.md +0 -61
- package/skills/kb-builder/references/tags.md +0 -45
- package/skills/kb-builder/references/upload.md +0 -130
- package/skills/kb-builder/scripts/batch_extract_ticket_qa.py +0 -333
- package/skills/kb-builder/scripts/build_directory_plan.py +0 -144
- package/skills/kb-builder/scripts/crawl_site.py +0 -433
- package/skills/kb-builder/scripts/create_dirs.py +0 -146
- package/skills/kb-builder/scripts/export_knowledge.py +0 -454
- package/skills/kb-builder/scripts/extract_ticket_qa.py +0 -441
- package/skills/kb-builder/scripts/extract_web_qa.py +0 -118
- package/skills/kb-builder/scripts/import_qa.py +0 -219
- package/skills/kb-builder/scripts/import_source_documents.py +0 -143
- package/skills/kb-builder/scripts/import_templates.py +0 -409
- package/skills/poc/SKILL.md +0 -125
- package/skills/poc/assets/default_manual.css +0 -279
- package/skills/poc/assets/notice_escalate.png +0 -0
- package/skills/poc/assets/notice_escalate2.png +0 -0
- package/skills/poc/assets/notice_new_chat.png +0 -0
- package/skills/poc/assets/poc_test_template.html +0 -645
- package/skills/poc/assets/poc_test_template.md +0 -104
- package/skills/poc/assets/step3_preview.png +0 -0
- package/skills/poc/assets/step4_order_note.png +0 -0
- package/skills/poc/interfaces/classify.md +0 -38
- package/skills/poc/interfaces/create-agents.md +0 -108
- package/skills/poc/interfaces/poc-test.md +0 -94
- package/skills/poc/references/flow.md +0 -47
- package/skills/poc/references/kb-directory.md +0 -22
- package/skills/poc/references/validation.md +0 -17
- package/skills/poc/scripts/classify.py +0 -22
- package/skills/poc/scripts/create_agents.py +0 -818
- package/skills/poc/scripts/gen_excel.py +0 -234
- package/skills/poc/scripts/gen_poc_manual.py +0 -249
- package/skills/poc/scripts/gen_report.py +0 -208
- package/skills/poc/scripts/inspect_chatflow.py +0 -209
- package/skills/poc/scripts/plan.py +0 -178
- package/skills/poc/scripts/poc_test.py +0 -495
- package/skills/poc/scripts/render.py +0 -713
- package/skills/poc/scripts/render_summary.py +0 -41
- package/skills/runtime-inspector/SKILL.md +0 -51
- package/skills/runtime-inspector/references/frontend-rendering.md +0 -126
- package/skills/runtime-inspector/references/model.md +0 -59
- package/skills/runtime-inspector/references/sandbox.md +0 -131
- package/skills/runtime-inspector/scripts/model_inspect.py +0 -68
- package/skills/runtime-inspector/scripts/prepare.py +0 -98
- package/skills/runtime-inspector/scripts/process.py +0 -84
- package/skills/runtime-inspector/scripts/read_state.py +0 -32
- package/skills/runtime-inspector/scripts/sandbox_inspect.py +0 -463
- package/skills/runtime-inspector/scripts/test_claude_proxy.py +0 -49
- package/skills/runtime-inspector/scripts/test_openai_proxy.py +0 -41
- package/skills/runtime-inspector/scripts/verify_persistence.py +0 -101
- package/skills/runtime-inspector/scripts/write_test.py +0 -41
- package/skills/setup/SKILL.md +0 -17
- package/skills/shulex-support/SKILL.md +0 -39
- package/skills/shulex-support/references/badcase-analysis/overview.md +0 -34
- package/skills/shulex-support/references/badcase-analysis/text/conversation-record.md +0 -105
- package/skills/shulex-support/references/badcase-analysis/text/core-flow.md +0 -89
- package/skills/shulex-support/references/badcase-analysis/text/log-query-rules.md +0 -36
- package/skills/shulex-support/references/badcase-analysis/text/m1-entry.md +0 -53
- package/skills/shulex-support/references/badcase-analysis/text/m10-chatflow-service.md +0 -259
- package/skills/shulex-support/references/badcase-analysis/text/m11-algorithm-service.md +0 -148
- package/skills/shulex-support/references/badcase-analysis/text/m12-quality-inspection.md +0 -57
- package/skills/shulex-support/references/badcase-analysis/text/m2-intent.md +0 -120
- package/skills/shulex-support/references/badcase-analysis/text/m3-strategy.md +0 -126
- package/skills/shulex-support/references/badcase-analysis/text/m4-aifield.md +0 -121
- package/skills/shulex-support/references/badcase-analysis/text/m5-agent.md +0 -208
- package/skills/shulex-support/references/badcase-analysis/text/m6-tool.md +0 -115
- package/skills/shulex-support/references/badcase-analysis/text/m7-reply.md +0 -72
- package/skills/shulex-support/references/badcase-analysis/text/m8-error.md +0 -92
- package/skills/shulex-support/references/badcase-analysis/text/m9-knowledge-search.md +0 -147
- package/skills/shulex-support/references/badcase-analysis/text/sls-query.md +0 -283
- package/skills/shulex-support/references/badcase-analysis/voice/core-flow.md +0 -77
- package/skills/shulex-support/references/badcase-analysis/voice/dependencies.md +0 -19
- package/skills/shulex-support/references/badcase-analysis/voice/platform-api.md +0 -23
- package/skills/shulex-support/references/badcase-analysis/voice/sls-playbook.md +0 -31
- package/skills/shulex-support/references/product-docs.md +0 -70
- package/skills/shulex-support/references/tickets.md +0 -184
- package/skills/shulex-support/scripts/support.py +0 -427
- package/skills/solvea-desk/SKILL.md +0 -39
- package/skills/solvea-desk/references/report/fields.md +0 -82
- package/skills/solvea-desk/references/report/report-design.md +0 -200
- package/skills/solvea-desk/references/report/report-template.html +0 -44
- package/skills/solvea-desk/references/report/report.css +0 -517
- package/skills/solvea-desk/references/report/workflow.md +0 -464
- package/skills/solvea-desk/references/tickets/query.md +0 -103
- package/skills/solvea-desk/references/tickets/tags.md +0 -83
- package/skills/solvea-desk/scripts/report/fetch_sources.py +0 -580
- package/skills/solvea-desk/scripts/report/render_report.py +0 -763
- package/skills/solvea-desk/scripts/tickets/desk_api.py +0 -73
- package/skills/solvea-desk/scripts/tickets/fetch_tickets.py +0 -403
- package/skills/solvea-desk/scripts/tickets/ticket_tags.py +0 -305
- package/skills/solvea-kit/SKILL.md +0 -84
- package/skills/solvea-kit/references/agent.md +0 -145
- package/skills/solvea-kit/references/chat.md +0 -244
- package/skills/solvea-kit/references/intent.md +0 -69
- package/skills/solvea-kit/references/plugin.md +0 -53
- package/skills/solvea-kit/references/quality-inspection.md +0 -75
- package/skills/solvea-kit/references/strategy.md +0 -62
- package/skills/solvea-kit/references/tagging.md +0 -231
- package/skills/solvea-kit/references/variables.md +0 -53
- package/skills/solvea-kit/scripts/generate_label_import_file.py +0 -151
- package/skills/solvea-kit/scripts/intent_classify.py +0 -843
|
@@ -1,695 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Delivery intent / agent / strategy creation pipeline.
|
|
3
|
-
|
|
4
|
-
Creates all standard delivery intents, copies fixed marketplace agents by
|
|
5
|
-
squareAgent_id when available, binds strategy routing, and patches X-Token in
|
|
6
|
-
order tools by default. Same-name intents are reused. Missing preset agents are
|
|
7
|
-
recorded as recoverable gaps instead of crashing the run.
|
|
8
|
-
|
|
9
|
-
Override the X-Token behaviour explicitly via --update-x-token / --no-update-x-token.
|
|
10
|
-
|
|
11
|
-
Usage:
|
|
12
|
-
# delivery: create all 7 intents + update X-Token
|
|
13
|
-
python3 scripts/create_agents.py
|
|
14
|
-
|
|
15
|
-
# disable SYSTEM intents after a creation run
|
|
16
|
-
python3 scripts/create_agents.py --disable-system
|
|
17
|
-
|
|
18
|
-
# dry-run preview (no API calls beyond marketplace listing in non-disable mode)
|
|
19
|
-
python3 scripts/create_agents.py --dry-run
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
import os, sys
|
|
23
|
-
sys.path.insert(0, os.path.join(os.environ.get("SOLVEA_LIB_DIR", os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "__lib__"))), "skill-runtime"))
|
|
24
|
-
from bootstrap import *
|
|
25
|
-
|
|
26
|
-
import json, argparse
|
|
27
|
-
from pathlib import Path
|
|
28
|
-
|
|
29
|
-
from client import SolveaClient
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _read_state() -> dict:
|
|
33
|
-
_sf = STATE_DIR / "progress.json"
|
|
34
|
-
if _sf.exists():
|
|
35
|
-
try:
|
|
36
|
-
return json.loads(_sf.read_text(encoding="utf-8"))
|
|
37
|
-
except Exception:
|
|
38
|
-
pass
|
|
39
|
-
return {}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def _write_state(data: dict) -> None:
|
|
43
|
-
_sf = STATE_DIR / "progress.json"
|
|
44
|
-
_sf.parent.mkdir(parents=True, exist_ok=True)
|
|
45
|
-
_sf.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# ── Fixed mapping table ────────────────────────────────────────────────────────
|
|
49
|
-
|
|
50
|
-
# Delivery 固定绑定:每个意图 → 一个广场 agent(按 squareAgent_id 直接克隆,
|
|
51
|
-
# 不再走"查广场列表 + 按名字匹配"链路,避免名字漂移。
|
|
52
|
-
# 不在此 map 中的意图(数据中新发现的 L1)→ 跳过 agent / 策略,标 SKIPPED。
|
|
53
|
-
# 固定 squareAgent_id 是首选来源,不是硬阻塞;复制失败时记录可恢复缺口。
|
|
54
|
-
DELIVERY_AGENT_BINDINGS = {
|
|
55
|
-
"Pre-Sales Inquiry": 37625,
|
|
56
|
-
"Invoices": 37627,
|
|
57
|
-
"Order Issues": 37631,
|
|
58
|
-
"Logistics Issues": 37633,
|
|
59
|
-
"Package issues": 37623,
|
|
60
|
-
"Product issues": 38114,
|
|
61
|
-
"Return and exchange": 37626,
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
# ── Data loading ───────────────────────────────────────────────────────────────
|
|
65
|
-
|
|
66
|
-
def load_results(state: dict) -> dict:
|
|
67
|
-
"""Load classification results pointed to by state.results_path.
|
|
68
|
-
|
|
69
|
-
eager 模型下 workspace/outputs/ 跨轮路径稳定,state 里存的绝对路径下轮直接可用。
|
|
70
|
-
"""
|
|
71
|
-
path = state.get("results_path")
|
|
72
|
-
if not path:
|
|
73
|
-
raise FileNotFoundError("state.results_path missing. Run classify.py first.")
|
|
74
|
-
if not os.path.exists(path):
|
|
75
|
-
raise FileNotFoundError(f"Results file missing: {path}")
|
|
76
|
-
with open(path) as f:
|
|
77
|
-
return json.load(f)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
# ── Feature pre-flight checks ─────────────────────────────────────────────────
|
|
81
|
-
|
|
82
|
-
def check_and_enable_intent_feature(solvea: SolveaClient) -> None:
|
|
83
|
-
"""创建意图前确保意图功能已开启(configurationWizard.intent=true)。
|
|
84
|
-
|
|
85
|
-
查询:GET /api_v2/gpt/project/{projectId}/config → configurationWizard.intent
|
|
86
|
-
开启:PUT /api_v2/gpt/project/{projectId} → body: {configurationWizard: {intent: true}}
|
|
87
|
-
"""
|
|
88
|
-
resp = solvea._curl("GET", f"/api_v2/gpt/project/{solvea.project_id}/config")
|
|
89
|
-
payload = resp.get("data", resp) or {}
|
|
90
|
-
wizard = payload.get("configurationWizard") or {}
|
|
91
|
-
if wizard.get("intent"):
|
|
92
|
-
return
|
|
93
|
-
solvea._curl("PUT", f"/api_v2/gpt/project/{solvea.project_id}", {
|
|
94
|
-
"configurationWizard": {"intent": True}
|
|
95
|
-
})
|
|
96
|
-
print("[NOTICE] 该租户意图功能(Intent)原本未开启,已自动启用")
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def check_and_enable_variable_feature(solvea: SolveaClient) -> None:
|
|
100
|
-
"""复制 agent 前确保变量功能已初始化(isInitAiField=true)。
|
|
101
|
-
|
|
102
|
-
查询:POST /api_v2/tars/api/field/manage/guide/record → isInitAiField
|
|
103
|
-
开启:GET /api_v2/tars/api/field/manage/pre/install
|
|
104
|
-
"""
|
|
105
|
-
resp = solvea._curl("POST", "/api_v2/tars/api/field/manage/guide/record")
|
|
106
|
-
payload = resp.get("data", resp) or {}
|
|
107
|
-
if payload.get("isInitAiField"):
|
|
108
|
-
return
|
|
109
|
-
solvea._curl("GET", "/api_v2/tars/api/field/manage/pre/install")
|
|
110
|
-
print("[NOTICE] 该租户变量功能(AI Field)原本未开启,已自动初始化预装字段")
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
# ── Marketplace agent lookup ───────────────────────────────────────────────────
|
|
114
|
-
|
|
115
|
-
def copy_marketplace_agent_by_id(solvea: SolveaClient, square_agent_id: int) -> str:
|
|
116
|
-
"""直接按广场 agent ID 克隆到当前租户,返回新 agent_id。
|
|
117
|
-
|
|
118
|
-
Delivery 按固定 squareAgent_id 复制广场 agent,取代"查广场→按名字匹配"。
|
|
119
|
-
链路,避免广场 agent 名字漂移。
|
|
120
|
-
"""
|
|
121
|
-
result = solvea._curl("POST", "/api_v2/gpt/ap/copyAgent", {
|
|
122
|
-
"projectId": int(solvea.project_id),
|
|
123
|
-
"agentId": int(square_agent_id),
|
|
124
|
-
})
|
|
125
|
-
new_id = str(
|
|
126
|
-
result.get("id") or
|
|
127
|
-
result.get("agentId") or
|
|
128
|
-
result.get("data", {}).get("id", "")
|
|
129
|
-
)
|
|
130
|
-
if not new_id:
|
|
131
|
-
raise RuntimeError(f"copyAgent(square_agent_id={square_agent_id}) returned no id: {result}")
|
|
132
|
-
print(f"[copy_agent] square_id={square_agent_id} → new id={new_id}")
|
|
133
|
-
return new_id
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
# ── L1 intent creation ─────────────────────────────────────────────────────────
|
|
137
|
-
|
|
138
|
-
def _intent_items(result) -> list:
|
|
139
|
-
if isinstance(result, list):
|
|
140
|
-
return result
|
|
141
|
-
if isinstance(result, dict):
|
|
142
|
-
for key in ("data", "list", "records"):
|
|
143
|
-
if isinstance(result.get(key), list):
|
|
144
|
-
return result[key]
|
|
145
|
-
return []
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def _find_existing_intent_by_name(solvea: SolveaClient, name: str) -> dict | None:
|
|
149
|
-
result = solvea.list_intents()
|
|
150
|
-
for item in _intent_items(result):
|
|
151
|
-
if isinstance(item, dict) and item.get("name") == name:
|
|
152
|
-
intent_id = item.get("id") or item.get("intentId")
|
|
153
|
-
if intent_id:
|
|
154
|
-
return item
|
|
155
|
-
return None
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
def create_l1_intent(
|
|
159
|
-
solvea: SolveaClient,
|
|
160
|
-
intent: dict,
|
|
161
|
-
dry_run: bool = False,
|
|
162
|
-
existing_by_name: dict | None = None,
|
|
163
|
-
) -> dict | None:
|
|
164
|
-
name = intent["name"]
|
|
165
|
-
definition = intent.get("definition") or f"Customer messages related to {name}."
|
|
166
|
-
data = {
|
|
167
|
-
"name": name,
|
|
168
|
-
"description": definition,
|
|
169
|
-
"example": definition,
|
|
170
|
-
"status": "ENABLED",
|
|
171
|
-
}
|
|
172
|
-
if dry_run:
|
|
173
|
-
print(f"[dry-run] create_intent: {name}")
|
|
174
|
-
return None
|
|
175
|
-
|
|
176
|
-
existing = (existing_by_name or {}).get(name)
|
|
177
|
-
if existing:
|
|
178
|
-
intent_id = existing.get("id") or existing.get("intentId")
|
|
179
|
-
print(f"[create_intent] existing same-name intent found, reusing id={intent_id} name={name}")
|
|
180
|
-
return {"id": intent_id, **existing}
|
|
181
|
-
|
|
182
|
-
print(f"[create_intent] {name} ...")
|
|
183
|
-
try:
|
|
184
|
-
result = solvea.create_intent(data)
|
|
185
|
-
except RuntimeError as exc:
|
|
186
|
-
if "already exists" not in str(exc):
|
|
187
|
-
raise
|
|
188
|
-
existing = _find_existing_intent_by_name(solvea, name)
|
|
189
|
-
if not existing:
|
|
190
|
-
raise
|
|
191
|
-
intent_id = existing.get("id") or existing.get("intentId")
|
|
192
|
-
print(f"[create_intent] already exists, reusing id={intent_id} name={name}")
|
|
193
|
-
return {"id": intent_id, **existing}
|
|
194
|
-
intent_id = result.get("id") or result.get("intentId")
|
|
195
|
-
if not intent_id:
|
|
196
|
-
if "already exists" in str(result.get("errmsg", "")):
|
|
197
|
-
existing = _find_existing_intent_by_name(solvea, name)
|
|
198
|
-
if existing:
|
|
199
|
-
intent_id = existing.get("id") or existing.get("intentId")
|
|
200
|
-
print(f"[create_intent] already exists, reusing id={intent_id} name={name}")
|
|
201
|
-
result["id"] = intent_id
|
|
202
|
-
if not intent_id:
|
|
203
|
-
raise RuntimeError(f"create_intent returned no id for '{name}': {result}")
|
|
204
|
-
else:
|
|
205
|
-
print(f"[create_intent] OK → id={intent_id}")
|
|
206
|
-
return result
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
# ── Strategy creation ──────────────────────────────────────────────────────────
|
|
210
|
-
|
|
211
|
-
def create_strategy_for_intent(
|
|
212
|
-
solvea: SolveaClient,
|
|
213
|
-
intent_id: str,
|
|
214
|
-
agent_id: str,
|
|
215
|
-
intent_name: str,
|
|
216
|
-
dry_run: bool = False,
|
|
217
|
-
) -> dict | None:
|
|
218
|
-
if dry_run:
|
|
219
|
-
print(f"[dry-run] create_strategy: intent={intent_id} → agent={agent_id}")
|
|
220
|
-
return None
|
|
221
|
-
data = {
|
|
222
|
-
"name": f"Route_{intent_name}",
|
|
223
|
-
"status": "ENABLED",
|
|
224
|
-
"priority": 0,
|
|
225
|
-
"trigger": "MESSAGE",
|
|
226
|
-
"condition": {
|
|
227
|
-
"and": [{
|
|
228
|
-
"rules": [{
|
|
229
|
-
"apiName": "__intent_ids",
|
|
230
|
-
"apiType": "TEXT",
|
|
231
|
-
"relation": "IN",
|
|
232
|
-
"type": "GENERIC",
|
|
233
|
-
"values": [int(intent_id)],
|
|
234
|
-
}]
|
|
235
|
-
}]
|
|
236
|
-
},
|
|
237
|
-
"action": {
|
|
238
|
-
"type": "AGENT",
|
|
239
|
-
"agentId": int(agent_id),
|
|
240
|
-
},
|
|
241
|
-
}
|
|
242
|
-
print(f"[create_strategy] Route_{intent_name} ...")
|
|
243
|
-
result = solvea.create_strategy(data)
|
|
244
|
-
strategy_id = result.get("id") or result.get("strategyId")
|
|
245
|
-
if not strategy_id:
|
|
246
|
-
raise RuntimeError(
|
|
247
|
-
f"create_strategy returned no id for '{intent_name}' "
|
|
248
|
-
f"(intent={intent_id}, agent={agent_id}): {result}"
|
|
249
|
-
)
|
|
250
|
-
print(f"[create_strategy] OK → id={strategy_id}")
|
|
251
|
-
return result
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
def create_strategy_for_intents(
|
|
255
|
-
solvea: SolveaClient,
|
|
256
|
-
intent_ids: list,
|
|
257
|
-
agent_id: str,
|
|
258
|
-
name: str,
|
|
259
|
-
priority: int = 0,
|
|
260
|
-
dry_run: bool = False,
|
|
261
|
-
) -> dict | None:
|
|
262
|
-
"""N 个意图 → 1 个 agent 的策略(rules.values 多值)。"""
|
|
263
|
-
if not intent_ids:
|
|
264
|
-
raise ValueError(f"create_strategy_for_intents '{name}': intent_ids is empty")
|
|
265
|
-
if dry_run:
|
|
266
|
-
print(f"[dry-run] create_strategy: intents={intent_ids} → agent={agent_id} (priority={priority})")
|
|
267
|
-
return None
|
|
268
|
-
data = {
|
|
269
|
-
"name": f"Route_{name}",
|
|
270
|
-
"status": "ENABLED",
|
|
271
|
-
"priority": priority,
|
|
272
|
-
"trigger": "MESSAGE",
|
|
273
|
-
"condition": {
|
|
274
|
-
"and": [{
|
|
275
|
-
"rules": [{
|
|
276
|
-
"apiName": "__intent_ids",
|
|
277
|
-
"apiType": "TEXT",
|
|
278
|
-
"relation": "IN",
|
|
279
|
-
"type": "GENERIC",
|
|
280
|
-
"values": [int(i) for i in intent_ids],
|
|
281
|
-
}]
|
|
282
|
-
}]
|
|
283
|
-
},
|
|
284
|
-
"action": {
|
|
285
|
-
"type": "AGENT",
|
|
286
|
-
"agentId": int(agent_id),
|
|
287
|
-
},
|
|
288
|
-
}
|
|
289
|
-
print(f"[create_strategy] Route_{name} (intents={intent_ids}, priority={priority}) ...")
|
|
290
|
-
result = solvea.create_strategy(data)
|
|
291
|
-
strategy_id = result.get("id") or result.get("strategyId")
|
|
292
|
-
if not strategy_id:
|
|
293
|
-
raise RuntimeError(
|
|
294
|
-
f"create_strategy returned no id for '{name}' "
|
|
295
|
-
f"(intents={intent_ids}, agent={agent_id}): {result}"
|
|
296
|
-
)
|
|
297
|
-
print(f"[create_strategy] OK → id={strategy_id}")
|
|
298
|
-
return result
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
# ── Update order tool X-Token ────────────────────────────────────────────────
|
|
302
|
-
|
|
303
|
-
def update_order_tool_x_token(solvea: SolveaClient, creation_log: list, dry_run: bool = False) -> None:
|
|
304
|
-
"""After all agent copies, patch X-Token header in any order information tools found."""
|
|
305
|
-
TARGET_TOOLS = {
|
|
306
|
-
"new_Order_information_product",
|
|
307
|
-
"new_Order_information_FBA",
|
|
308
|
-
"new_Order_information_ship",
|
|
309
|
-
}
|
|
310
|
-
copied_agent_ids = [
|
|
311
|
-
item["agent_id"] for item in creation_log
|
|
312
|
-
if not item.get("skipped") and item.get("agent_id")
|
|
313
|
-
]
|
|
314
|
-
if not copied_agent_ids:
|
|
315
|
-
return
|
|
316
|
-
|
|
317
|
-
raw = solvea.get_api_key()
|
|
318
|
-
if isinstance(raw, dict):
|
|
319
|
-
x_token = raw.get("apiKey") or raw.get("key") or raw.get("value") or raw.get("data") or str(raw)
|
|
320
|
-
else:
|
|
321
|
-
x_token = str(raw)
|
|
322
|
-
|
|
323
|
-
updated = 0
|
|
324
|
-
for agent_id in copied_agent_ids:
|
|
325
|
-
tools_resp = solvea._curl("GET", f"/api_v2/gpt/ap/agent/{agent_id}/tools", {})
|
|
326
|
-
for tool in (tools_resp if isinstance(tools_resp, list) else []):
|
|
327
|
-
if tool.get("name") not in TARGET_TOOLS:
|
|
328
|
-
continue
|
|
329
|
-
tool_id = tool["id"]
|
|
330
|
-
if dry_run:
|
|
331
|
-
print(f"[dry-run] would update X-Token for tool '{tool['name']}' (id={tool_id})")
|
|
332
|
-
continue
|
|
333
|
-
config = tool.get("config") or {}
|
|
334
|
-
headers = config.get("headers") or {}
|
|
335
|
-
headers["X-Token"] = x_token
|
|
336
|
-
config["headers"] = headers
|
|
337
|
-
solvea._curl("PUT", f"/api_v2/gpt/ap/tools/{tool_id}", {"config": config})
|
|
338
|
-
print(f"[x_token] updated '{tool['name']}' (tool_id={tool_id})")
|
|
339
|
-
updated += 1
|
|
340
|
-
|
|
341
|
-
if updated:
|
|
342
|
-
print(f"[x_token] {updated} tool(s) X-Token updated.")
|
|
343
|
-
elif not dry_run:
|
|
344
|
-
print("[x_token] No order information tools found — skipped.")
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
# ── Disable SYSTEM intents ────────────────────────────────────────────────────
|
|
348
|
-
|
|
349
|
-
def disable_system_intents(solvea: SolveaClient, keep_names: set, dry_run: bool = False) -> int:
|
|
350
|
-
"""Disable SYSTEM-source intents whose name is not in keep_names."""
|
|
351
|
-
result = solvea.list_intents()
|
|
352
|
-
intents = result if isinstance(result, list) else result.get("data", result.get("list", []))
|
|
353
|
-
to_disable = [
|
|
354
|
-
i for i in intents
|
|
355
|
-
if i.get("source") == "SYSTEM"
|
|
356
|
-
and i.get("name") not in keep_names
|
|
357
|
-
and i.get("status") != "DISABLED"
|
|
358
|
-
]
|
|
359
|
-
print(f"[disable_system] {len(to_disable)} SYSTEM intents to disable "
|
|
360
|
-
f"(keeping {len(keep_names)} by name match)")
|
|
361
|
-
for i in to_disable:
|
|
362
|
-
if dry_run:
|
|
363
|
-
print(f"[dry-run] disable intent: {i['name']} id={i['id']}")
|
|
364
|
-
else:
|
|
365
|
-
solvea.toggle_intent(i["id"], enabled=False)
|
|
366
|
-
print(f"[disable_system] disabled: {i['name']} id={i['id']}")
|
|
367
|
-
return len(to_disable)
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
def _load_agent_map(path: str) -> dict:
|
|
371
|
-
if not path or not os.path.exists(path):
|
|
372
|
-
raise FileNotFoundError(f"custom agent map not found: {path}")
|
|
373
|
-
with open(path, encoding="utf-8") as f:
|
|
374
|
-
data = json.load(f)
|
|
375
|
-
if isinstance(data, dict) and isinstance(data.get("intents"), dict):
|
|
376
|
-
data = data["intents"]
|
|
377
|
-
if not isinstance(data, dict):
|
|
378
|
-
raise ValueError("custom agent map must be a JSON object or {'intents': {...}}")
|
|
379
|
-
return {str(k): str(v) for k, v in data.items() if v not in (None, "")}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
def _run_delivery_custom_agent_link(
|
|
383
|
-
solvea: SolveaClient,
|
|
384
|
-
log_path: str,
|
|
385
|
-
map_path: str,
|
|
386
|
-
dry_run: bool = False,
|
|
387
|
-
) -> None:
|
|
388
|
-
"""把用户确认的新建/替代 Agent 按 Delivery 意图写入策略。"""
|
|
389
|
-
if not log_path or not os.path.exists(log_path):
|
|
390
|
-
raise FileNotFoundError(
|
|
391
|
-
f"created_log not found (state.created_log_path={log_path}). "
|
|
392
|
-
"Run delivery creation first."
|
|
393
|
-
)
|
|
394
|
-
with open(log_path, encoding="utf-8") as f:
|
|
395
|
-
log_data = json.load(f)
|
|
396
|
-
if log_data.get("mode") != "delivery":
|
|
397
|
-
raise RuntimeError(f"created_log mode is {log_data.get('mode')!r}, expected 'delivery'.")
|
|
398
|
-
|
|
399
|
-
agent_map = _load_agent_map(map_path)
|
|
400
|
-
linked = []
|
|
401
|
-
for item in log_data.get("intents", []):
|
|
402
|
-
intent_name = item.get("intent_name")
|
|
403
|
-
if not intent_name or intent_name not in agent_map:
|
|
404
|
-
continue
|
|
405
|
-
intent_id = item.get("intent_id")
|
|
406
|
-
agent_id = agent_map[intent_name]
|
|
407
|
-
if not intent_id:
|
|
408
|
-
print(f"[delivery.custom] WARNING intent '{intent_name}' has no intent_id; skipped")
|
|
409
|
-
continue
|
|
410
|
-
print(f"[delivery.custom] Binding {intent_name}: intent={intent_id} → agent {agent_id}")
|
|
411
|
-
strategy_result = create_strategy_for_intent(
|
|
412
|
-
solvea,
|
|
413
|
-
str(intent_id),
|
|
414
|
-
str(agent_id),
|
|
415
|
-
intent_name,
|
|
416
|
-
dry_run=dry_run,
|
|
417
|
-
)
|
|
418
|
-
strategy_id = str(strategy_result.get("id")) if strategy_result else f"dry_strategy_{intent_name}"
|
|
419
|
-
item["agent_id"] = str(agent_id)
|
|
420
|
-
item["strategy_id"] = strategy_id
|
|
421
|
-
item["skipped"] = False
|
|
422
|
-
item["skip_reason"] = ""
|
|
423
|
-
item["recoverable"] = False
|
|
424
|
-
item["custom_agent"] = True
|
|
425
|
-
linked.append({"intent_name": intent_name, "agent_id": str(agent_id), "intent_id": intent_id, "strategy_id": strategy_id})
|
|
426
|
-
|
|
427
|
-
if not linked:
|
|
428
|
-
raise ValueError("custom agent map did not match any created Delivery intent name")
|
|
429
|
-
|
|
430
|
-
log_data["custom_agent_strategies"] = [*(log_data.get("custom_agent_strategies") or []), *linked]
|
|
431
|
-
log_data["next_action"] = "Continue knowledge-base setup and delivery regression test."
|
|
432
|
-
with open(log_path, "w", encoding="utf-8") as f:
|
|
433
|
-
json.dump(log_data, f, ensure_ascii=False, indent=2)
|
|
434
|
-
|
|
435
|
-
state = _read_state()
|
|
436
|
-
state.update({
|
|
437
|
-
"status": "created",
|
|
438
|
-
"mode": "delivery",
|
|
439
|
-
"created_log_path": log_path,
|
|
440
|
-
"updated_at": timestamp(),
|
|
441
|
-
})
|
|
442
|
-
_write_state(state)
|
|
443
|
-
print(f"\n[delivery.custom] Done. Linked {len(linked)} intent(s); log updated → {log_path}")
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
# ── Main ───────────────────────────────────────────────────────────────────────
|
|
447
|
-
|
|
448
|
-
def main():
|
|
449
|
-
parser = argparse.ArgumentParser(
|
|
450
|
-
description="Create intents + copy marketplace agents + strategy routing"
|
|
451
|
-
)
|
|
452
|
-
parser.add_argument("--mode", choices=["delivery"], default="delivery",
|
|
453
|
-
help="Delivery only: create all intents + update X-Token.")
|
|
454
|
-
parser.add_argument("--select", default=None,
|
|
455
|
-
help="DEPRECATED — ignored in all modes (all intents are now created).")
|
|
456
|
-
parser.add_argument("--dry-run", action="store_true")
|
|
457
|
-
parser.add_argument("--disable-system", action="store_true",
|
|
458
|
-
help="Disable active SYSTEM intents not matching created intent names")
|
|
459
|
-
parser.add_argument("--custom-agent-map", default=None,
|
|
460
|
-
help="Delivery recovery stage: JSON map of intent name to custom/existing agent id, "
|
|
461
|
-
"for example {'Pre-Sales Inquiry': 2642, 'Product issues': 2643}.")
|
|
462
|
-
x_token_grp = parser.add_mutually_exclusive_group()
|
|
463
|
-
x_token_grp.add_argument("--update-x-token", dest="update_x_token", action="store_true",
|
|
464
|
-
help="Force X-Token patch after copy (default for --mode delivery)")
|
|
465
|
-
x_token_grp.add_argument("--no-update-x-token", dest="update_x_token", action="store_false",
|
|
466
|
-
help="Skip X-Token patch")
|
|
467
|
-
parser.set_defaults(update_x_token=None)
|
|
468
|
-
args = parser.parse_args()
|
|
469
|
-
|
|
470
|
-
# ── Early args sanity ──────────────────────────────────────────────────────
|
|
471
|
-
if args.select:
|
|
472
|
-
print(f"[main] WARNING: --select is deprecated and ignored (mode={args.mode}). "
|
|
473
|
-
"All intents will be created.")
|
|
474
|
-
|
|
475
|
-
# Resolve update_x_token default by mode when caller did not set it explicitly
|
|
476
|
-
if args.update_x_token is None:
|
|
477
|
-
update_x_token = (args.mode == "delivery")
|
|
478
|
-
else:
|
|
479
|
-
update_x_token = args.update_x_token
|
|
480
|
-
|
|
481
|
-
state = _read_state()
|
|
482
|
-
if not state:
|
|
483
|
-
raise FileNotFoundError(
|
|
484
|
-
"No state found. Run classify.py first (state should contain run_id and results_path)."
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
-
run_id = state.get("run_id")
|
|
488
|
-
if not run_id:
|
|
489
|
-
raise ValueError("state.run_id missing. Re-run classify.py.")
|
|
490
|
-
|
|
491
|
-
# ── Disable-only mode ──────────────────────────────────────────────────────
|
|
492
|
-
if args.disable_system:
|
|
493
|
-
log_path = state.get("created_log_path")
|
|
494
|
-
if not log_path or not os.path.exists(log_path):
|
|
495
|
-
raise FileNotFoundError(
|
|
496
|
-
f"No creation log found (state.created_log_path={log_path}). "
|
|
497
|
-
"Run without --disable-system first."
|
|
498
|
-
)
|
|
499
|
-
with open(log_path) as f:
|
|
500
|
-
existing_log = json.load(f)
|
|
501
|
-
our_names = {item["intent_name"] for item in existing_log.get("intents", [])}
|
|
502
|
-
solvea = SolveaClient()
|
|
503
|
-
print(f"[main] Disable-only mode. project_id={solvea.project_id}")
|
|
504
|
-
disable_system_intents(solvea, keep_names=our_names, dry_run=args.dry_run)
|
|
505
|
-
return
|
|
506
|
-
|
|
507
|
-
if args.custom_agent_map:
|
|
508
|
-
solvea = SolveaClient()
|
|
509
|
-
if not args.dry_run:
|
|
510
|
-
print(f"[main] project_id={solvea.project_id}")
|
|
511
|
-
_run_delivery_custom_agent_link(
|
|
512
|
-
solvea,
|
|
513
|
-
log_path=state.get("created_log_path"),
|
|
514
|
-
map_path=args.custom_agent_map,
|
|
515
|
-
dry_run=args.dry_run,
|
|
516
|
-
)
|
|
517
|
-
return
|
|
518
|
-
|
|
519
|
-
results = load_results(state)
|
|
520
|
-
all_intents = results.get("intents", []) + results.get("new_intents", [])
|
|
521
|
-
print(f"[main] {len(all_intents)} intents loaded, run_id={run_id} (mode={args.mode})")
|
|
522
|
-
|
|
523
|
-
if not all_intents:
|
|
524
|
-
print("[main] No intents found. Run classify.py first.")
|
|
525
|
-
return
|
|
526
|
-
|
|
527
|
-
solvea = SolveaClient()
|
|
528
|
-
if not args.dry_run:
|
|
529
|
-
print(f"[main] project_id={solvea.project_id}")
|
|
530
|
-
|
|
531
|
-
# ── 补全 Delivery 固定 7 个意图 ─────────────────────────────────────────────
|
|
532
|
-
# 客户数据里如果没有某个固定意图的样本,classify.py 不会产生它;
|
|
533
|
-
# Delivery 要建全部 7 个标准意图,这里强制补入缺失项。
|
|
534
|
-
found_names = {i["name"] for i in all_intents}
|
|
535
|
-
for fixed_name in DELIVERY_AGENT_BINDINGS:
|
|
536
|
-
if fixed_name not in found_names:
|
|
537
|
-
print(f"[main] '{fixed_name}' missing from classification results → added from fixed taxonomy")
|
|
538
|
-
all_intents.append({"name": fixed_name, "definition": ""})
|
|
539
|
-
|
|
540
|
-
# ── Feature pre-flight ─────────────────────────────────────────────────────
|
|
541
|
-
if not args.dry_run:
|
|
542
|
-
check_and_enable_intent_feature(solvea) # 建意图前
|
|
543
|
-
check_and_enable_variable_feature(solvea) # copyAgent 前
|
|
544
|
-
|
|
545
|
-
# ── Delivery ───────────────────────────────────────────────────────────────
|
|
546
|
-
# 走固定 squareAgent_id 直接 copyAgent,不查广场 / 不按名字匹配。
|
|
547
|
-
# 不在 DELIVERY_AGENT_BINDINGS 中的意图(数据中新发现的 L1)→ 跳过 agent/策略。
|
|
548
|
-
|
|
549
|
-
creation_log = []
|
|
550
|
-
preset_agent_errors = []
|
|
551
|
-
existing_by_name = {}
|
|
552
|
-
if not args.dry_run:
|
|
553
|
-
existing_by_name = {
|
|
554
|
-
item.get("name"): item
|
|
555
|
-
for item in _intent_items(solvea.list_intents())
|
|
556
|
-
if isinstance(item, dict) and item.get("name") and (item.get("id") or item.get("intentId"))
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
for intent in all_intents:
|
|
560
|
-
name = intent["name"]
|
|
561
|
-
print(f"\n{'='*60}")
|
|
562
|
-
print(f"[main] Processing: {name}")
|
|
563
|
-
|
|
564
|
-
# 1. Create L1 intent
|
|
565
|
-
intent_result = create_l1_intent(
|
|
566
|
-
solvea,
|
|
567
|
-
intent,
|
|
568
|
-
dry_run=args.dry_run,
|
|
569
|
-
existing_by_name=existing_by_name,
|
|
570
|
-
)
|
|
571
|
-
intent_id = str(intent_result.get("id")) if intent_result else f"dry_{name}"
|
|
572
|
-
|
|
573
|
-
# 2. Resolve square_agent_id from fixed binding
|
|
574
|
-
square_id = DELIVERY_AGENT_BINDINGS.get(name)
|
|
575
|
-
if not square_id:
|
|
576
|
-
print(f"[main] No mapping for '{name}' in DELIVERY_AGENT_BINDINGS — skipping")
|
|
577
|
-
creation_log.append({
|
|
578
|
-
"intent_name": name, "intent_id": intent_id,
|
|
579
|
-
"agent_name": None, "agent_id": None, "strategy_id": None,
|
|
580
|
-
"skipped": True, "skip_reason": "no mapping in DELIVERY_AGENT_BINDINGS",
|
|
581
|
-
})
|
|
582
|
-
continue
|
|
583
|
-
|
|
584
|
-
# 3. Copy by squareAgent_id
|
|
585
|
-
agent_name = f"{name}【Delivery Version】"
|
|
586
|
-
if args.dry_run:
|
|
587
|
-
print(f"[dry-run] copy square_agent_id={square_id} ('{agent_name}')")
|
|
588
|
-
agent_id = f"dry_agent_{name}"
|
|
589
|
-
else:
|
|
590
|
-
try:
|
|
591
|
-
agent_id = copy_marketplace_agent_by_id(solvea, square_id)
|
|
592
|
-
except Exception as exc:
|
|
593
|
-
preset_agent_errors.append({
|
|
594
|
-
"intent_name": name,
|
|
595
|
-
"intent_id": intent_id,
|
|
596
|
-
"square_agent_id": square_id,
|
|
597
|
-
"agent_name": agent_name,
|
|
598
|
-
"error": str(exc),
|
|
599
|
-
})
|
|
600
|
-
print(
|
|
601
|
-
f"[main] PRESET_AGENT_MISSING intent={name} "
|
|
602
|
-
f"square_agent_id={square_id}: {exc}"
|
|
603
|
-
)
|
|
604
|
-
creation_log.append({
|
|
605
|
-
"intent_name": name, "intent_id": intent_id,
|
|
606
|
-
"agent_name": agent_name, "agent_id": None, "strategy_id": None,
|
|
607
|
-
"skipped": True, "skip_reason": "preset agent unavailable",
|
|
608
|
-
"recoverable": True,
|
|
609
|
-
"square_agent_id": square_id,
|
|
610
|
-
})
|
|
611
|
-
continue
|
|
612
|
-
|
|
613
|
-
# 4. Create strategy routing
|
|
614
|
-
strategy_result = create_strategy_for_intent(
|
|
615
|
-
solvea, intent_id, agent_id, name, dry_run=args.dry_run
|
|
616
|
-
)
|
|
617
|
-
strategy_id = str(strategy_result.get("id")) if strategy_result else f"dry_strategy_{name}"
|
|
618
|
-
|
|
619
|
-
creation_log.append({
|
|
620
|
-
"intent_name": name, "intent_id": intent_id,
|
|
621
|
-
"agent_name": agent_name, "agent_id": agent_id,
|
|
622
|
-
"strategy_id": strategy_id, "skipped": False,
|
|
623
|
-
})
|
|
624
|
-
|
|
625
|
-
# 4. Patch X-Token in order information tools (delivery default; togglable)
|
|
626
|
-
if update_x_token:
|
|
627
|
-
update_order_tool_x_token(solvea, creation_log, dry_run=args.dry_run)
|
|
628
|
-
else:
|
|
629
|
-
print("[x_token] skipped (--no-update-x-token)")
|
|
630
|
-
|
|
631
|
-
OUTPUTS_DIR.mkdir(parents=True, exist_ok=True)
|
|
632
|
-
log_path = str(OUTPUTS_DIR / f"created_{timestamp()}.json")
|
|
633
|
-
with open(log_path, "w", encoding="utf-8") as f:
|
|
634
|
-
json.dump({
|
|
635
|
-
"intents": creation_log,
|
|
636
|
-
"mode": args.mode,
|
|
637
|
-
"run_id": run_id,
|
|
638
|
-
"preset_agent_errors": preset_agent_errors,
|
|
639
|
-
"next_action": (
|
|
640
|
-
"For skipped preset-agent intents, ask the user whether to build new "
|
|
641
|
-
"CHAT_FLOW agents from scratch or use substitute existing agent ids."
|
|
642
|
-
if preset_agent_errors else ""
|
|
643
|
-
),
|
|
644
|
-
}, f, ensure_ascii=False, indent=2)
|
|
645
|
-
|
|
646
|
-
status = "preset_agent_missing" if preset_agent_errors else "created"
|
|
647
|
-
state = _read_state()
|
|
648
|
-
state.update({
|
|
649
|
-
"status": status,
|
|
650
|
-
"mode": args.mode,
|
|
651
|
-
"created_log_path": log_path,
|
|
652
|
-
"preset_agent_errors": preset_agent_errors,
|
|
653
|
-
"updated_at": timestamp(),
|
|
654
|
-
})
|
|
655
|
-
_write_state(state)
|
|
656
|
-
|
|
657
|
-
# Report SYSTEM intents that could be disabled (user confirms separately)
|
|
658
|
-
if not args.dry_run:
|
|
659
|
-
our_names = {item["intent_name"] for item in creation_log}
|
|
660
|
-
result2 = solvea.list_intents()
|
|
661
|
-
intents2 = result2 if isinstance(result2, list) else result2.get("data", result2.get("list", []))
|
|
662
|
-
pending_disable = [
|
|
663
|
-
i for i in intents2
|
|
664
|
-
if i.get("source") == "SYSTEM"
|
|
665
|
-
and i.get("name") not in our_names
|
|
666
|
-
and i.get("status") != "DISABLED"
|
|
667
|
-
]
|
|
668
|
-
if pending_disable:
|
|
669
|
-
print(f"\n[system_intents] {len(pending_disable)} SYSTEM intents can be disabled:")
|
|
670
|
-
for i in pending_disable:
|
|
671
|
-
print(f" id={i['id']} name={i['name']}")
|
|
672
|
-
print("[system_intents] Run with --disable-system to disable them.")
|
|
673
|
-
else:
|
|
674
|
-
print("\n[system_intents] No active SYSTEM intents to disable.")
|
|
675
|
-
|
|
676
|
-
created = [x for x in creation_log if not x.get("skipped")]
|
|
677
|
-
skipped = [x for x in creation_log if x.get("skipped")]
|
|
678
|
-
print(f"\n[main] Done. {len(created)} created, {len(skipped)} skipped.")
|
|
679
|
-
print(f"[main] Log → {log_path}")
|
|
680
|
-
if preset_agent_errors:
|
|
681
|
-
print("[main] Recoverable preset-agent gap detected.")
|
|
682
|
-
print("[main] Ask the user to choose one option for each skipped standard intent:")
|
|
683
|
-
print(" 1. Build a new CHAT_FLOW agent from scratch.")
|
|
684
|
-
print(" 2. Use an existing substitute agent id.")
|
|
685
|
-
print(f"\n[main] Summary:")
|
|
686
|
-
for item in creation_log:
|
|
687
|
-
if item.get("skipped"):
|
|
688
|
-
print(f" {item['intent_name']}: SKIPPED ({item.get('skip_reason', '')})")
|
|
689
|
-
else:
|
|
690
|
-
print(f" {item['intent_name']}: intent={item['intent_id']} "
|
|
691
|
-
f"agent={item['agent_id']} strategy={item['strategy_id']}")
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if __name__ == "__main__":
|
|
695
|
-
main()
|