@rookiestar/eng-lang-tutor 1.0.2 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of @rookiestar/eng-lang-tutor might be problematic. Click here for more details.
- package/CLAUDE.md +5 -4
- package/README.md +16 -1
- package/README_EN.md +16 -1
- package/SKILL.md +113 -92
- package/docs/OPENCLAW_DEPLOYMENT.md +7 -4
- package/package.json +1 -1
- package/requirements.txt +2 -0
- package/scripts/__pycache__/audio_composer.cpython-313.pyc +0 -0
- package/scripts/__pycache__/audio_converter.cpython-313.pyc +0 -0
- package/scripts/__pycache__/audio_enhancer.cpython-313.pyc +0 -0
- package/scripts/__pycache__/state_manager.cpython-313.pyc +0 -0
- package/scripts/__pycache__/utils.cpython-313.pyc +0 -0
- package/scripts/audio_composer.py +389 -0
- package/scripts/audio_converter.py +245 -0
- package/scripts/cli.py +223 -0
- package/scripts/constants.py +56 -0
- package/scripts/feishu_voice.py +421 -0
- package/scripts/gamification.py +48 -34
- package/scripts/scorer.py +14 -32
- package/scripts/state_manager.py +131 -207
- package/scripts/tts/__pycache__/__init__.cpython-313.pyc +0 -0
- package/scripts/tts/__pycache__/base.cpython-313.pyc +0 -0
- package/scripts/tts/__pycache__/manager.cpython-313.pyc +0 -0
- package/scripts/tts/manager.py +14 -4
- package/scripts/tts/providers/__pycache__/__init__.cpython-313.pyc +0 -0
- package/scripts/tts/providers/__pycache__/xunfei.cpython-313.pyc +0 -0
- package/scripts/tts/providers/xunfei.py +10 -1
- package/scripts/utils.py +78 -0
- package/templates/prompt_templates.md +47 -1235
- package/templates/prompts/display_guide.md +106 -0
- package/templates/prompts/initialization.md +213 -0
- package/templates/prompts/keypoint_generation.md +268 -0
- package/templates/prompts/output_rules.md +106 -0
- package/templates/prompts/quiz_generation.md +187 -0
- package/templates/prompts/responses.md +291 -0
- package/templates/prompts/shared_enums.md +97 -0
- package/templates/state_schema.json +1 -6
package/scripts/tts/manager.py
CHANGED
|
@@ -27,9 +27,14 @@ from pathlib import Path
|
|
|
27
27
|
from typing import Dict, Any, Optional, Type, ClassVar
|
|
28
28
|
from datetime import date, datetime
|
|
29
29
|
import os
|
|
30
|
+
import sys
|
|
31
|
+
|
|
32
|
+
# 添加 scripts 目录到路径以导入 state_manager
|
|
33
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
|
30
34
|
|
|
31
35
|
from .base import TTSProvider, TTSConfig, TTSResult
|
|
32
36
|
from .providers.xunfei import XunFeiProvider
|
|
37
|
+
from state_manager import get_default_state_dir
|
|
33
38
|
|
|
34
39
|
|
|
35
40
|
# Provider 注册表
|
|
@@ -57,7 +62,7 @@ class TTSManager:
|
|
|
57
62
|
def __init__(
|
|
58
63
|
self,
|
|
59
64
|
provider: str = "xunfei",
|
|
60
|
-
data_dir: str =
|
|
65
|
+
data_dir: str = None,
|
|
61
66
|
config: Optional[TTSConfig] = None,
|
|
62
67
|
**credentials
|
|
63
68
|
):
|
|
@@ -66,12 +71,12 @@ class TTSManager:
|
|
|
66
71
|
|
|
67
72
|
Args:
|
|
68
73
|
provider: Provider 名称(目前仅支持 "xunfei")
|
|
69
|
-
data_dir:
|
|
74
|
+
data_dir: 数据目录(默认使用 OPENCLAW_STATE_DIR 或 ~/.openclaw/state/eng-lang-tutor/)
|
|
70
75
|
config: TTS 配置
|
|
71
76
|
**credentials: Provider 认证信息
|
|
72
77
|
|
|
73
78
|
示例:
|
|
74
|
-
#
|
|
79
|
+
# 讯飞(使用默认数据目录)
|
|
75
80
|
manager = TTSManager(provider="xunfei")
|
|
76
81
|
|
|
77
82
|
# 讯飞(直接传入密钥)
|
|
@@ -88,7 +93,12 @@ class TTSManager:
|
|
|
88
93
|
f"Available: {list(PROVIDERS.keys())}"
|
|
89
94
|
)
|
|
90
95
|
|
|
91
|
-
|
|
96
|
+
# 使用与 StateManager 相同的默认目录逻辑
|
|
97
|
+
if data_dir is None:
|
|
98
|
+
self.data_dir = get_default_state_dir()
|
|
99
|
+
else:
|
|
100
|
+
self.data_dir = Path(data_dir)
|
|
101
|
+
|
|
92
102
|
self.audio_dir = self.data_dir / "audio"
|
|
93
103
|
self.audio_dir.mkdir(parents=True, exist_ok=True)
|
|
94
104
|
|
|
Binary file
|
|
Binary file
|
|
@@ -16,6 +16,8 @@ import base64
|
|
|
16
16
|
import hmac
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
|
+
import ssl
|
|
20
|
+
import certifi
|
|
19
21
|
from pathlib import Path
|
|
20
22
|
from typing import Optional, ClassVar, Dict
|
|
21
23
|
from urllib.parse import urlencode
|
|
@@ -132,8 +134,12 @@ class XunFeiProvider(TTSProvider):
|
|
|
132
134
|
data = json.loads(message)
|
|
133
135
|
if data.get("code") == 0:
|
|
134
136
|
audio = data.get("data", {}).get("audio", "")
|
|
137
|
+
status = data.get("data", {}).get("status", 0)
|
|
135
138
|
if audio:
|
|
136
139
|
audio_data.extend(base64.b64decode(audio))
|
|
140
|
+
# status=2 表示合成完成,关闭连接
|
|
141
|
+
if status == 2:
|
|
142
|
+
ws.close()
|
|
137
143
|
else:
|
|
138
144
|
error_msg = f"XunFei API error: code={data.get('code')}, message={data.get('message')}"
|
|
139
145
|
except json.JSONDecodeError as e:
|
|
@@ -170,7 +176,10 @@ class XunFeiProvider(TTSProvider):
|
|
|
170
176
|
on_error=on_error,
|
|
171
177
|
)
|
|
172
178
|
ws.on_open = on_open
|
|
173
|
-
|
|
179
|
+
# 使用 certifi 提供的 SSL 证书
|
|
180
|
+
ws.run_forever(
|
|
181
|
+
sslopt={"cert_reqs": ssl.CERT_REQUIRED, "ca_certs": certifi.where()}
|
|
182
|
+
)
|
|
174
183
|
|
|
175
184
|
if error_msg:
|
|
176
185
|
return TTSResult(success=False, error_message=error_msg)
|
package/scripts/utils.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Utility functions for eng-lang-tutor.
|
|
4
|
+
|
|
5
|
+
Common utilities used across multiple modules.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Dict, Any
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def safe_divide(numerator: float, denominator: float, default: float = 0.0) -> float:
|
|
12
|
+
"""
|
|
13
|
+
Safe division that returns default if denominator is zero.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
numerator: The number to divide
|
|
17
|
+
denominator: The number to divide by
|
|
18
|
+
default: Value to return if denominator is zero
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Result of division, or default if denominator is zero
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
>>> safe_divide(10, 2)
|
|
25
|
+
5.0
|
|
26
|
+
>>> safe_divide(10, 0)
|
|
27
|
+
0.0
|
|
28
|
+
>>> safe_divide(10, 0, default=100)
|
|
29
|
+
100.0
|
|
30
|
+
"""
|
|
31
|
+
return numerator / denominator if denominator != 0 else default
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def deep_merge(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
|
|
35
|
+
"""
|
|
36
|
+
Recursively merge override dictionary into base dictionary.
|
|
37
|
+
|
|
38
|
+
Creates a new dictionary with values from base, updated with values from override.
|
|
39
|
+
Nested dictionaries are merged recursively; other values are overwritten.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
base: Base dictionary (not modified)
|
|
43
|
+
override: Dictionary with values to override/add
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
New merged dictionary
|
|
47
|
+
|
|
48
|
+
Examples:
|
|
49
|
+
>>> base = {'a': 1, 'b': {'c': 2, 'd': 3}}
|
|
50
|
+
>>> override = {'b': {'c': 10}}
|
|
51
|
+
>>> deep_merge(base, override)
|
|
52
|
+
{'a': 1, 'b': {'c': 10, 'd': 3}}
|
|
53
|
+
"""
|
|
54
|
+
import copy
|
|
55
|
+
result = copy.deepcopy(base)
|
|
56
|
+
|
|
57
|
+
for key, value in override.items():
|
|
58
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
59
|
+
result[key] = deep_merge(result[key], value)
|
|
60
|
+
else:
|
|
61
|
+
result[key] = copy.deepcopy(value)
|
|
62
|
+
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def clamp(value: float, min_val: float, max_val: float) -> float:
|
|
67
|
+
"""
|
|
68
|
+
Clamp a value to a range.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
value: Value to clamp
|
|
72
|
+
min_val: Minimum allowed value
|
|
73
|
+
max_val: Maximum allowed value
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Value clamped to [min_val, max_val]
|
|
77
|
+
"""
|
|
78
|
+
return max(min_val, min(max_val, value))
|