@shulex/cxclawctl 0.1.2 → 0.1.3
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/dist/cli.js +2202 -127
- package/dist/cli.js.map +1 -1
- package/package.json +19 -6
- package/capabilities.lock.json +0 -898
- 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,1341 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Solvea API Client - Manage Solvea customer service agents via Solvea bot backend.
|
|
4
|
-
|
|
5
|
-
Usage:
|
|
6
|
-
python3 solvea_api.py <command> [options]
|
|
7
|
-
|
|
8
|
-
Commands:
|
|
9
|
-
get-project Get projectId and botId from token
|
|
10
|
-
replace-mock-data Replace mock data by type (fully replaces old data for the same tenant and type)
|
|
11
|
-
query-mock-data Query mock data by type (--name required)
|
|
12
|
-
list-agents List all agents
|
|
13
|
-
get-agent Get agent detail by ID
|
|
14
|
-
get-chatflow-graph Get latest chatFlowGraph for a CHAT_FLOW agent (--agent-id required)
|
|
15
|
-
create-agent Create a new agent
|
|
16
|
-
update-agent Update an existing agent (requires full object)
|
|
17
|
-
update-chatflow Save or publish an existing CHAT_FLOW graph (--agent-id required)
|
|
18
|
-
list-plugins List available tools
|
|
19
|
-
get-plugin Get tool detail by ID
|
|
20
|
-
create-plugin Create a new tool
|
|
21
|
-
update-plugin Update a tool (requires full object)
|
|
22
|
-
delete-plugin Delete a tool
|
|
23
|
-
run-plugin Test-run an HTTP tool without saving
|
|
24
|
-
list-ai-fields List fields exposed by the CHAT_FLOW canvas
|
|
25
|
-
list-agent-ai-fields List fields accepted by Agent range validation
|
|
26
|
-
get-ai-field-guide Get Tars AI Field initialization state
|
|
27
|
-
initialize-ai-fields Initialize Tars system AI Fields
|
|
28
|
-
list-managed-ai-fields Page Tars AI Field definitions
|
|
29
|
-
get-managed-ai-field Get one Tars AI Field definition
|
|
30
|
-
create-managed-ai-field Create one Tars custom AI Field
|
|
31
|
-
update-managed-ai-field Update one Tars AI Field
|
|
32
|
-
delete-managed-ai-field Delete one unused Tars custom AI Field
|
|
33
|
-
toggle-managed-ai-field Enable or disable one Tars AI Field
|
|
34
|
-
get-quality-status Get project AI quality inspection status
|
|
35
|
-
set-quality-status Enable or disable project AI quality inspection
|
|
36
|
-
list-quality-rules List quality inspection rules
|
|
37
|
-
get-quality-rule Get one system quality inspection rule
|
|
38
|
-
update-quality-rule Update one system quality inspection rule
|
|
39
|
-
create-custom-quality-rule Create one custom quality inspection rule
|
|
40
|
-
get-custom-quality-rule Get one custom quality inspection rule
|
|
41
|
-
update-custom-quality-rule Update one custom quality inspection rule
|
|
42
|
-
delete-custom-quality-rule Delete one custom quality inspection rule
|
|
43
|
-
list-quality-alert-rules List quality inspection alert rules
|
|
44
|
-
get-quality-alert-rule Get one quality inspection alert rule
|
|
45
|
-
create-quality-alert-rule Create one quality inspection alert rule
|
|
46
|
-
update-quality-alert-rule Update one quality inspection alert rule
|
|
47
|
-
delete-quality-alert-rule Delete one quality inspection alert rule
|
|
48
|
-
list-quality-alert-messages Page quality inspection alert messages
|
|
49
|
-
get-quality-alert-message Get one quality inspection alert message
|
|
50
|
-
chat Send a chat message (single or multi-turn)
|
|
51
|
-
chat-stream Send a chat message via SSE stream and return aggregated final event
|
|
52
|
-
chat-async Submit an async chat message, returns AsyncReceipt
|
|
53
|
-
get-async-result Poll async chat result by receipt
|
|
54
|
-
list-intents List intents
|
|
55
|
-
get-conversations-by-intent Fetch historical USER conversations filtered by intent name
|
|
56
|
-
create-intent Create a new intent
|
|
57
|
-
get-intent Get intent detail
|
|
58
|
-
update-intent Update an intent
|
|
59
|
-
toggle-intent Enable/disable an intent
|
|
60
|
-
list-entities List strategy condition entities
|
|
61
|
-
create-strategy Create a new strategy
|
|
62
|
-
update-strategy Update a strategy
|
|
63
|
-
toggle-strategy Enable/disable a strategy
|
|
64
|
-
list-strategies List strategies (paged)
|
|
65
|
-
list-templates List reply templates
|
|
66
|
-
create-template Create one reply template
|
|
67
|
-
update-template Update one reply template (partial; --id + changed fields)
|
|
68
|
-
delete-templates Delete reply templates by IDs
|
|
69
|
-
list-knowledge List knowledge items
|
|
70
|
-
get-knowledge Get one knowledge item by ID
|
|
71
|
-
get-knowledge-isolation-keywords Get isolation keywords bound to one knowledge item
|
|
72
|
-
update-knowledge Update one knowledge item
|
|
73
|
-
batch-update-knowledge Batch update knowledge items (content, title, directoryId, etc.)
|
|
74
|
-
update-document-content Update FILE/WEBSITE body content
|
|
75
|
-
create-product-knowledge Create one product knowledge item
|
|
76
|
-
update-product-knowledge Update one product knowledge item
|
|
77
|
-
batch-update-products Batch update product status (mainPush, enable/published)
|
|
78
|
-
operate-knowledge Delete, move, publish, unpublish, show, or hide knowledge
|
|
79
|
-
edit-knowledge-tags Add, replace, or remove tags on knowledge items
|
|
80
|
-
list-knowledge-tags List configured knowledge tags
|
|
81
|
-
upload-qa Upload QA knowledge
|
|
82
|
-
upload-web Upload web knowledge
|
|
83
|
-
list-directories List the KB directory tree (slim by default; --raw for full)
|
|
84
|
-
create-folder Create a knowledge folder
|
|
85
|
-
update-directory Rename a knowledge folder
|
|
86
|
-
move-directory Move a knowledge folder
|
|
87
|
-
delete-directory Recursively delete a knowledge folder
|
|
88
|
-
list-isolation-keywords List isolation keywords (paged)
|
|
89
|
-
create-isolation-keyword Create one or more comma-separated isolation keywords
|
|
90
|
-
update-isolation-keyword Update one isolation keyword
|
|
91
|
-
delete-isolation-keyword Delete one isolation keyword
|
|
92
|
-
batch-update-isolation-keyword-bindings Bind or revise keyword bindings for knowledge/directories
|
|
93
|
-
list-transcripts Filter inbox conversation records
|
|
94
|
-
get-transcript Get one conversation record by chatId
|
|
95
|
-
resolve-transcript-chat-id Resolve an external inbox conversation ID to chatId
|
|
96
|
-
get-chat-context Compare one turn's saved context with current resources
|
|
97
|
-
export-transcripts Submit async transcript export task
|
|
98
|
-
get-export-tasks Poll export task status
|
|
99
|
-
upload-batch-test-file Upload a generated xlsx through temporary S3 credentials
|
|
100
|
-
submit-batch-test Create a Download Center REPLY or MULTI_TURN_REPLY test task
|
|
101
|
-
get-batch-test-tasks Query batch-test progress in Download Center
|
|
102
|
-
list-label-groups List label groups
|
|
103
|
-
create-label-group Create one label group
|
|
104
|
-
update-label-group Update one label group
|
|
105
|
-
delete-label-group Delete one label group
|
|
106
|
-
get-label-tree Query one label tree
|
|
107
|
-
get-label-detail-tree Query label node details
|
|
108
|
-
create-label-node Create one label node
|
|
109
|
-
update-label-node Update one label node
|
|
110
|
-
delete-label-node Delete one label node
|
|
111
|
-
download-label-template Download label import template
|
|
112
|
-
download-labels Download current labels of one group
|
|
113
|
-
upload-labels Upload local xlsx and import labels
|
|
114
|
-
report-transcript Ticket-level stats (resolution/escalation rate)
|
|
115
|
-
report-message Message-level stats and trend
|
|
116
|
-
report-agent Agent performance stats
|
|
117
|
-
report-intent Intent-level ticket stats
|
|
118
|
-
report-satisfaction Satisfaction summary (AI vs Human)
|
|
119
|
-
report-satisfaction-list Satisfaction detail list (paged)
|
|
120
|
-
report-knowledge Knowledge usage stats
|
|
121
|
-
report-escalation Escalation reason tree
|
|
122
|
-
report-livechat LiveChat pre-sales funnel summary
|
|
123
|
-
report-livechat-table LiveChat conversion detail table
|
|
124
|
-
report-inspection AI inspection overview metrics
|
|
125
|
-
report-self-learning Self-learning topic stats
|
|
126
|
-
chat-report-* Modern /chatReport page metrics (default Agent Analytics source)
|
|
127
|
-
list-voice-agents List voice agents
|
|
128
|
-
list-voice-transcripts List voice call transcript pages
|
|
129
|
-
get-voice-transcript Get voice call transcript detail
|
|
130
|
-
|
|
131
|
-
Environment:
|
|
132
|
-
SOLVEA_TOKEN JWT token for staging (_token_staging cookie)
|
|
133
|
-
SOLVEA_BASE_URL Base URL (default: https://apps-staging.voc.ai)
|
|
134
|
-
SOLVEA_PROJECT_ID Project ID (default: auto-detect or 950)
|
|
135
|
-
SOLVEA_PROD_TOKEN JWT token for production (_token cookie)
|
|
136
|
-
SOLVEA_PROD_URL Production URL (default: https://apps.voc.ai)
|
|
137
|
-
"""
|
|
138
|
-
|
|
139
|
-
import json
|
|
140
|
-
import os
|
|
141
|
-
import sys
|
|
142
|
-
import argparse
|
|
143
|
-
|
|
144
|
-
from client import SolveaClient
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def summarize_knowledge_result(result, include_tags=False):
|
|
148
|
-
items = result.get("list", result) if isinstance(result, dict) else result
|
|
149
|
-
if not isinstance(items, list):
|
|
150
|
-
return result
|
|
151
|
-
|
|
152
|
-
compact = []
|
|
153
|
-
for k in items:
|
|
154
|
-
item = {
|
|
155
|
-
"id": k.get("knowledgeId"),
|
|
156
|
-
"directoryId": k.get("directoryId"),
|
|
157
|
-
"type": k.get("type", ""),
|
|
158
|
-
"status": k.get("knowledgeStatus", k.get("status", "")),
|
|
159
|
-
"isolationKeywords": k.get("isolationKeywords", []),
|
|
160
|
-
}
|
|
161
|
-
# getKnowledge returns one unified DTO, but each UI type populates a
|
|
162
|
-
# different subset. Preserve the useful fields instead of forcing
|
|
163
|
-
# FILE/WEBSITE/PRODUCT into the QA question/answer shape.
|
|
164
|
-
for field in (
|
|
165
|
-
"question", "answer", "name", "title", "url", "brief", "storageSize",
|
|
166
|
-
"productTitle", "productId", "sku", "spuUrl", "describe",
|
|
167
|
-
):
|
|
168
|
-
if k.get(field) is not None:
|
|
169
|
-
item[field] = k[field]
|
|
170
|
-
if include_tags:
|
|
171
|
-
item["tags"] = k.get("tags", [])
|
|
172
|
-
compact.append(item)
|
|
173
|
-
if isinstance(result, dict):
|
|
174
|
-
return {
|
|
175
|
-
"total": result.get("total", result.get("totalCount", len(compact))),
|
|
176
|
-
"list": compact,
|
|
177
|
-
}
|
|
178
|
-
return compact
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def main():
|
|
182
|
-
parser = argparse.ArgumentParser(
|
|
183
|
-
description="Solvea API Client",
|
|
184
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
185
|
-
epilog=__doc__,
|
|
186
|
-
)
|
|
187
|
-
parser.add_argument("command", help="API command to execute")
|
|
188
|
-
parser.add_argument("--id", help="Resource ID (agent/intent/strategy/etc)")
|
|
189
|
-
parser.add_argument("--ids", nargs="+", type=int, help="Resource IDs for batch commands")
|
|
190
|
-
parser.add_argument("--data", help="JSON data string")
|
|
191
|
-
parser.add_argument("--data-file", help="JSON data file path")
|
|
192
|
-
parser.add_argument("--message", "-m", help="Chat message")
|
|
193
|
-
parser.add_argument("--chat-id", help="Chat ID for multi-turn")
|
|
194
|
-
parser.add_argument("--external-id", help="External inbox conversation ID, e.g. ZENDESK-store-123")
|
|
195
|
-
parser.add_argument("--bot-id", help="Bot ID for chat")
|
|
196
|
-
parser.add_argument("--category", default=None, help="Category filter")
|
|
197
|
-
parser.add_argument("--name", help="Resource name; MockData commands use it as mock_type")
|
|
198
|
-
parser.add_argument("--content", help="Content for create-template")
|
|
199
|
-
parser.add_argument("--page", type=int, default=1, help="Page index")
|
|
200
|
-
parser.add_argument("--page-size", type=int, default=50, help="Page size")
|
|
201
|
-
parser.add_argument("--keyword", default="", help="Search keyword")
|
|
202
|
-
parser.add_argument("--status", default=None, help="Status filter/value for reply templates, e.g. ENABLED")
|
|
203
|
-
parser.add_argument("--scene", action="append", default=None,
|
|
204
|
-
help="Reply template scene filter/value. Can be repeated for list-templates.")
|
|
205
|
-
parser.add_argument("--enable", action="store_true", help="Enable resource")
|
|
206
|
-
parser.add_argument("--disable", action="store_true", help="Disable resource")
|
|
207
|
-
parser.add_argument("--touch-point", default=None, help="Touch point filter")
|
|
208
|
-
parser.add_argument("--urls", nargs="+", help="URLs for web knowledge")
|
|
209
|
-
parser.add_argument("--parent-id", type=int, default=None, help="Parent folder ID")
|
|
210
|
-
parser.add_argument("--max-depth", type=int, default=3,
|
|
211
|
-
help="Maximum directory depth for create/move operations (default: 3)")
|
|
212
|
-
parser.add_argument("--tree-id", type=int, help="label group/tree ID")
|
|
213
|
-
parser.add_argument("--node-ids", nargs="+", type=int, help="label node IDs")
|
|
214
|
-
parser.add_argument("--node-names", nargs="+", help="label node names")
|
|
215
|
-
parser.add_argument("--parent-ids", nargs="+", type=int, help="label parent node IDs")
|
|
216
|
-
parser.add_argument("--template-type", choices=["init", "update"],
|
|
217
|
-
default="update", help="label template type")
|
|
218
|
-
parser.add_argument("--output", help="Output file path for download commands")
|
|
219
|
-
parser.add_argument("--init", action="store_true",
|
|
220
|
-
help="Import labels with uploadWay=INIT for an empty label group")
|
|
221
|
-
parser.add_argument("--debug", action="store_true", help="Include debug data")
|
|
222
|
-
parser.add_argument("--time-offset", type=int, default=8, help="Timezone offset in hours")
|
|
223
|
-
parser.add_argument("--begin-ts", type=int, help="Begin timestamp (ms)")
|
|
224
|
-
parser.add_argument("--end-ts", type=int, help="End timestamp (ms)")
|
|
225
|
-
parser.add_argument("--handoff", choices=["true", "false"], default=None,
|
|
226
|
-
help="Transcript handoff filter: true or false")
|
|
227
|
-
parser.add_argument("--agent-id", type=int, help="Agent ID")
|
|
228
|
-
parser.add_argument("--intent-id", type=int, help="Intent ID for report-intent detail")
|
|
229
|
-
parser.add_argument("--date-unit", default=None, help="Report date unit: DAY / WEEK / TOTAL")
|
|
230
|
-
parser.add_argument("--sort-by", default=None, help="Report sort field JSON, e.g. '{\"TOTAL\":\"DESCEND\"}'")
|
|
231
|
-
parser.add_argument("--assistant-role", default=None, help="Satisfaction role: ASSISTANT or HUMAN")
|
|
232
|
-
parser.add_argument("--satisfied", default=None, choices=["true", "false"],
|
|
233
|
-
help="Satisfaction filter: true or false")
|
|
234
|
-
parser.add_argument("--start-date", default=None, help="Start date YYYY-MM-DD (inspection reports)")
|
|
235
|
-
parser.add_argument("--end-date", default=None, help="End date YYYY-MM-DD (inspection reports)")
|
|
236
|
-
parser.add_argument("--channel", default=None, help="Agent Dashboard channel filter")
|
|
237
|
-
parser.add_argument("--business-type", default=None, help="Agent Dashboard businessType filter")
|
|
238
|
-
parser.add_argument("--granularity", choices=["day", "week"], default=None,
|
|
239
|
-
help="Agent Dashboard trend granularity")
|
|
240
|
-
parser.add_argument("--week-start-date", default=None, help="Dashboard handoff week YYYY-MM-DD")
|
|
241
|
-
parser.add_argument("--primary-reason", default=None, help="Dashboard handoff primary reason")
|
|
242
|
-
parser.add_argument("--secondary-reason", default=None, help="Dashboard handoff secondary reason")
|
|
243
|
-
parser.add_argument("--dimension", default="ticket", help="Inspection dimension (default: ticket)")
|
|
244
|
-
parser.add_argument("--source", default=None, help="Knowledge source filter")
|
|
245
|
-
parser.add_argument("--project-id", help="Project ID (overrides env var and auto-detection)")
|
|
246
|
-
parser.add_argument("--directory-id", type=int, default=None,
|
|
247
|
-
help="Knowledge directory ID filter (list-knowledge)")
|
|
248
|
-
parser.add_argument("--include-tags", action="store_true",
|
|
249
|
-
help="Opt in to include knowledge tags in list-knowledge summary; not sent to the API")
|
|
250
|
-
parser.add_argument("--type", dest="knowledge_type", default="ALL",
|
|
251
|
-
help="Knowledge type filter: ALL / QA / FILE / WEBSITE / PRODUCT / LEARN_SELF")
|
|
252
|
-
parser.add_argument("--raw", action="store_true",
|
|
253
|
-
help="Return raw API response for commands that normally summarize output")
|
|
254
|
-
parser.add_argument("--all", action="store_true",
|
|
255
|
-
help="Fetch all pages where supported")
|
|
256
|
-
parser.add_argument("--env", choices=["staging", "prod"],
|
|
257
|
-
default=os.environ.get("SOLVEA_ENV", "prod"),
|
|
258
|
-
help="Environment: prod (default) or staging")
|
|
259
|
-
parser.add_argument("--history-id", help="Voice transcript history ID")
|
|
260
|
-
parser.add_argument("--receipt", help="AsyncReceipt for get-async-result")
|
|
261
|
-
parser.add_argument("--callback", help="Callback URL for chat-async")
|
|
262
|
-
parser.add_argument("--file", help="Local xlsx path for upload-batch-test-file")
|
|
263
|
-
parser.add_argument("--file-key", help="S3 object key returned by upload-batch-test-file")
|
|
264
|
-
parser.add_argument("--task-type", choices=["REPLY", "MULTI_TURN_REPLY"],
|
|
265
|
-
help="Download Center batch-test task type")
|
|
266
|
-
parser.add_argument("--expire-minutes", type=int,
|
|
267
|
-
help="Expire minutes for chat-async receipt")
|
|
268
|
-
parser.add_argument("--intent", help="Intent name (get-conversations-by-intent)")
|
|
269
|
-
parser.add_argument("--inspection-key", help="Quality inspection rule key")
|
|
270
|
-
parser.add_argument("--limit", type=int, default=50,
|
|
271
|
-
help="Result limit (get-conversations-by-intent)")
|
|
272
|
-
parser.add_argument("--pretty", action="store_true", help="Pretty print JSON")
|
|
273
|
-
parser.add_argument("--publish", action="store_true",
|
|
274
|
-
help="Publish update-chatflow instead of saving a draft")
|
|
275
|
-
parser.add_argument("--confirm-delete", action="store_true",
|
|
276
|
-
help="Required safety acknowledgement for destructive delete commands")
|
|
277
|
-
args = parser.parse_args()
|
|
278
|
-
|
|
279
|
-
client = SolveaClient(env=args.env, project_id=args.project_id)
|
|
280
|
-
handoff = None if args.handoff is None else args.handoff == "true"
|
|
281
|
-
|
|
282
|
-
def load_data():
|
|
283
|
-
if args.data:
|
|
284
|
-
return json.loads(args.data)
|
|
285
|
-
if args.data_file:
|
|
286
|
-
with open(args.data_file) as f:
|
|
287
|
-
return json.load(f)
|
|
288
|
-
return None
|
|
289
|
-
|
|
290
|
-
def output(result):
|
|
291
|
-
if args.pretty:
|
|
292
|
-
print(json.dumps(result, indent=2, ensure_ascii=False))
|
|
293
|
-
else:
|
|
294
|
-
print(json.dumps(result, ensure_ascii=False))
|
|
295
|
-
|
|
296
|
-
def update_chatflow():
|
|
297
|
-
if not args.agent_id:
|
|
298
|
-
raise ValueError("update-chatflow requires --agent-id")
|
|
299
|
-
graph = load_data()
|
|
300
|
-
if graph is None:
|
|
301
|
-
raise ValueError("update-chatflow requires --data or --data-file")
|
|
302
|
-
return client.update_chatflow(args.agent_id, graph, publish=args.publish)
|
|
303
|
-
|
|
304
|
-
def replace_mock_data():
|
|
305
|
-
if not args.name:
|
|
306
|
-
raise ValueError("replace-mock-data requires --name")
|
|
307
|
-
records = load_data()
|
|
308
|
-
if not isinstance(records, list):
|
|
309
|
-
raise ValueError("replace-mock-data requires --data or --data-file with a JSON array")
|
|
310
|
-
return client.replace_mock_data(args.name, records)
|
|
311
|
-
|
|
312
|
-
def query_mock_data():
|
|
313
|
-
if not args.name:
|
|
314
|
-
raise ValueError("query-mock-data requires --name")
|
|
315
|
-
return client.query_mock_data(args.name)
|
|
316
|
-
|
|
317
|
-
def run_plugin():
|
|
318
|
-
data = load_data()
|
|
319
|
-
if isinstance(data, dict) and "externalHttpCallConfig" in data:
|
|
320
|
-
return client.run_http_plugin(
|
|
321
|
-
data["externalHttpCallConfig"],
|
|
322
|
-
data.get("externalHttpCallRequestBody"),
|
|
323
|
-
)
|
|
324
|
-
return client.run_http_plugin(data)
|
|
325
|
-
|
|
326
|
-
def require_object_data(command):
|
|
327
|
-
data = load_data()
|
|
328
|
-
if not isinstance(data, dict):
|
|
329
|
-
raise ValueError(f"{command} requires --data or --data-file with a JSON object")
|
|
330
|
-
return data
|
|
331
|
-
|
|
332
|
-
def set_quality_status():
|
|
333
|
-
if args.enable == args.disable:
|
|
334
|
-
raise ValueError("set-quality-status requires exactly one of --enable or --disable")
|
|
335
|
-
return client.update_quality_inspection_status(args.enable)
|
|
336
|
-
|
|
337
|
-
def require_inspection_key(command):
|
|
338
|
-
if not args.inspection_key:
|
|
339
|
-
raise ValueError(f"{command} requires --inspection-key")
|
|
340
|
-
return args.inspection_key
|
|
341
|
-
|
|
342
|
-
cmd = args.command.replace("-", "_")
|
|
343
|
-
|
|
344
|
-
# ── Summary helpers: CLI output must stay under ~200 lines to avoid Bash tool truncation ──
|
|
345
|
-
|
|
346
|
-
def summary_agents():
|
|
347
|
-
agents = client.list_agents(category=args.category or "text")
|
|
348
|
-
if isinstance(agents, list):
|
|
349
|
-
return [{"id": a.get("id"), "name": a.get("name"), "type": a.get("type"),
|
|
350
|
-
"description": a.get("description", "")} for a in agents]
|
|
351
|
-
return agents
|
|
352
|
-
|
|
353
|
-
def summary_plugins():
|
|
354
|
-
plugins = client.list_plugins()
|
|
355
|
-
if isinstance(plugins, list):
|
|
356
|
-
return [{"id": p.get("id"), "name": p.get("name"), "type": p.get("type"),
|
|
357
|
-
"description": p.get("description", ""), "visible": p.get("visible")}
|
|
358
|
-
for p in plugins]
|
|
359
|
-
return plugins
|
|
360
|
-
|
|
361
|
-
def summary_intents():
|
|
362
|
-
result = client.list_intents(category=args.category or "E_COMMERCE")
|
|
363
|
-
intents = result if isinstance(result, list) else result.get("data", result.get("list", []))
|
|
364
|
-
if isinstance(intents, list):
|
|
365
|
-
return [{"id": i.get("id"), "name": i.get("name"), "status": i.get("status"),
|
|
366
|
-
"description": i.get("example") or i.get("description", "")} for i in intents]
|
|
367
|
-
return result
|
|
368
|
-
|
|
369
|
-
def summary_knowledge():
|
|
370
|
-
result = client.list_knowledge(
|
|
371
|
-
page_index=args.page,
|
|
372
|
-
page_size=args.page_size,
|
|
373
|
-
keyword=args.keyword,
|
|
374
|
-
knowledge_type=args.knowledge_type,
|
|
375
|
-
directory_id=args.directory_id,
|
|
376
|
-
)
|
|
377
|
-
if args.raw:
|
|
378
|
-
return result
|
|
379
|
-
return summarize_knowledge_result(result, include_tags=args.include_tags)
|
|
380
|
-
|
|
381
|
-
def summary_directories():
|
|
382
|
-
# 默认精简:目录树每节点只留 id/name/kuCount/children + 顶层 totalKuCount;--raw 出完整结构
|
|
383
|
-
result = client.list_directories()
|
|
384
|
-
if args.raw:
|
|
385
|
-
return result
|
|
386
|
-
|
|
387
|
-
def slim(node):
|
|
388
|
-
return {
|
|
389
|
-
"id": node.get("id"),
|
|
390
|
-
"name": node.get("name"),
|
|
391
|
-
"kuCount": node.get("kuCount"),
|
|
392
|
-
"children": [slim(c) for c in (node.get("children") or [])],
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
if isinstance(result, list):
|
|
396
|
-
return [slim(n) for n in result]
|
|
397
|
-
if isinstance(result, dict):
|
|
398
|
-
key = next((k for k in ("list", "records", "data") if isinstance(result.get(k), list)), None)
|
|
399
|
-
out = {}
|
|
400
|
-
if result.get("totalKuCount") is not None:
|
|
401
|
-
out["totalKuCount"] = result.get("totalKuCount")
|
|
402
|
-
out["list"] = [slim(n) for n in result.get(key, [])] if key else []
|
|
403
|
-
return out
|
|
404
|
-
return result
|
|
405
|
-
|
|
406
|
-
def summary_templates():
|
|
407
|
-
result = client.list_templates(
|
|
408
|
-
page_index=args.page,
|
|
409
|
-
page_size=args.page_size,
|
|
410
|
-
keyword=args.keyword,
|
|
411
|
-
scenes=args.scene,
|
|
412
|
-
status=[args.status] if args.status else None,
|
|
413
|
-
)
|
|
414
|
-
items = result.get("list", result) if isinstance(result, dict) else result
|
|
415
|
-
if isinstance(items, list):
|
|
416
|
-
compact = [
|
|
417
|
-
{
|
|
418
|
-
"id": item.get("id"),
|
|
419
|
-
"name": item.get("name"),
|
|
420
|
-
"content": item.get("content", ""),
|
|
421
|
-
"status": item.get("status", ""),
|
|
422
|
-
"scene": item.get("scene", ""),
|
|
423
|
-
}
|
|
424
|
-
for item in items
|
|
425
|
-
]
|
|
426
|
-
if isinstance(result, dict):
|
|
427
|
-
return {"total": result.get("total", result.get("totalCount", len(compact))), "list": compact}
|
|
428
|
-
return compact
|
|
429
|
-
return result
|
|
430
|
-
|
|
431
|
-
def create_template():
|
|
432
|
-
data = load_data() or {}
|
|
433
|
-
if args.name:
|
|
434
|
-
data["name"] = args.name
|
|
435
|
-
if args.content:
|
|
436
|
-
data["content"] = args.content
|
|
437
|
-
if args.status:
|
|
438
|
-
data["status"] = args.status
|
|
439
|
-
if args.scene:
|
|
440
|
-
data["scene"] = args.scene[0]
|
|
441
|
-
if not data.get("name") or not data.get("content"):
|
|
442
|
-
raise ValueError("create-template requires --name/--content or --data/--data-file with name and content")
|
|
443
|
-
return client.create_template(data)
|
|
444
|
-
|
|
445
|
-
def update_template():
|
|
446
|
-
if not args.id:
|
|
447
|
-
raise ValueError("update-template requires --id (templateId)")
|
|
448
|
-
data = load_data() or {}
|
|
449
|
-
# 只传要改的字段;multiContent 走 --data/--data-file(含多语种时)
|
|
450
|
-
return client.update_template(
|
|
451
|
-
args.id,
|
|
452
|
-
name=args.name if args.name else data.get("name"),
|
|
453
|
-
content=args.content if args.content else data.get("content"),
|
|
454
|
-
status=args.status if args.status else data.get("status"),
|
|
455
|
-
multi_content=data.get("multiContent"),
|
|
456
|
-
)
|
|
457
|
-
|
|
458
|
-
def delete_templates():
|
|
459
|
-
if not args.confirm_delete:
|
|
460
|
-
raise ValueError("delete-templates requires --confirm-delete after explicit user acknowledgement")
|
|
461
|
-
data = load_data() or {}
|
|
462
|
-
template_ids = args.ids or data.get("templateIds") or data.get("ids")
|
|
463
|
-
if not template_ids:
|
|
464
|
-
raise ValueError("delete-templates requires --ids or --data/--data-file with templateIds")
|
|
465
|
-
return client.delete_templates(template_ids)
|
|
466
|
-
|
|
467
|
-
def require_tree_id(command):
|
|
468
|
-
if not args.tree_id:
|
|
469
|
-
raise ValueError(f"{command} requires --tree-id")
|
|
470
|
-
return args.tree_id
|
|
471
|
-
|
|
472
|
-
def require_id(command):
|
|
473
|
-
if not args.id:
|
|
474
|
-
raise ValueError(f"{command} requires --id")
|
|
475
|
-
return args.id
|
|
476
|
-
|
|
477
|
-
def create_label_group():
|
|
478
|
-
data = load_data() or {}
|
|
479
|
-
tree_name = args.name or data.get("treeName")
|
|
480
|
-
if not tree_name:
|
|
481
|
-
raise ValueError("create-label-group requires --name or --data/--data-file with treeName")
|
|
482
|
-
return client.create_label_group(
|
|
483
|
-
tree_name,
|
|
484
|
-
tree_describe=data.get("treeDescribe", ""),
|
|
485
|
-
condition=data.get("condition"),
|
|
486
|
-
is_multi_tag=data.get("isMultiTag"),
|
|
487
|
-
)
|
|
488
|
-
|
|
489
|
-
def update_label_group():
|
|
490
|
-
data = load_data() or {}
|
|
491
|
-
return client.update_label_group(
|
|
492
|
-
require_id("update-label-group"),
|
|
493
|
-
tree_name=args.name if args.name else data.get("treeName"),
|
|
494
|
-
tree_describe=data.get("treeDescribe"),
|
|
495
|
-
condition=data.get("condition"),
|
|
496
|
-
is_multi_tag=data.get("isMultiTag"),
|
|
497
|
-
)
|
|
498
|
-
|
|
499
|
-
def get_label_tree():
|
|
500
|
-
return client.get_label_tree(require_tree_id("get-label-tree"), name=args.name or args.keyword)
|
|
501
|
-
|
|
502
|
-
def get_label_detail_tree():
|
|
503
|
-
return client.get_label_detail_tree(
|
|
504
|
-
require_tree_id("get-label-detail-tree"),
|
|
505
|
-
node_ids=args.node_ids,
|
|
506
|
-
node_names=args.node_names,
|
|
507
|
-
parent_ids=args.parent_ids,
|
|
508
|
-
)
|
|
509
|
-
|
|
510
|
-
def create_label_node():
|
|
511
|
-
data = load_data() or {}
|
|
512
|
-
node_name = args.name or data.get("nodeName")
|
|
513
|
-
describe = args.content if args.content is not None else data.get("describe", "")
|
|
514
|
-
if not node_name:
|
|
515
|
-
raise ValueError("create-label-node requires --name or --data/--data-file with nodeName")
|
|
516
|
-
return client.create_label_node(
|
|
517
|
-
require_tree_id("create-label-node"),
|
|
518
|
-
node_name,
|
|
519
|
-
describe,
|
|
520
|
-
parent_id=data.get("parentId", args.parent_id),
|
|
521
|
-
example_list=data.get("exampleList") or [],
|
|
522
|
-
is_tag=data.get("isTag", True),
|
|
523
|
-
)
|
|
524
|
-
|
|
525
|
-
def update_label_node():
|
|
526
|
-
data = load_data() or {}
|
|
527
|
-
return client.update_label_node(
|
|
528
|
-
require_tree_id("update-label-node"),
|
|
529
|
-
require_id("update-label-node"),
|
|
530
|
-
node_name=args.name if args.name else data.get("nodeName"),
|
|
531
|
-
describe=args.content if args.content is not None else data.get("describe"),
|
|
532
|
-
parent_id=data.get("parentId"),
|
|
533
|
-
example_list=data.get("exampleList"),
|
|
534
|
-
is_tag=data.get("isTag"),
|
|
535
|
-
)
|
|
536
|
-
|
|
537
|
-
def delete_label_node():
|
|
538
|
-
tree_id = require_tree_id("delete-label-node")
|
|
539
|
-
node_id = require_id("delete-label-node")
|
|
540
|
-
return client.delete_label_node(
|
|
541
|
-
tree_id,
|
|
542
|
-
node_id,
|
|
543
|
-
)
|
|
544
|
-
|
|
545
|
-
def download_label_template():
|
|
546
|
-
output_path = args.output or f"{args.template_type.lower()}_template.xlsx"
|
|
547
|
-
template_type = "VOC_LABEL_INIT" if args.template_type == "init" else "VOC_LABEL"
|
|
548
|
-
return {"file": client.download_label_template(template_type, output_path)}
|
|
549
|
-
|
|
550
|
-
def download_labels():
|
|
551
|
-
tree_id = require_tree_id("download-labels")
|
|
552
|
-
output_path = args.output or f"labels_{tree_id}.xlsx"
|
|
553
|
-
return {"file": client.download_labels(tree_id, output_path)}
|
|
554
|
-
|
|
555
|
-
def upload_labels():
|
|
556
|
-
tree_id = require_tree_id("upload-labels")
|
|
557
|
-
if not args.file:
|
|
558
|
-
raise ValueError("upload-labels requires --file")
|
|
559
|
-
return client.upload_label_file(tree_id, args.file, init=args.init)
|
|
560
|
-
|
|
561
|
-
def get_knowledge():
|
|
562
|
-
if not args.id:
|
|
563
|
-
raise ValueError("get-knowledge requires --id")
|
|
564
|
-
if args.knowledge_type == "ALL":
|
|
565
|
-
raise ValueError("get-knowledge requires --type QA, LEARN_SELF, FILE, WEBSITE, or PRODUCT")
|
|
566
|
-
return client.get_knowledge_by_type(int(args.id), args.knowledge_type)
|
|
567
|
-
|
|
568
|
-
def get_knowledge_isolation_keywords():
|
|
569
|
-
if not args.id:
|
|
570
|
-
raise ValueError("get-knowledge-isolation-keywords requires --id")
|
|
571
|
-
if args.knowledge_type == "ALL":
|
|
572
|
-
raise ValueError("get-knowledge-isolation-keywords requires an explicit --type")
|
|
573
|
-
return client.get_knowledge_isolation_keywords(int(args.id), args.knowledge_type)
|
|
574
|
-
|
|
575
|
-
def update_knowledge():
|
|
576
|
-
if not args.id:
|
|
577
|
-
raise ValueError("update-knowledge requires --id")
|
|
578
|
-
data = load_data()
|
|
579
|
-
if not isinstance(data, dict):
|
|
580
|
-
raise ValueError("update-knowledge requires --data or --data-file with a JSON object")
|
|
581
|
-
payload = dict(data)
|
|
582
|
-
if "directoryId" not in payload:
|
|
583
|
-
current = client.get_knowledge(int(args.id))
|
|
584
|
-
if isinstance(current, dict) and current.get("directoryId") is not None:
|
|
585
|
-
payload["directoryId"] = current["directoryId"]
|
|
586
|
-
return client.update_knowledge(int(args.id), payload)
|
|
587
|
-
|
|
588
|
-
def batch_update_knowledge():
|
|
589
|
-
data = load_data()
|
|
590
|
-
items = data.get("list") if isinstance(data, dict) and "list" in data else data
|
|
591
|
-
if not isinstance(items, list) or not items:
|
|
592
|
-
raise ValueError("batch-update-knowledge requires a JSON array or object with list")
|
|
593
|
-
formatted = []
|
|
594
|
-
for item in items:
|
|
595
|
-
if not isinstance(item, dict):
|
|
596
|
-
raise ValueError("Every item in batch-update-knowledge must be a JSON object")
|
|
597
|
-
row = dict(item)
|
|
598
|
-
if "id" in row and "kuId" not in row:
|
|
599
|
-
row["kuId"] = row.pop("id")
|
|
600
|
-
if "kuId" not in row:
|
|
601
|
-
raise ValueError("Every item requires kuId or id")
|
|
602
|
-
if "directoryId" not in row:
|
|
603
|
-
current = client.get_knowledge(int(row["kuId"]))
|
|
604
|
-
if isinstance(current, dict) and current.get("directoryId") is not None:
|
|
605
|
-
row["directoryId"] = current["directoryId"]
|
|
606
|
-
formatted.append(row)
|
|
607
|
-
return client.update_kus(formatted)
|
|
608
|
-
|
|
609
|
-
def update_document_content():
|
|
610
|
-
if not args.id:
|
|
611
|
-
raise ValueError("update-document-content requires --id")
|
|
612
|
-
knowledge_type = str(args.knowledge_type).upper()
|
|
613
|
-
if knowledge_type not in {"FILE", "WEBSITE"}:
|
|
614
|
-
raise ValueError("update-document-content requires --type FILE or WEBSITE")
|
|
615
|
-
changes = require_object_data("update-document-content")
|
|
616
|
-
unknown = set(changes) - {"fileText", "filePath", "title"}
|
|
617
|
-
if unknown:
|
|
618
|
-
raise ValueError(f"Unsupported document content fields: {', '.join(sorted(unknown))}")
|
|
619
|
-
if changes.get("fileText") is None and changes.get("filePath") is None:
|
|
620
|
-
raise ValueError("update-document-content requires fileText or filePath")
|
|
621
|
-
|
|
622
|
-
current = client.get_document(int(args.id))
|
|
623
|
-
if not isinstance(current, dict):
|
|
624
|
-
raise ValueError(f"Could not read document {args.id} before update")
|
|
625
|
-
payload = {
|
|
626
|
-
"type": knowledge_type,
|
|
627
|
-
"directoryId": current.get("directoryId"),
|
|
628
|
-
"fileName": current.get("fileName") or current.get("name") or current.get("title"),
|
|
629
|
-
"title": current.get("title") or current.get("fileName") or current.get("name"),
|
|
630
|
-
"metadata": current.get("metadata") if isinstance(current.get("metadata"), dict) else {},
|
|
631
|
-
}
|
|
632
|
-
payload.update(changes)
|
|
633
|
-
payload["type"] = knowledge_type
|
|
634
|
-
return client.update_document_content(int(args.id), payload)
|
|
635
|
-
|
|
636
|
-
def create_product_knowledge():
|
|
637
|
-
data = require_object_data("create-product-knowledge")
|
|
638
|
-
for field in ("title", "spuUrl"):
|
|
639
|
-
if not data.get(field):
|
|
640
|
-
raise ValueError(f"create-product-knowledge requires {field}")
|
|
641
|
-
return client.create_product_knowledge(data)
|
|
642
|
-
|
|
643
|
-
def update_product_knowledge():
|
|
644
|
-
if not args.id:
|
|
645
|
-
raise ValueError("update-product-knowledge requires --id")
|
|
646
|
-
changes = require_object_data("update-product-knowledge")
|
|
647
|
-
current = client.get_product_knowledge(int(args.id))
|
|
648
|
-
if not isinstance(current, dict):
|
|
649
|
-
raise ValueError(f"Could not read product knowledge {args.id} before update")
|
|
650
|
-
allowed_fields = {
|
|
651
|
-
"productId", "tags", "documentId", "title", "handle", "vendor",
|
|
652
|
-
"category", "brand", "productStatus", "country", "spuUrl", "image",
|
|
653
|
-
"platform", "currency", "price", "compareAtPrice", "describe", "source",
|
|
654
|
-
"skuList", "directoryId", "published", "metadata", "specialTagMap",
|
|
655
|
-
"isVisible", "optionName",
|
|
656
|
-
}
|
|
657
|
-
unknown = set(changes) - allowed_fields - {"kuProductId", "id"}
|
|
658
|
-
if unknown:
|
|
659
|
-
raise ValueError(f"Unsupported product fields: {', '.join(sorted(unknown))}")
|
|
660
|
-
payload = {key: value for key, value in current.items() if key in allowed_fields}
|
|
661
|
-
payload.update({key: value for key, value in changes.items() if key in allowed_fields})
|
|
662
|
-
missing = [
|
|
663
|
-
field for field in ("title", "describe", "spuUrl", "image", "source", "published", "isVisible")
|
|
664
|
-
if payload.get(field) is None or payload.get(field) == ""
|
|
665
|
-
]
|
|
666
|
-
if missing:
|
|
667
|
-
raise ValueError(f"Product detail is incomplete: {', '.join(missing)}")
|
|
668
|
-
return client.update_product_knowledge(int(args.id), payload)
|
|
669
|
-
|
|
670
|
-
def batch_update_products():
|
|
671
|
-
data = load_data()
|
|
672
|
-
items = data.get("list") if isinstance(data, dict) and "list" in data else data
|
|
673
|
-
if not isinstance(items, list) or not items:
|
|
674
|
-
raise ValueError("batch-update-products requires a JSON array or object with list")
|
|
675
|
-
formatted = []
|
|
676
|
-
for item in items:
|
|
677
|
-
if not isinstance(item, dict):
|
|
678
|
-
raise ValueError("Every item in batch-update-products must be a JSON object")
|
|
679
|
-
row = dict(item)
|
|
680
|
-
if "kuProductId" in row and "id" not in row:
|
|
681
|
-
row["id"] = row.pop("kuProductId")
|
|
682
|
-
if "id" not in row:
|
|
683
|
-
raise ValueError("Every item requires id or kuProductId")
|
|
684
|
-
entry = {"id": int(row["id"])}
|
|
685
|
-
if "mainPush" in row:
|
|
686
|
-
entry["mainPush"] = bool(row["mainPush"])
|
|
687
|
-
if "enable" in row:
|
|
688
|
-
entry["enable"] = bool(row["enable"])
|
|
689
|
-
elif "published" in row:
|
|
690
|
-
entry["enable"] = bool(row["published"])
|
|
691
|
-
formatted.append(entry)
|
|
692
|
-
return client.update_ku_products(formatted)
|
|
693
|
-
|
|
694
|
-
def directory_index():
|
|
695
|
-
result = client.list_directories()
|
|
696
|
-
if isinstance(result, list):
|
|
697
|
-
roots = result
|
|
698
|
-
elif isinstance(result, dict):
|
|
699
|
-
roots = next(
|
|
700
|
-
(result.get(key) for key in ("list", "records", "data") if isinstance(result.get(key), list)),
|
|
701
|
-
[],
|
|
702
|
-
)
|
|
703
|
-
else:
|
|
704
|
-
roots = []
|
|
705
|
-
index = {}
|
|
706
|
-
|
|
707
|
-
def walk(node, depth, parent_id):
|
|
708
|
-
node_id = node.get("id")
|
|
709
|
-
if node_id is None:
|
|
710
|
-
return
|
|
711
|
-
index[int(node_id)] = {"node": node, "depth": depth, "parentId": parent_id}
|
|
712
|
-
for child in node.get("children") or []:
|
|
713
|
-
walk(child, depth + 1, int(node_id))
|
|
714
|
-
|
|
715
|
-
for root in roots:
|
|
716
|
-
walk(root, 1, 0)
|
|
717
|
-
return index
|
|
718
|
-
|
|
719
|
-
def subtree_ids(index, directory_id):
|
|
720
|
-
descendants = {directory_id}
|
|
721
|
-
changed = True
|
|
722
|
-
while changed:
|
|
723
|
-
changed = False
|
|
724
|
-
for node_id, item in index.items():
|
|
725
|
-
if item["parentId"] in descendants and node_id not in descendants:
|
|
726
|
-
descendants.add(node_id)
|
|
727
|
-
changed = True
|
|
728
|
-
return descendants
|
|
729
|
-
|
|
730
|
-
def create_folder():
|
|
731
|
-
if not args.name:
|
|
732
|
-
raise ValueError("create-folder requires --name")
|
|
733
|
-
if args.max_depth <= 0:
|
|
734
|
-
raise ValueError("--max-depth must be positive")
|
|
735
|
-
parent_id = args.parent_id if args.parent_id is not None else 0
|
|
736
|
-
if parent_id:
|
|
737
|
-
index = directory_index()
|
|
738
|
-
if parent_id not in index:
|
|
739
|
-
raise ValueError(f"Parent directory {parent_id} was not found")
|
|
740
|
-
if index[parent_id]["depth"] + 1 > args.max_depth:
|
|
741
|
-
raise ValueError(f"Directory would exceed maximum depth {args.max_depth}")
|
|
742
|
-
return client.create_folder(args.name, parent_id=parent_id)
|
|
743
|
-
|
|
744
|
-
def update_directory():
|
|
745
|
-
if not args.id:
|
|
746
|
-
raise ValueError("update-directory requires --id")
|
|
747
|
-
data = load_data() or {}
|
|
748
|
-
if not isinstance(data, dict):
|
|
749
|
-
raise ValueError("update-directory requires a JSON object")
|
|
750
|
-
if args.name:
|
|
751
|
-
data["name"] = args.name
|
|
752
|
-
if not data.get("name"):
|
|
753
|
-
raise ValueError("update-directory requires --name or data.name")
|
|
754
|
-
index = directory_index()
|
|
755
|
-
if int(args.id) not in index:
|
|
756
|
-
raise ValueError(f"Directory {args.id} was not found")
|
|
757
|
-
return client.update_directory(int(args.id), {"name": data["name"]})
|
|
758
|
-
|
|
759
|
-
def move_directory():
|
|
760
|
-
if not args.id:
|
|
761
|
-
raise ValueError("move-directory requires --id")
|
|
762
|
-
if args.parent_id is None:
|
|
763
|
-
raise ValueError("move-directory requires --parent-id; use 0 for root")
|
|
764
|
-
if args.max_depth <= 0:
|
|
765
|
-
raise ValueError("--max-depth must be positive")
|
|
766
|
-
directory_id = int(args.id)
|
|
767
|
-
parent_id = int(args.parent_id)
|
|
768
|
-
index = directory_index()
|
|
769
|
-
if directory_id not in index:
|
|
770
|
-
raise ValueError(f"Directory {directory_id} was not found")
|
|
771
|
-
if parent_id and parent_id not in index:
|
|
772
|
-
raise ValueError(f"Parent directory {parent_id} was not found")
|
|
773
|
-
descendants = subtree_ids(index, directory_id)
|
|
774
|
-
if parent_id in descendants:
|
|
775
|
-
raise ValueError("A directory cannot be moved into itself or its descendants")
|
|
776
|
-
current_depth = index[directory_id]["depth"]
|
|
777
|
-
subtree_height = max(index[node_id]["depth"] - current_depth + 1 for node_id in descendants)
|
|
778
|
-
parent_depth = index[parent_id]["depth"] if parent_id else 0
|
|
779
|
-
if parent_depth + subtree_height > args.max_depth:
|
|
780
|
-
raise ValueError(f"Moved subtree would exceed maximum depth {args.max_depth}")
|
|
781
|
-
return client.move_directory(directory_id, parent_id)
|
|
782
|
-
|
|
783
|
-
def delete_directory():
|
|
784
|
-
if not args.id:
|
|
785
|
-
raise ValueError("delete-directory requires --id")
|
|
786
|
-
if not args.confirm_delete:
|
|
787
|
-
raise ValueError("delete-directory requires --confirm-delete after explicit user acknowledgement")
|
|
788
|
-
index = directory_index()
|
|
789
|
-
if int(args.id) not in index:
|
|
790
|
-
raise ValueError(f"Directory {args.id} was not found")
|
|
791
|
-
return client.delete_directory(int(args.id))
|
|
792
|
-
|
|
793
|
-
def operate_knowledge():
|
|
794
|
-
data = load_data()
|
|
795
|
-
request_list = data.get("requestList") if isinstance(data, dict) else data
|
|
796
|
-
if not isinstance(request_list, list) or not request_list:
|
|
797
|
-
raise ValueError("operate-knowledge requires a JSON array or object with requestList")
|
|
798
|
-
supported_types = {"QA", "LEARN_SELF", "FILE", "WEBSITE", "PRODUCT"}
|
|
799
|
-
supported_operations = {"DELETE", "MOVE", "PUBLISH", "UNPUBLISH", "VISIBLE", "INVISIBLE"}
|
|
800
|
-
normalized = []
|
|
801
|
-
move_directory_ids = set()
|
|
802
|
-
for item in request_list:
|
|
803
|
-
if not isinstance(item, dict):
|
|
804
|
-
raise ValueError("Every knowledge operation must be an object")
|
|
805
|
-
knowledge_type = str(item.get("knowledgeType", "")).upper()
|
|
806
|
-
operation = str(item.get("operateType", "")).upper()
|
|
807
|
-
if knowledge_type not in supported_types:
|
|
808
|
-
raise ValueError(f"Unsupported knowledge type: {knowledge_type}")
|
|
809
|
-
if operation not in supported_operations:
|
|
810
|
-
raise ValueError(f"Unsupported knowledge operation: {operation}")
|
|
811
|
-
try:
|
|
812
|
-
knowledge_id = int(item["knowledgeId"])
|
|
813
|
-
except (KeyError, TypeError, ValueError):
|
|
814
|
-
raise ValueError("Every knowledge operation requires a numeric knowledgeId") from None
|
|
815
|
-
normalized_item = {
|
|
816
|
-
"knowledgeType": knowledge_type,
|
|
817
|
-
"knowledgeId": knowledge_id,
|
|
818
|
-
"operateType": operation,
|
|
819
|
-
}
|
|
820
|
-
if operation == "MOVE":
|
|
821
|
-
try:
|
|
822
|
-
normalized_item["directoryId"] = int(item["directoryId"])
|
|
823
|
-
except (KeyError, TypeError, ValueError):
|
|
824
|
-
raise ValueError("MOVE requires a numeric directoryId") from None
|
|
825
|
-
move_directory_ids.add(normalized_item["directoryId"])
|
|
826
|
-
client.get_knowledge_by_type(knowledge_id, knowledge_type)
|
|
827
|
-
normalized.append(normalized_item)
|
|
828
|
-
if any(item["operateType"] == "DELETE" for item in normalized) and not args.confirm_delete:
|
|
829
|
-
raise ValueError("DELETE requires --confirm-delete after explicit user acknowledgement")
|
|
830
|
-
if move_directory_ids:
|
|
831
|
-
index = directory_index()
|
|
832
|
-
missing_directories = sorted(directory_id for directory_id in move_directory_ids if directory_id not in index)
|
|
833
|
-
if missing_directories:
|
|
834
|
-
raise ValueError(f"Target directories were not found: {missing_directories}")
|
|
835
|
-
return client.operate_knowledge(normalized)
|
|
836
|
-
|
|
837
|
-
def edit_knowledge_tags():
|
|
838
|
-
data = require_object_data("edit-knowledge-tags")
|
|
839
|
-
knowledge_ids = data.get("knowledgeIds")
|
|
840
|
-
requested_tags = data.get("tagList")
|
|
841
|
-
if not isinstance(knowledge_ids, list) or not knowledge_ids:
|
|
842
|
-
raise ValueError("edit-knowledge-tags requires non-empty knowledgeIds")
|
|
843
|
-
if not isinstance(requested_tags, list):
|
|
844
|
-
raise ValueError("edit-knowledge-tags requires tagList array")
|
|
845
|
-
operation = data.get("operation", "append")
|
|
846
|
-
if operation not in {"append", "replace", "remove"}:
|
|
847
|
-
raise ValueError("edit-knowledge-tags operation must be append, replace, or remove")
|
|
848
|
-
|
|
849
|
-
knowledge_type = str(data.get("type", "QA")).upper()
|
|
850
|
-
updates = []
|
|
851
|
-
for knowledge_id in knowledge_ids:
|
|
852
|
-
knowledge = client.get_knowledge_by_type(int(knowledge_id), knowledge_type)
|
|
853
|
-
if not knowledge:
|
|
854
|
-
raise ValueError(f"Could not read knowledge {knowledge_id} before tag update")
|
|
855
|
-
current = knowledge.get("tags")
|
|
856
|
-
if not isinstance(current, list):
|
|
857
|
-
current = (knowledge.get("metadata") or {}).get("tags", [])
|
|
858
|
-
if not isinstance(current, list):
|
|
859
|
-
current = []
|
|
860
|
-
if operation == "append":
|
|
861
|
-
target = list(dict.fromkeys([*current, *requested_tags]))
|
|
862
|
-
elif operation == "remove":
|
|
863
|
-
target = [tag for tag in current if tag not in requested_tags]
|
|
864
|
-
else:
|
|
865
|
-
target = list(dict.fromkeys(requested_tags))
|
|
866
|
-
updates.append(client.edit_knowledge_tags(
|
|
867
|
-
knowledge_ids=[int(knowledge_id)],
|
|
868
|
-
tag_list=target,
|
|
869
|
-
knowledge_type=knowledge_type,
|
|
870
|
-
))
|
|
871
|
-
return {"success": True, "results": updates}
|
|
872
|
-
|
|
873
|
-
def summary_knowledge_tags():
|
|
874
|
-
result = client.list_knowledge_tags(keyword=args.keyword)
|
|
875
|
-
if args.raw:
|
|
876
|
-
return result
|
|
877
|
-
if isinstance(result, dict) and isinstance(result.get("metadataTagAll"), list):
|
|
878
|
-
return {"tags": result["metadataTagAll"]}
|
|
879
|
-
return result
|
|
880
|
-
|
|
881
|
-
def summary_isolation_keywords():
|
|
882
|
-
if args.all:
|
|
883
|
-
return client.list_all_isolation_keywords(keyword=args.keyword, page_size=args.page_size)
|
|
884
|
-
result = client.list_isolation_keywords(
|
|
885
|
-
keyword=args.keyword,
|
|
886
|
-
page_index=args.page,
|
|
887
|
-
page_size=args.page_size,
|
|
888
|
-
)
|
|
889
|
-
if args.raw:
|
|
890
|
-
return result
|
|
891
|
-
items = result.get("list", result) if isinstance(result, dict) else result
|
|
892
|
-
if not isinstance(items, list):
|
|
893
|
-
return result
|
|
894
|
-
compact = [
|
|
895
|
-
{
|
|
896
|
-
"id": item.get("id"),
|
|
897
|
-
"keyword": item.get("keyword", ""),
|
|
898
|
-
"bindCount": item.get("bindCount", 0),
|
|
899
|
-
"projectId": item.get("projectId"),
|
|
900
|
-
"tenantId": item.get("tenantId"),
|
|
901
|
-
"createdAt": item.get("createdAt"),
|
|
902
|
-
"updatedAt": item.get("updatedAt"),
|
|
903
|
-
}
|
|
904
|
-
for item in items
|
|
905
|
-
]
|
|
906
|
-
return compact
|
|
907
|
-
|
|
908
|
-
def create_isolation_keyword():
|
|
909
|
-
if not args.keyword:
|
|
910
|
-
raise ValueError("create-isolation-keyword requires --keyword")
|
|
911
|
-
requested = list(dict.fromkeys(part.strip() for part in args.keyword.split(",") if part.strip()))
|
|
912
|
-
if not requested:
|
|
913
|
-
raise ValueError("create-isolation-keyword requires at least one non-empty keyword")
|
|
914
|
-
existing = client.list_all_isolation_keywords(page_size=args.page_size)
|
|
915
|
-
existing_names = {str(item.get("keyword", "")).strip() for item in existing}
|
|
916
|
-
duplicates = [name for name in requested if name in existing_names]
|
|
917
|
-
pending = [name for name in requested if name not in existing_names]
|
|
918
|
-
if not pending:
|
|
919
|
-
return {"duplicate": duplicates, "failed": [], "success": [], "created": False}
|
|
920
|
-
result = client.create_isolation_keyword(",".join(pending))
|
|
921
|
-
if isinstance(result, dict):
|
|
922
|
-
result = dict(result)
|
|
923
|
-
result["duplicate"] = duplicates + list(result.get("duplicate", []))
|
|
924
|
-
result["created"] = True
|
|
925
|
-
return result
|
|
926
|
-
|
|
927
|
-
def update_isolation_keyword():
|
|
928
|
-
if not args.id:
|
|
929
|
-
raise ValueError("update-isolation-keyword requires --id")
|
|
930
|
-
if not args.keyword:
|
|
931
|
-
raise ValueError("update-isolation-keyword requires --keyword")
|
|
932
|
-
return client.update_isolation_keyword(int(args.id), args.keyword)
|
|
933
|
-
|
|
934
|
-
def delete_isolation_keyword():
|
|
935
|
-
if not args.id:
|
|
936
|
-
raise ValueError("delete-isolation-keyword requires --id")
|
|
937
|
-
if not args.confirm_delete:
|
|
938
|
-
raise ValueError("delete-isolation-keyword requires --confirm-delete after explicit user acknowledgement")
|
|
939
|
-
return client.delete_isolation_keyword(int(args.id))
|
|
940
|
-
|
|
941
|
-
def batch_update_isolation_keyword_bindings():
|
|
942
|
-
data = require_object_data("batch-update-isolation-keyword-bindings")
|
|
943
|
-
operation = str(data.get("operation", "append")).lower()
|
|
944
|
-
if operation not in {"append", "remove", "replace"}:
|
|
945
|
-
raise ValueError("operation must be append, remove, or replace")
|
|
946
|
-
|
|
947
|
-
items = data.get("items") or []
|
|
948
|
-
directory_ids = data.get("directoryIds") or []
|
|
949
|
-
if not items and not directory_ids:
|
|
950
|
-
raise ValueError("binding update requires knowledge items or directory IDs")
|
|
951
|
-
if directory_ids and operation != "replace":
|
|
952
|
-
raise ValueError(
|
|
953
|
-
"directory bindings require operation=replace because the backend replaces the final binding set"
|
|
954
|
-
)
|
|
955
|
-
if directory_ids and data.get("confirmReplace") is not True:
|
|
956
|
-
raise ValueError(
|
|
957
|
-
"directory binding replacement requires confirmReplace=true after explicit user confirmation"
|
|
958
|
-
)
|
|
959
|
-
|
|
960
|
-
resolved_items = []
|
|
961
|
-
for item in items:
|
|
962
|
-
knowledge_id = item.get("knowledgeId") if isinstance(item, dict) else None
|
|
963
|
-
knowledge_type = (item.get("knowledgeType") if isinstance(item, dict) else None)
|
|
964
|
-
knowledge_type = knowledge_type.upper() if isinstance(knowledge_type, str) else None
|
|
965
|
-
if knowledge_id is None:
|
|
966
|
-
raise ValueError("Could not read existing binding for every knowledge item")
|
|
967
|
-
if knowledge_type is None:
|
|
968
|
-
raise ValueError("items[].knowledgeType is required before binding isolation keywords")
|
|
969
|
-
client.get_knowledge_by_type(int(knowledge_id), knowledge_type)
|
|
970
|
-
current_bindings = client.get_knowledge_isolation_keywords(int(knowledge_id), knowledge_type)
|
|
971
|
-
if not isinstance(current_bindings, list):
|
|
972
|
-
raise ValueError("existing isolation keyword bindings must be an array")
|
|
973
|
-
current_ids = []
|
|
974
|
-
for binding in current_bindings:
|
|
975
|
-
keyword_id = binding.get("id") if isinstance(binding, dict) else None
|
|
976
|
-
if isinstance(keyword_id, bool) or not isinstance(keyword_id, int) or keyword_id <= 0:
|
|
977
|
-
raise ValueError("existing isolation keyword bindings contain an invalid ID")
|
|
978
|
-
current_ids.append(keyword_id)
|
|
979
|
-
|
|
980
|
-
requested_ids = item.get("keywordIds") if isinstance(item, dict) else None
|
|
981
|
-
if not isinstance(requested_ids, list):
|
|
982
|
-
raise ValueError("items[].keywordIds must be an array")
|
|
983
|
-
if any(
|
|
984
|
-
isinstance(keyword_id, bool)
|
|
985
|
-
or not isinstance(keyword_id, int)
|
|
986
|
-
or keyword_id <= 0
|
|
987
|
-
for keyword_id in requested_ids
|
|
988
|
-
):
|
|
989
|
-
raise ValueError("items[].keywordIds must contain positive integer IDs")
|
|
990
|
-
if operation == "append":
|
|
991
|
-
final_ids = list(dict.fromkeys([*current_ids, *requested_ids]))
|
|
992
|
-
elif operation == "remove":
|
|
993
|
-
removed_ids = set(requested_ids)
|
|
994
|
-
final_ids = [keyword_id for keyword_id in current_ids if keyword_id not in removed_ids]
|
|
995
|
-
else:
|
|
996
|
-
if data.get("confirmReplace") is not True:
|
|
997
|
-
raise ValueError("binding replacement requires confirmReplace=true after explicit user confirmation")
|
|
998
|
-
final_ids = list(dict.fromkeys(requested_ids))
|
|
999
|
-
resolved_items.append({**item, "knowledgeType": knowledge_type, "keywordIds": final_ids})
|
|
1000
|
-
|
|
1001
|
-
for directory_id in directory_ids:
|
|
1002
|
-
client.list_knowledge(directory_id=int(directory_id), page_size=1000)
|
|
1003
|
-
return client.batch_update_isolation_keyword_bindings(
|
|
1004
|
-
items=resolved_items,
|
|
1005
|
-
directory_ids=directory_ids,
|
|
1006
|
-
keyword_ids=data.get("keywordIds"),
|
|
1007
|
-
)
|
|
1008
|
-
|
|
1009
|
-
def summary_transcripts():
|
|
1010
|
-
result = client.list_transcripts(
|
|
1011
|
-
page_size=args.page_size,
|
|
1012
|
-
page_index=args.page,
|
|
1013
|
-
debug=args.debug,
|
|
1014
|
-
begin_ts=args.begin_ts,
|
|
1015
|
-
end_ts=args.end_ts,
|
|
1016
|
-
touch_point=args.touch_point,
|
|
1017
|
-
bot_id=args.bot_id,
|
|
1018
|
-
agent_id=args.agent_id,
|
|
1019
|
-
intent_id=args.intent_id,
|
|
1020
|
-
handoff=handoff,
|
|
1021
|
-
keyword=args.keyword or None,
|
|
1022
|
-
)
|
|
1023
|
-
items = result.get("list", result) if isinstance(result, dict) else result
|
|
1024
|
-
if isinstance(items, list):
|
|
1025
|
-
compact = [{"chatId": t.get("chatId", t.get("id")),
|
|
1026
|
-
"agentName": t.get("agentName", ""),
|
|
1027
|
-
"touchPoint": t.get("touchPoint", t.get("touchpoint", "")),
|
|
1028
|
-
"handoff": t.get("handoff"), "messageCount": t.get("messageCount"),
|
|
1029
|
-
"externalId": t.get("externId", t.get("externalId", t.get("external_id"))),
|
|
1030
|
-
"createdAt": t.get("createdAt", "")} for t in items]
|
|
1031
|
-
if isinstance(result, dict):
|
|
1032
|
-
return {
|
|
1033
|
-
"pageIndex": result.get("pageIndex", args.page),
|
|
1034
|
-
"pageSize": result.get("pageSize", args.page_size),
|
|
1035
|
-
"hasNext": bool(result.get("hasNext")),
|
|
1036
|
-
"list": compact,
|
|
1037
|
-
}
|
|
1038
|
-
return compact
|
|
1039
|
-
return result
|
|
1040
|
-
|
|
1041
|
-
def resolve_transcript_chat_id():
|
|
1042
|
-
if not args.external_id:
|
|
1043
|
-
raise ValueError("resolve-transcript-chat-id requires --external-id")
|
|
1044
|
-
return client.resolve_transcript_chat_id(
|
|
1045
|
-
args.external_id,
|
|
1046
|
-
page_size=args.page_size,
|
|
1047
|
-
debug=args.debug,
|
|
1048
|
-
)
|
|
1049
|
-
|
|
1050
|
-
def summary_voice_agents():
|
|
1051
|
-
agents = client.list_voice_agents()
|
|
1052
|
-
if isinstance(agents, list):
|
|
1053
|
-
return [{"id": a.get("id"), "name": a.get("name"), "type": a.get("type"),
|
|
1054
|
-
"description": a.get("description", "")} for a in agents]
|
|
1055
|
-
return agents
|
|
1056
|
-
|
|
1057
|
-
def summary_voice_transcripts():
|
|
1058
|
-
result = client.list_voice_transcripts(
|
|
1059
|
-
page_num=args.page, page_size=args.page_size,
|
|
1060
|
-
begin_ts=args.begin_ts, end_ts=args.end_ts)
|
|
1061
|
-
items = result.get("records", result.get("list", result)) if isinstance(result, dict) else result
|
|
1062
|
-
if isinstance(items, list):
|
|
1063
|
-
compact = [{"callId": t.get("callId", t.get("chatId", "")),
|
|
1064
|
-
"agentId": t.get("agentId"),
|
|
1065
|
-
"callerNumber": t.get("callerNumber", ""),
|
|
1066
|
-
"callDuration": t.get("callDuration"),
|
|
1067
|
-
"endReason": t.get("endReason", ""),
|
|
1068
|
-
"createdAt": t.get("createdAt", "")} for t in items]
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
if isinstance(result, dict):
|
|
1074
|
-
return {"total": result.get("total", len(compact)), "list": compact}
|
|
1075
|
-
return compact
|
|
1076
|
-
return result
|
|
1077
|
-
|
|
1078
|
-
commands = {
|
|
1079
|
-
"get_project": lambda: client.get_project_info(),
|
|
1080
|
-
"replace_mock_data": replace_mock_data,
|
|
1081
|
-
"query_mock_data": query_mock_data,
|
|
1082
|
-
"list_agents": summary_agents,
|
|
1083
|
-
"get_agent": lambda: client.get_agent(args.id),
|
|
1084
|
-
"get_chatflow_graph": lambda: client.get_chatflow_graph(args.agent_id),
|
|
1085
|
-
"create_agent": lambda: client.create_agent(load_data()),
|
|
1086
|
-
"update_agent": lambda: client.update_agent(load_data()),
|
|
1087
|
-
"update_chatflow": update_chatflow,
|
|
1088
|
-
"list_plugins": summary_plugins,
|
|
1089
|
-
"get_plugin": lambda: client.get_plugin(args.id),
|
|
1090
|
-
"create_plugin": lambda: client.create_plugin(load_data()),
|
|
1091
|
-
"update_plugin": lambda: client.update_plugin(load_data()),
|
|
1092
|
-
"delete_plugin": lambda: client.delete_plugin(args.id),
|
|
1093
|
-
"run_plugin": run_plugin,
|
|
1094
|
-
"list_ai_fields": lambda: client.list_ai_fields(),
|
|
1095
|
-
"list_agent_ai_fields": lambda: client.list_agent_ai_fields(),
|
|
1096
|
-
"get_ai_field_guide": lambda: client.get_ai_field_guide_record(),
|
|
1097
|
-
"initialize_ai_fields": lambda: client.initialize_ai_fields(),
|
|
1098
|
-
"list_managed_ai_fields": lambda: client.list_managed_ai_fields(
|
|
1099
|
-
page_index=args.page, page_size=args.page_size, name=args.keyword),
|
|
1100
|
-
"get_managed_ai_field": lambda: client.get_managed_ai_field(args.id),
|
|
1101
|
-
"create_managed_ai_field": lambda: client.create_managed_ai_field(
|
|
1102
|
-
require_object_data("create-managed-ai-field")),
|
|
1103
|
-
"update_managed_ai_field": lambda: client.update_managed_ai_field(
|
|
1104
|
-
require_object_data("update-managed-ai-field")),
|
|
1105
|
-
"delete_managed_ai_field": lambda: client.delete_managed_ai_field(args.id),
|
|
1106
|
-
"toggle_managed_ai_field": lambda: client.toggle_managed_ai_field(args.id),
|
|
1107
|
-
"get_quality_status": lambda: client.get_quality_inspection_status(),
|
|
1108
|
-
"set_quality_status": set_quality_status,
|
|
1109
|
-
"list_quality_rules": lambda: client.list_quality_inspection_rules(
|
|
1110
|
-
begin_timestamp=args.begin_ts, end_timestamp=args.end_ts),
|
|
1111
|
-
"get_quality_rule": lambda: client.get_quality_inspection_rule(
|
|
1112
|
-
require_inspection_key("get-quality-rule")),
|
|
1113
|
-
"update_quality_rule": lambda: client.update_quality_inspection_rule(
|
|
1114
|
-
require_object_data("update-quality-rule")),
|
|
1115
|
-
"create_custom_quality_rule": lambda: client.create_custom_quality_rule(
|
|
1116
|
-
require_object_data("create-custom-quality-rule")),
|
|
1117
|
-
"get_custom_quality_rule": lambda: client.get_custom_quality_rule(
|
|
1118
|
-
require_inspection_key("get-custom-quality-rule")),
|
|
1119
|
-
"update_custom_quality_rule": lambda: client.update_custom_quality_rule(
|
|
1120
|
-
require_object_data("update-custom-quality-rule")),
|
|
1121
|
-
"delete_custom_quality_rule": lambda: client.delete_custom_quality_rule(
|
|
1122
|
-
require_inspection_key("delete-custom-quality-rule")),
|
|
1123
|
-
"list_quality_alert_rules": lambda: client.list_quality_alert_rules(),
|
|
1124
|
-
"get_quality_alert_rule": lambda: client.get_quality_alert_rule(args.id),
|
|
1125
|
-
"create_quality_alert_rule": lambda: client.create_quality_alert_rule(
|
|
1126
|
-
require_object_data("create-quality-alert-rule")),
|
|
1127
|
-
"update_quality_alert_rule": lambda: client.update_quality_alert_rule(
|
|
1128
|
-
require_object_data("update-quality-alert-rule")),
|
|
1129
|
-
"delete_quality_alert_rule": lambda: client.delete_quality_alert_rule(args.id),
|
|
1130
|
-
"list_quality_alert_messages": lambda: client.list_quality_alert_messages(
|
|
1131
|
-
require_object_data("list-quality-alert-messages")),
|
|
1132
|
-
"get_quality_alert_message": lambda: client.get_quality_alert_message(args.id),
|
|
1133
|
-
"chat": lambda: client.chat(
|
|
1134
|
-
message=args.message, bot_id=args.bot_id, chat_id=args.chat_id,
|
|
1135
|
-
touch_point=args.touch_point or "LIVE_CHAT",
|
|
1136
|
-
metadata=(load_data() or {}).get("metadata") if (args.data or args.data_file) else None),
|
|
1137
|
-
"chat_stream": lambda: client.chat_stream(
|
|
1138
|
-
message=args.message, bot_id=args.bot_id, chat_id=args.chat_id,
|
|
1139
|
-
touch_point=args.touch_point or "LIVE_CHAT",
|
|
1140
|
-
metadata=(load_data() or {}).get("metadata") if (args.data or args.data_file) else None),
|
|
1141
|
-
"chat_async": lambda: client.chat_async(
|
|
1142
|
-
message=args.message, bot_id=args.bot_id, chat_id=args.chat_id,
|
|
1143
|
-
touch_point=args.touch_point or "LIVE_CHAT",
|
|
1144
|
-
metadata=(load_data() or {}).get("metadata") if (args.data or args.data_file) else None,
|
|
1145
|
-
callback=args.callback, expire_minutes=args.expire_minutes),
|
|
1146
|
-
"get_async_result": lambda: client.get_async_result(
|
|
1147
|
-
args.receipt, bot_id=args.bot_id),
|
|
1148
|
-
"list_strategies": lambda: client.list_strategies(page_size=args.page_size),
|
|
1149
|
-
"list_templates": summary_templates,
|
|
1150
|
-
"create_template": create_template,
|
|
1151
|
-
"update_template": update_template,
|
|
1152
|
-
"delete_templates": delete_templates,
|
|
1153
|
-
"get_conversations_by_intent": lambda: client.get_conversations_by_intent(
|
|
1154
|
-
args.intent, limit=args.limit),
|
|
1155
|
-
"list_intents": summary_intents,
|
|
1156
|
-
"create_intent": lambda: client.create_intent(load_data()),
|
|
1157
|
-
"get_intent": lambda: client.get_intent(args.id),
|
|
1158
|
-
"update_intent": lambda: client.update_intent(args.id, load_data()),
|
|
1159
|
-
"toggle_intent": lambda: client.toggle_intent(
|
|
1160
|
-
args.id, enabled=not args.disable),
|
|
1161
|
-
"list_entities": lambda: client.list_entities(),
|
|
1162
|
-
"create_strategy": lambda: client.create_strategy(load_data()),
|
|
1163
|
-
"update_strategy": lambda: client.update_strategy(args.id, load_data()),
|
|
1164
|
-
"toggle_strategy": lambda: client.toggle_strategy(
|
|
1165
|
-
args.id, enabled=not args.disable),
|
|
1166
|
-
"list_knowledge": summary_knowledge,
|
|
1167
|
-
"get_knowledge": get_knowledge,
|
|
1168
|
-
"get_knowledge_isolation_keywords": get_knowledge_isolation_keywords,
|
|
1169
|
-
"update_knowledge": update_knowledge,
|
|
1170
|
-
"batch_update_knowledge": batch_update_knowledge,
|
|
1171
|
-
"update_document_content": update_document_content,
|
|
1172
|
-
"create_product_knowledge": create_product_knowledge,
|
|
1173
|
-
"update_product_knowledge": update_product_knowledge,
|
|
1174
|
-
"batch_update_products": batch_update_products,
|
|
1175
|
-
"operate_knowledge": operate_knowledge,
|
|
1176
|
-
"edit_knowledge_tags": edit_knowledge_tags,
|
|
1177
|
-
"list_knowledge_tags": summary_knowledge_tags,
|
|
1178
|
-
"upload_qa": lambda: client.upload_qa(load_data()),
|
|
1179
|
-
"upload_web": lambda: client.upload_web(args.urls or []),
|
|
1180
|
-
"list_directories": summary_directories,
|
|
1181
|
-
"create_folder": create_folder,
|
|
1182
|
-
"update_directory": update_directory,
|
|
1183
|
-
"move_directory": move_directory,
|
|
1184
|
-
"delete_directory": delete_directory,
|
|
1185
|
-
"list_isolation_keywords": summary_isolation_keywords,
|
|
1186
|
-
"create_isolation_keyword": create_isolation_keyword,
|
|
1187
|
-
"update_isolation_keyword": update_isolation_keyword,
|
|
1188
|
-
"delete_isolation_keyword": delete_isolation_keyword,
|
|
1189
|
-
"batch_update_isolation_keyword_bindings": batch_update_isolation_keyword_bindings,
|
|
1190
|
-
"list_transcripts": summary_transcripts,
|
|
1191
|
-
"get_transcript": lambda: client.get_transcript(args.chat_id),
|
|
1192
|
-
"resolve_transcript_chat_id": resolve_transcript_chat_id,
|
|
1193
|
-
"get_chat_context": lambda: client.get_chat_context(
|
|
1194
|
-
args.bot_id, json.loads(args.data) if args.data else {}
|
|
1195
|
-
),
|
|
1196
|
-
"export_transcripts": lambda: client.export_transcripts(
|
|
1197
|
-
page_size=args.page_size, page_index=args.page, debug=args.debug,
|
|
1198
|
-
begin_ts=args.begin_ts, end_ts=args.end_ts,
|
|
1199
|
-
time_offset=args.time_offset),
|
|
1200
|
-
"get_export_tasks": lambda: client.get_export_tasks(
|
|
1201
|
-
page=args.page, page_size=args.page_size),
|
|
1202
|
-
"upload_batch_test_file": lambda: client.upload_batch_test_file(args.file),
|
|
1203
|
-
"submit_batch_test": lambda: client.submit_batch_test(
|
|
1204
|
-
args.file_key,
|
|
1205
|
-
args.task_type,
|
|
1206
|
-
bot_id=args.bot_id,
|
|
1207
|
-
metadata=(load_data() or {}).get("metadata") if (args.data or args.data_file) else None,
|
|
1208
|
-
),
|
|
1209
|
-
"get_batch_test_tasks": lambda: client.get_label_tasks(
|
|
1210
|
-
page=args.page, page_size=args.page_size, task_type=args.task_type),
|
|
1211
|
-
"list_label_groups": lambda: client.list_label_groups(tree_name=args.name or args.keyword),
|
|
1212
|
-
"create_label_group": create_label_group,
|
|
1213
|
-
"update_label_group": update_label_group,
|
|
1214
|
-
"delete_label_group": lambda: client.delete_label_group(require_id("delete-label-group")),
|
|
1215
|
-
"get_label_tree": get_label_tree,
|
|
1216
|
-
"get_label_detail_tree": get_label_detail_tree,
|
|
1217
|
-
"create_label_node": create_label_node,
|
|
1218
|
-
"update_label_node": update_label_node,
|
|
1219
|
-
"delete_label_node": delete_label_node,
|
|
1220
|
-
"download_label_template": download_label_template,
|
|
1221
|
-
"download_labels": download_labels,
|
|
1222
|
-
"upload_labels": upload_labels,
|
|
1223
|
-
"report_transcript": lambda: client.report_transcript(
|
|
1224
|
-
args.begin_ts, args.end_ts, time_offset=args.time_offset,
|
|
1225
|
-
bot_id=args.bot_id, touch_point=args.touch_point,
|
|
1226
|
-
date_unit=args.date_unit),
|
|
1227
|
-
"report_message": lambda: client.report_message(
|
|
1228
|
-
args.begin_ts, args.end_ts, time_offset=args.time_offset,
|
|
1229
|
-
bot_id=args.bot_id, touch_point=args.touch_point,
|
|
1230
|
-
date_unit=args.date_unit),
|
|
1231
|
-
"report_agent": lambda: client.report_agent(
|
|
1232
|
-
args.begin_ts, args.end_ts, args.time_offset, args.bot_id, args.touch_point,
|
|
1233
|
-
args.date_unit, args.agent_id),
|
|
1234
|
-
"report_intent": lambda: client.report_intent(
|
|
1235
|
-
args.begin_ts, args.end_ts, time_offset=args.time_offset,
|
|
1236
|
-
bot_id=args.bot_id, touch_point=args.touch_point,
|
|
1237
|
-
intent_id=args.intent_id, date_unit=args.date_unit),
|
|
1238
|
-
"report_satisfaction": lambda: client.report_satisfaction_summary(
|
|
1239
|
-
args.begin_ts, args.end_ts, args.time_offset, args.bot_id, args.touch_point),
|
|
1240
|
-
"report_satisfaction_list": lambda: client.report_satisfaction_list(
|
|
1241
|
-
args.begin_ts, args.end_ts, args.time_offset, args.page, args.page_size,
|
|
1242
|
-
args.bot_id, args.touch_point, args.assistant_role,
|
|
1243
|
-
None if args.satisfied is None else args.satisfied == "true"),
|
|
1244
|
-
"report_knowledge": lambda: client.report_knowledge(
|
|
1245
|
-
args.begin_ts, args.end_ts, args.time_offset, args.page, args.page_size,
|
|
1246
|
-
json.loads(args.sort_by) if args.sort_by else None,
|
|
1247
|
-
args.bot_id, args.touch_point, args.source),
|
|
1248
|
-
"report_escalation": lambda: client.report_escalation_reasons(
|
|
1249
|
-
args.begin_ts, args.end_ts, args.time_offset,
|
|
1250
|
-
args.date_unit or "DAY", args.bot_id, args.touch_point),
|
|
1251
|
-
"report_livechat": lambda: client.report_livechat_summary(
|
|
1252
|
-
args.begin_ts, args.end_ts, args.time_offset),
|
|
1253
|
-
"report_livechat_table": lambda: client.report_livechat_table(
|
|
1254
|
-
args.begin_ts, args.end_ts, args.time_offset, args.page, args.page_size),
|
|
1255
|
-
"report_inspection": lambda: client.report_inspection(
|
|
1256
|
-
args.start_date, args.end_date, args.dimension),
|
|
1257
|
-
"report_inspection_history": lambda: client.report_inspection_history(
|
|
1258
|
-
args.start_date, args.end_date, args.dimension),
|
|
1259
|
-
"report_self_learning": lambda: client.report_self_learning(
|
|
1260
|
-
args.begin_ts, args.end_ts, args.time_offset, args.bot_id, args.touch_point),
|
|
1261
|
-
"report_feedback": lambda: client.report_feedback(
|
|
1262
|
-
args.begin_ts, args.end_ts, args.time_offset, args.bot_id, args.touch_point),
|
|
1263
|
-
# Modern /chatReport endpoints (the official Agent Analytics page)
|
|
1264
|
-
"chat_report_tickets": lambda: client.chat_report(
|
|
1265
|
-
"tickets", args.start_date, args.end_date, args.bot_id,
|
|
1266
|
-
args.touch_point or args.channel, args.intent_id, args.agent_id),
|
|
1267
|
-
"chat_report_messages": lambda: client.chat_report(
|
|
1268
|
-
"messages", args.start_date, args.end_date, args.bot_id,
|
|
1269
|
-
args.touch_point or args.channel, args.intent_id, args.agent_id),
|
|
1270
|
-
"chat_report_intents": lambda: client.chat_report(
|
|
1271
|
-
"intents", args.start_date, args.end_date, args.bot_id,
|
|
1272
|
-
args.touch_point or args.channel, args.intent_id, args.agent_id),
|
|
1273
|
-
"chat_report_agents": lambda: client.chat_report(
|
|
1274
|
-
"agents", args.start_date, args.end_date, args.bot_id,
|
|
1275
|
-
args.touch_point or args.channel, args.intent_id, args.agent_id),
|
|
1276
|
-
"chat_report_satisfaction": lambda: client.chat_report(
|
|
1277
|
-
"satisfaction", args.start_date, args.end_date, args.bot_id,
|
|
1278
|
-
args.touch_point or args.channel, args.intent_id, args.agent_id),
|
|
1279
|
-
"chat_report_first_response": lambda: client.chat_report(
|
|
1280
|
-
"first-response", args.start_date, args.end_date, args.bot_id,
|
|
1281
|
-
args.touch_point or args.channel, args.intent_id, args.agent_id),
|
|
1282
|
-
"chat_report_return_retention": lambda: client.chat_report(
|
|
1283
|
-
"return-retention", args.start_date, args.end_date, args.bot_id,
|
|
1284
|
-
args.touch_point or args.channel, args.intent_id, args.agent_id),
|
|
1285
|
-
"chat_report_inspection_results": lambda: client.chat_report(
|
|
1286
|
-
"inspection-results", args.start_date, args.end_date, args.bot_id,
|
|
1287
|
-
args.touch_point or args.channel, args.intent_id, args.agent_id),
|
|
1288
|
-
"chat_report_inbox_scripts": lambda: client.chat_report_inbox_scripts(),
|
|
1289
|
-
# Transitional /dashboard endpoints (kept for backwards compatibility)
|
|
1290
|
-
"dashboard_overview": lambda: client.dashboard(
|
|
1291
|
-
"overview", args.start_date, args.end_date, args.bot_id, args.channel,
|
|
1292
|
-
args.business_type, args.granularity, args.agent_id),
|
|
1293
|
-
"dashboard_message_trend": lambda: client.dashboard(
|
|
1294
|
-
"message-trend", args.start_date, args.end_date, args.bot_id, args.channel,
|
|
1295
|
-
args.business_type, args.granularity, args.agent_id),
|
|
1296
|
-
"dashboard_intent_distribution": lambda: client.dashboard(
|
|
1297
|
-
"intent-distribution", args.start_date, args.end_date, args.bot_id, args.channel,
|
|
1298
|
-
args.business_type, args.granularity, args.agent_id),
|
|
1299
|
-
"dashboard_agent_performance": lambda: client.dashboard(
|
|
1300
|
-
"agent-performance", args.start_date, args.end_date, args.bot_id, args.channel,
|
|
1301
|
-
args.business_type, args.granularity, args.agent_id),
|
|
1302
|
-
"dashboard_intent_trend": lambda: client.dashboard(
|
|
1303
|
-
"intent-trend", args.start_date, args.end_date, args.bot_id, args.channel,
|
|
1304
|
-
args.business_type, args.granularity, args.agent_id),
|
|
1305
|
-
"dashboard_agent_trend": lambda: client.dashboard(
|
|
1306
|
-
"agent-trend", args.start_date, args.end_date, args.bot_id, args.channel,
|
|
1307
|
-
args.business_type, args.granularity, args.agent_id),
|
|
1308
|
-
"dashboard_handoff_heatmap": lambda: client.dashboard(
|
|
1309
|
-
"handoff-heatmap", args.start_date, args.end_date, args.bot_id, args.channel,
|
|
1310
|
-
args.business_type, args.granularity, args.agent_id),
|
|
1311
|
-
"dashboard_turn_funnel": lambda: client.dashboard(
|
|
1312
|
-
"turn-funnel", args.start_date, args.end_date, args.bot_id, args.channel,
|
|
1313
|
-
args.business_type, args.granularity, args.agent_id),
|
|
1314
|
-
"dashboard_handoff_chat_ids": lambda: client.dashboard_handoff_chat_ids(
|
|
1315
|
-
args.week_start_date, args.primary_reason, args.secondary_reason, args.limit),
|
|
1316
|
-
"report_escalation_drilldown": lambda: client.report_escalation_drilldown(
|
|
1317
|
-
args.begin_ts, args.end_ts, args.reason, args.time_offset,
|
|
1318
|
-
args.page, args.page_size, args.bot_id, args.touch_point),
|
|
1319
|
-
"report_agent_weekly_graph": lambda: client.report_agent_weekly_graph(
|
|
1320
|
-
args.begin_ts, args.end_ts, args.time_offset),
|
|
1321
|
-
# Voice Agent
|
|
1322
|
-
"list_voice_agents": summary_voice_agents,
|
|
1323
|
-
"list_voice_transcripts": summary_voice_transcripts,
|
|
1324
|
-
"get_voice_transcript": lambda: client.get_voice_transcript(args.history_id),
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
if cmd not in commands:
|
|
1328
|
-
print(f"Unknown command: {args.command}")
|
|
1329
|
-
print(f"Available: {', '.join(k.replace('_', '-') for k in commands)}")
|
|
1330
|
-
sys.exit(1)
|
|
1331
|
-
|
|
1332
|
-
try:
|
|
1333
|
-
result = commands[cmd]()
|
|
1334
|
-
output(result)
|
|
1335
|
-
except Exception as e:
|
|
1336
|
-
print(f"Error: {e}", file=sys.stderr)
|
|
1337
|
-
sys.exit(1)
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
if __name__ == "__main__":
|
|
1341
|
-
main()
|