@tencent-rtc/trtc-agent-skills 0.1.5 → 0.1.7

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.
Files changed (103) hide show
  1. package/AGENTS.md +1 -1
  2. package/CLAUDE.md +1 -1
  3. package/CODEBUDDY.md +1 -1
  4. package/README.md +15 -70
  5. package/README.zh.md +15 -66
  6. package/bin/cli.js +9 -7
  7. package/package.json +3 -2
  8. package/skills/trtc/SKILL.md +56 -5
  9. package/skills/trtc/tests/test_reporting_v2_docs_query.py +175 -0
  10. package/skills/trtc/tools/reporting_v2.py +243 -5
  11. package/skills/trtc-ai-oral-coach/README.ja.md +131 -0
  12. package/skills/trtc-ai-oral-coach/README.md +132 -0
  13. package/skills/trtc-ai-oral-coach/README.zh-CN.md +131 -0
  14. package/skills/trtc-ai-oral-coach/SKILL.md +395 -0
  15. package/skills/trtc-ai-oral-coach/auto_adapters/integration_templates/custom-learning-kb-sample.md +88 -0
  16. package/skills/trtc-ai-oral-coach/auto_adapters/integration_templates/generic-integration.md +104 -0
  17. package/skills/trtc-ai-oral-coach/auto_adapters/manifest.yaml +27 -0
  18. package/skills/trtc-ai-oral-coach/auto_adapters/python/README.md +49 -0
  19. package/skills/trtc-ai-oral-coach/auto_adapters/python/coach_client.py.tpl +185 -0
  20. package/skills/trtc-ai-oral-coach/auto_adapters/web/README.md +46 -0
  21. package/skills/trtc-ai-oral-coach/auto_adapters/web/oral-coach-client.js.tpl +210 -0
  22. package/skills/trtc-ai-oral-coach/capabilities/ability-report/manifest.yaml +52 -0
  23. package/skills/trtc-ai-oral-coach/capabilities/ability-report/src/__init__.py +2 -0
  24. package/skills/trtc-ai-oral-coach/capabilities/ability-report/src/adapters/__init__.py +2 -0
  25. package/skills/trtc-ai-oral-coach/capabilities/ability-report/src/adapters/default.py +177 -0
  26. package/skills/trtc-ai-oral-coach/capabilities/ability-report/src/handler.py +29 -0
  27. package/skills/trtc-ai-oral-coach/capabilities/ability-report/src/router.py +23 -0
  28. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/.env.example +59 -0
  29. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/manifest.yaml +46 -0
  30. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/requirements.txt +6 -0
  31. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/TLSSigAPIv2.py +275 -0
  32. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/__init__.py +2 -0
  33. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/_capability_loader.py +227 -0
  34. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/agent.py +197 -0
  35. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/config.py +149 -0
  36. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/core/__init__.py +2 -0
  37. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/core/report_llm.py +129 -0
  38. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/health.py +36 -0
  39. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/server.py +205 -0
  40. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/trtc_client.py +86 -0
  41. package/skills/trtc-ai-oral-coach/capabilities/conversation-core/src/usersig.py +33 -0
  42. package/skills/trtc-ai-oral-coach/capabilities/custom-learning-kb/manifest.yaml +65 -0
  43. package/skills/trtc-ai-oral-coach/capabilities/custom-learning-kb/src/__init__.py +2 -0
  44. package/skills/trtc-ai-oral-coach/capabilities/custom-learning-kb/src/adapters/__init__.py +2 -0
  45. package/skills/trtc-ai-oral-coach/capabilities/custom-learning-kb/src/adapters/clients.py +126 -0
  46. package/skills/trtc-ai-oral-coach/capabilities/custom-learning-kb/src/ports.py +46 -0
  47. package/skills/trtc-ai-oral-coach/capabilities/custom-learning-kb/src/router.py +27 -0
  48. package/skills/trtc-ai-oral-coach/capabilities/quick-correct/manifest.yaml +48 -0
  49. package/skills/trtc-ai-oral-coach/capabilities/quick-correct/src/__init__.py +2 -0
  50. package/skills/trtc-ai-oral-coach/capabilities/quick-correct/src/adapters/__init__.py +2 -0
  51. package/skills/trtc-ai-oral-coach/capabilities/quick-correct/src/adapters/default.py +106 -0
  52. package/skills/trtc-ai-oral-coach/capabilities/quick-correct/src/handler.py +33 -0
  53. package/skills/trtc-ai-oral-coach/capabilities/quick-correct/src/router.py +23 -0
  54. package/skills/trtc-ai-oral-coach/capabilities/reply-suggestion/manifest.yaml +49 -0
  55. package/skills/trtc-ai-oral-coach/capabilities/reply-suggestion/src/__init__.py +2 -0
  56. package/skills/trtc-ai-oral-coach/capabilities/reply-suggestion/src/adapters/__init__.py +2 -0
  57. package/skills/trtc-ai-oral-coach/capabilities/reply-suggestion/src/adapters/default.py +86 -0
  58. package/skills/trtc-ai-oral-coach/capabilities/reply-suggestion/src/handler.py +29 -0
  59. package/skills/trtc-ai-oral-coach/capabilities/reply-suggestion/src/router.py +23 -0
  60. package/skills/trtc-ai-oral-coach/capabilities/scenario-roleplay/data/practice-scenarios.json +500 -0
  61. package/skills/trtc-ai-oral-coach/capabilities/scenario-roleplay/manifest.yaml +54 -0
  62. package/skills/trtc-ai-oral-coach/capabilities/scenario-roleplay/src/__init__.py +2 -0
  63. package/skills/trtc-ai-oral-coach/capabilities/scenario-roleplay/src/adapters/__init__.py +2 -0
  64. package/skills/trtc-ai-oral-coach/capabilities/scenario-roleplay/src/adapters/default.py +113 -0
  65. package/skills/trtc-ai-oral-coach/capabilities/scenario-roleplay/src/compose.py +143 -0
  66. package/skills/trtc-ai-oral-coach/capabilities/scenario-roleplay/src/defaults.py +140 -0
  67. package/skills/trtc-ai-oral-coach/capabilities/scenario-roleplay/src/handler.py +29 -0
  68. package/skills/trtc-ai-oral-coach/capabilities/scenario-roleplay/src/router.py +22 -0
  69. package/skills/trtc-ai-oral-coach/capabilities/scenario-roleplay/src/scenario_source.py +179 -0
  70. package/skills/trtc-ai-oral-coach/references/design-specs.md +36 -0
  71. package/skills/trtc-ai-oral-coach/references/evaluator-port.md +75 -0
  72. package/skills/trtc-ai-oral-coach/scenarios/speaking-coach/recipe.yaml +86 -0
  73. package/skills/trtc-ai-oral-coach/scenarios/speaking-coach/ui/avatars/friend.png +0 -0
  74. package/skills/trtc-ai-oral-coach/scenarios/speaking-coach/ui/avatars/listener.png +0 -0
  75. package/skills/trtc-ai-oral-coach/scenarios/speaking-coach/ui/avatars/local.png +0 -0
  76. package/skills/trtc-ai-oral-coach/scenarios/speaking-coach/ui/coach.html +1986 -0
  77. package/skills/trtc-ai-oral-coach/scenarios/speaking-coach/ui/i18n.js +323 -0
  78. package/skills/trtc-ai-oral-coach/scenarios/speaking-coach/ui/practice-scenarios.json +500 -0
  79. package/skills/trtc-ai-oral-coach/scenarios/speaking-coach/ui/tokens.css +100 -0
  80. package/skills/trtc-ai-oral-coach/scripts/add-capability.py +256 -0
  81. package/skills/trtc-ai-oral-coach/scripts/verify-credentials.py +96 -0
  82. package/skills/trtc-ai-oral-coach/start.sh +59 -0
  83. package/skills/trtc-ai-oral-coach/triggers.yaml +32 -0
  84. package/skills/trtc-ai-service/README.ja.md +27 -34
  85. package/skills/trtc-ai-service/README.md +27 -34
  86. package/skills/trtc-ai-service/README.zh-CN.md +27 -34
  87. package/skills/trtc-ai-service/SKILL.md +189 -121
  88. package/skills/trtc-chat/.docs-query.yaml +1 -0
  89. package/skills/trtc-chat/SKILL.md +1 -1
  90. package/skills/trtc-chat/docs/SKILL.md +2 -2
  91. package/skills/trtc-chat/references/05-path-d-script.md +213 -125
  92. package/skills/trtc-chat/references/08-state-config.md +3 -1
  93. package/skills/trtc-chat/references/13-reporting.md +47 -9
  94. package/skills/trtc-chat/tests/test_chat_bundle_contract.py +37 -0
  95. package/hooks/__pycache__/cursor-adapter.cpython-313.pyc +0 -0
  96. package/skills/trtc/hooks/__pycache__/report_prompt.cpython-313.pyc +0 -0
  97. package/skills/trtc/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  98. package/skills/trtc/tools/__pycache__/query_classifier.cpython-313.pyc +0 -0
  99. package/skills/trtc/tools/__pycache__/reporting.cpython-313.pyc +0 -0
  100. package/skills/trtc/tools/__pycache__/search.cpython-313.pyc +0 -0
  101. package/skills/trtc/tools/__pycache__/session.cpython-313.pyc +0 -0
  102. package/skills/trtc-conference/tests/__pycache__/test_conference_onboarding_contract.cpython-313-pytest-9.0.2.pyc +0 -0
  103. package/skills/trtc-conference/tests/__pycache__/test_conference_topic_flow_contract.cpython-313-pytest-9.0.2.pyc +0 -0
