@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.

Files changed (37) hide show
  1. package/CLAUDE.md +5 -4
  2. package/README.md +16 -1
  3. package/README_EN.md +16 -1
  4. package/SKILL.md +113 -92
  5. package/docs/OPENCLAW_DEPLOYMENT.md +7 -4
  6. package/package.json +1 -1
  7. package/requirements.txt +2 -0
  8. package/scripts/__pycache__/audio_composer.cpython-313.pyc +0 -0
  9. package/scripts/__pycache__/audio_converter.cpython-313.pyc +0 -0
  10. package/scripts/__pycache__/audio_enhancer.cpython-313.pyc +0 -0
  11. package/scripts/__pycache__/state_manager.cpython-313.pyc +0 -0
  12. package/scripts/__pycache__/utils.cpython-313.pyc +0 -0
  13. package/scripts/audio_composer.py +389 -0
  14. package/scripts/audio_converter.py +245 -0
  15. package/scripts/cli.py +223 -0
  16. package/scripts/constants.py +56 -0
  17. package/scripts/feishu_voice.py +421 -0
  18. package/scripts/gamification.py +48 -34
  19. package/scripts/scorer.py +14 -32
  20. package/scripts/state_manager.py +131 -207
  21. package/scripts/tts/__pycache__/__init__.cpython-313.pyc +0 -0
  22. package/scripts/tts/__pycache__/base.cpython-313.pyc +0 -0
  23. package/scripts/tts/__pycache__/manager.cpython-313.pyc +0 -0
  24. package/scripts/tts/manager.py +14 -4
  25. package/scripts/tts/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  26. package/scripts/tts/providers/__pycache__/xunfei.cpython-313.pyc +0 -0
  27. package/scripts/tts/providers/xunfei.py +10 -1
  28. package/scripts/utils.py +78 -0
  29. package/templates/prompt_templates.md +47 -1235
  30. package/templates/prompts/display_guide.md +106 -0
  31. package/templates/prompts/initialization.md +213 -0
  32. package/templates/prompts/keypoint_generation.md +268 -0
  33. package/templates/prompts/output_rules.md +106 -0
  34. package/templates/prompts/quiz_generation.md +187 -0
  35. package/templates/prompts/responses.md +291 -0
  36. package/templates/prompts/shared_enums.md +97 -0
  37. package/templates/state_schema.json +1 -6
@@ -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 = "data",
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
- self.data_dir = Path(data_dir)
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
 
@@ -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
- ws.run_forever()
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)
@@ -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))