@@ -0,0 +1,49 @@
1
+ # Python Adapter — Oral Coach Client
2
+
3
+ > Auto-generated by ai-oral-coach auto_adapters/python.
4
+ > This adapter generates `coach_client.py` — a headless Python client for backend aggregation.
5
+
6
+ ## What's generated
7
+
8
+ | File | Contents |
9
+ |:---|:---|
10
+ | `coach_client.py` | `CoachClient` class — full API wrapper (config / agent control / coach capabilities / KB) |
11
+
12
+ ## Usage
13
+
14
+ ```python
15
+ from coach_client import CoachClient
16
+
17
+ # Initialize
18
+ coach = CoachClient(base_url="http://localhost:8000")
19
+
20
+ # Health check
21
+ coach.health() # → { status: "ok", tencent_cloud: ok, trtc: ok, llm: ok }
22
+
23
+ # Get TRTC credentials (your frontend uses these to enter room)
24
+ config = coach.get_config("student_001")
25
+ # → { sdkAppId, roomId, userId, userSig, agentUserId, ... }
26
+
27
+ # Start agent (after frontend enters room)
28
+ result = coach.start_agent(
29
+ room_id=config["roomId"],
30
+ scenario="travel",
31
+ level="intermediate",
32
+ style="friend",
33
+ agent_config=config.get("agentConfig"),
34
+ )
35
+
36
+ # Coach capabilities (call from your backend as needed)
37
+ correction = coach.correct("I go to park yesterday")
38
+ suggestions = coach.suggest("What did you do last weekend?")
39
+ report = coach.generate_report(transcript=[...], duration_sec=120)
40
+
41
+ # Graceful end
42
+ coach.farewell(lang="en")
43
+ ```
44
+
45
+ ## Dependencies
46
+
47
+ ```bash
48
+ pip install requests
49
+ ```
@@ -0,0 +1,185 @@
1
+ # coach_client.py
2
+ # Auto-generated by ai-oral-coach auto_adapters/python
3
+ # Python client wrapper — full oral coach API coverage for backend aggregation.
4
+ #
5
+ # Usage:
6
+ # from coach_client import CoachClient
7
+ # coach = CoachClient(base_url="http://localhost:8000")
8
+ # health = coach.health()
9
+ # config = coach.get_config("student_001")
10
+ # result = coach.start_agent(room_id="...", scenario="travel", level="intermediate", style="friend", agent_config=config["agent_config"])
11
+
12
+ from __future__ import annotations
13
+
14
+ import json
15
+ from typing import Any, Dict, List, Optional
16
+
17
+ import requests
18
+
19
+
20
+ DEFAULT_BASE_URL = "${SKELETON_BASE_URL}"
21
+ API_PREFIX = "${API_PREFIX}"
22
+
23
+
24
+ class CoachClient:
25
+ """Headless client for the AI Oral Coach backend.
26
+
27
+ All coach capabilities (correct / suggest / report / scene) are exposed as
28
+ methods. TRTC room entry is handled by your own TRTC SDK — this client
29
+ provides the credentials and control-plane API.
30
+ """
31
+
32
+ def __init__(self, base_url: str = DEFAULT_BASE_URL, api_key: Optional[str] = None):
33
+ self.base_url = base_url.rstrip("/")
34
+ self.api_key = api_key
35
+ self.session = requests.Session()
36
+ if api_key:
37
+ self.session.headers["Authorization"] = f"Bearer {api_key}"
38
+ self.session.headers["Content-Type"] = "application/json"
39
+ self.task_id: Optional[str] = None
40
+
41
+ # ─── HTTP helpers ──────────────────────────────────────────────
42
+
43
+ def _post(self, path: str, body: dict = None) -> dict:
44
+ url = f"{self.base_url}{API_PREFIX}{path}"
45
+ resp = self.session.post(url, json=body or {}, timeout=(5, 30))
46
+ resp.raise_for_status()
47
+ return resp.json()
48
+
49
+ def _get(self, path: str) -> dict:
50
+ url = f"{self.base_url}{API_PREFIX}{path}"
51
+ resp = self.session.get(url, timeout=5)
52
+ resp.raise_for_status()
53
+ return resp.json()
54
+
55
+ # ─── Core ──────────────────────────────────────────────────────
56
+
57
+ def health(self) -> dict:
58
+ """Health check — returns { status, tencent_cloud, trtc, llm }."""
59
+ return self._get("/health")
60
+
61
+ def get_config(self, user_id: str) -> dict:
62
+ """Get UserSig + room credentials. Call before entering TRTC room."""
63
+ return self._post("/config", {"userid": user_id})
64
+
65
+ # ─── Agent Control ─────────────────────────────────────────────
66
+
67
+ def start_agent(
68
+ self,
69
+ room_id: str,
70
+ scenario: str = "travel",
71
+ level: str = "intermediate",
72
+ style: str = "friend",
73
+ agent_config: Optional[dict] = None,
74
+ ) -> dict:
75
+ """Start the voice coach agent (requires room already joined via TRTC SDK)."""
76
+ body: Dict[str, Any] = {
77
+ "RoomId": room_id,
78
+ "Scenario": scenario,
79
+ "Level": level,
80
+ "Style": style,
81
+ }
82
+ if agent_config:
83
+ body["AgentConfig"] = agent_config
84
+ data = self._post("/agent/start", body)
85
+ self.task_id = data.get("TaskId") or data.get("task_id")
86
+ return data
87
+
88
+ def stop_agent(self) -> dict:
89
+ """Stop the current agent session."""
90
+ return self._post("/agent/stop", {"TaskId": self.task_id})
91
+
92
+ def farewell(self, lang: str = "en", text: str = None) -> dict:
93
+ """Graceful farewell — triggers closing remarks + auto-stop."""
94
+ body: Dict[str, Any] = {"TaskId": self.task_id, "Lang": lang}
95
+ if text:
96
+ body["FarewellText"] = text
97
+ return self._post("/agent/farewell", body)
98
+
99
+ def invoke(self, text: str = "") -> dict:
100
+ """Push-to-talk: manually trigger AI reply."""
101
+ return self._post("/agent/invoke", {"TaskId": self.task_id, "Text": text})
102
+
103
+ # ─── Coach Capabilities ────────────────────────────────────────
104
+
105
+ def generate_scene(
106
+ self,
107
+ field: str,
108
+ scenario: str = "travel",
109
+ level: str = "intermediate",
110
+ style: str = "friend",
111
+ language: str = "en",
112
+ context: dict = None,
113
+ ) -> dict:
114
+ """Generate a random scene element (aiRole / myRole / scene)."""
115
+ body = {
116
+ "Field": field,
117
+ "Scenario": scenario,
118
+ "Level": level,
119
+ "Style": style,
120
+ "Language": language,
121
+ }
122
+ if context:
123
+ body["Context"] = context
124
+ return self._post("/scene/generate", body)
125
+
126
+ def correct(
127
+ self,
128
+ sentence: str,
129
+ scenario: str = "travel",
130
+ level: str = "intermediate",
131
+ ui_language: str = "en",
132
+ ) -> dict:
133
+ """Correct a single spoken sentence — returns corrected, explanation, grammar tips."""
134
+ return self._post("/correct", {
135
+ "UserSentence": sentence,
136
+ "Scenario": scenario,
137
+ "Level": level,
138
+ "UILanguage": ui_language,
139
+ })
140
+
141
+ def suggest(
142
+ self,
143
+ last_message: str,
144
+ scenario: str = "travel",
145
+ level: str = "intermediate",
146
+ style: str = "friend",
147
+ ) -> dict:
148
+ """Get 3 reply suggestions based on AI's last message."""
149
+ return self._post("/suggest", {
150
+ "AiLastMessage": last_message,
151
+ "Scenario": scenario,
152
+ "Level": level,
153
+ "Style": style,
154
+ })
155
+
156
+ def generate_report(
157
+ self,
158
+ transcript: List[dict],
159
+ duration_sec: int,
160
+ scenario: str = "travel",
161
+ level: str = "intermediate",
162
+ style: str = "friend",
163
+ language: str = "en",
164
+ ) -> dict:
165
+ """Generate a 4-dimension analysis report.
166
+
167
+ Args:
168
+ transcript: [{speaker, text, timestamp}, ...]
169
+ duration_sec: Session duration in seconds
170
+ language: en | zh
171
+ """
172
+ return self._post("/report", {
173
+ "Scenario": scenario,
174
+ "Level": level,
175
+ "Style": style,
176
+ "DurationSec": duration_sec,
177
+ "Transcript": transcript,
178
+ "Language": language,
179
+ })
180
+
181
+ # ─── Knowledge Base (optional) ─────────────────────────────────
182
+
183
+ def retrieve_kb(self, query: str, top_k: int = 3) -> dict:
184
+ """Retrieve KB segments (only works if custom-learning-kb is configured)."""
185
+ return self._post("/kb/retrieve", {"query": query, "top_k": top_k})
@@ -0,0 +1,46 @@
1
+ # Web Adapter — Oral Coach Headless Client
2
+
3
+ > Auto-generated by ai-oral-coach auto_adapters/web.
4
+ > This adapter generates `oral-coach-client.js` — a **headless** client module.
5
+ > It handles HTTP API calls and TRTC custom message routing. No UI components.
6
+
7
+ ## What's generated
8
+
9
+ | File | Contents |
10
+ |:---|:---|
11
+ | `oral-coach-client.js` | `OralCoachClient` class — full API wrapper (config / agent control / coach capabilities / KB) |
12
+
13
+ ## What you need to provide
14
+
15
+ - **TRTC Web SDK** (`trtc-sdk-v5` or `trtc-js-sdk`): room entry, audio publish, custom message handling
16
+ - **Your UI layer**: render subtitles, AI status, report, suggestions — the client provides data via callbacks
17
+
18
+ ## Integration flow
19
+
20
+ ```
21
+ 1. coach = new OralCoachClient({ userId: '...' })
22
+ 2. config = await coach.getConfig() # GET /api/v1/config → UserSig, roomId
23
+ 3. Your code: use TRTC SDK to enter room with config
24
+ 4. coach.start({ roomId, scenario, level, style, agentConfig }) # POST /api/v1/agent/start
25
+ 5. Practice loop:
26
+ - coach.correct(sentence) # POST /api/v1/correct
27
+ - coach.suggest(lastAIMessage) # POST /api/v1/suggest
28
+ - coach.invoke(text) # POST /api/v1/agent/invoke (push-to-talk)
29
+ 6. coach.generateReport({ transcript, durationSec }) # POST /api/v1/report
30
+ 7. coach.farewell() # POST /api/v1/agent/farewell
31
+
32
+ TRTC hooks:
33
+ onCustomMessage(cmd, data) → coach.handleTRTCMessage(cmd, data)
34
+ ```
35
+
36
+ ## Callback hooks (override in your UI)
37
+
38
+ ```js
39
+ coach.onSubtitle = (text) => { /* render subtitle in your UI */ };
40
+ coach.onAIStatus = (status) => { /* show listening/speaking/thinking */ };
41
+ coach.onCorrectReady = (result) => { /* display correction card */ };
42
+ coach.onSuggestReady = (hints) => { /* display suggestion chips */ };
43
+ coach.onReportReady = (report) => { /* render 4-dimension report */ };
44
+ coach.onSceneReady = (scene) => { /* update setup screen */ };
45
+ coach.onError = (err) => { /* handle errors gracefully */ };
46
+ ```
@@ -0,0 +1,210 @@
1
+ // oral-coach-client.js
2
+ // Auto-generated by ai-oral-coach auto_adapters/web
3
+ // Headless client — your UI, our APIs. No UI components emitted.
4
+ //
5
+ // Usage:
6
+ // import { OralCoachClient } from './oral-coach-client.js';
7
+ // const coach = new OralCoachClient({ baseUrl: '${SKELETON_BASE_URL}', userId: 'student_001' });
8
+ // await coach.init();
9
+ // const { config } = await coach.getConfig();
10
+ // // ... use TRTC SDK to enter room with config ...
11
+ // await coach.start({ scenario: 'travel', level: 'intermediate', style: 'friend', roomId: config.roomId, agentConfig: config.agentConfig });
12
+ // // ... coach callbacks fire: onSubtitle / onAIStatus ...
13
+
14
+ const DEFAULT_BASE_URL = '${SKELETON_BASE_URL}';
15
+ const API_PREFIX = '${API_PREFIX}';
16
+
17
+ class OralCoachClient {
18
+ /**
19
+ * @param {Object} opts
20
+ * @param {string} [opts.baseUrl] Coach backend URL
21
+ * @param {string} opts.userId Unique user identifier
22
+ * @param {Object} [opts.trtc] TRTC SDK instance (if you manage it yourself)
23
+ */
24
+ constructor(opts = {}) {
25
+ this.baseUrl = (opts.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, '');
26
+ this.userId = opts.userId || `user_${Date.now()}`;
27
+ this.taskId = null;
28
+ this.sessionId = null;
29
+ this._config = null;
30
+
31
+ // Callback hooks — override these in your UI layer
32
+ this.onSubtitle = (text) => {}; // CMD 10000: AI speech text
33
+ this.onAIStatus = (status) => {}; // CMD 10001: listening | speaking | thinking
34
+ this.onError = (err) => { console.error('[OralCoach]', err); };
35
+ this.onReportReady = (report) => {}; // After report generated
36
+ this.onCorrectReady = (result) => {}; // After quick-correct
37
+ this.onSuggestReady = (hints) => {}; // After reply-suggestion
38
+ this.onSceneReady = (scene) => {}; // After scene/generate
39
+ }
40
+
41
+ // ─── HTTP helpers ──────────────────────────────────────────
42
+
43
+ async _post(path, body = {}) {
44
+ const url = `${this.baseUrl}${API_PREFIX}${path}`;
45
+ const resp = await fetch(url, {
46
+ method: 'POST',
47
+ headers: { 'Content-Type': 'application/json' },
48
+ body: JSON.stringify(body),
49
+ });
50
+ if (!resp.ok) {
51
+ const err = await resp.text().catch(() => resp.statusText);
52
+ throw new Error(`POST ${path}: ${resp.status} ${err}`);
53
+ }
54
+ return resp.json();
55
+ }
56
+
57
+ async _get(path) {
58
+ const url = `${this.baseUrl}${API_PREFIX}${path}`;
59
+ const resp = await fetch(url);
60
+ if (!resp.ok) throw new Error(`GET ${path}: ${resp.status}`);
61
+ return resp.json();
62
+ }
63
+
64
+ // ─── Core: Config & Health ──────────────────────────────────
65
+
66
+ /** Get UserSig + room credentials. Call once before entering room. */
67
+ async getConfig() {
68
+ const data = await this._post('/config', { userid: this.userId });
69
+ this._config = data;
70
+ return data;
71
+ }
72
+
73
+ /** Health check — returns { status, tencent_cloud, trtc, llm } */
74
+ async health() {
75
+ return this._get('/health');
76
+ }
77
+
78
+ // ─── Agent Control ──────────────────────────────────────────
79
+
80
+ /**
81
+ * Start the voice coach agent (requires room already joined via TRTC SDK).
82
+ * @param {Object} params
83
+ * @param {string} params.roomId TRTC room ID
84
+ * @param {string} params.scenario travel | work | study | free
85
+ * @param {string} params.level beginner | intermediate | advanced
86
+ * @param {string} params.style friend | listener
87
+ * @param {Object} params.agentConfig { UserId, UserSig, TargetUserId }
88
+ */
89
+ async start({ roomId, scenario, level, style, agentConfig }) {
90
+ const data = await this._post('/agent/start', {
91
+ RoomId: roomId,
92
+ Scenario: scenario,
93
+ Level: level,
94
+ Style: style,
95
+ AgentConfig: agentConfig,
96
+ });
97
+ this.taskId = data.TaskId || data.task_id;
98
+ this.sessionId = data.SessionId || data.session_id;
99
+ return data;
100
+ }
101
+
102
+ /** Stop the current agent session. */
103
+ async stop() {
104
+ return this._post('/agent/stop', { TaskId: this.taskId });
105
+ }
106
+
107
+ /** Graceful farewell — triggers closing remarks + auto-stop. */
108
+ async farewell(lang = 'en') {
109
+ return this._post('/agent/farewell', { TaskId: this.taskId, Lang: lang });
110
+ }
111
+
112
+ /** Push-to-talk: manually trigger AI reply (no voice input needed). */
113
+ async invoke(text) {
114
+ return this._post('/agent/invoke', { TaskId: this.taskId, Text: text || '' });
115
+ }
116
+
117
+ // ─── Coach Capabilities ─────────────────────────────────────
118
+
119
+ /**
120
+ * Generate a random scene element (aiRole / myRole / scene description).
121
+ * @param {string} field aiRole | myRole | scene
122
+ * @param {Object} context { Scenario, Level, Style, Language }
123
+ */
124
+ async generateScene(field, context = {}) {
125
+ const data = await this._post('/scene/generate', { Field: field, ...context });
126
+ this.onSceneReady(data);
127
+ return data;
128
+ }
129
+
130
+ /**
131
+ * Correct a single spoken sentence.
132
+ * @param {string} sentence The user's spoken sentence
133
+ * @param {Object} context { Scenario, Level, UILanguage? }
134
+ */
135
+ async correct(sentence, context = {}) {
136
+ const data = await this._post('/correct', {
137
+ UserSentence: sentence,
138
+ Scenario: context.Scenario || 'travel',
139
+ Level: context.Level || 'intermediate',
140
+ UILanguage: context.UILanguage || 'en',
141
+ });
142
+ this.onCorrectReady(data);
143
+ return data;
144
+ }
145
+
146
+ /**
147
+ * Get 3 reply suggestions based on AI's last message.
148
+ * @param {string} lastMessage AI's most recent utterance
149
+ * @param {Object} context { Scenario, Level, Style }
150
+ */
151
+ async suggest(lastMessage, context = {}) {
152
+ const data = await this._post('/suggest', {
153
+ AiLastMessage: lastMessage,
154
+ Scenario: context.Scenario || 'travel',
155
+ Level: context.Level || 'intermediate',
156
+ Style: context.Style || 'friend',
157
+ });
158
+ this.onSuggestReady(data);
159
+ return data;
160
+ }
161
+
162
+ /**
163
+ * Generate a 4-dimension analysis report.
164
+ * @param {Object} params
165
+ * @param {Array} params.transcript [{speaker, text, timestamp}]
166
+ * @param {number} params.durationSec Session duration in seconds
167
+ * @param {string} params.level beginner | intermediate | advanced
168
+ * @param {string} params.style friend | listener
169
+ * @param {string} params.scenario travel | work | study | free
170
+ * @param {string} params.language en | zh
171
+ */
172
+ async generateReport({ transcript, durationSec, level, style, scenario, language = 'en' }) {
173
+ const data = await this._post('/report', {
174
+ Scenario: scenario || 'travel',
175
+ Level: level || 'intermediate',
176
+ Style: style || 'friend',
177
+ DurationSec: durationSec,
178
+ Transcript: transcript,
179
+ Language: language,
180
+ });
181
+ this.onReportReady(data);
182
+ return data;
183
+ }
184
+
185
+ // ─── Knowledge Base (optional) ──────────────────────────────
186
+
187
+ /** Retrieve KB segments (only works if custom-learning-kb is configured). */
188
+ async retrieveKb(query, topK = 3) {
189
+ return this._post('/kb/retrieve', { query, top_k: topK });
190
+ }
191
+
192
+ // ─── TRTC Custom Message Handler ────────────────────────────
193
+ // Call this from your TRTC SDK onCustomMessage callback.
194
+
195
+ handleTRTCMessage(cmd, data) {
196
+ switch (cmd) {
197
+ case 10000: // Subtitle
198
+ this.onSubtitle(typeof data === 'string' ? data : data?.text || '');
199
+ break;
200
+ case 10001: // AI Status
201
+ this.onAIStatus(typeof data === 'string' ? data : data?.status || '');
202
+ break;
203
+ default:
204
+ break;
205
+ }
206
+ }
207
+ }
208
+
209
+ export { OralCoachClient };
210
+ export default OralCoachClient;
@@ -0,0 +1,52 @@
1
+ # ability-report —— 4 维能力分析报告
2
+ name: ability-report
3
+ version: "1.0.0"
4
+ type: capability
5
+ description: "练习结束时,把整段 transcript 生成 4 维能力分析(流利度/词汇/语法/完整度)+ 高光句 + 替代表达 + 下一步建议。双语并行。"
6
+
7
+ dependencies:
8
+ - { name: conversation-core, version: ">=1.0.0,<2.0.0" }
9
+
10
+ extensions:
11
+ - inject_at: server.router_extension
12
+ inline_code: |
13
+ from ._capability_loader import try_load_capability as _try
14
+ _m = _try("ability-report", "src/router.py")
15
+ if _m is not None and hasattr(_m, "router"):
16
+ app.include_router(_m.router, prefix="/api/v1/report", tags=["ability-report"])
17
+
18
+ config:
19
+ max_transcript_turns: { default: 200 } # 上限,防滥用
20
+ languages: { default: ["en", "zh"] } # 双语并行生成
21
+ adapter: { default: "default", env: "AR_ADAPTER" }
22
+
23
+ endpoints:
24
+ - method: POST
25
+ path: /api/v1/report
26
+ description: "提交 transcript → 返回 4 维能力报告({Reports:{en,zh}})"
27
+
28
+ business_contract:
29
+ port_class: "src.ports.evaluator.Evaluator"
30
+ default_adapter: "src.adapters.default.LLMEvaluator"
31
+ customization_sop: "references/evaluator-port.md#1-evaluator-port"
32
+ external_apis:
33
+ - name: report.generate
34
+ direction: inbound
35
+ method: POST
36
+ path: /api/v1/report
37
+ request_schema: { scenario: string, level: string, style: string, durationSec: int, transcript: "array", language: string }
38
+ # 自由文本字段为目标语言;引用学习者原话保留英文
39
+ response_schema:
40
+ Reports:
41
+ en: "{ summary, abilityAnalysis{fluency,vocabulary,grammar,completeness}, mistakes[], betterExpressions[], nextSuggestions[] }"
42
+ zh: "{ 同 en 结构 }"
43
+
44
+ integration:
45
+ auto_adapters:
46
+ - { tech_stack: ["react","vue","angular"], adapter: frontend-spa }
47
+ - { tech_stack: ["express","next"], adapter: node-backend }
48
+ - { tech_stack: ["flask","fastapi"], adapter: python-backend }
49
+
50
+ acceptance:
51
+ - "提交 transcript 返回 4 维能力分析,无数字评分"
52
+ - "JSON 解析失败时走兜底骨架(至少 summary 不为空)"
@@ -0,0 +1,2 @@
1
+ # -*- coding: utf-8 -*-
2
+ """ability-report 能力包。"""
@@ -0,0 +1,2 @@
1
+ # -*- coding: utf-8 -*-
2
+ """adapters 子包。"""